add INIT_G()'s. No code changes.
[oweals/busybox.git] / procps / nmeter.c
index 127a3d1c132b78ba866e8f3464879da5962cacfd..99995598225272e0762966ddfdbe047cb124341a 100644 (file)
@@ -1,9 +1,44 @@
 /*
-** Licensed under the GPL v2, see the file LICENSE in this tarball
-**
-** Based on nanotop.c from floppyfw project
-**
-** Contact me: vda.linux@googlemail.com */
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ *
+ * Based on nanotop.c from floppyfw project
+ *
+ * Contact me: vda.linux@googlemail.com
+ */
+
+//config:config NMETER
+//config:      bool "nmeter"
+//config:      default y
+//config:      help
+//config:        Prints selected system stats continuously, one line per update.
+
+//applet:IF_NMETER(APPLET(nmeter, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_NMETER) += nmeter.o
+
+//usage:#define nmeter_trivial_usage
+//usage:       "[-d MSEC] FORMAT_STRING"
+//usage:#define nmeter_full_usage "\n\n"
+//usage:       "Monitor system in real time"
+//usage:     "\n"
+//usage:     "\n -d MSEC       Milliseconds between updates (default:1000)"
+//usage:     "\n"
+//usage:     "\nFormat specifiers:"
+//usage:     "\n %Nc or %[cN]  CPU. N - bar size (default:10)"
+//usage:     "\n               (displays: S:system U:user N:niced D:iowait I:irq i:softirq)"
+//usage:     "\n %[nINTERFACE] Network INTERFACE"
+//usage:     "\n %m            Allocated memory"
+//usage:     "\n %[mf]         Free memory"
+//usage:     "\n %[mt]         Total memory"
+//usage:     "\n %s            Allocated swap"
+//usage:     "\n %f            Number of used file descriptors"
+//usage:     "\n %Ni           Total/specific IRQ rate"
+//usage:     "\n %x            Context switch rate"
+//usage:     "\n %p            Forks"
+//usage:     "\n %[pn]         # of processes"
+//usage:     "\n %b            Block io"
+//usage:     "\n %Nt           Time (with N decimal points)"
+//usage:     "\n %r            Print <cr> instead of <lf> at EOL"
 
 //TODO:
 // simplify code
 //  totalram=2107416576, freeram=211525632, sharedram=0, bufferram=157204480}
 //  totalswap=134209536, freeswap=134209536, procs=157})
 
-#include <time.h>
 #include "libbb.h"
 
 typedef unsigned long long ullong;
 
-enum { PROC_FILE_SIZE = 4096 };
+enum {  /* Preferably use powers of 2 */
+       PROC_MIN_FILE_SIZE = 256,
+       PROC_MAX_FILE_SIZE = 16 * 1024,
+};
 
 typedef struct proc_file {
        char *file;
-       //const char *name;
+       int file_sz;
        smallint last_gen;
 } proc_file;
 
@@ -75,11 +112,11 @@ struct globals {
 #define proc_diskstats     (G.proc_diskstats    )
 #define proc_sys_fs_filenr (G.proc_sys_fs_filenr)
 #define INIT_G() do { \
-               PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
-               cur_outbuf = outbuf; \
-               final_str = "\n"; \
-               deltanz = delta = 1000000; \
-       } while (0)
+       SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+       cur_outbuf = outbuf; \
+       final_str = "\n"; \
+       deltanz = delta = 1000000; \
+} while (0)
 
 // We depend on this being a char[], not char* - we take sizeof() of it
 #define outbuf bb_common_bufsiz1
@@ -98,7 +135,7 @@ static void print_outbuf(void)
 {
        int sz = cur_outbuf - outbuf;
        if (sz > 0) {
-               xwrite(1, outbuf, sz);
+               xwrite(STDOUT_FILENO, outbuf, sz);
                cur_outbuf = outbuf;
        }
 }
@@ -124,36 +161,47 @@ static void put_question_marks(int count)
                put_c('?');
 }
 
-static void readfile_z(char *buf, int sz, const char* fname)
+static void readfile_z(proc_file *pf, const char* fname)
 {
 // open_read_close() will do two reads in order to be sure we are at EOF,
 // and we don't need/want that.
-//     sz = open_read_close(fname, buf, sz-1);
-
-       int fd = xopen(fname, O_RDONLY);
+       int fd;
+       int sz, rdsz;
+       char *buf;
+
+       sz = pf->file_sz;
+       buf = pf->file;
+       if (!buf) {
+               buf = xmalloc(PROC_MIN_FILE_SIZE);
+               sz = PROC_MIN_FILE_SIZE;
+       }
+ again:
+       fd = xopen(fname, O_RDONLY);
        buf[0] = '\0';
-       if (fd >= 0) {
-               sz = read(fd, buf, sz-1);
-               if (sz > 0) buf[sz] = '\0';
-               close(fd);
+       rdsz = read(fd, buf, sz-1);
+       close(fd);
+       if (rdsz > 0) {
+               if (rdsz == sz-1 && sz < PROC_MAX_FILE_SIZE) {
+                       sz *= 2;
+                       buf = xrealloc(buf, sz);
+                       goto again;
+               }
+               buf[rdsz] = '\0';
        }
+       pf->file_sz = sz;
+       pf->file = buf;
 }
 
 static const char* get_file(proc_file *pf)
 {
        if (pf->last_gen != gen) {
                pf->last_gen = gen;
-               // We allocate PROC_FILE_SIZE bytes. This wastes memory,
-               // but allows us to allocate only once (at first sample)
-               // per proc file, and reuse buffer for each sample
-               if (!pf->file)
-                       pf->file = xmalloc(PROC_FILE_SIZE);
-               readfile_z(pf->file, PROC_FILE_SIZE, proc_name[pf - &first_proc_file]);
+               readfile_z(pf, proc_name[pf - &first_proc_file]);
        }
        return pf->file;
 }
 
-static inline ullong read_after_slash(const char *p)
+static ullong read_after_slash(const char *p)
 {
        p = strchr(p, '/');
        if (!p) return 0;
@@ -228,7 +276,7 @@ static int rdval_loadavg(const char* p, ullong *vec, ...)
 //   3  1 hda1 0 0 0 0 <- ignore if only 4 fields
 static int rdval_diskstats(const char* p, ullong *vec)
 {
-       ullong rd = 0; // to avoid "warning: 'rd' might be used uninitialized"
+       ullong rd = rd; // for compiler
        int indexline = 0;
        vec[0] = 0;
        vec[1] = 0;
@@ -257,7 +305,10 @@ static int rdval_diskstats(const char* p, ullong *vec)
 static void scale(ullong ul)
 {
        char buf[5];
-       smart_ulltoa5(ul, buf);
+
+       /* see http://en.wikipedia.org/wiki/Tera */
+       smart_ulltoa4(ul, buf, " kmgtpezy");
+       buf[4] = '\0';
        put(buf);
 }
 
@@ -265,33 +316,33 @@ static void scale(ullong ul)
 #define S_STAT(a) \
 typedef struct a { \
        struct s_stat *next; \
-       void (*collect)(struct a *s); \
+       void (*collect)(struct a *s) FAST_FUNC; \
        const char *label;
 #define S_STAT_END(a) } a;
 
 S_STAT(s_stat)
 S_STAT_END(s_stat)
 
-static void collect_literal(s_stat *s)
+static void FAST_FUNC collect_literal(s_stat *s UNUSED_PARAM)
 {
 }
 
 static s_stat* init_literal(void)
 {
-       s_stat *s = xmalloc(sizeof(s_stat));
+       s_stat *s = xzalloc(sizeof(*s));
        s->collect = collect_literal;
        return (s_stat*)s;
 }
 
 static s_stat* init_delay(const char *param)
 {
-       delta = bb_strtoi(param, NULL, 0) * 1000;
+       delta = strtoul(param, NULL, 0) * 1000; /* param can be "" */
        deltanz = delta > 0 ? delta : 1;
        need_seconds = (1000000%deltanz) != 0;
        return NULL;
 }
 
-static s_stat* init_cr(const char *param)
+static s_stat* init_cr(const char *param UNUSED_PARAM)
 {
        final_str = "\r";
        return (s_stat*)0;
@@ -309,7 +360,7 @@ S_STAT(cpu_stat)
 S_STAT_END(cpu_stat)
 
 
-static void collect_cpu(cpu_stat *s)
+static void FAST_FUNC collect_cpu(cpu_stat *s)
 {
        ullong data[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
        unsigned frac[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
@@ -366,13 +417,13 @@ static void collect_cpu(cpu_stat *s)
 static s_stat* init_cpu(const char *param)
 {
        int sz;
-       cpu_stat *s = xmalloc(sizeof(cpu_stat));
+       cpu_stat *s = xzalloc(sizeof(*s));
        s->collect = collect_cpu;
-       sz = strtol(param, NULL, 0);
+       sz = strtoul(param, NULL, 0); /* param can be "" */
        if (sz < 10) sz = 10;
        if (sz > 1000) sz = 1000;
-       s->bar = xmalloc(sz+1);
-       s->bar[sz] = '\0';
+       s->bar = xzalloc(sz+1);
+       /*s->bar[sz] = '\0'; - xzalloc did it */
        s->bar_sz = sz;
        return (s_stat*)s;
 }
@@ -383,7 +434,7 @@ S_STAT(int_stat)
        int no;
 S_STAT_END(int_stat)
 
-static void collect_int(int_stat *s)
+static void FAST_FUNC collect_int(int_stat *s)
 {
        ullong data[1];
        ullong old;
@@ -401,13 +452,13 @@ static void collect_int(int_stat *s)
 
 static s_stat* init_int(const char *param)
 {
-       int_stat *s = xmalloc(sizeof(int_stat));
+       int_stat *s = xzalloc(sizeof(*s));
        s->collect = collect_int;
-       if (param[0]=='\0') {
+       if (param[0] == '\0') {
                s->no = 1;
        } else {
-               int n = strtoul(param, NULL, 0);
-               s->no = n+2;
+               int n = xatoi_positive(param);
+               s->no = n + 2;
        }
        return (s_stat*)s;
 }
@@ -417,7 +468,7 @@ S_STAT(ctx_stat)
        ullong old;
 S_STAT_END(ctx_stat)
 
-static void collect_ctx(ctx_stat *s)
+static void FAST_FUNC collect_ctx(ctx_stat *s)
 {
        ullong data[1];
        ullong old;
@@ -433,9 +484,9 @@ static void collect_ctx(ctx_stat *s)
        scale(data[0] - old);
 }
 
-static s_stat* init_ctx(const char *param)
+static s_stat* init_ctx(const char *param UNUSED_PARAM)
 {
-       ctx_stat *s = xmalloc(sizeof(ctx_stat));
+       ctx_stat *s = xzalloc(sizeof(*s));
        s->collect = collect_ctx;
        return (s_stat*)s;
 }
@@ -446,7 +497,7 @@ S_STAT(blk_stat)
        ullong old[2];
 S_STAT_END(blk_stat)
 
-static void collect_blk(blk_stat *s)
+static void FAST_FUNC collect_blk(blk_stat *s)
 {
        ullong data[2];
        int i;
@@ -475,9 +526,9 @@ static void collect_blk(blk_stat *s)
        scale(data[1]*512);
 }
 
-static s_stat* init_blk(const char *param)
+static s_stat* init_blk(const char *param UNUSED_PARAM)
 {
-       blk_stat *s = xmalloc(sizeof(blk_stat));
+       blk_stat *s = xzalloc(sizeof(*s));
        s->collect = collect_blk;
        s->lookfor = "page";
        return (s_stat*)s;
@@ -488,7 +539,7 @@ S_STAT(fork_stat)
        ullong old;
 S_STAT_END(fork_stat)
 
-static void collect_thread_nr(fork_stat *s)
+static void FAST_FUNC collect_thread_nr(fork_stat *s UNUSED_PARAM)
 {
        ullong data[1];
 
@@ -499,7 +550,7 @@ static void collect_thread_nr(fork_stat *s)
        scale(data[0]);
 }
 
-static void collect_fork(fork_stat *s)
+static void FAST_FUNC collect_fork(fork_stat *s)
 {
        ullong data[1];
        ullong old;
@@ -517,7 +568,7 @@ static void collect_fork(fork_stat *s)
 
 static s_stat* init_fork(const char *param)
 {
-       fork_stat *s = xmalloc(sizeof(fork_stat));
+       fork_stat *s = xzalloc(sizeof(*s));
        if (*param == 'n') {
                s->collect = collect_thread_nr;
        } else {
@@ -533,7 +584,7 @@ S_STAT(if_stat)
        char *device_colon;
 S_STAT_END(if_stat)
 
-static void collect_if(if_stat *s)
+static void FAST_FUNC collect_if(if_stat *s)
 {
        ullong data[4];
        int i;
@@ -557,16 +608,14 @@ static void collect_if(if_stat *s)
 
 static s_stat* init_if(const char *device)
 {
-       if_stat *s = xmalloc(sizeof(if_stat));
+       if_stat *s = xzalloc(sizeof(*s));
 
        if (!device || !device[0])
                bb_show_usage();
        s->collect = collect_if;
 
        s->device = device;
-       s->device_colon = xmalloc(strlen(device)+2);
-       strcpy(s->device_colon, device);
-       strcat(s->device_colon, ":");
+       s->device_colon = xasprintf("%s:", device);
        return (s_stat*)s;
 }
 
@@ -610,7 +659,7 @@ S_STAT_END(mem_stat)
 //HugePages_Total:     0
 //HugePages_Free:      0
 //Hugepagesize:     4096 kB
-static void collect_mem(mem_stat *s)
+static void FAST_FUNC collect_mem(mem_stat *s)
 {
        ullong m_total = 0;
        ullong m_free = 0;
@@ -622,7 +671,7 @@ static void collect_mem(mem_stat *s)
                put_question_marks(4);
                return;
        }
-       if (s->opt == 'f') {
+       if (s->opt == 't') {
                scale(m_total << 10);
                return;
        }
@@ -647,7 +696,7 @@ static void collect_mem(mem_stat *s)
 
 static s_stat* init_mem(const char *param)
 {
-       mem_stat *s = xmalloc(sizeof(mem_stat));
+       mem_stat *s = xzalloc(sizeof(*s));
        s->collect = collect_mem;
        s->opt = param[0];
        return (s_stat*)s;
@@ -657,7 +706,7 @@ static s_stat* init_mem(const char *param)
 S_STAT(swp_stat)
 S_STAT_END(swp_stat)
 
-static void collect_swp(swp_stat *s)
+static void FAST_FUNC collect_swp(swp_stat *s UNUSED_PARAM)
 {
        ullong s_total[1];
        ullong s_free[1];
@@ -670,9 +719,9 @@ static void collect_swp(swp_stat *s)
        scale((s_total[0]-s_free[0]) << 10);
 }
 
-static s_stat* init_swp(const char *param)
+static s_stat* init_swp(const char *param UNUSED_PARAM)
 {
-       swp_stat *s = xmalloc(sizeof(swp_stat));
+       swp_stat *s = xzalloc(sizeof(*s));
        s->collect = collect_swp;
        return (s_stat*)s;
 }
@@ -681,7 +730,7 @@ static s_stat* init_swp(const char *param)
 S_STAT(fd_stat)
 S_STAT_END(fd_stat)
 
-static void collect_fd(fd_stat *s)
+static void FAST_FUNC collect_fd(fd_stat *s UNUSED_PARAM)
 {
        ullong data[2];
 
@@ -693,9 +742,9 @@ static void collect_fd(fd_stat *s)
        scale(data[0] - data[1]);
 }
 
-static s_stat* init_fd(const char *param)
+static s_stat* init_fd(const char *param UNUSED_PARAM)
 {
-       fd_stat *s = xmalloc(sizeof(fd_stat));
+       fd_stat *s = xzalloc(sizeof(*s));
        s->collect = collect_fd;
        return (s_stat*)s;
 }
@@ -706,7 +755,7 @@ S_STAT(time_stat)
        int scale;
 S_STAT_END(time_stat)
 
-static void collect_time(time_stat *s)
+static void FAST_FUNC collect_time(time_stat *s)
 {
        char buf[sizeof("12:34:56.123456")];
        struct tm* tm;
@@ -728,10 +777,10 @@ static void collect_time(time_stat *s)
 static s_stat* init_time(const char *param)
 {
        int prec;
-       time_stat *s = xmalloc(sizeof(time_stat));
+       time_stat *s = xzalloc(sizeof(*s));
 
        s->collect = collect_time;
-       prec = param[0]-'0';
+       prec = param[0] - '0';
        if (prec < 0) prec = 0;
        else if (prec > 6) prec = 6;
        s->prec = prec;
@@ -741,7 +790,7 @@ static s_stat* init_time(const char *param)
        return (s_stat*)s;
 }
 
-static void collect_info(s_stat *s)
+static void FAST_FUNC collect_info(s_stat *s)
 {
        gen ^= 1;
        while (s) {
@@ -754,6 +803,7 @@ static void collect_info(s_stat *s)
 
 typedef s_stat* init_func(const char *param);
 
+// Deprecated %NNNd is to be removed, -d MSEC supersedes it
 static const char options[] ALIGN1 = "ncmsfixptbdr";
 static init_func *const init_functions[] = {
        init_if,
@@ -770,28 +820,35 @@ static init_func *const init_functions[] = {
        init_cr
 };
 
-int nmeter_main(int argc, char **argv);
-int nmeter_main(int argc, char **argv)
+int nmeter_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int nmeter_main(int argc UNUSED_PARAM, char **argv)
 {
        char buf[32];
        s_stat *first = NULL;
        s_stat *last = NULL;
        s_stat *s;
+       char *opt_d;
        char *cur, *prev;
 
        INIT_G();
 
        xchdir("/proc");
 
-       if (argc != 2)
-               bb_show_usage();
+       if (open_read_close("version", buf, sizeof(buf)-1) > 0) {
+               buf[sizeof(buf)-1] = '\0';
+               is26 = (strstr(buf, " 2.4.") == NULL);
+       }
 
-       if (open_read_close("version", buf, sizeof(buf)) > 0)
-               is26 = (strstr(buf, " 2.4.")==NULL);
+       if (getopt32(argv, "d:", &opt_d))
+               init_delay(opt_d);
+       argv += optind;
+
+       if (!argv[0])
+               bb_show_usage();
 
-       // Can use argv[1] directly, but this will mess up
+       // Can use argv[0] directly, but this will mess up
        // parameters as seen by e.g. ps. Making a copy...
-       cur = xstrdup(argv[1]);
+       cur = xstrdup(argv[0]);
        while (1) {
                char *param, *p;
                prev = cur;
@@ -800,7 +857,7 @@ int nmeter_main(int argc, char **argv)
                if (!cur)
                        break;
                if (cur[1] == '%') {    // %%
-                       strcpy(cur, cur+1);
+                       overlapping_strcpy(cur, cur + 1);
                        cur++;
                        goto again;
                }
@@ -831,7 +888,7 @@ int nmeter_main(int argc, char **argv)
                s = init_functions[p-options](param);
                if (s) {
                        s->label = prev;
-                       s->next = 0;
+                       /*s->next = NULL; - all initXXX funcs use xzalloc */
                        if (!first)
                                first = s;
                        else
@@ -846,7 +903,7 @@ int nmeter_main(int argc, char **argv)
        if (prev[0]) {
                s = init_literal();
                s->label = prev;
-               s->next = 0;
+               /*s->next = NULL; - all initXXX funcs use xzalloc */
                if (!first)
                        first = s;
                else