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