busybox/procps/nmeter.c
<<
>>
Prefs
   1/*
   2** Licensed under the GPL v2, see the file LICENSE in this tarball
   3**
   4** Based on nanotop.c from floppyfw project
   5**
   6** Contact me: vda.linux@googlemail.com */
   7
   8//TODO:
   9// simplify code
  10// /proc/locks
  11// /proc/stat:
  12// disk_io: (3,0):(22272,17897,410702,4375,54750)
  13// btime 1059401962
  14//TODO: use sysinfo libc call/syscall, if appropriate
  15// (faster than open/read/close):
  16// sysinfo({uptime=15017, loads=[5728, 15040, 16480]
  17//  totalram=2107416576, freeram=211525632, sharedram=0, bufferram=157204480}
  18//  totalswap=134209536, freeswap=134209536, procs=157})
  19
  20#include "libbb.h"
  21
  22typedef unsigned long long ullong;
  23
  24enum {  /* Preferably use powers of 2 */
  25        PROC_MIN_FILE_SIZE = 256,
  26        PROC_MAX_FILE_SIZE = 16 * 1024,
  27};
  28
  29typedef struct proc_file {
  30        char *file;
  31        int file_sz;
  32        smallint last_gen;
  33} proc_file;
  34
  35static const char *const proc_name[] = {
  36        "stat",         // Must match the order of proc_file's!
  37        "loadavg",
  38        "net/dev",
  39        "meminfo",
  40        "diskstats",
  41        "sys/fs/file-nr"
  42};
  43
  44struct globals {
  45        // Sample generation flip-flop
  46        smallint gen;
  47        // Linux 2.6? (otherwise assumes 2.4)
  48        smallint is26;
  49        // 1 if sample delay is not an integer fraction of a second
  50        smallint need_seconds;
  51        char *cur_outbuf;
  52        const char *final_str;
  53        int delta;
  54        int deltanz;
  55        struct timeval tv;
  56#define first_proc_file proc_stat
  57        proc_file proc_stat;    // Must match the order of proc_name's!
  58        proc_file proc_loadavg;
  59        proc_file proc_net_dev;
  60        proc_file proc_meminfo;
  61        proc_file proc_diskstats;
  62        proc_file proc_sys_fs_filenr;
  63};
  64#define G (*ptr_to_globals)
  65#define gen                (G.gen               )
  66#define is26               (G.is26              )
  67#define need_seconds       (G.need_seconds      )
  68#define cur_outbuf         (G.cur_outbuf        )
  69#define final_str          (G.final_str         )
  70#define delta              (G.delta             )
  71#define deltanz            (G.deltanz           )
  72#define tv                 (G.tv                )
  73#define proc_stat          (G.proc_stat         )
  74#define proc_loadavg       (G.proc_loadavg      )
  75#define proc_net_dev       (G.proc_net_dev      )
  76#define proc_meminfo       (G.proc_meminfo      )
  77#define proc_diskstats     (G.proc_diskstats    )
  78#define proc_sys_fs_filenr (G.proc_sys_fs_filenr)
  79#define INIT_G() do { \
  80        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
  81        cur_outbuf = outbuf; \
  82        final_str = "\n"; \
  83        deltanz = delta = 1000000; \
  84} while (0)
  85
  86// We depend on this being a char[], not char* - we take sizeof() of it
  87#define outbuf bb_common_bufsiz1
  88
  89static inline void reset_outbuf(void)
  90{
  91        cur_outbuf = outbuf;
  92}
  93
  94static inline int outbuf_count(void)
  95{
  96        return cur_outbuf - outbuf;
  97}
  98
  99static void print_outbuf(void)
 100{
 101        int sz = cur_outbuf - outbuf;
 102        if (sz > 0) {
 103                xwrite(STDOUT_FILENO, outbuf, sz);
 104                cur_outbuf = outbuf;
 105        }
 106}
 107
 108static void put(const char *s)
 109{
 110        int sz = strlen(s);
 111        if (sz > outbuf + sizeof(outbuf) - cur_outbuf)
 112                sz = outbuf + sizeof(outbuf) - cur_outbuf;
 113        memcpy(cur_outbuf, s, sz);
 114        cur_outbuf += sz;
 115}
 116
 117static void put_c(char c)
 118{
 119        if (cur_outbuf < outbuf + sizeof(outbuf))
 120                *cur_outbuf++ = c;
 121}
 122
 123static void put_question_marks(int count)
 124{
 125        while (count--)
 126                put_c('?');
 127}
 128
 129static void readfile_z(proc_file *pf, const char* fname)
 130{
 131// open_read_close() will do two reads in order to be sure we are at EOF,
 132// and we don't need/want that.
 133        int fd;
 134        int sz, rdsz;
 135        char *buf;
 136
 137        sz = pf->file_sz;
 138        buf = pf->file;
 139        if (!buf) {
 140                buf = xmalloc(PROC_MIN_FILE_SIZE);
 141                sz = PROC_MIN_FILE_SIZE;
 142        }
 143 again:
 144        fd = xopen(fname, O_RDONLY);
 145        buf[0] = '\0';
 146        rdsz = read(fd, buf, sz-1);
 147        close(fd);
 148        if (rdsz > 0) {
 149                if (rdsz == sz-1 && sz < PROC_MAX_FILE_SIZE) {
 150                        sz *= 2;
 151                        buf = xrealloc(buf, sz);
 152                        goto again;
 153                }
 154                buf[rdsz] = '\0';
 155        }
 156        pf->file_sz = sz;
 157        pf->file = buf;
 158}
 159
 160static const char* get_file(proc_file *pf)
 161{
 162        if (pf->last_gen != gen) {
 163                pf->last_gen = gen;
 164                readfile_z(pf, proc_name[pf - &first_proc_file]);
 165        }
 166        return pf->file;
 167}
 168
 169static ullong read_after_slash(const char *p)
 170{
 171        p = strchr(p, '/');
 172        if (!p) return 0;
 173        return strtoull(p+1, NULL, 10);
 174}
 175
 176enum conv_type { conv_decimal, conv_slash };
 177
 178// Reads decimal values from line. Values start after key, for example:
 179// "cpu  649369 0 341297 4336769..." - key is "cpu" here.
 180// Values are stored in vec[]. arg_ptr has list of positions
 181// we are interested in: for example: 1,2,5 - we want 1st, 2nd and 5th value.
 182static int vrdval(const char* p, const char* key,
 183        enum conv_type conv, ullong *vec, va_list arg_ptr)
 184{
 185        int indexline;
 186        int indexnext;
 187
 188        p = strstr(p, key);
 189        if (!p) return 1;
 190
 191        p += strlen(key);
 192        indexline = 1;
 193        indexnext = va_arg(arg_ptr, int);
 194        while (1) {
 195                while (*p == ' ' || *p == '\t') p++;
 196                if (*p == '\n' || *p == '\0') break;
 197
 198                if (indexline == indexnext) { // read this value
 199                        *vec++ = conv==conv_decimal ?
 200                                strtoull(p, NULL, 10) :
 201                                read_after_slash(p);
 202                        indexnext = va_arg(arg_ptr, int);
 203                }
 204                while (*p > ' ') p++; // skip over value
 205                indexline++;
 206        }
 207        return 0;
 208}
 209
 210// Parses files with lines like "cpu0 21727 0 15718 1813856 9461 10485 0 0":
 211// rdval(file_contents, "string_to_find", result_vector, value#, value#...)
 212// value# start with 1
 213static int rdval(const char* p, const char* key, ullong *vec, ...)
 214{
 215        va_list arg_ptr;
 216        int result;
 217
 218        va_start(arg_ptr, vec);
 219        result = vrdval(p, key, conv_decimal, vec, arg_ptr);
 220        va_end(arg_ptr);
 221
 222        return result;
 223}
 224
 225// Parses files with lines like "... ... ... 3/148 ...."
 226static int rdval_loadavg(const char* p, ullong *vec, ...)
 227{
 228        va_list arg_ptr;
 229        int result;
 230
 231        va_start(arg_ptr, vec);
 232        result = vrdval(p, "", conv_slash, vec, arg_ptr);
 233        va_end(arg_ptr);
 234
 235        return result;
 236}
 237
 238// Parses /proc/diskstats
 239//   1  2 3   4  5        6(rd)  7      8     9     10(wr) 11     12 13     14
 240//   3  0 hda 51292 14441 841783 926052 25717 79650 843256 3029804 0 148459 3956933
 241//   3  1 hda1 0 0 0 0 <- ignore if only 4 fields
 242static int rdval_diskstats(const char* p, ullong *vec)
 243{
 244        ullong rd = rd; // for compiler
 245        int indexline = 0;
 246        vec[0] = 0;
 247        vec[1] = 0;
 248        while (1) {
 249                indexline++;
 250                while (*p == ' ' || *p == '\t') p++;
 251                if (*p == '\0') break;
 252                if (*p == '\n') {
 253                        indexline = 0;
 254                        p++;
 255                        continue;
 256                }
 257                if (indexline == 6) {
 258                        rd = strtoull(p, NULL, 10);
 259                } else if (indexline == 10) {
 260                        vec[0] += rd;  // TODO: *sectorsize (don't know how to find out sectorsize)
 261                        vec[1] += strtoull(p, NULL, 10);
 262                        while (*p != '\n' && *p != '\0') p++;
 263                        continue;
 264                }
 265                while (*p > ' ') p++; // skip over value
 266        }
 267        return 0;
 268}
 269
 270static void scale(ullong ul)
 271{
 272        char buf[5];
 273
 274        /* see http://en.wikipedia.org/wiki/Tera */
 275        smart_ulltoa4(ul, buf, " kmgtpezy");
 276        buf[4] = '\0';
 277        put(buf);
 278}
 279
 280
 281#define S_STAT(a) \
 282typedef struct a { \
 283        struct s_stat *next; \
 284        void (*collect)(struct a *s); \
 285        const char *label;
 286#define S_STAT_END(a) } a;
 287
 288S_STAT(s_stat)
 289S_STAT_END(s_stat)
 290
 291static void collect_literal(s_stat *s UNUSED_PARAM)
 292{
 293}
 294
 295static s_stat* init_literal(void)
 296{
 297        s_stat *s = xzalloc(sizeof(*s));
 298        s->collect = collect_literal;
 299        return (s_stat*)s;
 300}
 301
 302static s_stat* init_delay(const char *param)
 303{
 304        delta = strtoul(param, NULL, 0) * 1000; /* param can be "" */
 305        deltanz = delta > 0 ? delta : 1;
 306        need_seconds = (1000000%deltanz) != 0;
 307        return NULL;
 308}
 309
 310static s_stat* init_cr(const char *param UNUSED_PARAM)
 311{
 312        final_str = "\r";
 313        return (s_stat*)0;
 314}
 315
 316
 317//     user nice system idle  iowait irq  softirq (last 3 only in 2.6)
 318//cpu  649369 0 341297 4336769 11640 7122 1183
 319//cpuN 649369 0 341297 4336769 11640 7122 1183
 320enum { CPU_FIELDCNT = 7 };
 321S_STAT(cpu_stat)
 322        ullong old[CPU_FIELDCNT];
 323        int bar_sz;
 324        char *bar;
 325S_STAT_END(cpu_stat)
 326
 327
 328static void collect_cpu(cpu_stat *s)
 329{
 330        ullong data[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
 331        unsigned frac[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
 332        ullong all = 0;
 333        int norm_all = 0;
 334        int bar_sz = s->bar_sz;
 335        char *bar = s->bar;
 336        int i;
 337
 338        if (rdval(get_file(&proc_stat), "cpu ", data, 1, 2, 3, 4, 5, 6, 7)) {
 339                put_question_marks(bar_sz);
 340                return;
 341        }
 342
 343        for (i = 0; i < CPU_FIELDCNT; i++) {
 344                ullong old = s->old[i];
 345                if (data[i] < old) old = data[i];               //sanitize
 346                s->old[i] = data[i];
 347                all += (data[i] -= old);
 348        }
 349
 350        if (all) {
 351                for (i = 0; i < CPU_FIELDCNT; i++) {
 352                        ullong t = bar_sz * data[i];
 353                        norm_all += data[i] = t / all;
 354                        frac[i] = t % all;
 355                }
 356
 357                while (norm_all < bar_sz) {
 358                        unsigned max = frac[0];
 359                        int pos = 0;
 360                        for (i = 1; i < CPU_FIELDCNT; i++) {
 361                                if (frac[i] > max) max = frac[i], pos = i;
 362                        }
 363                        frac[pos] = 0;  //avoid bumping up same value twice
 364                        data[pos]++;
 365                        norm_all++;
 366                }
 367
 368                memset(bar, '.', bar_sz);
 369                memset(bar, 'S', data[2]); bar += data[2]; //sys
 370                memset(bar, 'U', data[0]); bar += data[0]; //usr
 371                memset(bar, 'N', data[1]); bar += data[1]; //nice
 372                memset(bar, 'D', data[4]); bar += data[4]; //iowait
 373                memset(bar, 'I', data[5]); bar += data[5]; //irq
 374                memset(bar, 'i', data[6]); bar += data[6]; //softirq
 375        } else {
 376                memset(bar, '?', bar_sz);
 377        }
 378        put(s->bar);
 379}
 380
 381
 382static s_stat* init_cpu(const char *param)
 383{
 384        int sz;
 385        cpu_stat *s = xzalloc(sizeof(*s));
 386        s->collect = collect_cpu;
 387        sz = strtoul(param, NULL, 0); /* param can be "" */
 388        if (sz < 10) sz = 10;
 389        if (sz > 1000) sz = 1000;
 390        s->bar = xzalloc(sz+1);
 391        /*s->bar[sz] = '\0'; - xzalloc did it */
 392        s->bar_sz = sz;
 393        return (s_stat*)s;
 394}
 395
 396
 397S_STAT(int_stat)
 398        ullong old;
 399        int no;
 400S_STAT_END(int_stat)
 401
 402static void collect_int(int_stat *s)
 403{
 404        ullong data[1];
 405        ullong old;
 406
 407        if (rdval(get_file(&proc_stat), "intr", data, s->no)) {
 408                put_question_marks(4);
 409                return;
 410        }
 411
 412        old = s->old;
 413        if (data[0] < old) old = data[0];               //sanitize
 414        s->old = data[0];
 415        scale(data[0] - old);
 416}
 417
 418static s_stat* init_int(const char *param)
 419{
 420        int_stat *s = xzalloc(sizeof(*s));
 421        s->collect = collect_int;
 422        if (param[0] == '\0') {
 423                s->no = 1;
 424        } else {
 425                int n = xatoi_u(param);
 426                s->no = n + 2;
 427        }
 428        return (s_stat*)s;
 429}
 430
 431
 432S_STAT(ctx_stat)
 433        ullong old;
 434S_STAT_END(ctx_stat)
 435
 436static void collect_ctx(ctx_stat *s)
 437{
 438        ullong data[1];
 439        ullong old;
 440
 441        if (rdval(get_file(&proc_stat), "ctxt", data, 1)) {
 442                put_question_marks(4);
 443                return;
 444        }
 445
 446        old = s->old;
 447        if (data[0] < old) old = data[0];               //sanitize
 448        s->old = data[0];
 449        scale(data[0] - old);
 450}
 451
 452static s_stat* init_ctx(const char *param UNUSED_PARAM)
 453{
 454        ctx_stat *s = xzalloc(sizeof(*s));
 455        s->collect = collect_ctx;
 456        return (s_stat*)s;
 457}
 458
 459
 460S_STAT(blk_stat)
 461        const char* lookfor;
 462        ullong old[2];
 463S_STAT_END(blk_stat)
 464
 465static void collect_blk(blk_stat *s)
 466{
 467        ullong data[2];
 468        int i;
 469
 470        if (is26) {
 471                i = rdval_diskstats(get_file(&proc_diskstats), data);
 472        } else {
 473                i = rdval(get_file(&proc_stat), s->lookfor, data, 1, 2);
 474                // Linux 2.4 reports bio in Kbytes, convert to sectors:
 475                data[0] *= 2;
 476                data[1] *= 2;
 477        }
 478        if (i) {
 479                put_question_marks(9);
 480                return;
 481        }
 482
 483        for (i=0; i<2; i++) {
 484                ullong old = s->old[i];
 485                if (data[i] < old) old = data[i];               //sanitize
 486                s->old[i] = data[i];
 487                data[i] -= old;
 488        }
 489        scale(data[0]*512); // TODO: *sectorsize
 490        put_c(' ');
 491        scale(data[1]*512);
 492}
 493
 494static s_stat* init_blk(const char *param UNUSED_PARAM)
 495{
 496        blk_stat *s = xzalloc(sizeof(*s));
 497        s->collect = collect_blk;
 498        s->lookfor = "page";
 499        return (s_stat*)s;
 500}
 501
 502
 503S_STAT(fork_stat)
 504        ullong old;
 505S_STAT_END(fork_stat)
 506
 507static void collect_thread_nr(fork_stat *s UNUSED_PARAM)
 508{
 509        ullong data[1];
 510
 511        if (rdval_loadavg(get_file(&proc_loadavg), data, 4)) {
 512                put_question_marks(4);
 513                return;
 514        }
 515        scale(data[0]);
 516}
 517
 518static void collect_fork(fork_stat *s)
 519{
 520        ullong data[1];
 521        ullong old;
 522
 523        if (rdval(get_file(&proc_stat), "processes", data, 1)) {
 524                put_question_marks(4);
 525                return;
 526        }
 527
 528        old = s->old;
 529        if (data[0] < old) old = data[0];       //sanitize
 530        s->old = data[0];
 531        scale(data[0] - old);
 532}
 533
 534static s_stat* init_fork(const char *param)
 535{
 536        fork_stat *s = xzalloc(sizeof(*s));
 537        if (*param == 'n') {
 538                s->collect = collect_thread_nr;
 539        } else {
 540                s->collect = collect_fork;
 541        }
 542        return (s_stat*)s;
 543}
 544
 545
 546S_STAT(if_stat)
 547        ullong old[4];
 548        const char *device;
 549        char *device_colon;
 550S_STAT_END(if_stat)
 551
 552static void collect_if(if_stat *s)
 553{
 554        ullong data[4];
 555        int i;
 556
 557        if (rdval(get_file(&proc_net_dev), s->device_colon, data, 1, 3, 9, 11)) {
 558                put_question_marks(10);
 559                return;
 560        }
 561
 562        for (i=0; i<4; i++) {
 563                ullong old = s->old[i];
 564                if (data[i] < old) old = data[i];               //sanitize
 565                s->old[i] = data[i];
 566                data[i] -= old;
 567        }
 568        put_c(data[1] ? '*' : ' ');
 569        scale(data[0]);
 570        put_c(data[3] ? '*' : ' ');
 571        scale(data[2]);
 572}
 573
 574static s_stat* init_if(const char *device)
 575{
 576        if_stat *s = xzalloc(sizeof(*s));
 577
 578        if (!device || !device[0])
 579                bb_show_usage();
 580        s->collect = collect_if;
 581
 582        s->device = device;
 583        s->device_colon = xasprintf("%s:", device);
 584        return (s_stat*)s;
 585}
 586
 587
 588S_STAT(mem_stat)
 589        char opt;
 590S_STAT_END(mem_stat)
 591
 592// "Memory" value should not include any caches.
 593// IOW: neither "ls -laR /" nor heavy read/write activity
 594//      should affect it. We'd like to also include any
 595//      long-term allocated kernel-side mem, but it is hard
 596//      to figure out. For now, bufs, cached & slab are
 597//      counted as "free" memory
 598//2.6.16:
 599//MemTotal:       773280 kB
 600//MemFree:         25912 kB - genuinely free
 601//Buffers:        320672 kB - cache
 602//Cached:         146396 kB - cache
 603//SwapCached:          0 kB
 604//Active:         183064 kB
 605//Inactive:       356892 kB
 606//HighTotal:           0 kB
 607//HighFree:            0 kB
 608//LowTotal:       773280 kB
 609//LowFree:         25912 kB
 610//SwapTotal:      131064 kB
 611//SwapFree:       131064 kB
 612//Dirty:              48 kB
 613//Writeback:           0 kB
 614//Mapped:          96620 kB
 615//Slab:           200668 kB - takes 7 Mb on my box fresh after boot,
 616//                            but includes dentries and inodes
 617//                            (== can take arbitrary amount of mem)
 618//CommitLimit:    517704 kB
 619//Committed_AS:   236776 kB
 620//PageTables:       1248 kB
 621//VmallocTotal:   516052 kB
 622//VmallocUsed:      3852 kB
 623//VmallocChunk:   512096 kB
 624//HugePages_Total:     0
 625//HugePages_Free:      0
 626//Hugepagesize:     4096 kB
 627static void collect_mem(mem_stat *s)
 628{
 629        ullong m_total = 0;
 630        ullong m_free = 0;
 631        ullong m_bufs = 0;
 632        ullong m_cached = 0;
 633        ullong m_slab = 0;
 634
 635        if (rdval(get_file(&proc_meminfo), "MemTotal:", &m_total, 1)) {
 636                put_question_marks(4);
 637                return;
 638        }
 639        if (s->opt == 't') {
 640                scale(m_total << 10);
 641                return;
 642        }
 643
 644        if (rdval(proc_meminfo.file, "MemFree:", &m_free  , 1)
 645         || rdval(proc_meminfo.file, "Buffers:", &m_bufs  , 1)
 646         || rdval(proc_meminfo.file, "Cached:",  &m_cached, 1)
 647         || rdval(proc_meminfo.file, "Slab:",    &m_slab  , 1)
 648        ) {
 649                put_question_marks(4);
 650                return;
 651        }
 652
 653        m_free += m_bufs + m_cached + m_slab;
 654        switch (s->opt) {
 655        case 'f':
 656                scale(m_free << 10); break;
 657        default:
 658                scale((m_total - m_free) << 10); break;
 659        }
 660}
 661
 662static s_stat* init_mem(const char *param)
 663{
 664        mem_stat *s = xzalloc(sizeof(*s));
 665        s->collect = collect_mem;
 666        s->opt = param[0];
 667        return (s_stat*)s;
 668}
 669
 670
 671S_STAT(swp_stat)
 672S_STAT_END(swp_stat)
 673
 674static void collect_swp(swp_stat *s UNUSED_PARAM)
 675{
 676        ullong s_total[1];
 677        ullong s_free[1];
 678        if (rdval(get_file(&proc_meminfo), "SwapTotal:", s_total, 1)
 679         || rdval(proc_meminfo.file,       "SwapFree:" , s_free,  1)
 680        ) {
 681                put_question_marks(4);
 682                return;
 683        }
 684        scale((s_total[0]-s_free[0]) << 10);
 685}
 686
 687static s_stat* init_swp(const char *param UNUSED_PARAM)
 688{
 689        swp_stat *s = xzalloc(sizeof(*s));
 690        s->collect = collect_swp;
 691        return (s_stat*)s;
 692}
 693
 694
 695S_STAT(fd_stat)
 696S_STAT_END(fd_stat)
 697
 698static void collect_fd(fd_stat *s UNUSED_PARAM)
 699{
 700        ullong data[2];
 701
 702        if (rdval(get_file(&proc_sys_fs_filenr), "", data, 1, 2)) {
 703                put_question_marks(4);
 704                return;
 705        }
 706
 707        scale(data[0] - data[1]);
 708}
 709
 710static s_stat* init_fd(const char *param UNUSED_PARAM)
 711{
 712        fd_stat *s = xzalloc(sizeof(*s));
 713        s->collect = collect_fd;
 714        return (s_stat*)s;
 715}
 716
 717
 718S_STAT(time_stat)
 719        int prec;
 720        int scale;
 721S_STAT_END(time_stat)
 722
 723static void collect_time(time_stat *s)
 724{
 725        char buf[sizeof("12:34:56.123456")];
 726        struct tm* tm;
 727        int us = tv.tv_usec + s->scale/2;
 728        time_t t = tv.tv_sec;
 729
 730        if (us >= 1000000) {
 731                t++;
 732                us -= 1000000;
 733        }
 734        tm = localtime(&t);
 735
 736        sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
 737        if (s->prec)
 738                sprintf(buf+8, ".%0*d", s->prec, us / s->scale);
 739        put(buf);
 740}
 741
 742static s_stat* init_time(const char *param)
 743{
 744        int prec;
 745        time_stat *s = xzalloc(sizeof(*s));
 746
 747        s->collect = collect_time;
 748        prec = param[0] - '0';
 749        if (prec < 0) prec = 0;
 750        else if (prec > 6) prec = 6;
 751        s->prec = prec;
 752        s->scale = 1;
 753        while (prec++ < 6)
 754                s->scale *= 10;
 755        return (s_stat*)s;
 756}
 757
 758static void collect_info(s_stat *s)
 759{
 760        gen ^= 1;
 761        while (s) {
 762                put(s->label);
 763                s->collect(s);
 764                s = s->next;
 765        }
 766}
 767
 768
 769typedef s_stat* init_func(const char *param);
 770
 771static const char options[] ALIGN1 = "ncmsfixptbdr";
 772static init_func *const init_functions[] = {
 773        init_if,
 774        init_cpu,
 775        init_mem,
 776        init_swp,
 777        init_fd,
 778        init_int,
 779        init_ctx,
 780        init_fork,
 781        init_time,
 782        init_blk,
 783        init_delay,
 784        init_cr
 785};
 786
 787int nmeter_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 788int nmeter_main(int argc, char **argv)
 789{
 790        char buf[32];
 791        s_stat *first = NULL;
 792        s_stat *last = NULL;
 793        s_stat *s;
 794        char *cur, *prev;
 795
 796        INIT_G();
 797
 798        xchdir("/proc");
 799
 800        if (argc != 2)
 801                bb_show_usage();
 802
 803        if (open_read_close("version", buf, sizeof(buf)-1) > 0) {
 804                buf[sizeof(buf)-1] = '\0';
 805                is26 = (strstr(buf, " 2.4.") == NULL);
 806        }
 807
 808        // Can use argv[1] directly, but this will mess up
 809        // parameters as seen by e.g. ps. Making a copy...
 810        cur = xstrdup(argv[1]);
 811        while (1) {
 812                char *param, *p;
 813                prev = cur;
 814 again:
 815                cur = strchr(cur, '%');
 816                if (!cur)
 817                        break;
 818                if (cur[1] == '%') {    // %%
 819                        overlapping_strcpy(cur, cur + 1);
 820                        cur++;
 821                        goto again;
 822                }
 823                *cur++ = '\0';          // overwrite %
 824                if (cur[0] == '[') {
 825                        // format: %[foptstring]
 826                        cur++;
 827                        p = strchr(options, cur[0]);
 828                        param = cur+1;
 829                        while (cur[0] != ']') {
 830                                if (!cur[0])
 831                                        bb_show_usage();
 832                                cur++;
 833                        }
 834                        *cur++ = '\0';  // overwrite [
 835                } else {
 836                        // format: %NNNNNNf
 837                        param = cur;
 838                        while (cur[0] >= '0' && cur[0] <= '9')
 839                                cur++;
 840                        if (!cur[0])
 841                                bb_show_usage();
 842                        p = strchr(options, cur[0]);
 843                        *cur++ = '\0';  // overwrite format char
 844                }
 845                if (!p)
 846                        bb_show_usage();
 847                s = init_functions[p-options](param);
 848                if (s) {
 849                        s->label = prev;
 850                        /*s->next = NULL; - all initXXX funcs use xzalloc */
 851                        if (!first)
 852                                first = s;
 853                        else
 854                                last->next = s;
 855                        last = s;
 856                } else {
 857                        // %NNNNd or %r option. remove it from string
 858                        strcpy(prev + strlen(prev), cur);
 859                        cur = prev;
 860                }
 861        }
 862        if (prev[0]) {
 863                s = init_literal();
 864                s->label = prev;
 865                /*s->next = NULL; - all initXXX funcs use xzalloc */
 866                if (!first)
 867                        first = s;
 868                else
 869                        last->next = s;
 870                last = s;
 871        }
 872
 873        // Generate first samples but do not print them, they're bogus
 874        collect_info(first);
 875        reset_outbuf();
 876        if (delta >= 0) {
 877                gettimeofday(&tv, NULL);
 878                usleep(delta > 1000000 ? 1000000 : delta - tv.tv_usec%deltanz);
 879        }
 880
 881        while (1) {
 882                gettimeofday(&tv, NULL);
 883                collect_info(first);
 884                put(final_str);
 885                print_outbuf();
 886
 887                // Negative delta -> no usleep at all
 888                // This will hog the CPU but you can have REALLY GOOD
 889                // time resolution ;)
 890                // TODO: detect and avoid useless updates
 891                // (like: nothing happens except time)
 892                if (delta >= 0) {
 893                        int rem;
 894                        // can be commented out, will sacrifice sleep time precision a bit
 895                        gettimeofday(&tv, NULL);
 896                        if (need_seconds)
 897                                rem = delta - ((ullong)tv.tv_sec*1000000 + tv.tv_usec) % deltanz;
 898                        else
 899                                rem = delta - tv.tv_usec%deltanz;
 900                        // Sometimes kernel wakes us up just a tiny bit earlier than asked
 901                        // Do not go to very short sleep in this case
 902                        if (rem < delta/128) {
 903                                rem += delta;
 904                        }
 905                        usleep(rem);
 906                }
 907        }
 908
 909        /*return 0;*/
 910}
 911