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