source: branches/stable/mindi-busybox/procps/nmeter.c @ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 13 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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