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