wprobe-util: always print regular output to stdout instead of stderr
[openwrt.git] / package / wprobe / src / user / wprobe-util.c
1 /*
2 * wprobe-test.c: Wireless probe user space test code
3 * Copyright (C) 2008-2009 Felix Fietkau <nbd@openwrt.org>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <sys/wait.h>
19 #include <arpa/inet.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <inttypes.h>
24 #include <errno.h>
25 #include <stdint.h>
26 #include <getopt.h>
27 #include <stdbool.h>
28 #include <unistd.h>
29 #include <netdb.h>
30 #include <fcntl.h>
31 #include <signal.h>
32
33 #include <linux/wprobe.h>
34 #include "wprobe.h"
35
36 static bool simple_mode = false;
37
38 static const char *
39 wprobe_dump_value(struct wprobe_attribute *attr)
40 {
41 static char buf[128];
42
43 #define HANDLE_TYPE(_type, _format) \
44 case WPROBE_VAL_##_type: \
45 snprintf(buf, sizeof(buf), _format, attr->val._type); \
46 break
47
48 switch(attr->type) {
49 HANDLE_TYPE(S8, "%d");
50 HANDLE_TYPE(S16, "%d");
51 HANDLE_TYPE(S32, "%d");
52 HANDLE_TYPE(S64, "%lld");
53 HANDLE_TYPE(U8, "%d");
54 HANDLE_TYPE(U16, "%d");
55 HANDLE_TYPE(U32, "%d");
56 HANDLE_TYPE(U64, "%lld");
57 case WPROBE_VAL_STRING:
58 /* FIXME: implement this */
59 default:
60 strncpy(buf, "<unknown>", sizeof(buf));
61 break;
62 }
63 if ((attr->flags & WPROBE_F_KEEPSTAT) &&
64 (attr->val.n > 0)) {
65 int len = strlen(buf);
66 if (simple_mode)
67 snprintf(buf + len, sizeof(buf) - len, ";%.02f;%.02f;%d;%lld;%lld", attr->val.avg, attr->val.stdev, attr->val.n, attr->val.s, attr->val.ss);
68 else
69 snprintf(buf + len, sizeof(buf) - len, " (avg: %.02f; stdev: %.02f, n=%d)", attr->val.avg, attr->val.stdev, attr->val.n);
70 }
71 #undef HANDLE_TYPE
72
73 return buf;
74 }
75
76
77 static void
78 wprobe_dump_data(struct wprobe_iface *dev)
79 {
80 struct wprobe_attribute *attr;
81 struct wprobe_link *link;
82 bool first = true;
83
84 if (!simple_mode)
85 fprintf(stdout, "\n");
86 wprobe_request_data(dev, NULL);
87 list_for_each_entry(attr, &dev->global_attr, list) {
88 if (simple_mode) {
89 if (first)
90 fprintf(stdout, "[global]\n");
91 fprintf(stdout, "%s=%s\n", attr->name, wprobe_dump_value(attr));
92 } else {
93 fprintf(stdout, (first ?
94 "Global: %s=%s\n" :
95 " %s=%s\n"),
96 attr->name,
97 wprobe_dump_value(attr)
98 );
99 }
100 first = false;
101 }
102
103 list_for_each_entry(link, &dev->links, list) {
104 first = true;
105 wprobe_request_data(dev, link->addr);
106 list_for_each_entry(attr, &dev->link_attr, list) {
107 if (first) {
108 fprintf(stdout,
109 (simple_mode ?
110 "[%02x:%02x:%02x:%02x:%02x:%02x]\n%s=%s\n" :
111 "%02x:%02x:%02x:%02x:%02x:%02x: %s=%s\n"),
112 link->addr[0], link->addr[1], link->addr[2],
113 link->addr[3], link->addr[4], link->addr[5],
114 attr->name,
115 wprobe_dump_value(attr));
116 first = false;
117 } else {
118 fprintf(stdout,
119 (simple_mode ? "%s=%s\n" :
120 " %s=%s\n"),
121 attr->name,
122 wprobe_dump_value(attr));
123 }
124 }
125 }
126 fflush(stdout);
127 }
128
129 static const char *attr_typestr[] = {
130 [0] = "Unknown",
131 [WPROBE_VAL_STRING] = "String",
132 [WPROBE_VAL_U8] = "Unsigned 8 bit",
133 [WPROBE_VAL_U16] = "Unsigned 16 bit",
134 [WPROBE_VAL_U32] = "Unsigned 32 bit",
135 [WPROBE_VAL_U64] = "Unsigned 64 bit",
136 [WPROBE_VAL_S8] = "Signed 8 bit",
137 [WPROBE_VAL_S16] = "Signed 16 bit",
138 [WPROBE_VAL_S32] = "Signed 32 bit",
139 [WPROBE_VAL_S64] = "Signed 64 bit",
140 };
141
142 static int usage(const char *prog)
143 {
144 fprintf(stderr,
145 #ifndef NO_LOCAL_ACCESS
146 "Usage: %s <interface>|<host>:<device>|-P [options]\n"
147 #else
148 "Usage: %s <host>:<device> [options]\n"
149 #endif
150 "\n"
151 "Options:\n"
152 " -c: Only apply configuration\n"
153 " -d: Delay between measurement dumps (in milliseconds, default: 1000)\n"
154 " -f: Dump contents of layer 2 filter counters during measurement\n"
155 " -F <file>: Apply layer 2 filters from <file>\n"
156 " -h: This help text\n"
157 " -i <interval>: Set measurement interval\n"
158 " -m: Run measurement loop\n"
159 " -p: Set the TCP port for server/client (default: 17990)\n"
160 #ifndef NO_LOCAL_ACCESS
161 " -P: Run in proxy mode (listen on network)\n"
162 #endif
163 "\n"
164 , prog);
165 exit(1);
166 }
167
168 static void show_attributes(struct wprobe_iface *dev)
169 {
170 struct wprobe_attribute *attr;
171 if (simple_mode)
172 return;
173 list_for_each_entry(attr, &dev->global_attr, list) {
174 fprintf(stdout, "Global attribute: '%s' (%s)\n",
175 attr->name, attr_typestr[attr->type]);
176 }
177 list_for_each_entry(attr, &dev->link_attr, list) {
178 fprintf(stdout, "Link attribute: '%s' (%s)\n",
179 attr->name, attr_typestr[attr->type]);
180 }
181 }
182
183 static void show_filter_simple(void *arg, const char *group, struct wprobe_filter_item *items, int n_items)
184 {
185 int i;
186
187 fprintf(stdout, "[filter:%s]\n", group);
188 for (i = 0; i < n_items; i++) {
189 fprintf(stdout, "%s=%lld;%lld\n",
190 items[i].name, items[i].tx, items[i].rx);
191 }
192 fflush(stdout);
193 }
194
195
196 static void show_filter(void *arg, const char *group, struct wprobe_filter_item *items, int n_items)
197 {
198 int i;
199 fprintf(stdout, "Filter group: '%s' (tx/rx)\n", group);
200 for (i = 0; i < n_items; i++) {
201 fprintf(stdout, " - %s (%lld/%lld)\n",
202 items[i].name, items[i].tx, items[i].rx);
203 }
204 }
205
206 static void loop_measurement(struct wprobe_iface *dev, bool print_filters, unsigned long delay)
207 {
208 while (1) {
209 usleep(delay * 1000);
210 wprobe_update_links(dev);
211 wprobe_dump_data(dev);
212 if (print_filters)
213 wprobe_dump_filters(dev, simple_mode ? show_filter_simple : show_filter, NULL);
214 }
215 }
216
217 static void set_filter(struct wprobe_iface *dev, const char *filename)
218 {
219 unsigned char *buf = NULL;
220 unsigned int buflen = 0;
221 unsigned int len = 0;
222 int fd;
223
224 /* clear filter */
225 if (filename[0] == 0) {
226 dev->filter_len = -1;
227 return;
228 }
229
230 fd = open(filename, O_RDONLY);
231 if (fd < 0) {
232 perror("open filter");
233 return;
234 }
235
236 do {
237 int rlen;
238
239 if (!buf) {
240 len = 0;
241 buflen = 1024;
242 buf = malloc(1024);
243 } else {
244 buflen *= 2;
245 buf = realloc(buf, buflen);
246 }
247 rlen = read(fd, buf + len, buflen - len);
248 if (rlen < 0)
249 break;
250
251 len += rlen;
252 } while (len == buflen);
253
254 dev->filter = buf;
255 dev->filter_len = len;
256 close(fd);
257 }
258
259 #ifndef NO_LOCAL_ACCESS
260
261 static void sigchld_handler(int s)
262 {
263 while (waitpid(-1, NULL, WNOHANG) > 0);
264 }
265
266 static int run_proxy(int port)
267 {
268 struct sockaddr_in sa;
269 struct sigaction sig;
270 int v = 1;
271 int s;
272
273 s = socket(AF_INET, SOCK_STREAM, 0);
274 if (s < 0) {
275 perror("socket");
276 return 1;
277 }
278
279 sig.sa_handler = sigchld_handler; // Signal Handler fuer Zombie Prozesse
280 sigemptyset(&sig.sa_mask);
281 sig.sa_flags = SA_RESTART;
282 sigaction(SIGCHLD, &sig, NULL);
283
284 memset(&sa, 0, sizeof(sa));
285 sa.sin_family = AF_INET;
286 sa.sin_addr.s_addr = htonl(INADDR_ANY);
287 sa.sin_port = htons(wprobe_port);
288
289 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v));
290 if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
291 perror("bind");
292 return 1;
293 }
294 if (listen(s, 10)) {
295 perror("listen");
296 return 1;
297 }
298 while(1) {
299 unsigned int addrlen = sizeof(struct sockaddr_in);
300 int ret, c;
301
302 c = accept(s, (struct sockaddr *)&sa, &addrlen);
303 if (c < 0) {
304 if (errno == EINTR)
305 continue;
306
307 perror("accept");
308 return 1;
309 }
310 if (fork() == 0) {
311 /* close server socket, stdin, stdout, stderr */
312 close(s);
313 close(0);
314 close(1);
315 close(2);
316
317 wprobe_server_init(c);
318 do {
319 ret = wprobe_server_handle(c);
320 } while (ret >= 0);
321 wprobe_server_done();
322 close(c);
323 exit(0);
324 }
325 close(c);
326 }
327
328 return 0;
329 }
330 #endif
331
332 int main(int argc, char **argv)
333 {
334 struct wprobe_iface *dev = NULL;
335 const char *ifname;
336 const char *prog = argv[0];
337 char *err = NULL;
338 enum {
339 CMD_NONE,
340 CMD_CONFIG,
341 CMD_MEASURE,
342 CMD_PROXY,
343 } cmd = CMD_NONE;
344 const char *filter = NULL;
345 bool print_filters = false;
346 unsigned long delay = 1000;
347 int interval = -1;
348 int ch;
349
350 if (argc < 2)
351 return usage(prog);
352
353 #ifndef NO_LOCAL_ACCESS
354 if (!strcmp(argv[1], "-P")) {
355 while ((ch = getopt(argc - 1, argv + 1, "p:")) != -1) {
356 switch(ch) {
357 case 'p':
358 /* set port */
359 wprobe_port = strtoul(optarg, NULL, 0);
360 break;
361 default:
362 return usage(prog);
363 }
364 }
365 return run_proxy(wprobe_port);
366 }
367 #endif
368
369 if (argv[1][0] == '-')
370 return usage(prog);
371
372 ifname = argv[1];
373 argv++;
374 argc--;
375
376 while ((ch = getopt(argc, argv, "cd:fF:hi:msp:")) != -1) {
377 switch(ch) {
378 case 'c':
379 cmd = CMD_CONFIG;
380 break;
381 case 'd':
382 delay = strtoul(optarg, NULL, 10);
383 break;
384 case 'm':
385 cmd = CMD_MEASURE;
386 break;
387 case 'i':
388 interval = strtoul(optarg, NULL, 10);
389 break;
390 case 'f':
391 print_filters = true;
392 break;
393 case 'F':
394 if (filter) {
395 fprintf(stderr, "Cannot set multiple filters\n");
396 return usage(prog);
397 }
398 filter = optarg;
399 break;
400 case 's':
401 simple_mode = true;
402 break;
403 case 'p':
404 /* set port */
405 wprobe_port = strtoul(optarg, NULL, 0);
406 break;
407 case 'h':
408 default:
409 usage(prog);
410 break;
411 }
412 }
413
414 dev = wprobe_get_auto(ifname, &err);
415 if (!dev || (list_empty(&dev->global_attr) &&
416 list_empty(&dev->link_attr))) {
417 if (err)
418 fprintf(stdout, "%s\n", err);
419 else
420 fprintf(stderr, "Interface '%s' not found\n", ifname);
421 return 1;
422 }
423
424 if (filter || interval >= 0) {
425 if (filter)
426 set_filter(dev, filter);
427 if (interval >= 0)
428 dev->interval = interval;
429
430 wprobe_apply_config(dev);
431 }
432
433 if (cmd != CMD_CONFIG)
434 show_attributes(dev);
435 if (cmd == CMD_MEASURE)
436 loop_measurement(dev, print_filters, delay);
437
438 wprobe_free_dev(dev);
439
440 return 0;
441 }
This page took 0.064278 seconds and 5 git commands to generate.