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