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