awk: Fix handling of functions with empty body
[oweals/busybox.git] / procps / nmeter.c
index 573052921eb44b76a06b5b94b1a8575eea787b0d..6a3b3274382feee6d67b8e0dc7ba07816f1c6fe5 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;
 
@@ -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;
@@ -223,33 +271,59 @@ static int rdval_loadavg(const char* p, ullong *vec, ...)
 }
 
 // Parses /proc/diskstats
-//   1  2 3   4         5        6(rd)  7      8     9     10(wr) 11     12 13     14
+//   1  2 3   4     5     6(rd)  7      8     9     10(wr) 11     12 13     14
 //   3  0 hda 51292 14441 841783 926052 25717 79650 843256 3029804 0 148459 3956933
 //   3  1 hda1 0 0 0 0 <- ignore if only 4 fields
+// Linux 3.0 (maybe earlier) started printing full stats for hda1 too.
+// Had to add code which skips such devices.
 static int rdval_diskstats(const char* p, ullong *vec)
 {
-       ullong rd = 0; // to avoid "warning: 'rd' might be used uninitialized"
-       int indexline = 0;
+       char devname[32];
+       unsigned devname_len = 0;
+       int value_idx = 0;
+
        vec[0] = 0;
        vec[1] = 0;
        while (1) {
-               indexline++;
-               while (*p == ' ' || *p == '\t') p++;
-               if (*p == '\0') break;
+               value_idx++;
+               while (*p == ' ' || *p == '\t')
+                       p++;
+               if (*p == '\0')
+                       break;
                if (*p == '\n') {
-                       indexline = 0;
+                       value_idx = 0;
                        p++;
                        continue;
                }
-               if (indexline == 6) {
-                       rd = strtoull(p, NULL, 10);
-               } else if (indexline == 10) {
-                       vec[0] += rd;  // TODO: *sectorsize (don't know how to find out sectorsize)
+               if (value_idx == 3) {
+                       char *end = strchrnul(p, ' ');
+                       /* If this a hda1-like device (same prefix as last one + digit)? */
+                       if (devname_len && strncmp(devname, p, devname_len) == 0 && isdigit(p[devname_len])) {
+                               p = end;
+                               goto skip_line; /* skip entire line */
+                       }
+                       /* It is not. Remember the name for future checks */
+                       devname_len = end - p;
+                       if (devname_len > sizeof(devname)-1)
+                               devname_len = sizeof(devname)-1;
+                       strncpy(devname, p, devname_len);
+                       /* devname[devname_len] = '\0'; - not really needed */
+                       p = end;
+               } else
+               if (value_idx == 6) {
+                       // TODO: *sectorsize (don't know how to find out sectorsize)
+                       vec[0] += strtoull(p, NULL, 10);
+               } else
+               if (value_idx == 10) {
+                       // TODO: *sectorsize (don't know how to find out sectorsize)
                        vec[1] += strtoull(p, NULL, 10);
-                       while (*p != '\n' && *p != '\0') p++;
+ skip_line:
+                       while (*p != '\n' && *p != '\0')
+                               p++;
                        continue;
                }
-               while (*p > ' ') p++; // skip over value
+               while ((unsigned char)(*p) > ' ') // skip over value
+                       p++;
        }
        return 0;
 }
@@ -268,33 +342,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;
@@ -312,7 +386,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 };
@@ -369,13 +443,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;
 }
@@ -386,7 +460,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;
@@ -404,13 +478,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;
 }
@@ -420,7 +494,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;
@@ -436,9 +510,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;
 }
@@ -449,7 +523,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;
@@ -478,9 +552,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;
@@ -491,7 +565,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];
 
@@ -502,7 +576,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;
@@ -520,7 +594,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 {
@@ -536,7 +610,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;
@@ -560,16 +634,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;
 }
 
@@ -613,7 +685,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;
@@ -625,7 +697,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;
        }
@@ -650,7 +722,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;
@@ -660,7 +732,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];
@@ -673,9 +745,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;
 }
@@ -684,7 +756,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];
 
@@ -696,9 +768,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;
 }
@@ -709,7 +781,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;
@@ -731,10 +803,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;
@@ -744,7 +816,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) {
@@ -757,6 +829,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,
@@ -774,27 +847,34 @@ static init_func *const init_functions[] = {
 };
 
 int nmeter_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int nmeter_main(int argc, char **argv)
+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;
@@ -803,7 +883,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;
                }
@@ -834,7 +914,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
@@ -849,7 +929,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