package/wprobe: {enhancement} make printing attributes optional. A delay interval...
[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                 "  -a:            Print attributes\n"
153                 "  -c:            Only apply configuration\n"
154                 "  -d:            Delay between measurement dumps (in milliseconds, default: 1000)\n"
155                 "                 A value of 0 (zero) prints once and exits; useful for scripts\n"
156                 "  -f:            Dump contents of layer 2 filter counters during measurement\n"
157                 "  -F <file>:     Apply layer 2 filters from <file>\n"
158                 "  -h:            This help text\n"
159                 "  -i <interval>: Set measurement interval\n"
160                 "  -m:            Run measurement loop\n"
161                 "  -p:            Set the TCP port for server/client (default: 17990)\n"
162 #ifndef NO_LOCAL_ACCESS
163                 "  -P:            Run in proxy mode (listen on network)\n"
164 #endif
165                 "\n"
166                 , prog);
167         exit(1);
168 }
169
170 static void show_attributes(struct wprobe_iface *dev)
171 {
172         struct wprobe_attribute *attr;
173         if (simple_mode)
174                 return;
175         list_for_each_entry(attr, &dev->global_attr, list) {
176                 fprintf(stdout, "Global attribute: '%s' (%s)\n",
177                         attr->name, attr_typestr[attr->type]);
178         }
179         list_for_each_entry(attr, &dev->link_attr, list) {
180                 fprintf(stdout, "Link attribute: '%s' (%s)\n",
181                         attr->name, attr_typestr[attr->type]);
182         }
183 }
184
185 static void show_filter_simple(void *arg, const char *group, struct wprobe_filter_item *items, int n_items)
186 {
187         int i;
188
189         fprintf(stdout, "[filter:%s]\n", group);
190         for (i = 0; i < n_items; i++) {
191                 fprintf(stdout, "%s=%lld;%lld\n",
192                         items[i].name, items[i].tx, items[i].rx);
193         }
194         fflush(stdout);
195 }
196
197
198 static void show_filter(void *arg, const char *group, struct wprobe_filter_item *items, int n_items)
199 {
200         int i;
201         fprintf(stdout, "Filter group: '%s' (tx/rx)\n", group);
202         for (i = 0; i < n_items; i++) {
203                 fprintf(stdout, " - %s (%lld/%lld)\n",
204                         items[i].name, items[i].tx, items[i].rx);
205         }
206 }
207
208 static void loop_measurement(struct wprobe_iface *dev, bool print_filters, unsigned long delay)
209 {
210         do {
211                 wprobe_update_links(dev);
212                 wprobe_dump_data(dev);
213                 if (print_filters)
214                         wprobe_dump_filters(dev, simple_mode ? show_filter_simple : show_filter, NULL);
215                 usleep(delay * 1000);
216         }
217         while (delay);
218 }
219
220 static void set_filter(struct wprobe_iface *dev, const char *filename)
221 {
222         unsigned char *buf = NULL;
223         unsigned int buflen = 0;
224         unsigned int len = 0;
225         int fd;
226
227         /* clear filter */
228         if (filename[0] == 0) {
229                 dev->filter_len = -1;
230                 return;
231         }
232
233         fd = open(filename, O_RDONLY);
234         if (fd < 0) {
235                 perror("open filter");
236                 return;
237         }
238
239         do {
240                 int rlen;
241
242                 if (!buf) {
243                         len = 0;
244                         buflen = 1024;
245                         buf = malloc(1024);
246                 } else {
247                         buflen *= 2;
248                         buf = realloc(buf, buflen);
249                 }
250                 rlen = read(fd, buf + len, buflen - len);
251                 if (rlen < 0)
252                         break;
253
254                 len += rlen;
255         } while (len == buflen);
256
257         dev->filter = buf;
258         dev->filter_len = len;
259         close(fd);
260 }
261
262 #ifndef NO_LOCAL_ACCESS
263
264 static void sigchld_handler(int s)
265 {
266         while (waitpid(-1, NULL, WNOHANG) > 0);
267 }
268
269 static int run_proxy(int port)
270 {
271         struct sockaddr_in sa;
272         struct sigaction sig;
273         int v = 1;
274         int s;
275
276         s = socket(AF_INET, SOCK_STREAM, 0);
277         if (s < 0) {
278                 perror("socket");
279                 return 1;
280         }
281
282         sig.sa_handler = sigchld_handler;  // Signal Handler fuer Zombie Prozesse
283         sigemptyset(&sig.sa_mask);
284         sig.sa_flags = SA_RESTART;
285         sigaction(SIGCHLD, &sig, NULL);
286
287         memset(&sa, 0, sizeof(sa));
288         sa.sin_family = AF_INET;
289         sa.sin_addr.s_addr = htonl(INADDR_ANY);
290         sa.sin_port = htons(wprobe_port);
291
292         setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v));
293         if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
294                 perror("bind");
295                 return 1;
296         }
297         if (listen(s, 10)) {
298                 perror("listen");
299                 return 1;
300         }
301         while(1) {
302                 unsigned int addrlen = sizeof(struct sockaddr_in);
303                 int ret, c;
304
305                 c = accept(s, (struct sockaddr *)&sa, &addrlen);
306                 if (c < 0) {
307                         if (errno == EINTR)
308                                 continue;
309
310                         perror("accept");
311                         return 1;
312                 }
313                 if (fork() == 0) {
314                         /* close server socket, stdin, stdout, stderr */
315                         close(s);
316                         close(0);
317                         close(1);
318                         close(2);
319
320                         wprobe_server_init(c);
321                         do {
322                                 ret = wprobe_server_handle(c);
323                         } while (ret >= 0);
324                         wprobe_server_done();
325                         close(c);
326                         exit(0);
327                 }
328                 close(c);
329         }
330
331         return 0;
332 }
333 #endif
334
335 int main(int argc, char **argv)
336 {
337         struct wprobe_iface *dev = NULL;
338         const char *ifname;
339         const char *prog = argv[0];
340         char *err = NULL;
341         enum {
342                 CMD_NONE,
343                 CMD_CONFIG,
344                 CMD_MEASURE,
345                 CMD_PROXY,
346         } cmd = CMD_NONE;
347         const char *filter = NULL;
348         bool print_attributes = false;
349         bool print_filters = false;
350         unsigned long delay = 1000;
351         int interval = -1;
352         int ch;
353
354         if (argc < 2)
355                 return usage(prog);
356
357 #ifndef NO_LOCAL_ACCESS
358         if (!strcmp(argv[1], "-P")) {
359                 while ((ch = getopt(argc - 1, argv + 1, "p:")) != -1) {
360                         switch(ch) {
361                         case 'p':
362                                 /* set port */
363                                 wprobe_port = strtoul(optarg, NULL, 0);
364                                 break;
365                         default:
366                                 return usage(prog);
367                         }
368                 }
369                 return run_proxy(wprobe_port);
370         }
371 #endif
372
373         if (argv[1][0] == '-')
374                 return usage(prog);
375
376         ifname = argv[1];
377         argv++;
378         argc--;
379
380         while ((ch = getopt(argc, argv, "acd:fF:hi:msp:")) != -1) {
381                 switch(ch) {
382                 case 'a':
383                         print_attributes = true;
384                         break;
385                 case 'c':
386                         cmd = CMD_CONFIG;
387                         break;
388                 case 'd':
389                         delay = strtoul(optarg, NULL, 10);
390                         break;
391                 case 'm':
392                         cmd = CMD_MEASURE;
393                         break;
394                 case 'i':
395                         interval = strtoul(optarg, NULL, 10);
396                         break;
397                 case 'f':
398                         print_filters = true;
399                         break;
400                 case 'F':
401                         if (filter) {
402                                 fprintf(stderr, "Cannot set multiple filters\n");
403                                 return usage(prog);
404                         }
405                         filter = optarg;
406                         break;
407                 case 's':
408                         simple_mode = true;
409                         break;
410                 case 'p':
411                         /* set port */
412                         wprobe_port = strtoul(optarg, NULL, 0);
413                         break;
414                 case 'h':
415                 default:
416                         usage(prog);
417                         break;
418                 }
419         }
420
421         dev = wprobe_get_auto(ifname, &err);
422         if (!dev || (list_empty(&dev->global_attr) &&
423                 list_empty(&dev->link_attr))) {
424                 if (err)
425                         fprintf(stdout, "%s\n", err);
426                 else
427                         fprintf(stderr, "Interface '%s' not found\n", ifname);
428                 return 1;
429         }
430
431         if (filter || interval >= 0) {
432                 if (filter)
433                         set_filter(dev, filter);
434                 if (interval >= 0)
435                         dev->interval = interval;
436
437                 wprobe_apply_config(dev);
438         }
439
440         if (cmd != CMD_CONFIG) {
441                 if (print_attributes)
442                         show_attributes(dev);
443         }
444         if (cmd == CMD_MEASURE)
445                 loop_measurement(dev, print_filters, delay);
446
447         wprobe_free_dev(dev);
448
449         return 0;
450 }