xref: /openbmc/linux/samples/connector/ucon.c (revision 78700c0a)
1 /*
2  * 	ucon.c
3  *
4  * Copyright (c) 2004+ Evgeniy Polyakov <zbr@ioremap.net>
5  *
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21 
22 #include <asm/types.h>
23 
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <sys/poll.h>
27 
28 #include <linux/netlink.h>
29 #include <linux/rtnetlink.h>
30 
31 #include <arpa/inet.h>
32 
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <time.h>
40 #include <getopt.h>
41 
42 #include <linux/connector.h>
43 
44 #define DEBUG
45 #define NETLINK_CONNECTOR 	11
46 
47 /* Hopefully your userspace connector.h matches this kernel */
48 #define CN_TEST_IDX		CN_NETLINK_USERS + 3
49 #define CN_TEST_VAL		0x456
50 
51 #ifdef DEBUG
52 #define ulog(f, a...) fprintf(stdout, f, ##a)
53 #else
54 #define ulog(f, a...) do {} while (0)
55 #endif
56 
57 static int need_exit;
58 static __u32 seq;
59 
60 static int netlink_send(int s, struct cn_msg *msg)
61 {
62 	struct nlmsghdr *nlh;
63 	unsigned int size;
64 	int err;
65 	char buf[128];
66 	struct cn_msg *m;
67 
68 	size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
69 
70 	nlh = (struct nlmsghdr *)buf;
71 	nlh->nlmsg_seq = seq++;
72 	nlh->nlmsg_pid = getpid();
73 	nlh->nlmsg_type = NLMSG_DONE;
74 	nlh->nlmsg_len = size;
75 	nlh->nlmsg_flags = 0;
76 
77 	m = NLMSG_DATA(nlh);
78 #if 0
79 	ulog("%s: [%08x.%08x] len=%u, seq=%u, ack=%u.\n",
80 	       __func__, msg->id.idx, msg->id.val, msg->len, msg->seq, msg->ack);
81 #endif
82 	memcpy(m, msg, sizeof(*m) + msg->len);
83 
84 	err = send(s, nlh, size, 0);
85 	if (err == -1)
86 		ulog("Failed to send: %s [%d].\n",
87 			strerror(errno), errno);
88 
89 	return err;
90 }
91 
92 static void usage(void)
93 {
94 	printf(
95 		"Usage: ucon [options] [output file]\n"
96 		"\n"
97 		"\t-h\tthis help screen\n"
98 		"\t-s\tsend buffers to the test module\n"
99 		"\n"
100 		"The default behavior of ucon is to subscribe to the test module\n"
101 		"and wait for state messages.  Any ones received are dumped to the\n"
102 		"specified output file (or stdout).  The test module is assumed to\n"
103 		"have an id of {%u.%u}\n"
104 		"\n"
105 		"If you get no output, then verify the cn_test module id matches\n"
106 		"the expected id above.\n"
107 		, CN_TEST_IDX, CN_TEST_VAL
108 	);
109 }
110 
111 int main(int argc, char *argv[])
112 {
113 	int s;
114 	char buf[1024];
115 	int len;
116 	struct nlmsghdr *reply;
117 	struct sockaddr_nl l_local;
118 	struct cn_msg *data;
119 	FILE *out;
120 	time_t tm;
121 	struct pollfd pfd;
122 	bool send_msgs = false;
123 
124 	while ((s = getopt(argc, argv, "hs")) != -1) {
125 		switch (s) {
126 		case 's':
127 			send_msgs = true;
128 			break;
129 
130 		case 'h':
131 			usage();
132 			return 0;
133 
134 		default:
135 			/* getopt() outputs an error for us */
136 			usage();
137 			return 1;
138 		}
139 	}
140 
141 	if (argc != optind) {
142 		out = fopen(argv[optind], "a+");
143 		if (!out) {
144 			ulog("Unable to open %s for writing: %s\n",
145 				argv[1], strerror(errno));
146 			out = stdout;
147 		}
148 	} else
149 		out = stdout;
150 
151 	memset(buf, 0, sizeof(buf));
152 
153 	s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
154 	if (s == -1) {
155 		perror("socket");
156 		return -1;
157 	}
158 
159 	l_local.nl_family = AF_NETLINK;
160 	l_local.nl_groups = -1; /* bitmask of requested groups */
161 	l_local.nl_pid = 0;
162 
163 	ulog("subscribing to %u.%u\n", CN_TEST_IDX, CN_TEST_VAL);
164 
165 	if (bind(s, (struct sockaddr *)&l_local, sizeof(struct sockaddr_nl)) == -1) {
166 		perror("bind");
167 		close(s);
168 		return -1;
169 	}
170 
171 #if 0
172 	{
173 		int on = 0x57; /* Additional group number */
174 		setsockopt(s, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &on, sizeof(on));
175 	}
176 #endif
177 	if (send_msgs) {
178 		int i, j;
179 
180 		memset(buf, 0, sizeof(buf));
181 
182 		data = (struct cn_msg *)buf;
183 
184 		data->id.idx = CN_TEST_IDX;
185 		data->id.val = CN_TEST_VAL;
186 		data->seq = seq++;
187 		data->ack = 0;
188 		data->len = 0;
189 
190 		for (j=0; j<10; ++j) {
191 			for (i=0; i<1000; ++i) {
192 				len = netlink_send(s, data);
193 			}
194 
195 			ulog("%d messages have been sent to %08x.%08x.\n", i, data->id.idx, data->id.val);
196 		}
197 
198 		return 0;
199 	}
200 
201 
202 	pfd.fd = s;
203 
204 	while (!need_exit) {
205 		pfd.events = POLLIN;
206 		pfd.revents = 0;
207 		switch (poll(&pfd, 1, -1)) {
208 			case 0:
209 				need_exit = 1;
210 				break;
211 			case -1:
212 				if (errno != EINTR) {
213 					need_exit = 1;
214 					break;
215 				}
216 				continue;
217 		}
218 		if (need_exit)
219 			break;
220 
221 		memset(buf, 0, sizeof(buf));
222 		len = recv(s, buf, sizeof(buf), 0);
223 		if (len == -1) {
224 			perror("recv buf");
225 			close(s);
226 			return -1;
227 		}
228 		reply = (struct nlmsghdr *)buf;
229 
230 		switch (reply->nlmsg_type) {
231 		case NLMSG_ERROR:
232 			fprintf(out, "Error message received.\n");
233 			fflush(out);
234 			break;
235 		case NLMSG_DONE:
236 			data = (struct cn_msg *)NLMSG_DATA(reply);
237 
238 			time(&tm);
239 			fprintf(out, "%.24s : [%x.%x] [%08u.%08u].\n",
240 				ctime(&tm), data->id.idx, data->id.val, data->seq, data->ack);
241 			fflush(out);
242 			break;
243 		default:
244 			break;
245 		}
246 	}
247 
248 	close(s);
249 	return 0;
250 }
251