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