1 /* 2 * rtacct.c Applet to display contents of /proc/net/rt_acct. 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 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <unistd.h> 16 #include <fcntl.h> 17 #include <string.h> 18 #include <errno.h> 19 #include <time.h> 20 #include <sys/time.h> 21 #include <fnmatch.h> 22 #include <sys/file.h> 23 #include <sys/socket.h> 24 #include <sys/un.h> 25 #include <sys/poll.h> 26 #include <sys/wait.h> 27 #include <sys/stat.h> 28 #include <sys/mman.h> 29 #include <signal.h> 30 #include <math.h> 31 32 #include "rt_names.h" 33 34 #include <SNAPSHOT.h> 35 36 int reset_history = 0; 37 int ignore_history = 0; 38 int no_output = 0; 39 int no_update = 0; 40 int scan_interval = 0; 41 int time_constant = 0; 42 int dump_zeros = 0; 43 unsigned long magic_number = 0; 44 double W; 45 46 static int generic_proc_open(const char *env, const char *name) 47 { 48 char store[1024]; 49 char *p = getenv(env); 50 if (!p) { 51 p = getenv("PROC_ROOT") ? : "/proc"; 52 snprintf(store, sizeof(store)-1, "%s/%s", p, name); 53 p = store; 54 } 55 return open(p, O_RDONLY); 56 } 57 58 static int net_rtacct_open(void) 59 { 60 return generic_proc_open("PROC_NET_RTACCT", "net/rt_acct"); 61 } 62 63 static __u32 rmap[256/4]; 64 65 struct rtacct_data 66 { 67 __u32 ival[256*4]; 68 69 unsigned long long val[256*4]; 70 double rate[256*4]; 71 char signature[128]; 72 }; 73 74 static struct rtacct_data kern_db_static; 75 76 static struct rtacct_data *kern_db = &kern_db_static; 77 static struct rtacct_data *hist_db; 78 79 static void nread(int fd, char *buf, int tot) 80 { 81 int count = 0; 82 83 while (count < tot) { 84 int n = read(fd, buf+count, tot-count); 85 if (n < 0) { 86 if (errno == EINTR) 87 continue; 88 exit(-1); 89 } 90 if (n == 0) 91 exit(-1); 92 count += n; 93 } 94 } 95 96 static __u32 *read_kern_table(__u32 *tbl) 97 { 98 static __u32 *tbl_ptr; 99 int fd; 100 101 if (magic_number) { 102 if (tbl_ptr != NULL) 103 return tbl_ptr; 104 105 fd = open("/dev/mem", O_RDONLY); 106 if (fd < 0) { 107 perror("magic open"); 108 exit(-1); 109 } 110 tbl_ptr = mmap(NULL, 4096, 111 PROT_READ, 112 MAP_SHARED, 113 fd, magic_number); 114 if ((unsigned long)tbl_ptr == ~0UL) { 115 perror("magic mmap"); 116 exit(-1); 117 } 118 close(fd); 119 return tbl_ptr; 120 } 121 122 fd = net_rtacct_open(); 123 if (fd >= 0) { 124 nread(fd, (char*)tbl, 256*16); 125 close(fd); 126 } else { 127 memset(tbl, 0, 256*16); 128 } 129 return tbl; 130 } 131 132 static void format_rate(FILE *fp, double rate) 133 { 134 char temp[64]; 135 136 if (rate > 1024*1024) { 137 sprintf(temp, "%uM", (unsigned)rint(rate/(1024*1024))); 138 fprintf(fp, " %-10s", temp); 139 } else if (rate > 1024) { 140 sprintf(temp, "%uK", (unsigned)rint(rate/1024)); 141 fprintf(fp, " %-10s", temp); 142 } else 143 fprintf(fp, " %-10u", (unsigned)rate); 144 } 145 146 static void format_count(FILE *fp, unsigned long long val) 147 { 148 if (val > 1024*1024*1024) 149 fprintf(fp, " %10lluM", val/(1024*1024)); 150 else if (val > 1024*1024) 151 fprintf(fp, " %10lluK", val/1024); 152 else 153 fprintf(fp, " %10llu", val); 154 } 155 156 static void dump_abs_db(FILE *fp) 157 { 158 int realm; 159 char b1[16]; 160 161 if (!no_output) { 162 fprintf(fp, "#%s\n", kern_db->signature); 163 fprintf(fp, 164 "%-10s " 165 "%-10s " 166 "%-10s " 167 "%-10s " 168 "%-10s " 169 "\n" 170 , "Realm", "BytesTo", "PktsTo", "BytesFrom", "PktsFrom"); 171 fprintf(fp, 172 "%-10s " 173 "%-10s " 174 "%-10s " 175 "%-10s " 176 "%-10s " 177 "\n" 178 , "", "BPSTo", "PPSTo", "BPSFrom", "PPSFrom"); 179 180 } 181 182 for (realm=0; realm<256; realm++) { 183 int i; 184 unsigned long long *val; 185 double *rate; 186 187 if (!(rmap[realm>>5] & (1<<(realm&0x1f)))) 188 continue; 189 190 val = &kern_db->val[realm*4]; 191 rate = &kern_db->rate[realm*4]; 192 193 if (!dump_zeros && 194 !val[0] && !rate[0] && 195 !val[1] && !rate[1] && 196 !val[2] && !rate[2] && 197 !val[3] && !rate[3]) 198 continue; 199 200 if (hist_db) { 201 memcpy(&hist_db->val[realm*4], val, sizeof(*val)*4); 202 } 203 204 if (no_output) 205 continue; 206 207 fprintf(fp, "%-10s", rtnl_rtrealm_n2a(realm, b1, sizeof(b1))); 208 for (i = 0; i < 4; i++) 209 format_count(fp, val[i]); 210 fprintf(fp, "\n%-10s", ""); 211 for (i = 0; i < 4; i++) 212 format_rate(fp, rate[i]); 213 fprintf(fp, "\n"); 214 } 215 } 216 217 218 static void dump_incr_db(FILE *fp) 219 { 220 int k, realm; 221 char b1[16]; 222 223 if (!no_output) { 224 fprintf(fp, "#%s\n", kern_db->signature); 225 fprintf(fp, 226 "%-10s " 227 "%-10s " 228 "%-10s " 229 "%-10s " 230 "%-10s " 231 "\n" 232 , "Realm", "BytesTo", "PktsTo", "BytesFrom", "PktsFrom"); 233 fprintf(fp, 234 "%-10s " 235 "%-10s " 236 "%-10s " 237 "%-10s " 238 "%-10s " 239 "\n" 240 , "", "BPSTo", "PPSTo", "BPSFrom", "PPSFrom"); 241 } 242 243 for (realm=0; realm<256; realm++) { 244 int ovfl = 0; 245 int i; 246 unsigned long long *val; 247 double *rate; 248 unsigned long long rval[4]; 249 250 if (!(rmap[realm>>5] & (1<<(realm&0x1f)))) 251 continue; 252 253 val = &kern_db->val[realm*4]; 254 rate = &kern_db->rate[realm*4]; 255 256 for (k=0; k<4; k++) { 257 rval[k] = val[k]; 258 if (rval[k] < hist_db->val[realm*4+k]) 259 ovfl = 1; 260 else 261 rval[k] -= hist_db->val[realm*4+k]; 262 } 263 if (ovfl) { 264 for (k=0; k<4; k++) 265 rval[k] = val[k]; 266 } 267 if (hist_db) { 268 memcpy(&hist_db->val[realm*4], val, sizeof(*val)*4); 269 } 270 271 if (no_output) 272 continue; 273 274 if (!dump_zeros && 275 !rval[0] && !rate[0] && 276 !rval[1] && !rate[1] && 277 !rval[2] && !rate[2] && 278 !rval[3] && !rate[3]) 279 continue; 280 281 282 fprintf(fp, "%-10s", rtnl_rtrealm_n2a(realm, b1, sizeof(b1))); 283 for (i = 0; i < 4; i++) 284 format_count(fp, rval[i]); 285 fprintf(fp, "\n%-10s", ""); 286 for (i = 0; i < 4; i++) 287 format_rate(fp, rate[i]); 288 fprintf(fp, "\n"); 289 } 290 } 291 292 293 static int children; 294 295 static void sigchild(int signo) 296 { 297 } 298 299 /* Server side only: read kernel data, update tables, calculate rates. */ 300 301 static void update_db(int interval) 302 { 303 int i; 304 __u32 *ival; 305 __u32 _ival[256*4]; 306 307 ival = read_kern_table(_ival); 308 309 for (i=0; i<256*4; i++) { 310 double sample; 311 __u32 incr = ival[i] - kern_db->ival[i]; 312 313 if (ival[i] == 0 && incr == 0 && 314 kern_db->val[i] == 0 && kern_db->rate[i] == 0) 315 continue; 316 317 kern_db->val[i] += incr; 318 kern_db->ival[i] = ival[i]; 319 sample = (double)(incr*1000)/interval; 320 if (interval >= scan_interval) { 321 kern_db->rate[i] += W*(sample-kern_db->rate[i]); 322 } else if (interval >= 1000) { 323 if (interval >= time_constant) { 324 kern_db->rate[i] = sample; 325 } else { 326 double w = W*(double)interval/scan_interval; 327 kern_db->rate[i] += w*(sample-kern_db->rate[i]); 328 } 329 } 330 } 331 } 332 333 static void send_db(int fd) 334 { 335 int tot = 0; 336 337 while (tot < sizeof(*kern_db)) { 338 int n = write(fd, ((char*)kern_db) + tot, sizeof(*kern_db)-tot); 339 if (n < 0) { 340 if (errno == EINTR) 341 continue; 342 return; 343 } 344 tot += n; 345 } 346 } 347 348 349 350 #define T_DIFF(a,b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000) 351 352 353 static void pad_kern_table(struct rtacct_data *dat, __u32 *ival) 354 { 355 int i; 356 memset(dat->rate, 0, sizeof(dat->rate)); 357 if (dat->ival != ival) 358 memcpy(dat->ival, ival, sizeof(dat->ival)); 359 for (i=0; i<256*4; i++) 360 dat->val[i] = ival[i]; 361 } 362 363 static void server_loop(int fd) 364 { 365 struct timeval snaptime = { 0 }; 366 struct pollfd p; 367 p.fd = fd; 368 p.events = p.revents = POLLIN; 369 370 sprintf(kern_db->signature, 371 "%u.%lu sampling_interval=%d time_const=%d", 372 (unsigned) getpid(), (unsigned long)random(), 373 scan_interval/1000, time_constant/1000); 374 375 pad_kern_table(kern_db, read_kern_table(kern_db->ival)); 376 377 for (;;) { 378 int status; 379 int tdiff; 380 struct timeval now; 381 gettimeofday(&now, NULL); 382 tdiff = T_DIFF(now, snaptime); 383 if (tdiff >= scan_interval) { 384 update_db(tdiff); 385 snaptime = now; 386 tdiff = 0; 387 } 388 if (poll(&p, 1, tdiff + scan_interval) > 0 389 && (p.revents&POLLIN)) { 390 int clnt = accept(fd, NULL, NULL); 391 if (clnt >= 0) { 392 pid_t pid; 393 if (children >= 5) { 394 close(clnt); 395 } else if ((pid = fork()) != 0) { 396 if (pid>0) 397 children++; 398 close(clnt); 399 } else { 400 if (tdiff > 0) 401 update_db(tdiff); 402 send_db(clnt); 403 exit(0); 404 } 405 } 406 } 407 while (children && waitpid(-1, &status, WNOHANG) > 0) 408 children--; 409 } 410 } 411 412 static int verify_forging(int fd) 413 { 414 struct ucred cred; 415 socklen_t olen = sizeof(cred); 416 417 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void*)&cred, &olen) || 418 olen < sizeof(cred)) 419 return -1; 420 if (cred.uid == getuid() || cred.uid == 0) 421 return 0; 422 return -1; 423 } 424 425 static void usage(void) __attribute__((noreturn)); 426 427 static void usage(void) 428 { 429 fprintf(stderr, 430 "Usage: rtacct [ -h?vVzrnasd:t: ] [ ListOfRealms ]\n" 431 ); 432 exit(-1); 433 } 434 435 int main(int argc, char *argv[]) 436 { 437 char hist_name[128]; 438 struct sockaddr_un sun; 439 int ch; 440 int fd; 441 442 while ((ch = getopt(argc, argv, "h?vVzrM:nasd:t:")) != EOF) { 443 switch(ch) { 444 case 'z': 445 dump_zeros = 1; 446 break; 447 case 'r': 448 reset_history = 1; 449 break; 450 case 'a': 451 ignore_history = 1; 452 break; 453 case 's': 454 no_update = 1; 455 break; 456 case 'n': 457 no_output = 1; 458 break; 459 case 'd': 460 scan_interval = 1000*atoi(optarg); 461 break; 462 case 't': 463 if (sscanf(optarg, "%d", &time_constant) != 1 || 464 time_constant <= 0) { 465 fprintf(stderr, "rtacct: invalid time constant divisor\n"); 466 exit(-1); 467 } 468 break; 469 case 'v': 470 case 'V': 471 printf("rtacct utility, iproute2-ss%s\n", SNAPSHOT); 472 exit(0); 473 case 'M': 474 /* Some secret undocumented option, nobody 475 * is expected to ask about its sense. See? 476 */ 477 sscanf(optarg, "%lx", &magic_number); 478 break; 479 case 'h': 480 case '?': 481 default: 482 usage(); 483 } 484 } 485 486 argc -= optind; 487 argv += optind; 488 489 if (argc) { 490 while (argc > 0) { 491 __u32 realm; 492 if (rtnl_rtrealm_a2n(&realm, argv[0])) { 493 fprintf(stderr, "Warning: realm \"%s\" does not exist.\n", argv[0]); 494 exit(-1); 495 } 496 rmap[realm>>5] |= (1<<(realm&0x1f)); 497 argc--; argv++; 498 } 499 } else { 500 memset(rmap, ~0, sizeof(rmap)); 501 /* Always suppress zeros. */ 502 dump_zeros = 0; 503 } 504 505 sun.sun_family = AF_UNIX; 506 sun.sun_path[0] = 0; 507 sprintf(sun.sun_path+1, "rtacct%d", getuid()); 508 509 if (scan_interval > 0) { 510 if (time_constant == 0) 511 time_constant = 60; 512 time_constant *= 1000; 513 W = 1 - 1/exp(log(10)*(double)scan_interval/time_constant); 514 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { 515 perror("rtacct: socket"); 516 exit(-1); 517 } 518 if (bind(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) < 0) { 519 perror("rtacct: bind"); 520 exit(-1); 521 } 522 if (listen(fd, 5) < 0) { 523 perror("rtacct: listen"); 524 exit(-1); 525 } 526 if (daemon(0, 0)) { 527 perror("rtacct: daemon"); 528 exit(-1); 529 } 530 signal(SIGPIPE, SIG_IGN); 531 signal(SIGCHLD, sigchild); 532 server_loop(fd); 533 exit(0); 534 } 535 536 if (getenv("RTACCT_HISTORY")) 537 snprintf(hist_name, sizeof(hist_name), "%s", getenv("RTACCT_HISTORY")); 538 else 539 sprintf(hist_name, "/tmp/.rtacct.u%d", getuid()); 540 541 if (reset_history) 542 unlink(hist_name); 543 544 if (!ignore_history || !no_update) { 545 struct stat stb; 546 547 fd = open(hist_name, O_RDWR|O_CREAT|O_NOFOLLOW, 0600); 548 if (fd < 0) { 549 perror("rtacct: open history file"); 550 exit(-1); 551 } 552 if (flock(fd, LOCK_EX)) { 553 perror("rtacct: flock history file"); 554 exit(-1); 555 } 556 if (fstat(fd, &stb) != 0) { 557 perror("rtacct: fstat history file"); 558 exit(-1); 559 } 560 if (stb.st_nlink != 1 || stb.st_uid != getuid()) { 561 fprintf(stderr, "rtacct: something is so wrong with history file, that I prefer not to proceed.\n"); 562 exit(-1); 563 } 564 if (stb.st_size != sizeof(*hist_db)) 565 if (write(fd, kern_db, sizeof(*hist_db)) < 0) { 566 perror("rtacct: write history file"); 567 exit(-1); 568 } 569 570 hist_db = mmap(NULL, sizeof(*hist_db), 571 PROT_READ|PROT_WRITE, 572 no_update ? MAP_PRIVATE : MAP_SHARED, 573 fd, 0); 574 575 if ((unsigned long)hist_db == ~0UL) { 576 perror("mmap"); 577 exit(-1); 578 } 579 580 if (!ignore_history) { 581 FILE *tfp; 582 long uptime = -1; 583 if ((tfp = fopen("/proc/uptime", "r")) != NULL) { 584 if (fscanf(tfp, "%ld", &uptime) != 1) 585 uptime = -1; 586 fclose(tfp); 587 } 588 589 if (uptime >= 0 && time(NULL) >= stb.st_mtime+uptime) { 590 fprintf(stderr, "rtacct: history is aged out, resetting\n"); 591 memset(hist_db, 0, sizeof(*hist_db)); 592 } 593 } 594 595 close(fd); 596 } 597 598 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 && 599 (connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0 600 || (strcpy(sun.sun_path+1, "rtacct0"), 601 connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0)) 602 && verify_forging(fd) == 0) { 603 nread(fd, (char*)kern_db, sizeof(*kern_db)); 604 if (hist_db && hist_db->signature[0] && 605 strcmp(kern_db->signature, hist_db->signature)) { 606 fprintf(stderr, "rtacct: history is stale, ignoring it.\n"); 607 hist_db = NULL; 608 } 609 close(fd); 610 } else { 611 if (fd >= 0) 612 close(fd); 613 614 if (hist_db && hist_db->signature[0] && 615 strcmp(hist_db->signature, "kernel")) { 616 fprintf(stderr, "rtacct: history is stale, ignoring it.\n"); 617 hist_db = NULL; 618 } 619 620 pad_kern_table(kern_db, read_kern_table(kern_db->ival)); 621 strcpy(kern_db->signature, "kernel"); 622 } 623 624 if (ignore_history || hist_db == NULL) 625 dump_abs_db(stdout); 626 else 627 dump_incr_db(stdout); 628 629 exit(0); 630 } 631