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