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