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