summaryrefslogtreecommitdiff
path: root/release/src/router/busybox/procps/nmeter.c
diff options
context:
space:
mode:
Diffstat (limited to 'release/src/router/busybox/procps/nmeter.c')
-rw-r--r--release/src/router/busybox/procps/nmeter.c910
1 files changed, 910 insertions, 0 deletions
diff --git a/release/src/router/busybox/procps/nmeter.c b/release/src/router/busybox/procps/nmeter.c
new file mode 100644
index 00000000..0358ccd3
--- /dev/null
+++ b/release/src/router/busybox/procps/nmeter.c
@@ -0,0 +1,910 @@
+/*
+** 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 */
+
+//TODO:
+// simplify code
+// /proc/locks
+// /proc/stat:
+// disk_io: (3,0):(22272,17897,410702,4375,54750)
+// btime 1059401962
+//TODO: use sysinfo libc call/syscall, if appropriate
+// (faster than open/read/close):
+// sysinfo({uptime=15017, loads=[5728, 15040, 16480]
+// totalram=2107416576, freeram=211525632, sharedram=0, bufferram=157204480}
+// totalswap=134209536, freeswap=134209536, procs=157})
+
+#include "libbb.h"
+
+typedef unsigned long long ullong;
+
+enum { /* Preferably use powers of 2 */
+ PROC_MIN_FILE_SIZE = 256,
+ PROC_MAX_FILE_SIZE = 16 * 1024,
+};
+
+typedef struct proc_file {
+ char *file;
+ int file_sz;
+ smallint last_gen;
+} proc_file;
+
+static const char *const proc_name[] = {
+ "stat", // Must match the order of proc_file's!
+ "loadavg",
+ "net/dev",
+ "meminfo",
+ "diskstats",
+ "sys/fs/file-nr"
+};
+
+struct globals {
+ // Sample generation flip-flop
+ smallint gen;
+ // Linux 2.6? (otherwise assumes 2.4)
+ smallint is26;
+ // 1 if sample delay is not an integer fraction of a second
+ smallint need_seconds;
+ char *cur_outbuf;
+ const char *final_str;
+ int delta;
+ int deltanz;
+ struct timeval tv;
+#define first_proc_file proc_stat
+ proc_file proc_stat; // Must match the order of proc_name's!
+ proc_file proc_loadavg;
+ proc_file proc_net_dev;
+ proc_file proc_meminfo;
+ proc_file proc_diskstats;
+ proc_file proc_sys_fs_filenr;
+};
+#define G (*ptr_to_globals)
+#define gen (G.gen )
+#define is26 (G.is26 )
+#define need_seconds (G.need_seconds )
+#define cur_outbuf (G.cur_outbuf )
+#define final_str (G.final_str )
+#define delta (G.delta )
+#define deltanz (G.deltanz )
+#define tv (G.tv )
+#define proc_stat (G.proc_stat )
+#define proc_loadavg (G.proc_loadavg )
+#define proc_net_dev (G.proc_net_dev )
+#define proc_meminfo (G.proc_meminfo )
+#define proc_diskstats (G.proc_diskstats )
+#define proc_sys_fs_filenr (G.proc_sys_fs_filenr)
+#define INIT_G() do { \
+ 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
+
+static inline void reset_outbuf(void)
+{
+ cur_outbuf = outbuf;
+}
+
+static inline int outbuf_count(void)
+{
+ return cur_outbuf - outbuf;
+}
+
+static void print_outbuf(void)
+{
+ int sz = cur_outbuf - outbuf;
+ if (sz > 0) {
+ xwrite(STDOUT_FILENO, outbuf, sz);
+ cur_outbuf = outbuf;
+ }
+}
+
+static void put(const char *s)
+{
+ int sz = strlen(s);
+ if (sz > outbuf + sizeof(outbuf) - cur_outbuf)
+ sz = outbuf + sizeof(outbuf) - cur_outbuf;
+ memcpy(cur_outbuf, s, sz);
+ cur_outbuf += sz;
+}
+
+static void put_c(char c)
+{
+ if (cur_outbuf < outbuf + sizeof(outbuf))
+ *cur_outbuf++ = c;
+}
+
+static void put_question_marks(int count)
+{
+ while (count--)
+ put_c('?');
+}
+
+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.
+ 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';
+ 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;
+ readfile_z(pf, proc_name[pf - &first_proc_file]);
+ }
+ return pf->file;
+}
+
+static ullong read_after_slash(const char *p)
+{
+ p = strchr(p, '/');
+ if (!p) return 0;
+ return strtoull(p+1, NULL, 10);
+}
+
+enum conv_type { conv_decimal, conv_slash };
+
+// Reads decimal values from line. Values start after key, for example:
+// "cpu 649369 0 341297 4336769..." - key is "cpu" here.
+// Values are stored in vec[]. arg_ptr has list of positions
+// we are interested in: for example: 1,2,5 - we want 1st, 2nd and 5th value.
+static int vrdval(const char* p, const char* key,
+ enum conv_type conv, ullong *vec, va_list arg_ptr)
+{
+ int indexline;
+ int indexnext;
+
+ p = strstr(p, key);
+ if (!p) return 1;
+
+ p += strlen(key);
+ indexline = 1;
+ indexnext = va_arg(arg_ptr, int);
+ while (1) {
+ while (*p == ' ' || *p == '\t') p++;
+ if (*p == '\n' || *p == '\0') break;
+
+ if (indexline == indexnext) { // read this value
+ *vec++ = conv==conv_decimal ?
+ strtoull(p, NULL, 10) :
+ read_after_slash(p);
+ indexnext = va_arg(arg_ptr, int);
+ }
+ while (*p > ' ') p++; // skip over value
+ indexline++;
+ }
+ return 0;
+}
+
+// Parses files with lines like "cpu0 21727 0 15718 1813856 9461 10485 0 0":
+// rdval(file_contents, "string_to_find", result_vector, value#, value#...)
+// value# start with 1
+static int rdval(const char* p, const char* key, ullong *vec, ...)
+{
+ va_list arg_ptr;
+ int result;
+
+ va_start(arg_ptr, vec);
+ result = vrdval(p, key, conv_decimal, vec, arg_ptr);
+ va_end(arg_ptr);
+
+ return result;
+}
+
+// Parses files with lines like "... ... ... 3/148 ...."
+static int rdval_loadavg(const char* p, ullong *vec, ...)
+{
+ va_list arg_ptr;
+ int result;
+
+ va_start(arg_ptr, vec);
+ result = vrdval(p, "", conv_slash, vec, arg_ptr);
+ va_end(arg_ptr);
+
+ return result;
+}
+
+// Parses /proc/diskstats
+// 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
+static int rdval_diskstats(const char* p, ullong *vec)
+{
+ ullong rd = rd; // for compiler
+ int indexline = 0;
+ vec[0] = 0;
+ vec[1] = 0;
+ while (1) {
+ indexline++;
+ while (*p == ' ' || *p == '\t') p++;
+ if (*p == '\0') break;
+ if (*p == '\n') {
+ indexline = 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)
+ vec[1] += strtoull(p, NULL, 10);
+ while (*p != '\n' && *p != '\0') p++;
+ continue;
+ }
+ while (*p > ' ') p++; // skip over value
+ }
+ return 0;
+}
+
+static void scale(ullong ul)
+{
+ char buf[5];
+
+ /* see http://en.wikipedia.org/wiki/Tera */
+ smart_ulltoa4(ul, buf, " kmgtpezy");
+ buf[4] = '\0';
+ put(buf);
+}
+
+
+#define S_STAT(a) \
+typedef struct a { \
+ struct s_stat *next; \
+ void (*collect)(struct a *s); \
+ 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 UNUSED_PARAM)
+{
+}
+
+static s_stat* init_literal(void)
+{
+ s_stat *s = xzalloc(sizeof(*s));
+ s->collect = collect_literal;
+ return (s_stat*)s;
+}
+
+static s_stat* init_delay(const char *param)
+{
+ 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 UNUSED_PARAM)
+{
+ final_str = "\r";
+ return (s_stat*)0;
+}
+
+
+// user nice system idle iowait irq softirq (last 3 only in 2.6)
+//cpu 649369 0 341297 4336769 11640 7122 1183
+//cpuN 649369 0 341297 4336769 11640 7122 1183
+enum { CPU_FIELDCNT = 7 };
+S_STAT(cpu_stat)
+ ullong old[CPU_FIELDCNT];
+ int bar_sz;
+ char *bar;
+S_STAT_END(cpu_stat)
+
+
+static void 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 };
+ ullong all = 0;
+ int norm_all = 0;
+ int bar_sz = s->bar_sz;
+ char *bar = s->bar;
+ int i;
+
+ if (rdval(get_file(&proc_stat), "cpu ", data, 1, 2, 3, 4, 5, 6, 7)) {
+ put_question_marks(bar_sz);
+ return;
+ }
+
+ for (i = 0; i < CPU_FIELDCNT; i++) {
+ ullong old = s->old[i];
+ if (data[i] < old) old = data[i]; //sanitize
+ s->old[i] = data[i];
+ all += (data[i] -= old);
+ }
+
+ if (all) {
+ for (i = 0; i < CPU_FIELDCNT; i++) {
+ ullong t = bar_sz * data[i];
+ norm_all += data[i] = t / all;
+ frac[i] = t % all;
+ }
+
+ while (norm_all < bar_sz) {
+ unsigned max = frac[0];
+ int pos = 0;
+ for (i = 1; i < CPU_FIELDCNT; i++) {
+ if (frac[i] > max) max = frac[i], pos = i;
+ }
+ frac[pos] = 0; //avoid bumping up same value twice
+ data[pos]++;
+ norm_all++;
+ }
+
+ memset(bar, '.', bar_sz);
+ memset(bar, 'S', data[2]); bar += data[2]; //sys
+ memset(bar, 'U', data[0]); bar += data[0]; //usr
+ memset(bar, 'N', data[1]); bar += data[1]; //nice
+ memset(bar, 'D', data[4]); bar += data[4]; //iowait
+ memset(bar, 'I', data[5]); bar += data[5]; //irq
+ memset(bar, 'i', data[6]); bar += data[6]; //softirq
+ } else {
+ memset(bar, '?', bar_sz);
+ }
+ put(s->bar);
+}
+
+
+static s_stat* init_cpu(const char *param)
+{
+ int sz;
+ cpu_stat *s = xzalloc(sizeof(*s));
+ s->collect = collect_cpu;
+ sz = strtoul(param, NULL, 0); /* param can be "" */
+ if (sz < 10) sz = 10;
+ if (sz > 1000) sz = 1000;
+ s->bar = xzalloc(sz+1);
+ /*s->bar[sz] = '\0'; - xzalloc did it */
+ s->bar_sz = sz;
+ return (s_stat*)s;
+}
+
+
+S_STAT(int_stat)
+ ullong old;
+ int no;
+S_STAT_END(int_stat)
+
+static void collect_int(int_stat *s)
+{
+ ullong data[1];
+ ullong old;
+
+ if (rdval(get_file(&proc_stat), "intr", data, s->no)) {
+ put_question_marks(4);
+ return;
+ }
+
+ old = s->old;
+ if (data[0] < old) old = data[0]; //sanitize
+ s->old = data[0];
+ scale(data[0] - old);
+}
+
+static s_stat* init_int(const char *param)
+{
+ int_stat *s = xzalloc(sizeof(*s));
+ s->collect = collect_int;
+ if (param[0] == '\0') {
+ s->no = 1;
+ } else {
+ int n = xatoi_u(param);
+ s->no = n + 2;
+ }
+ return (s_stat*)s;
+}
+
+
+S_STAT(ctx_stat)
+ ullong old;
+S_STAT_END(ctx_stat)
+
+static void collect_ctx(ctx_stat *s)
+{
+ ullong data[1];
+ ullong old;
+
+ if (rdval(get_file(&proc_stat), "ctxt", data, 1)) {
+ put_question_marks(4);
+ return;
+ }
+
+ old = s->old;
+ if (data[0] < old) old = data[0]; //sanitize
+ s->old = data[0];
+ scale(data[0] - old);
+}
+
+static s_stat* init_ctx(const char *param UNUSED_PARAM)
+{
+ ctx_stat *s = xzalloc(sizeof(*s));
+ s->collect = collect_ctx;
+ return (s_stat*)s;
+}
+
+
+S_STAT(blk_stat)
+ const char* lookfor;
+ ullong old[2];
+S_STAT_END(blk_stat)
+
+static void collect_blk(blk_stat *s)
+{
+ ullong data[2];
+ int i;
+
+ if (is26) {
+ i = rdval_diskstats(get_file(&proc_diskstats), data);
+ } else {
+ i = rdval(get_file(&proc_stat), s->lookfor, data, 1, 2);
+ // Linux 2.4 reports bio in Kbytes, convert to sectors:
+ data[0] *= 2;
+ data[1] *= 2;
+ }
+ if (i) {
+ put_question_marks(9);
+ return;
+ }
+
+ for (i=0; i<2; i++) {
+ ullong old = s->old[i];
+ if (data[i] < old) old = data[i]; //sanitize
+ s->old[i] = data[i];
+ data[i] -= old;
+ }
+ scale(data[0]*512); // TODO: *sectorsize
+ put_c(' ');
+ scale(data[1]*512);
+}
+
+static s_stat* init_blk(const char *param UNUSED_PARAM)
+{
+ blk_stat *s = xzalloc(sizeof(*s));
+ s->collect = collect_blk;
+ s->lookfor = "page";
+ return (s_stat*)s;
+}
+
+
+S_STAT(fork_stat)
+ ullong old;
+S_STAT_END(fork_stat)
+
+static void collect_thread_nr(fork_stat *s UNUSED_PARAM)
+{
+ ullong data[1];
+
+ if (rdval_loadavg(get_file(&proc_loadavg), data, 4)) {
+ put_question_marks(4);
+ return;
+ }
+ scale(data[0]);
+}
+
+static void collect_fork(fork_stat *s)
+{
+ ullong data[1];
+ ullong old;
+
+ if (rdval(get_file(&proc_stat), "processes", data, 1)) {
+ put_question_marks(4);
+ return;
+ }
+
+ old = s->old;
+ if (data[0] < old) old = data[0]; //sanitize
+ s->old = data[0];
+ scale(data[0] - old);
+}
+
+static s_stat* init_fork(const char *param)
+{
+ fork_stat *s = xzalloc(sizeof(*s));
+ if (*param == 'n') {
+ s->collect = collect_thread_nr;
+ } else {
+ s->collect = collect_fork;
+ }
+ return (s_stat*)s;
+}
+
+
+S_STAT(if_stat)
+ ullong old[4];
+ const char *device;
+ char *device_colon;
+S_STAT_END(if_stat)
+
+static void collect_if(if_stat *s)
+{
+ ullong data[4];
+ int i;
+
+ if (rdval(get_file(&proc_net_dev), s->device_colon, data, 1, 3, 9, 11)) {
+ put_question_marks(10);
+ return;
+ }
+
+ for (i=0; i<4; i++) {
+ ullong old = s->old[i];
+ if (data[i] < old) old = data[i]; //sanitize
+ s->old[i] = data[i];
+ data[i] -= old;
+ }
+ put_c(data[1] ? '*' : ' ');
+ scale(data[0]);
+ put_c(data[3] ? '*' : ' ');
+ scale(data[2]);
+}
+
+static s_stat* init_if(const char *device)
+{
+ if_stat *s = xzalloc(sizeof(*s));
+
+ if (!device || !device[0])
+ bb_show_usage();
+ s->collect = collect_if;
+
+ s->device = device;
+ s->device_colon = xasprintf("%s:", device);
+ return (s_stat*)s;
+}
+
+
+S_STAT(mem_stat)
+ char opt;
+S_STAT_END(mem_stat)
+
+// "Memory" value should not include any caches.
+// IOW: neither "ls -laR /" nor heavy read/write activity
+// should affect it. We'd like to also include any
+// long-term allocated kernel-side mem, but it is hard
+// to figure out. For now, bufs, cached & slab are
+// counted as "free" memory
+//2.6.16:
+//MemTotal: 773280 kB
+//MemFree: 25912 kB - genuinely free
+//Buffers: 320672 kB - cache
+//Cached: 146396 kB - cache
+//SwapCached: 0 kB
+//Active: 183064 kB
+//Inactive: 356892 kB
+//HighTotal: 0 kB
+//HighFree: 0 kB
+//LowTotal: 773280 kB
+//LowFree: 25912 kB
+//SwapTotal: 131064 kB
+//SwapFree: 131064 kB
+//Dirty: 48 kB
+//Writeback: 0 kB
+//Mapped: 96620 kB
+//Slab: 200668 kB - takes 7 Mb on my box fresh after boot,
+// but includes dentries and inodes
+// (== can take arbitrary amount of mem)
+//CommitLimit: 517704 kB
+//Committed_AS: 236776 kB
+//PageTables: 1248 kB
+//VmallocTotal: 516052 kB
+//VmallocUsed: 3852 kB
+//VmallocChunk: 512096 kB
+//HugePages_Total: 0
+//HugePages_Free: 0
+//Hugepagesize: 4096 kB
+static void collect_mem(mem_stat *s)
+{
+ ullong m_total = 0;
+ ullong m_free = 0;
+ ullong m_bufs = 0;
+ ullong m_cached = 0;
+ ullong m_slab = 0;
+
+ if (rdval(get_file(&proc_meminfo), "MemTotal:", &m_total, 1)) {
+ put_question_marks(4);
+ return;
+ }
+ if (s->opt == 't') {
+ scale(m_total << 10);
+ return;
+ }
+
+ if (rdval(proc_meminfo.file, "MemFree:", &m_free , 1)
+ || rdval(proc_meminfo.file, "Buffers:", &m_bufs , 1)
+ || rdval(proc_meminfo.file, "Cached:", &m_cached, 1)
+ || rdval(proc_meminfo.file, "Slab:", &m_slab , 1)
+ ) {
+ put_question_marks(4);
+ return;
+ }
+
+ m_free += m_bufs + m_cached + m_slab;
+ switch (s->opt) {
+ case 'f':
+ scale(m_free << 10); break;
+ default:
+ scale((m_total - m_free) << 10); break;
+ }
+}
+
+static s_stat* init_mem(const char *param)
+{
+ mem_stat *s = xzalloc(sizeof(*s));
+ s->collect = collect_mem;
+ s->opt = param[0];
+ return (s_stat*)s;
+}
+
+
+S_STAT(swp_stat)
+S_STAT_END(swp_stat)
+
+static void collect_swp(swp_stat *s UNUSED_PARAM)
+{
+ ullong s_total[1];
+ ullong s_free[1];
+ if (rdval(get_file(&proc_meminfo), "SwapTotal:", s_total, 1)
+ || rdval(proc_meminfo.file, "SwapFree:" , s_free, 1)
+ ) {
+ put_question_marks(4);
+ return;
+ }
+ scale((s_total[0]-s_free[0]) << 10);
+}
+
+static s_stat* init_swp(const char *param UNUSED_PARAM)
+{
+ swp_stat *s = xzalloc(sizeof(*s));
+ s->collect = collect_swp;
+ return (s_stat*)s;
+}
+
+
+S_STAT(fd_stat)
+S_STAT_END(fd_stat)
+
+static void collect_fd(fd_stat *s UNUSED_PARAM)
+{
+ ullong data[2];
+
+ if (rdval(get_file(&proc_sys_fs_filenr), "", data, 1, 2)) {
+ put_question_marks(4);
+ return;
+ }
+
+ scale(data[0] - data[1]);
+}
+
+static s_stat* init_fd(const char *param UNUSED_PARAM)
+{
+ fd_stat *s = xzalloc(sizeof(*s));
+ s->collect = collect_fd;
+ return (s_stat*)s;
+}
+
+
+S_STAT(time_stat)
+ int prec;
+ int scale;
+S_STAT_END(time_stat)
+
+static void collect_time(time_stat *s)
+{
+ char buf[sizeof("12:34:56.123456")];
+ struct tm* tm;
+ int us = tv.tv_usec + s->scale/2;
+ time_t t = tv.tv_sec;
+
+ if (us >= 1000000) {
+ t++;
+ us -= 1000000;
+ }
+ tm = localtime(&t);
+
+ sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
+ if (s->prec)
+ sprintf(buf+8, ".%0*d", s->prec, us / s->scale);
+ put(buf);
+}
+
+static s_stat* init_time(const char *param)
+{
+ int prec;
+ time_stat *s = xzalloc(sizeof(*s));
+
+ s->collect = collect_time;
+ prec = param[0] - '0';
+ if (prec < 0) prec = 0;
+ else if (prec > 6) prec = 6;
+ s->prec = prec;
+ s->scale = 1;
+ while (prec++ < 6)
+ s->scale *= 10;
+ return (s_stat*)s;
+}
+
+static void collect_info(s_stat *s)
+{
+ gen ^= 1;
+ while (s) {
+ put(s->label);
+ s->collect(s);
+ s = s->next;
+ }
+}
+
+
+typedef s_stat* init_func(const char *param);
+
+static const char options[] ALIGN1 = "ncmsfixptbdr";
+static init_func *const init_functions[] = {
+ init_if,
+ init_cpu,
+ init_mem,
+ init_swp,
+ init_fd,
+ init_int,
+ init_ctx,
+ init_fork,
+ init_time,
+ init_blk,
+ init_delay,
+ init_cr
+};
+
+int nmeter_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int nmeter_main(int argc, char **argv)
+{
+ char buf[32];
+ s_stat *first = NULL;
+ s_stat *last = NULL;
+ s_stat *s;
+ 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);
+ }
+
+ // Can use argv[1] directly, but this will mess up
+ // parameters as seen by e.g. ps. Making a copy...
+ cur = xstrdup(argv[1]);
+ while (1) {
+ char *param, *p;
+ prev = cur;
+ again:
+ cur = strchr(cur, '%');
+ if (!cur)
+ break;
+ if (cur[1] == '%') { // %%
+ overlapping_strcpy(cur, cur + 1);
+ cur++;
+ goto again;
+ }
+ *cur++ = '\0'; // overwrite %
+ if (cur[0] == '[') {
+ // format: %[foptstring]
+ cur++;
+ p = strchr(options, cur[0]);
+ param = cur+1;
+ while (cur[0] != ']') {
+ if (!cur[0])
+ bb_show_usage();
+ cur++;
+ }
+ *cur++ = '\0'; // overwrite [
+ } else {
+ // format: %NNNNNNf
+ param = cur;
+ while (cur[0] >= '0' && cur[0] <= '9')
+ cur++;
+ if (!cur[0])
+ bb_show_usage();
+ p = strchr(options, cur[0]);
+ *cur++ = '\0'; // overwrite format char
+ }
+ if (!p)
+ bb_show_usage();
+ s = init_functions[p-options](param);
+ if (s) {
+ s->label = prev;
+ /*s->next = NULL; - all initXXX funcs use xzalloc */
+ if (!first)
+ first = s;
+ else
+ last->next = s;
+ last = s;
+ } else {
+ // %NNNNd or %r option. remove it from string
+ strcpy(prev + strlen(prev), cur);
+ cur = prev;
+ }
+ }
+ if (prev[0]) {
+ s = init_literal();
+ s->label = prev;
+ /*s->next = NULL; - all initXXX funcs use xzalloc */
+ if (!first)
+ first = s;
+ else
+ last->next = s;
+ last = s;
+ }
+
+ // Generate first samples but do not print them, they're bogus
+ collect_info(first);
+ reset_outbuf();
+ if (delta >= 0) {
+ gettimeofday(&tv, NULL);
+ usleep(delta > 1000000 ? 1000000 : delta - tv.tv_usec%deltanz);
+ }
+
+ while (1) {
+ gettimeofday(&tv, NULL);
+ collect_info(first);
+ put(final_str);
+ print_outbuf();
+
+ // Negative delta -> no usleep at all
+ // This will hog the CPU but you can have REALLY GOOD
+ // time resolution ;)
+ // TODO: detect and avoid useless updates
+ // (like: nothing happens except time)
+ if (delta >= 0) {
+ int rem;
+ // can be commented out, will sacrifice sleep time precision a bit
+ gettimeofday(&tv, NULL);
+ if (need_seconds)
+ rem = delta - ((ullong)tv.tv_sec*1000000 + tv.tv_usec) % deltanz;
+ else
+ rem = delta - tv.tv_usec%deltanz;
+ // Sometimes kernel wakes us up just a tiny bit earlier than asked
+ // Do not go to very short sleep in this case
+ if (rem < delta/128) {
+ rem += delta;
+ }
+ usleep(rem);
+ }
+ }
+
+ /*return 0;*/
+}