aboutsummaryrefslogtreecommitdiff
path: root/procps
diff options
context:
space:
mode:
Diffstat (limited to 'procps')
-rw-r--r--procps/Config.src13
-rw-r--r--procps/Kbuild.src1
-rw-r--r--procps/iostat.c10
-rw-r--r--procps/mpstat.c14
-rw-r--r--procps/nmeter.c50
-rw-r--r--procps/powertop.c113
-rw-r--r--procps/pstree.c406
-rw-r--r--procps/smemcap.c2
-rw-r--r--procps/sysctl.c2
9 files changed, 514 insertions, 97 deletions
diff --git a/procps/Config.src b/procps/Config.src
index 1ff6dfd30..cf664eeb2 100644
--- a/procps/Config.src
+++ b/procps/Config.src
@@ -46,12 +46,6 @@ config KILLALL5
46 default y 46 default y
47 depends on KILL 47 depends on KILL
48 48
49config NMETER
50 bool "nmeter"
51 default y
52 help
53 Prints selected system stats continuously, one line per update.
54
55config PGREP 49config PGREP
56 bool "pgrep" 50 bool "pgrep"
57 default y 51 default y
@@ -192,11 +186,12 @@ config FEATURE_TOPMEM
192 Enable 's' in top (gives lots of memory info). 186 Enable 's' in top (gives lots of memory info).
193 187
194config FEATURE_SHOW_THREADS 188config FEATURE_SHOW_THREADS
195 bool "Support for showing threads in ps/top" 189 bool "Support for showing threads in ps/pstree/top"
196 default y 190 default y
197 depends on PS || TOP 191 depends on PS || TOP || PSTREE
198 help 192 help
199 Enables ps -T option and 'h' command in top 193 Enables the ps -T option, showing of threads in pstree,
194 and 'h' command in top.
200 195
201config UPTIME 196config UPTIME
202 bool "uptime" 197 bool "uptime"
diff --git a/procps/Kbuild.src b/procps/Kbuild.src
index 791d65670..89b1cc094 100644
--- a/procps/Kbuild.src
+++ b/procps/Kbuild.src
@@ -11,7 +11,6 @@ lib-$(CONFIG_FREE) += free.o
11lib-$(CONFIG_FUSER) += fuser.o 11lib-$(CONFIG_FUSER) += fuser.o
12lib-$(CONFIG_KILL) += kill.o 12lib-$(CONFIG_KILL) += kill.o
13lib-$(CONFIG_ASH) += kill.o # used for built-in kill by ash 13lib-$(CONFIG_ASH) += kill.o # used for built-in kill by ash
14lib-$(CONFIG_NMETER) += nmeter.o
15lib-$(CONFIG_PGREP) += pgrep.o 14lib-$(CONFIG_PGREP) += pgrep.o
16lib-$(CONFIG_PKILL) += pgrep.o 15lib-$(CONFIG_PKILL) += pgrep.o
17lib-$(CONFIG_PIDOF) += pidof.o 16lib-$(CONFIG_PIDOF) += pidof.o
diff --git a/procps/iostat.c b/procps/iostat.c
index 5d829861e..a9ff13a05 100644
--- a/procps/iostat.c
+++ b/procps/iostat.c
@@ -18,14 +18,14 @@
18//config: Report CPU and I/O statistics 18//config: Report CPU and I/O statistics
19 19
20#include "libbb.h" 20#include "libbb.h"
21#include <sys/utsname.h> /* Need struct utsname */ 21#include <sys/utsname.h> /* Need struct utsname */
22 22
23//#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) 23//#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
24#define debug(fmt, ...) ((void)0) 24#define debug(fmt, ...) ((void)0)
25 25
26#define MAX_DEVICE_NAME 12 26#define MAX_DEVICE_NAME 12
27#define CURRENT 0 27#define CURRENT 0
28#define LAST 1 28#define LAST 1
29 29
30#if 1 30#if 1
31typedef unsigned long long cputime_t; 31typedef unsigned long long cputime_t;
@@ -327,7 +327,7 @@ static void do_disk_statistics(cputime_t itv)
327 int i = 0; 327 int i = 0;
328 char buf[128]; 328 char buf[128];
329 unsigned major, minor; 329 unsigned major, minor;
330 unsigned long wr_ops, dummy; /* %*lu for suppres the conversion wouldn't work */ 330 unsigned long wr_ops, dummy; /* %*lu for suppress the conversion wouldn't work */
331 unsigned long long rd_sec_or_wr_ops; 331 unsigned long long rd_sec_or_wr_ops;
332 unsigned long long rd_sec_or_dummy, wr_sec_or_dummy, wr_sec; 332 unsigned long long rd_sec_or_dummy, wr_sec_or_dummy, wr_sec;
333 struct stats_dev sd; 333 struct stats_dev sd;
diff --git a/procps/mpstat.c b/procps/mpstat.c
index f1a0b00b8..25efedf62 100644
--- a/procps/mpstat.c
+++ b/procps/mpstat.c
@@ -18,7 +18,7 @@
18//config: Per-processor statistics 18//config: Per-processor statistics
19 19
20#include "libbb.h" 20#include "libbb.h"
21#include <sys/utsname.h> /* struct utsname */ 21#include <sys/utsname.h> /* struct utsname */
22 22
23//#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) 23//#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
24#define debug(fmt, ...) ((void)0) 24#define debug(fmt, ...) ((void)0)
@@ -634,7 +634,7 @@ static void get_irqs_from_interrupts(const char *fname,
634 while (irq < irqs_per_cpu) { 634 while (irq < irqs_per_cpu) {
635 /* Number of interrupts per CPU has changed */ 635 /* Number of interrupts per CPU has changed */
636 ic = &per_cpu_stats[current][irq]; 636 ic = &per_cpu_stats[current][irq];
637 ic->irq_name[0] = '\0'; /* False interrupt */ 637 ic->irq_name[0] = '\0'; /* False interrupt */
638 irq++; 638 irq++;
639 } 639 }
640} 640}
@@ -820,7 +820,7 @@ static int get_irqcpu_nr(const char *f, int max_irqs)
820 unsigned irq; 820 unsigned irq;
821 821
822 fp = fopen_for_read(f); 822 fp = fopen_for_read(f);
823 if (!fp) /* No interrupts file */ 823 if (!fp) /* No interrupts file */
824 return 0; 824 return 0;
825 825
826 linelen = INTERRUPTS_LINE + 16 * G.cpu_nr; 826 linelen = INTERRUPTS_LINE + 16 * G.cpu_nr;
@@ -858,10 +858,10 @@ int mpstat_main(int UNUSED_PARAM argc, char **argv)
858 char *opt_set_cpu; 858 char *opt_set_cpu;
859 int i, opt; 859 int i, opt;
860 enum { 860 enum {
861 OPT_ALL = 1 << 0, /* -A */ 861 OPT_ALL = 1 << 0, /* -A */
862 OPT_INTS = 1 << 1, /* -I */ 862 OPT_INTS = 1 << 1, /* -I */
863 OPT_SETCPU = 1 << 2, /* -P */ 863 OPT_SETCPU = 1 << 2, /* -P */
864 OPT_UTIL = 1 << 3, /* -u */ 864 OPT_UTIL = 1 << 3, /* -u */
865 }; 865 };
866 866
867 /* Dont buffer data if redirected to a pipe */ 867 /* Dont buffer data if redirected to a pipe */
diff --git a/procps/nmeter.c b/procps/nmeter.c
index 7836a90d5..ac019eb53 100644
--- a/procps/nmeter.c
+++ b/procps/nmeter.c
@@ -6,6 +6,40 @@
6 * Contact me: vda.linux@googlemail.com 6 * Contact me: vda.linux@googlemail.com
7 */ 7 */
8 8
9//config:config NMETER
10//config: bool "nmeter"
11//config: default y
12//config: help
13//config: Prints selected system stats continuously, one line per update.
14
15//applet:IF_NMETER(APPLET(nmeter, _BB_DIR_USR_BIN, _BB_SUID_DROP))
16
17//kbuild:lib-$(CONFIG_NMETER) += nmeter.o
18
19//usage:#define nmeter_trivial_usage
20//usage: "[-d MSEC] FORMAT_STRING"
21//usage:#define nmeter_full_usage "\n\n"
22//usage: "Monitor system in real time"
23//usage: "\n"
24//usage: "\n -d MSEC Milliseconds between updates (default:1000)"
25//usage: "\n"
26//usage: "\nFormat specifiers:"
27//usage: "\n %Nc or %[cN] Monitor CPU. N - bar size (default:10)"
28//usage: "\n (displays: S:system U:user N:niced D:iowait I:irq i:softirq)"
29//usage: "\n %[niface] Monitor network interface 'iface'"
30//usage: "\n %m Monitor allocated memory"
31//usage: "\n %[mf] Monitor free memory"
32//usage: "\n %[mt] Monitor total memory"
33//usage: "\n %s Monitor allocated swap"
34//usage: "\n %f Monitor number of used file descriptors"
35//usage: "\n %Ni Monitor total/specific IRQ rate"
36//usage: "\n %x Monitor context switch rate"
37//usage: "\n %p Monitor forks"
38//usage: "\n %[pn] Monitor # of processes"
39//usage: "\n %b Monitor block io"
40//usage: "\n %Nt Show time (with N decimal points)"
41//usage: "\n %r Print <cr> instead of <lf> at EOL"
42
9//TODO: 43//TODO:
10// simplify code 44// simplify code
11// /proc/locks 45// /proc/locks
@@ -769,6 +803,7 @@ static void FAST_FUNC collect_info(s_stat *s)
769 803
770typedef s_stat* init_func(const char *param); 804typedef s_stat* init_func(const char *param);
771 805
806// Deprecated %NNNd is to be removed, -d MSEC supersedes it
772static const char options[] ALIGN1 = "ncmsfixptbdr"; 807static const char options[] ALIGN1 = "ncmsfixptbdr";
773static init_func *const init_functions[] = { 808static init_func *const init_functions[] = {
774 init_if, 809 init_if,
@@ -792,23 +827,28 @@ int nmeter_main(int argc UNUSED_PARAM, char **argv)
792 s_stat *first = NULL; 827 s_stat *first = NULL;
793 s_stat *last = NULL; 828 s_stat *last = NULL;
794 s_stat *s; 829 s_stat *s;
830 char *opt_d;
795 char *cur, *prev; 831 char *cur, *prev;
796 832
797 INIT_G(); 833 INIT_G();
798 834
799 xchdir("/proc"); 835 xchdir("/proc");
800 836
801 if (!argv[1])
802 bb_show_usage();
803
804 if (open_read_close("version", buf, sizeof(buf)-1) > 0) { 837 if (open_read_close("version", buf, sizeof(buf)-1) > 0) {
805 buf[sizeof(buf)-1] = '\0'; 838 buf[sizeof(buf)-1] = '\0';
806 is26 = (strstr(buf, " 2.4.") == NULL); 839 is26 = (strstr(buf, " 2.4.") == NULL);
807 } 840 }
808 841
809 // Can use argv[1] directly, but this will mess up 842 if (getopt32(argv, "d:", &opt_d))
843 init_delay(opt_d);
844 argv += optind;
845
846 if (!argv[0])
847 bb_show_usage();
848
849 // Can use argv[0] directly, but this will mess up
810 // parameters as seen by e.g. ps. Making a copy... 850 // parameters as seen by e.g. ps. Making a copy...
811 cur = xstrdup(argv[1]); 851 cur = xstrdup(argv[0]);
812 while (1) { 852 while (1) {
813 char *param, *p; 853 char *param, *p;
814 prev = cur; 854 prev = cur;
diff --git a/procps/powertop.c b/procps/powertop.c
index 250da817b..2f977a03b 100644
--- a/procps/powertop.c
+++ b/procps/powertop.c
@@ -169,7 +169,7 @@ static void read_cstate_counts(ullong *usage, ullong *duration)
169 if (len < 3 || len > BIG_SYSNAME_LEN) 169 if (len < 3 || len > BIG_SYSNAME_LEN)
170 continue; 170 continue;
171 171
172 sprintf(buf, "/proc/acpi/processor/%s/power", d->d_name); 172 sprintf(buf, "%s/%s/power", "/proc/acpi/processor", d->d_name);
173 fp = fopen_for_read(buf); 173 fp = fopen_for_read(buf);
174 if (!fp) 174 if (!fp)
175 continue; 175 continue;
@@ -321,31 +321,18 @@ static void process_irq_counts(void)
321 /* 0: 143646045 153901007 IO-APIC-edge timer 321 /* 0: 143646045 153901007 IO-APIC-edge timer
322 * ^ 322 * ^
323 */ 323 */
324 *p = '\0';
324 /* Deal with non-maskable interrupts -- make up fake numbers */ 325 /* Deal with non-maskable interrupts -- make up fake numbers */
325 nr = -1; 326 nr = index_in_strings("NMI\0RES\0CAL\0TLB\0TRM\0THR\0SPU\0", buf);
326 if (buf[0] != ' ' && !isdigit(buf[0])) { 327 if (nr >= 0) {
327//TODO: optimize 328 nr += 20000;
328 if (strncmp(buf, "NMI:", 4) == 0)
329 nr = 20000;
330 if (strncmp(buf, "RES:", 4) == 0)
331 nr = 20001;
332 if (strncmp(buf, "CAL:", 4) == 0)
333 nr = 20002;
334 if (strncmp(buf, "TLB:", 4) == 0)
335 nr = 20003;
336 if (strncmp(buf, "TRM:", 4) == 0)
337 nr = 20004;
338 if (strncmp(buf, "THR:", 4) == 0)
339 nr = 20005;
340 if (strncmp(buf, "SPU:", 4) == 0)
341 nr = 20006;
342 } else { 329 } else {
343 /* bb_strtou doesn't eat leading spaces, using strtoul */ 330 /* bb_strtou doesn't eat leading spaces, using strtoul */
331 errno = 0;
344 nr = strtoul(buf, NULL, 10); 332 nr = strtoul(buf, NULL, 10);
333 if (errno)
334 continue;
345 } 335 }
346 if (nr == -1)
347 continue;
348
349 p++; 336 p++;
350 /* 0: 143646045 153901007 IO-APIC-edge timer 337 /* 0: 143646045 153901007 IO-APIC-edge timer
351 * ^ 338 * ^
@@ -412,6 +399,7 @@ static NOINLINE int process_timer_stats(void)
412 buf[0] = '\0'; 399 buf[0] = '\0';
413 totalticks = 0; 400 totalticks = 0;
414 401
402 n = 0;
415 fp = NULL; 403 fp = NULL;
416 if (!G.cant_enable_timer_stats) 404 if (!G.cant_enable_timer_stats)
417 fp = fopen_for_read("/proc/timer_stats"); 405 fp = fopen_for_read("/proc/timer_stats");
@@ -430,33 +418,41 @@ static NOINLINE int process_timer_stats(void)
430 while (fgets(buf, sizeof(buf), fp)) { 418 while (fgets(buf, sizeof(buf), fp)) {
431 const char *count, *process, *func; 419 const char *count, *process, *func;
432 char *p; 420 char *p;
433 int cnt; 421 int idx;
422 unsigned cnt;
434 423
435 count = skip_whitespace(buf); 424 count = skip_whitespace(buf);
436 p = strchr(count, ','); 425 p = strchr(count, ',');
437 if (!p) 426 if (!p)
438 continue; 427 continue;
439 *p++ = '\0'; 428 *p++ = '\0';
440 if (strcmp(strchrnul(count, ' '), " total events") == 0) 429 cnt = bb_strtou(count, NULL, 10);
430 if (strcmp(skip_non_whitespace(count), " total events") == 0) {
431#if ENABLE_FEATURE_POWERTOP_PROCIRQ
432 n = cnt / G.total_cpus;
433 if (n > 0 && n < G.interrupt_0) {
434 sprintf(line, " <interrupt> : %s", "extra timer interrupt");
435 save_line(line, G.interrupt_0 - n);
436 }
437#endif
441 break; 438 break;
442 p = skip_whitespace(p); /* points to pid */ 439 }
443 440 if (strchr(count, 'D'))
444/* Find char ' ', then eat remaining spaces */ 441 continue; /* deferred */
445#define ADVANCE(p) do { \ 442 p = skip_whitespace(p); /* points to pid now */
446 (p) = strchr((p), ' '); \ 443 process = NULL;
447 if (!(p)) \ 444 get_func_name:
448 continue; \ 445 p = strchr(p, ' ');
449 *(p) = '\0'; \ 446 if (!p)
450 (p)++; \ 447 continue;
451 (p) = skip_whitespace(p); \ 448 *p++ = '\0';
452} while (0) 449 p = skip_whitespace(p);
453 /* Get process name */ 450 if (process == NULL) {
454 ADVANCE(p); 451 process = p;
455 process = p; 452 goto get_func_name;
456 /* Get function */ 453 }
457 ADVANCE(p);
458 func = p; 454 func = p;
459#undef ADVANCE 455
460 //if (strcmp(process, "swapper") == 0 456 //if (strcmp(process, "swapper") == 0
461 // && strcmp(func, "hrtimer_start_range_ns (tick_sched_timer)\n") == 0 457 // && strcmp(func, "hrtimer_start_range_ns (tick_sched_timer)\n") == 0
462 //) { 458 //) {
@@ -471,45 +467,26 @@ static NOINLINE int process_timer_stats(void)
471 //if (strcmp(process, "powertop") == 0) 467 //if (strcmp(process, "powertop") == 0)
472 // continue; 468 // continue;
473 469
474 if (strcmp(process, "insmod") == 0) 470 idx = index_in_strings("insmod\0modprobe\0swapper\0", process);
475 process = "[kernel module]"; 471 if (idx != -1) {
476 if (strcmp(process, "modprobe") == 0) 472 process = idx < 2 ? "[kernel module]" : "<kernel core>";
477 process = "[kernel module]"; 473 }
478 if (strcmp(process, "swapper") == 0)
479 process = "<kernel core>";
480 474
481 strchrnul(p, '\n')[0] = '\0'; 475 strchrnul(p, '\n')[0] = '\0';
482 476
483 { 477 // 46D\01136\0kondemand/1\0do_dbs_timer (delayed_work_timer_fn)
484 char *tmp; 478 // ^ ^ ^
485 cnt = bb_strtoull(count, &tmp, 10); 479 // count process func
486 p = tmp;
487 }
488 while (*p != '\0') {
489 if (*p++ == 'D') /* deferred */
490 goto skip;
491 }
492 480
493 //if (strchr(process, '[')) 481 //if (strchr(process, '['))
494 sprintf(line, "%15.15s : %s", process, func); 482 sprintf(line, "%15.15s : %s", process, func);
495 //else 483 //else
496 // sprintf(line, "%s", process); 484 // sprintf(line, "%s", process);
497 save_line(line, cnt); 485 save_line(line, cnt);
498 skip: ;
499 } 486 }
500 fclose(fp); 487 fclose(fp);
501 } 488 }
502 489
503 n = 0;
504#if ENABLE_FEATURE_POWERTOP_PROCIRQ
505 if (strstr(buf, "total events")) {
506 n = bb_strtoull(buf, NULL, 10) / G.total_cpus;
507 if (n > 0 && n < G.interrupt_0) {
508 sprintf(line, " <interrupt> : %s", "extra timer interrupt");
509 save_line(line, G.interrupt_0 - n);
510 }
511 }
512#endif
513 return n; 490 return n;
514} 491}
515 492
@@ -566,7 +543,7 @@ static NOINLINE void print_intel_cstates(void)
566 if (!isdigit(d->d_name[3])) 543 if (!isdigit(d->d_name[3]))
567 continue; 544 continue;
568 545
569 len = sprintf(fname, "/sys/devices/system/cpu/%s/cpuidle", d->d_name); 546 len = sprintf(fname, "%s/%s/cpuidle", "/sys/devices/system/cpu", d->d_name);
570 dir = opendir(fname); 547 dir = opendir(fname);
571 if (!dir) 548 if (!dir)
572 continue; 549 continue;
@@ -635,7 +612,7 @@ static NOINLINE void print_intel_cstates(void)
635 bb_putchar('\n'); 612 bb_putchar('\n');
636} 613}
637#else 614#else
638# define print_intel_cstates(void) ((void)0) 615# define print_intel_cstates() ((void)0)
639#endif 616#endif
640 617
641static void show_timerstats(void) 618static void show_timerstats(void)
diff --git a/procps/pstree.c b/procps/pstree.c
new file mode 100644
index 000000000..180d0939a
--- /dev/null
+++ b/procps/pstree.c
@@ -0,0 +1,406 @@
1/*
2 * pstree.c - display process tree
3 *
4 * Copyright (C) 1993-2002 Werner Almesberger
5 * Copyright (C) 2002-2009 Craig Small
6 * Copyright (C) 2010 Lauri Kasanen
7 *
8 * Based on pstree (PSmisc) 22.13.
9 *
10 * Licensed under GPLv2, see file LICENSE in this source tree.
11 */
12
13//config:config PSTREE
14//config: bool "pstree"
15//config: default y
16//config: help
17//config: Display a tree of processes.
18
19//applet:IF_PSTREE(APPLET(pstree, _BB_DIR_USR_BIN, _BB_SUID_DROP))
20
21//kbuild:lib-$(CONFIG_PSTREE) += pstree.o
22
23//usage:#define pstree_trivial_usage
24//usage: "[-p] [PID|USER]"
25//usage:#define pstree_full_usage "\n\n"
26//usage: "Display process tree, optionally start from USER or PID\n"
27//usage: "\nOptions:"
28//usage: "\n -p Show pids"
29
30#include "libbb.h"
31
32#define PROC_BASE "/proc"
33
34#define OPT_PID (1 << 0)
35
36struct child;
37
38typedef struct proc {
39 char comm[COMM_LEN + 1];
40// char flags; - unused, delete?
41 pid_t pid;
42 uid_t uid;
43 struct child *children;
44 struct proc *parent;
45 struct proc *next;
46} PROC;
47
48/* For flags above */
49//#define PFLAG_THREAD 0x01
50
51typedef struct child {
52 PROC *child;
53 struct child *next;
54} CHILD;
55
56#define empty_2 " "
57#define branch_2 "|-"
58#define vert_2 "| "
59#define last_2 "`-"
60#define single_3 "---"
61#define first_3 "-+-"
62
63struct globals {
64 /* 0-based. IOW: the number of chars we printed on current line */
65 unsigned cur_x;
66 unsigned output_width;
67
68 /* The buffers will be dynamically increased in size as needed */
69 unsigned capacity;
70 unsigned *width;
71 uint8_t *more;
72
73 PROC *list;
74
75 smallint dumped; /* used by dump_by_user */
76};
77#define G (*ptr_to_globals)
78#define INIT_G() do { \
79 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
80} while (0)
81
82
83/*
84 * Allocates additional buffer space for width and more as needed.
85 * The first call will allocate the first buffer.
86 *
87 * bufindex the index that will be used after the call to this function.
88 */
89static void ensure_buffer_capacity(int bufindex)
90{
91 if (bufindex >= G.capacity) {
92 G.capacity += 0x100;
93 G.width = xrealloc(G.width, G.capacity * sizeof(G.width[0]));
94 G.more = xrealloc(G.more, G.capacity * sizeof(G.more[0]));
95 }
96}
97
98/* NB: this function is never called with "bad" chars
99 * (control chars or chars >= 0x7f)
100 */
101static void out_char(char c)
102{
103 G.cur_x++;
104 if (G.cur_x > G.output_width)
105 return;
106 if (G.cur_x == G.output_width)
107 c = '+';
108 putchar(c);
109}
110
111/* NB: this function is never called with "bad" chars
112 * (control chars or chars >= 0x7f)
113 */
114static void out_string(const char *str)
115{
116 while (*str)
117 out_char(*str++);
118}
119
120static void out_newline(void)
121{
122 putchar('\n');
123 G.cur_x = 0;
124}
125
126static PROC *find_proc(pid_t pid)
127{
128 PROC *walk;
129
130 for (walk = G.list; walk; walk = walk->next)
131 if (walk->pid == pid)
132 break;
133
134 return walk;
135}
136
137static PROC *new_proc(const char *comm, pid_t pid, uid_t uid)
138{
139 PROC *new = xzalloc(sizeof(*new));
140
141 strcpy(new->comm, comm);
142 new->pid = pid;
143 new->uid = uid;
144 new->next = G.list;
145
146 G.list = new;
147 return G.list;
148}
149
150static void add_child(PROC *parent, PROC *child)
151{
152 CHILD *new, **walk;
153 int cmp;
154
155 new = xmalloc(sizeof(*new));
156
157 new->child = child;
158 for (walk = &parent->children; *walk; walk = &(*walk)->next) {
159 cmp = strcmp((*walk)->child->comm, child->comm);
160 if (cmp > 0)
161 break;
162 if (cmp == 0 && (*walk)->child->uid > child->uid)
163 break;
164 }
165 new->next = *walk;
166 *walk = new;
167}
168
169static void add_proc(const char *comm, pid_t pid, pid_t ppid,
170 uid_t uid /*, char isthread*/)
171{
172 PROC *this, *parent;
173
174 this = find_proc(pid);
175 if (!this)
176 this = new_proc(comm, pid, uid);
177 else {
178 strcpy(this->comm, comm);
179 this->uid = uid;
180 }
181
182 if (pid == ppid)
183 ppid = 0;
184// if (isthread)
185// this->flags |= PFLAG_THREAD;
186
187 parent = find_proc(ppid);
188 if (!parent)
189 parent = new_proc("?", ppid, 0);
190
191 add_child(parent, this);
192 this->parent = parent;
193}
194
195static int tree_equal(const PROC *a, const PROC *b)
196{
197 const CHILD *walk_a, *walk_b;
198
199 if (strcmp(a->comm, b->comm) != 0)
200 return 0;
201 if ((option_mask32 /*& OPT_PID*/) && a->pid != b->pid)
202 return 0;
203
204 for (walk_a = a->children, walk_b = b->children;
205 walk_a && walk_b;
206 walk_a = walk_a->next, walk_b = walk_b->next
207 ) {
208 if (!tree_equal(walk_a->child, walk_b->child))
209 return 0;
210 }
211
212 return !(walk_a || walk_b);
213}
214
215static int out_args(const char *mystr)
216{
217 const char *here;
218 int strcount = 0;
219 char tmpstr[5];
220
221 for (here = mystr; *here; here++) {
222 if (*here == '\\') {
223 out_string("\\\\");
224 strcount += 2;
225 } else if (*here >= ' ' && *here < 0x7f) {
226 out_char(*here);
227 strcount++;
228 } else {
229 sprintf(tmpstr, "\\%03o", (unsigned char) *here);
230 out_string(tmpstr);
231 strcount += 4;
232 }
233 }
234
235 return strcount;
236}
237
238static void
239dump_tree(PROC *current, int level, int rep, int leaf, int last, int closing)
240{
241 CHILD *walk, *next, **scan;
242 int lvl, i, add, offset, count, comm_len, first;
243 char tmp[sizeof(int)*3 + 4];
244
245 if (!current)
246 return;
247
248 if (!leaf) {
249 for (lvl = 0; lvl < level; lvl++) {
250 i = G.width[lvl] + 1;
251 while (--i >= 0)
252 out_char(' ');
253
254 if (lvl == level - 1) {
255 if (last) {
256 out_string(last_2);
257 } else {
258 out_string(branch_2);
259 }
260 } else {
261 if (G.more[lvl + 1]) {
262 out_string(vert_2);
263 } else {
264 out_string(empty_2);
265 }
266 }
267 }
268 }
269
270 add = 0;
271 if (rep > 1) {
272 add += sprintf(tmp, "%d*[", rep);
273 out_string(tmp);
274 }
275 comm_len = out_args(current->comm);
276 if (option_mask32 /*& OPT_PID*/) {
277 comm_len += sprintf(tmp, "(%d)", (int)current->pid);
278 out_string(tmp);
279 }
280 offset = G.cur_x;
281
282 if (!current->children) {
283 while (closing--)
284 out_char(']');
285 out_newline();
286 }
287 ensure_buffer_capacity(level);
288 G.more[level] = !last;
289
290 G.width[level] = comm_len + G.cur_x - offset + add;
291 if (G.cur_x >= G.output_width) {
292 //out_string(first_3); - why? it won't print anything
293 //out_char('+');
294 out_newline();
295 return;
296 }
297
298 first = 1;
299 for (walk = current->children; walk; walk = next) {
300 count = 0;
301 next = walk->next;
302 scan = &walk->next;
303 while (*scan) {
304 if (!tree_equal(walk->child, (*scan)->child))
305 scan = &(*scan)->next;
306 else {
307 if (next == *scan)
308 next = (*scan)->next;
309 count++;
310 *scan = (*scan)->next;
311 }
312 }
313 if (first) {
314 out_string(next ? first_3 : single_3);
315 first = 0;
316 }
317
318 dump_tree(walk->child, level + 1, count + 1,
319 walk == current->children, !next,
320 closing + (count ? 1 : 0));
321 }
322}
323
324static void dump_by_user(PROC *current, uid_t uid)
325{
326 const CHILD *walk;
327
328 if (!current)
329 return;
330
331 if (current->uid == uid) {
332 if (G.dumped)
333 putchar('\n');
334 dump_tree(current, 0, 1, 1, 1, 0);
335 G.dumped = 1;
336 return;
337 }
338 for (walk = current->children; walk; walk = walk->next)
339 dump_by_user(walk->child, uid);
340}
341
342static void handle_thread(const char *comm, pid_t pid, pid_t ppid, uid_t uid)
343{
344 char threadname[COMM_LEN + 2];
345 sprintf(threadname, "{%.*s}", COMM_LEN - 2, comm);
346 add_proc(threadname, pid, ppid, uid/*, 1*/);
347}
348
349static void mread_proc(void)
350{
351 procps_status_t *p = NULL;
352 pid_t parent = 0;
353 int flags = PSSCAN_COMM | PSSCAN_PID | PSSCAN_PPID | PSSCAN_UIDGID | PSSCAN_TASKS;
354
355 while ((p = procps_scan(p, flags)) != NULL) {
356#if ENABLE_FEATURE_SHOW_THREADS
357 if (p->pid != p->main_thread_pid)
358 handle_thread(p->comm, p->pid, parent, p->uid);
359 else
360#endif
361 {
362 add_proc(p->comm, p->pid, p->ppid, p->uid/*, 0*/);
363 parent = p->pid;
364 }
365 }
366}
367
368int pstree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
369int pstree_main(int argc UNUSED_PARAM, char **argv)
370{
371 pid_t pid = 1;
372 long uid = 0;
373
374 INIT_G();
375
376 get_terminal_width_height(0, &G.output_width, NULL);
377
378 opt_complementary = "?1";
379 getopt32(argv, "p");
380 argv += optind;
381
382 if (argv[0]) {
383 if (argv[0][0] >= '0' && argv[0][0] <= '9') {
384 pid = xatoi(argv[0]);
385 } else {
386 uid = xuname2uid(argv[0]);
387 }
388 }
389
390 mread_proc();
391
392 if (!uid)
393 dump_tree(find_proc(pid), 0, 1, 1, 1, 0);
394 else {
395 dump_by_user(find_proc(1), uid);
396 if (!G.dumped) {
397 bb_error_msg_and_die("no processes found");
398 }
399 }
400
401 if (ENABLE_FEATURE_CLEAN_UP) {
402 free(G.width);
403 free(G.more);
404 }
405 return 0;
406}
diff --git a/procps/smemcap.c b/procps/smemcap.c
index 196c91f54..200df6795 100644
--- a/procps/smemcap.c
+++ b/procps/smemcap.c
@@ -20,7 +20,7 @@
20//config: a memory usage statistic tool. 20//config: a memory usage statistic tool.
21 21
22#include "libbb.h" 22#include "libbb.h"
23#include "unarchive.h" 23#include "archive.h"
24 24
25struct fileblock { 25struct fileblock {
26 struct fileblock *next; 26 struct fileblock *next;
diff --git a/procps/sysctl.c b/procps/sysctl.c
index 20b372c54..aba966e7f 100644
--- a/procps/sysctl.c
+++ b/procps/sysctl.c
@@ -91,7 +91,7 @@ static int sysctl_act_on_setting(char *setting)
91 retval = EXIT_FAILURE; 91 retval = EXIT_FAILURE;
92 goto end; 92 goto end;
93 } 93 }
94 value = cptr + 1; /* point to the value in name=value */ 94 value = cptr + 1; /* point to the value in name=value */
95 if (setting == cptr || !*value) { 95 if (setting == cptr || !*value) {
96 bb_error_msg("error: malformed setting '%s'", outname); 96 bb_error_msg("error: malformed setting '%s'", outname);
97 retval = EXIT_FAILURE; 97 retval = EXIT_FAILURE;