]> git.proxmox.com Git - mirror_iproute2.git/blob - misc/ifstat.c
libnetlink: Convert GETNEIGH dumps to use rtnl_neighdump_req
[mirror_iproute2.git] / misc / ifstat.c
1 /*
2 * ifstat.c handy utility to read net interface statistics
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <fcntl.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <time.h>
19 #include <sys/time.h>
20 #include <fnmatch.h>
21 #include <sys/file.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 #include <sys/poll.h>
25 #include <sys/wait.h>
26 #include <sys/stat.h>
27 #include <signal.h>
28 #include <math.h>
29 #include <getopt.h>
30
31 #include <linux/if.h>
32 #include <linux/if_link.h>
33
34 #include "libnetlink.h"
35 #include "json_writer.h"
36 #include "SNAPSHOT.h"
37 #include "utils.h"
38
39 int dump_zeros;
40 int reset_history;
41 int ignore_history;
42 int no_output;
43 int json_output;
44 int no_update;
45 int scan_interval;
46 int time_constant;
47 int show_errors;
48 double W;
49 char **patterns;
50 int npatterns;
51 bool is_extended;
52 int filter_type;
53 int sub_type;
54
55 char info_source[128];
56 int source_mismatch;
57
58 #define MAXS (sizeof(struct rtnl_link_stats)/sizeof(__u32))
59 #define NO_SUB_TYPE 0xffff
60
61 struct ifstat_ent {
62 struct ifstat_ent *next;
63 char *name;
64 int ifindex;
65 __u64 val[MAXS];
66 double rate[MAXS];
67 __u32 ival[MAXS];
68 };
69
70 static const char *stats[MAXS] = {
71 "rx_packets",
72 "tx_packets",
73 "rx_bytes",
74 "tx_bytes",
75 "rx_errors",
76 "tx_errors",
77 "rx_dropped",
78 "tx_dropped",
79 "multicast",
80 "collisions",
81 "rx_length_errors",
82 "rx_over_errors",
83 "rx_crc_errors",
84 "rx_frame_errors",
85 "rx_fifo_errors",
86 "rx_missed_errors",
87 "tx_aborted_errors",
88 "tx_carrier_errors",
89 "tx_fifo_errors",
90 "tx_heartbeat_errors",
91 "tx_window_errors",
92 "rx_compressed",
93 "tx_compressed"
94 };
95
96 struct ifstat_ent *kern_db;
97 struct ifstat_ent *hist_db;
98
99 static int match(const char *id)
100 {
101 int i;
102
103 if (npatterns == 0)
104 return 1;
105
106 for (i = 0; i < npatterns; i++) {
107 if (!fnmatch(patterns[i], id, 0))
108 return 1;
109 }
110 return 0;
111 }
112
113 static int get_nlmsg_extended(const struct sockaddr_nl *who,
114 struct nlmsghdr *m, void *arg)
115 {
116 struct if_stats_msg *ifsm = NLMSG_DATA(m);
117 struct rtattr *tb[IFLA_STATS_MAX+1];
118 int len = m->nlmsg_len;
119 struct ifstat_ent *n;
120
121 if (m->nlmsg_type != RTM_NEWSTATS)
122 return 0;
123
124 len -= NLMSG_LENGTH(sizeof(*ifsm));
125 if (len < 0)
126 return -1;
127
128 parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len);
129 if (tb[filter_type] == NULL)
130 return 0;
131
132 n = malloc(sizeof(*n));
133 if (!n)
134 abort();
135
136 n->ifindex = ifsm->ifindex;
137 n->name = strdup(ll_index_to_name(ifsm->ifindex));
138
139 if (sub_type == NO_SUB_TYPE) {
140 memcpy(&n->val, RTA_DATA(tb[filter_type]), sizeof(n->val));
141 } else {
142 struct rtattr *attr;
143
144 attr = parse_rtattr_one_nested(sub_type, tb[filter_type]);
145 if (attr == NULL) {
146 free(n);
147 return 0;
148 }
149 memcpy(&n->val, RTA_DATA(attr), sizeof(n->val));
150 }
151 memset(&n->rate, 0, sizeof(n->rate));
152 n->next = kern_db;
153 kern_db = n;
154 return 0;
155 }
156
157 static int get_nlmsg(const struct sockaddr_nl *who,
158 struct nlmsghdr *m, void *arg)
159 {
160 struct ifinfomsg *ifi = NLMSG_DATA(m);
161 struct rtattr *tb[IFLA_MAX+1];
162 int len = m->nlmsg_len;
163 struct ifstat_ent *n;
164 int i;
165
166 if (m->nlmsg_type != RTM_NEWLINK)
167 return 0;
168
169 len -= NLMSG_LENGTH(sizeof(*ifi));
170 if (len < 0)
171 return -1;
172
173 if (!(ifi->ifi_flags&IFF_UP))
174 return 0;
175
176 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
177 if (tb[IFLA_IFNAME] == NULL || tb[IFLA_STATS] == NULL)
178 return 0;
179
180 n = malloc(sizeof(*n));
181 if (!n)
182 abort();
183 n->ifindex = ifi->ifi_index;
184 n->name = strdup(RTA_DATA(tb[IFLA_IFNAME]));
185 memcpy(&n->ival, RTA_DATA(tb[IFLA_STATS]), sizeof(n->ival));
186 memset(&n->rate, 0, sizeof(n->rate));
187 for (i = 0; i < MAXS; i++)
188 n->val[i] = n->ival[i];
189 n->next = kern_db;
190 kern_db = n;
191 return 0;
192 }
193
194 static void load_info(void)
195 {
196 struct ifstat_ent *db, *n;
197 struct rtnl_handle rth;
198 __u32 filter_mask;
199
200 if (rtnl_open(&rth, 0) < 0)
201 exit(1);
202
203 if (is_extended) {
204 ll_init_map(&rth);
205 filter_mask = IFLA_STATS_FILTER_BIT(filter_type);
206 if (rtnl_wilddump_stats_req_filter(&rth, AF_UNSPEC, RTM_GETSTATS,
207 filter_mask) < 0) {
208 perror("Cannot send dump request");
209 exit(1);
210 }
211
212 if (rtnl_dump_filter(&rth, get_nlmsg_extended, NULL) < 0) {
213 fprintf(stderr, "Dump terminated\n");
214 exit(1);
215 }
216 } else {
217 if (rtnl_wilddump_request(&rth, AF_INET, RTM_GETLINK) < 0) {
218 perror("Cannot send dump request");
219 exit(1);
220 }
221
222 if (rtnl_dump_filter(&rth, get_nlmsg, NULL) < 0) {
223 fprintf(stderr, "Dump terminated\n");
224 exit(1);
225 }
226 }
227
228 rtnl_close(&rth);
229
230 db = kern_db;
231 kern_db = NULL;
232
233 while (db) {
234 n = db;
235 db = db->next;
236 n->next = kern_db;
237 kern_db = n;
238 }
239 }
240
241 static void load_raw_table(FILE *fp)
242 {
243 char buf[4096];
244 struct ifstat_ent *db = NULL;
245 struct ifstat_ent *n;
246
247 while (fgets(buf, sizeof(buf), fp) != NULL) {
248 char *p;
249 char *next;
250 int i;
251
252 if (buf[0] == '#') {
253 buf[strlen(buf)-1] = 0;
254 if (info_source[0] && strcmp(info_source, buf+1))
255 source_mismatch = 1;
256 strncpy(info_source, buf+1, sizeof(info_source)-1);
257 continue;
258 }
259 if ((n = malloc(sizeof(*n))) == NULL)
260 abort();
261
262 if (!(p = strchr(buf, ' ')))
263 abort();
264 *p++ = 0;
265
266 if (sscanf(buf, "%d", &n->ifindex) != 1)
267 abort();
268 if (!(next = strchr(p, ' ')))
269 abort();
270 *next++ = 0;
271
272 n->name = strdup(p);
273 p = next;
274
275 for (i = 0; i < MAXS; i++) {
276 unsigned int rate;
277
278 if (!(next = strchr(p, ' ')))
279 abort();
280 *next++ = 0;
281 if (sscanf(p, "%llu", n->val+i) != 1)
282 abort();
283 n->ival[i] = (__u32)n->val[i];
284 p = next;
285 if (!(next = strchr(p, ' ')))
286 abort();
287 *next++ = 0;
288 if (sscanf(p, "%u", &rate) != 1)
289 abort();
290 n->rate[i] = rate;
291 p = next;
292 }
293 n->next = db;
294 db = n;
295 }
296
297 while (db) {
298 n = db;
299 db = db->next;
300 n->next = kern_db;
301 kern_db = n;
302 }
303 }
304
305 static void dump_raw_db(FILE *fp, int to_hist)
306 {
307 json_writer_t *jw = json_output ? jsonw_new(fp) : NULL;
308 struct ifstat_ent *n, *h;
309
310 h = hist_db;
311 if (jw) {
312 jsonw_start_object(jw);
313 jsonw_pretty(jw, pretty);
314 jsonw_name(jw, info_source);
315 jsonw_start_object(jw);
316 } else
317 fprintf(fp, "#%s\n", info_source);
318
319 for (n = kern_db; n; n = n->next) {
320 int i;
321 unsigned long long *vals = n->val;
322 double *rates = n->rate;
323
324 if (!match(n->name)) {
325 struct ifstat_ent *h1;
326
327 if (!to_hist)
328 continue;
329 for (h1 = h; h1; h1 = h1->next) {
330 if (h1->ifindex == n->ifindex) {
331 vals = h1->val;
332 rates = h1->rate;
333 h = h1->next;
334 break;
335 }
336 }
337 }
338
339 if (jw) {
340 jsonw_name(jw, n->name);
341 jsonw_start_object(jw);
342
343 for (i = 0; i < MAXS && stats[i]; i++)
344 jsonw_uint_field(jw, stats[i], vals[i]);
345 jsonw_end_object(jw);
346 } else {
347 fprintf(fp, "%d %s ", n->ifindex, n->name);
348 for (i = 0; i < MAXS; i++)
349 fprintf(fp, "%llu %u ", vals[i],
350 (unsigned int)rates[i]);
351 fprintf(fp, "\n");
352 }
353 }
354 if (jw) {
355 jsonw_end_object(jw);
356
357 jsonw_end_object(jw);
358 jsonw_destroy(&jw);
359 }
360 }
361
362 /* use communication definitions of meg/kilo etc */
363 static const unsigned long long giga = 1000000000ull;
364 static const unsigned long long mega = 1000000;
365 static const unsigned long long kilo = 1000;
366
367 static void format_rate(FILE *fp, const unsigned long long *vals,
368 const double *rates, int i)
369 {
370 char temp[64];
371
372 if (vals[i] > giga)
373 fprintf(fp, "%7lluM ", vals[i]/mega);
374 else if (vals[i] > mega)
375 fprintf(fp, "%7lluK ", vals[i]/kilo);
376 else
377 fprintf(fp, "%8llu ", vals[i]);
378
379 if (rates[i] > mega) {
380 sprintf(temp, "%uM", (unsigned int)(rates[i]/mega));
381 fprintf(fp, "%-6s ", temp);
382 } else if (rates[i] > kilo) {
383 sprintf(temp, "%uK", (unsigned int)(rates[i]/kilo));
384 fprintf(fp, "%-6s ", temp);
385 } else
386 fprintf(fp, "%-6u ", (unsigned int)rates[i]);
387 }
388
389 static void format_pair(FILE *fp, const unsigned long long *vals, int i, int k)
390 {
391 char temp[64];
392
393 if (vals[i] > giga)
394 fprintf(fp, "%7lluM ", vals[i]/mega);
395 else if (vals[i] > mega)
396 fprintf(fp, "%7lluK ", vals[i]/kilo);
397 else
398 fprintf(fp, "%8llu ", vals[i]);
399
400 if (vals[k] > giga) {
401 sprintf(temp, "%uM", (unsigned int)(vals[k]/mega));
402 fprintf(fp, "%-6s ", temp);
403 } else if (vals[k] > mega) {
404 sprintf(temp, "%uK", (unsigned int)(vals[k]/kilo));
405 fprintf(fp, "%-6s ", temp);
406 } else
407 fprintf(fp, "%-6u ", (unsigned int)vals[k]);
408 }
409
410 static void print_head(FILE *fp)
411 {
412 fprintf(fp, "#%s\n", info_source);
413 fprintf(fp, "%-15s ", "Interface");
414
415 fprintf(fp, "%8s/%-6s ", "RX Pkts", "Rate");
416 fprintf(fp, "%8s/%-6s ", "TX Pkts", "Rate");
417 fprintf(fp, "%8s/%-6s ", "RX Data", "Rate");
418 fprintf(fp, "%8s/%-6s\n", "TX Data", "Rate");
419
420 if (!show_errors) {
421 fprintf(fp, "%-15s ", "");
422 fprintf(fp, "%8s/%-6s ", "RX Errs", "Drop");
423 fprintf(fp, "%8s/%-6s ", "TX Errs", "Drop");
424 fprintf(fp, "%8s/%-6s ", "RX Over", "Rate");
425 fprintf(fp, "%8s/%-6s\n", "TX Coll", "Rate");
426 } else {
427 fprintf(fp, "%-15s ", "");
428 fprintf(fp, "%8s/%-6s ", "RX Errs", "Rate");
429 fprintf(fp, "%8s/%-6s ", "RX Drop", "Rate");
430 fprintf(fp, "%8s/%-6s ", "RX Over", "Rate");
431 fprintf(fp, "%8s/%-6s\n", "RX Leng", "Rate");
432
433 fprintf(fp, "%-15s ", "");
434 fprintf(fp, "%8s/%-6s ", "RX Crc", "Rate");
435 fprintf(fp, "%8s/%-6s ", "RX Frm", "Rate");
436 fprintf(fp, "%8s/%-6s ", "RX Fifo", "Rate");
437 fprintf(fp, "%8s/%-6s\n", "RX Miss", "Rate");
438
439 fprintf(fp, "%-15s ", "");
440 fprintf(fp, "%8s/%-6s ", "TX Errs", "Rate");
441 fprintf(fp, "%8s/%-6s ", "TX Drop", "Rate");
442 fprintf(fp, "%8s/%-6s ", "TX Coll", "Rate");
443 fprintf(fp, "%8s/%-6s\n", "TX Carr", "Rate");
444
445 fprintf(fp, "%-15s ", "");
446 fprintf(fp, "%8s/%-6s ", "TX Abrt", "Rate");
447 fprintf(fp, "%8s/%-6s ", "TX Fifo", "Rate");
448 fprintf(fp, "%8s/%-6s ", "TX Hear", "Rate");
449 fprintf(fp, "%8s/%-6s\n", "TX Wind", "Rate");
450 }
451 }
452
453 static void print_one_json(json_writer_t *jw, const struct ifstat_ent *n,
454 const unsigned long long *vals)
455 {
456 int i, m = show_errors ? 20 : 10;
457
458 jsonw_name(jw, n->name);
459 jsonw_start_object(jw);
460
461 for (i = 0; i < m && stats[i]; i++)
462 jsonw_uint_field(jw, stats[i], vals[i]);
463
464 jsonw_end_object(jw);
465 }
466
467 static void print_one_if(FILE *fp, const struct ifstat_ent *n,
468 const unsigned long long *vals)
469 {
470 int i;
471
472 fprintf(fp, "%-15s ", n->name);
473 for (i = 0; i < 4; i++)
474 format_rate(fp, vals, n->rate, i);
475 fprintf(fp, "\n");
476
477 if (!show_errors) {
478 fprintf(fp, "%-15s ", "");
479 format_pair(fp, vals, 4, 6);
480 format_pair(fp, vals, 5, 7);
481 format_rate(fp, vals, n->rate, 11);
482 format_rate(fp, vals, n->rate, 9);
483 fprintf(fp, "\n");
484 } else {
485 fprintf(fp, "%-15s ", "");
486 format_rate(fp, vals, n->rate, 4);
487 format_rate(fp, vals, n->rate, 6);
488 format_rate(fp, vals, n->rate, 11);
489 format_rate(fp, vals, n->rate, 10);
490 fprintf(fp, "\n");
491
492 fprintf(fp, "%-15s ", "");
493 format_rate(fp, vals, n->rate, 12);
494 format_rate(fp, vals, n->rate, 13);
495 format_rate(fp, vals, n->rate, 14);
496 format_rate(fp, vals, n->rate, 15);
497 fprintf(fp, "\n");
498
499 fprintf(fp, "%-15s ", "");
500 format_rate(fp, vals, n->rate, 5);
501 format_rate(fp, vals, n->rate, 7);
502 format_rate(fp, vals, n->rate, 9);
503 format_rate(fp, vals, n->rate, 17);
504 fprintf(fp, "\n");
505
506 fprintf(fp, "%-15s ", "");
507 format_rate(fp, vals, n->rate, 16);
508 format_rate(fp, vals, n->rate, 18);
509 format_rate(fp, vals, n->rate, 19);
510 format_rate(fp, vals, n->rate, 20);
511 fprintf(fp, "\n");
512 }
513 }
514
515 static void dump_kern_db(FILE *fp)
516 {
517 json_writer_t *jw = json_output ? jsonw_new(fp) : NULL;
518 struct ifstat_ent *n;
519
520 if (jw) {
521 jsonw_start_object(jw);
522 jsonw_pretty(jw, pretty);
523 jsonw_name(jw, info_source);
524 jsonw_start_object(jw);
525 } else
526 print_head(fp);
527
528 for (n = kern_db; n; n = n->next) {
529 if (!match(n->name))
530 continue;
531
532 if (jw)
533 print_one_json(jw, n, n->val);
534 else
535 print_one_if(fp, n, n->val);
536 }
537 if (jw) {
538 jsonw_end_object(jw);
539
540 jsonw_end_object(jw);
541 jsonw_destroy(&jw);
542 }
543 }
544
545 static void dump_incr_db(FILE *fp)
546 {
547 struct ifstat_ent *n, *h;
548 json_writer_t *jw = json_output ? jsonw_new(fp) : NULL;
549
550 h = hist_db;
551 if (jw) {
552 jsonw_start_object(jw);
553 jsonw_pretty(jw, pretty);
554 jsonw_name(jw, info_source);
555 jsonw_start_object(jw);
556 } else
557 print_head(fp);
558
559 for (n = kern_db; n; n = n->next) {
560 int i;
561 unsigned long long vals[MAXS];
562 struct ifstat_ent *h1;
563
564 memcpy(vals, n->val, sizeof(vals));
565
566 for (h1 = h; h1; h1 = h1->next) {
567 if (h1->ifindex == n->ifindex) {
568 for (i = 0; i < MAXS; i++)
569 vals[i] -= h1->val[i];
570 h = h1->next;
571 break;
572 }
573 }
574 if (!match(n->name))
575 continue;
576
577 if (jw)
578 print_one_json(jw, n, n->val);
579 else
580 print_one_if(fp, n, vals);
581 }
582
583 if (jw) {
584 jsonw_end_object(jw);
585
586 jsonw_end_object(jw);
587 jsonw_destroy(&jw);
588 }
589 }
590
591 static int children;
592
593 static void sigchild(int signo)
594 {
595 }
596
597 static void update_db(int interval)
598 {
599 struct ifstat_ent *n, *h;
600
601 n = kern_db;
602 kern_db = NULL;
603
604 load_info();
605
606 h = kern_db;
607 kern_db = n;
608
609 for (n = kern_db; n; n = n->next) {
610 struct ifstat_ent *h1;
611
612 for (h1 = h; h1; h1 = h1->next) {
613 if (h1->ifindex == n->ifindex) {
614 int i;
615
616 for (i = 0; i < MAXS; i++) {
617 if ((long)(h1->ival[i] - n->ival[i]) < 0) {
618 memset(n->ival, 0, sizeof(n->ival));
619 break;
620 }
621 }
622 for (i = 0; i < MAXS; i++) {
623 double sample;
624 __u64 incr;
625
626 if (is_extended) {
627 incr = h1->val[i] - n->val[i];
628 n->val[i] = h1->val[i];
629 } else {
630 incr = (__u32) (h1->ival[i] - n->ival[i]);
631 n->val[i] += incr;
632 n->ival[i] = h1->ival[i];
633 }
634
635 sample = (double)(incr*1000)/interval;
636 if (interval >= scan_interval) {
637 n->rate[i] += W*(sample-n->rate[i]);
638 } else if (interval >= 1000) {
639 if (interval >= time_constant) {
640 n->rate[i] = sample;
641 } else {
642 double w = W*(double)interval/scan_interval;
643
644 n->rate[i] += w*(sample-n->rate[i]);
645 }
646 }
647 }
648
649 while (h != h1) {
650 struct ifstat_ent *tmp = h;
651
652 h = h->next;
653 free(tmp->name);
654 free(tmp);
655 };
656 h = h1->next;
657 free(h1->name);
658 free(h1);
659 break;
660 }
661 }
662 }
663 }
664
665 #define T_DIFF(a, b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000)
666
667
668 static void server_loop(int fd)
669 {
670 struct timeval snaptime = { 0 };
671 struct pollfd p;
672
673 p.fd = fd;
674 p.events = p.revents = POLLIN;
675
676 sprintf(info_source, "%d.%lu sampling_interval=%d time_const=%d",
677 getpid(), (unsigned long)random(), scan_interval/1000, time_constant/1000);
678
679 load_info();
680
681 for (;;) {
682 int status;
683 time_t tdiff;
684 struct timeval now;
685
686 gettimeofday(&now, NULL);
687 tdiff = T_DIFF(now, snaptime);
688 if (tdiff >= scan_interval) {
689 update_db(tdiff);
690 snaptime = now;
691 tdiff = 0;
692 }
693
694 if (poll(&p, 1, scan_interval - tdiff) > 0
695 && (p.revents&POLLIN)) {
696 int clnt = accept(fd, NULL, NULL);
697
698 if (clnt >= 0) {
699 pid_t pid;
700
701 if (children >= 5) {
702 close(clnt);
703 } else if ((pid = fork()) != 0) {
704 if (pid > 0)
705 children++;
706 close(clnt);
707 } else {
708 FILE *fp = fdopen(clnt, "w");
709
710 if (fp)
711 dump_raw_db(fp, 0);
712 exit(0);
713 }
714 }
715 }
716 while (children && waitpid(-1, &status, WNOHANG) > 0)
717 children--;
718 }
719 }
720
721 static int verify_forging(int fd)
722 {
723 struct ucred cred;
724 socklen_t olen = sizeof(cred);
725
726 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void *)&cred, &olen) ||
727 olen < sizeof(cred))
728 return -1;
729 if (cred.uid == getuid() || cred.uid == 0)
730 return 0;
731 return -1;
732 }
733
734 static void xstat_usage(void)
735 {
736 fprintf(stderr,
737 "Usage: ifstat supported xstats:\n"
738 " cpu_hits Counts only packets that went via the CPU.\n");
739 }
740
741 struct extended_stats_options_t {
742 char *name;
743 int id;
744 int sub_type;
745 };
746
747 /* Note: if one xstat name is subset of another, it should be before it in this
748 * list.
749 * Name length must be under 64 chars.
750 */
751 static const struct extended_stats_options_t extended_stats_options[] = {
752 {"cpu_hits", IFLA_STATS_LINK_OFFLOAD_XSTATS, IFLA_OFFLOAD_XSTATS_CPU_HIT},
753 };
754
755 static const char *get_filter_type(const char *name)
756 {
757 int name_len;
758 int i;
759
760 name_len = strlen(name);
761 for (i = 0; i < ARRAY_SIZE(extended_stats_options); i++) {
762 const struct extended_stats_options_t *xstat;
763
764 xstat = &extended_stats_options[i];
765 if (strncmp(name, xstat->name, name_len) == 0) {
766 filter_type = xstat->id;
767 sub_type = xstat->sub_type;
768 return xstat->name;
769 }
770 }
771
772 fprintf(stderr, "invalid ifstat extension %s\n", name);
773 xstat_usage();
774 return NULL;
775 }
776
777 static void usage(void) __attribute__((noreturn));
778
779 static void usage(void)
780 {
781 fprintf(stderr,
782 "Usage: ifstat [OPTION] [ PATTERN [ PATTERN ] ]\n"
783 " -h, --help this message\n"
784 " -a, --ignore ignore history\n"
785 " -d, --scan=SECS sample every statistics every SECS\n"
786 " -e, --errors show errors\n"
787 " -j, --json format output in JSON\n"
788 " -n, --nooutput do history only\n"
789 " -p, --pretty pretty print\n"
790 " -r, --reset reset history\n"
791 " -s, --noupdate don't update history\n"
792 " -t, --interval=SECS report average over the last SECS\n"
793 " -V, --version output version information\n"
794 " -z, --zeros show entries with zero activity\n"
795 " -x, --extended=TYPE show extended stats of TYPE\n");
796
797 exit(-1);
798 }
799
800 static const struct option longopts[] = {
801 { "help", 0, 0, 'h' },
802 { "ignore", 0, 0, 'a' },
803 { "scan", 1, 0, 'd'},
804 { "errors", 0, 0, 'e' },
805 { "nooutput", 0, 0, 'n' },
806 { "json", 0, 0, 'j' },
807 { "reset", 0, 0, 'r' },
808 { "pretty", 0, 0, 'p' },
809 { "noupdate", 0, 0, 's' },
810 { "interval", 1, 0, 't' },
811 { "version", 0, 0, 'V' },
812 { "zeros", 0, 0, 'z' },
813 { "extended", 1, 0, 'x'},
814 { 0 }
815 };
816
817 int main(int argc, char *argv[])
818 {
819 char hist_name[128];
820 struct sockaddr_un sun;
821 FILE *hist_fp = NULL;
822 const char *stats_type = NULL;
823 int ch;
824 int fd;
825
826 is_extended = false;
827 while ((ch = getopt_long(argc, argv, "hjpvVzrnasd:t:ex:",
828 longopts, NULL)) != EOF) {
829 switch (ch) {
830 case 'z':
831 dump_zeros = 1;
832 break;
833 case 'r':
834 reset_history = 1;
835 break;
836 case 'a':
837 ignore_history = 1;
838 break;
839 case 's':
840 no_update = 1;
841 break;
842 case 'n':
843 no_output = 1;
844 break;
845 case 'e':
846 show_errors = 1;
847 break;
848 case 'j':
849 json_output = 1;
850 break;
851 case 'p':
852 pretty = 1;
853 break;
854 case 'd':
855 scan_interval = atoi(optarg) * 1000;
856 if (scan_interval <= 0) {
857 fprintf(stderr, "ifstat: invalid scan interval\n");
858 exit(-1);
859 }
860 break;
861 case 't':
862 time_constant = atoi(optarg);
863 if (time_constant <= 0) {
864 fprintf(stderr, "ifstat: invalid time constant divisor\n");
865 exit(-1);
866 }
867 break;
868 case 'x':
869 stats_type = optarg;
870 is_extended = true;
871 break;
872 case 'v':
873 case 'V':
874 printf("ifstat utility, iproute2-ss%s\n", SNAPSHOT);
875 exit(0);
876 case 'h':
877 case '?':
878 default:
879 usage();
880 }
881 }
882
883 argc -= optind;
884 argv += optind;
885
886 if (stats_type) {
887 stats_type = get_filter_type(stats_type);
888 if (!stats_type)
889 exit(-1);
890 }
891
892 sun.sun_family = AF_UNIX;
893 sun.sun_path[0] = 0;
894 sprintf(sun.sun_path+1, "ifstat%d", getuid());
895
896 if (scan_interval > 0) {
897 if (time_constant == 0)
898 time_constant = 60;
899 time_constant *= 1000;
900 W = 1 - 1/exp(log(10)*(double)scan_interval/time_constant);
901 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
902 perror("ifstat: socket");
903 exit(-1);
904 }
905 if (bind(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) < 0) {
906 perror("ifstat: bind");
907 exit(-1);
908 }
909 if (listen(fd, 5) < 0) {
910 perror("ifstat: listen");
911 exit(-1);
912 }
913 if (daemon(0, 0)) {
914 perror("ifstat: daemon");
915 exit(-1);
916 }
917 signal(SIGPIPE, SIG_IGN);
918 signal(SIGCHLD, sigchild);
919 server_loop(fd);
920 exit(0);
921 }
922
923 patterns = argv;
924 npatterns = argc;
925
926 if (getenv("IFSTAT_HISTORY"))
927 snprintf(hist_name, sizeof(hist_name),
928 "%s", getenv("IFSTAT_HISTORY"));
929 else
930 if (!stats_type)
931 snprintf(hist_name, sizeof(hist_name),
932 "%s/.ifstat.u%d", P_tmpdir, getuid());
933 else
934 snprintf(hist_name, sizeof(hist_name),
935 "%s/.%s_ifstat.u%d", P_tmpdir, stats_type,
936 getuid());
937
938 if (reset_history)
939 unlink(hist_name);
940
941 if (!ignore_history || !no_update) {
942 struct stat stb;
943
944 fd = open(hist_name, O_RDWR|O_CREAT|O_NOFOLLOW, 0600);
945 if (fd < 0) {
946 perror("ifstat: open history file");
947 exit(-1);
948 }
949 if ((hist_fp = fdopen(fd, "r+")) == NULL) {
950 perror("ifstat: fdopen history file");
951 exit(-1);
952 }
953 if (flock(fileno(hist_fp), LOCK_EX)) {
954 perror("ifstat: flock history file");
955 exit(-1);
956 }
957 if (fstat(fileno(hist_fp), &stb) != 0) {
958 perror("ifstat: fstat history file");
959 exit(-1);
960 }
961 if (stb.st_nlink != 1 || stb.st_uid != getuid()) {
962 fprintf(stderr, "ifstat: something is so wrong with history file, that I prefer not to proceed.\n");
963 exit(-1);
964 }
965 if (!ignore_history) {
966 FILE *tfp;
967 long uptime = -1;
968
969 if ((tfp = fopen("/proc/uptime", "r")) != NULL) {
970 if (fscanf(tfp, "%ld", &uptime) != 1)
971 uptime = -1;
972 fclose(tfp);
973 }
974 if (uptime >= 0 && time(NULL) >= stb.st_mtime+uptime) {
975 fprintf(stderr, "ifstat: history is aged out, resetting\n");
976 if (ftruncate(fileno(hist_fp), 0))
977 perror("ifstat: ftruncate");
978 }
979 }
980
981 load_raw_table(hist_fp);
982
983 hist_db = kern_db;
984 kern_db = NULL;
985 }
986
987 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 &&
988 (connect(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) == 0
989 || (strcpy(sun.sun_path+1, "ifstat0"),
990 connect(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) == 0))
991 && verify_forging(fd) == 0) {
992 FILE *sfp = fdopen(fd, "r");
993
994 if (!sfp) {
995 fprintf(stderr, "ifstat: fdopen failed: %s\n",
996 strerror(errno));
997 close(fd);
998 } else {
999 load_raw_table(sfp);
1000 if (hist_db && source_mismatch) {
1001 fprintf(stderr, "ifstat: history is stale, ignoring it.\n");
1002 hist_db = NULL;
1003 }
1004 fclose(sfp);
1005 }
1006 } else {
1007 if (fd >= 0)
1008 close(fd);
1009 if (hist_db && info_source[0] && strcmp(info_source, "kernel")) {
1010 fprintf(stderr, "ifstat: history is stale, ignoring it.\n");
1011 hist_db = NULL;
1012 info_source[0] = 0;
1013 }
1014 load_info();
1015 if (info_source[0] == 0)
1016 strcpy(info_source, "kernel");
1017 }
1018
1019 if (!no_output) {
1020 if (ignore_history || hist_db == NULL)
1021 dump_kern_db(stdout);
1022 else
1023 dump_incr_db(stdout);
1024 }
1025
1026 if (!no_update) {
1027 if (ftruncate(fileno(hist_fp), 0))
1028 perror("ifstat: ftruncate");
1029 rewind(hist_fp);
1030
1031 json_output = 0;
1032 dump_raw_db(hist_fp, 1);
1033 fclose(hist_fp);
1034 }
1035 exit(0);
1036 }