2 * pstree.c - display process tree
4 * Copyright (C) 1993-2002 Werner Almesberger
5 * Copyright (C) 2002-2009 Craig Small
6 * Copyright (C) 2010 Lauri Kasanen
8 * Based on pstree (PSmisc) 22.13.
10 * Licensed under GPLv2, see file LICENSE in this source tree.
13 //config:config PSTREE
14 //config: bool "pstree"
17 //config: Display a tree of processes.
19 //applet:IF_PSTREE(APPLET(pstree, BB_DIR_USR_BIN, BB_SUID_DROP))
21 //kbuild:lib-$(CONFIG_PSTREE) += pstree.o
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: "\n -p Show pids"
31 #define PROC_BASE "/proc"
33 #define OPT_PID (1 << 0)
37 #ifdef ENABLE_FEATURE_SHOW_THREADS
38 /* For threads, we add {...} around the comm, so we need two extra bytes */
39 # define COMM_DISP_LEN (COMM_LEN + 2)
41 # define COMM_DISP_LEN COMM_LEN
45 char comm
[COMM_DISP_LEN
+ 1];
46 // char flags; - unused, delete?
49 struct child
*children
;
55 //#define PFLAG_THREAD 0x01
57 typedef struct child
{
66 #define single_3 "---"
70 /* 0-based. IOW: the number of chars we printed on current line */
72 unsigned output_width
;
74 /* The buffers will be dynamically increased in size as needed */
81 smallint dumped
; /* used by dump_by_user */
83 #define G (*ptr_to_globals)
84 #define INIT_G() do { \
85 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
90 * Allocates additional buffer space for width and more as needed.
91 * The first call will allocate the first buffer.
93 * bufindex the index that will be used after the call to this function.
95 static void ensure_buffer_capacity(int bufindex
)
97 if (bufindex
>= G
.capacity
) {
99 G
.width
= xrealloc(G
.width
, G
.capacity
* sizeof(G
.width
[0]));
100 G
.more
= xrealloc(G
.more
, G
.capacity
* sizeof(G
.more
[0]));
104 /* NB: this function is never called with "bad" chars
105 * (control chars or chars >= 0x7f)
107 static void out_char(char c
)
110 if (G
.cur_x
> G
.output_width
)
112 if (G
.cur_x
== G
.output_width
)
117 /* NB: this function is never called with "bad" chars
118 * (control chars or chars >= 0x7f)
120 static void out_string(const char *str
)
126 static void out_newline(void)
132 static PROC
*find_proc(pid_t pid
)
136 for (walk
= G
.list
; walk
; walk
= walk
->next
)
137 if (walk
->pid
== pid
)
143 static PROC
*new_proc(const char *comm
, pid_t pid
, uid_t uid
)
145 PROC
*new = xzalloc(sizeof(*new));
147 strcpy(new->comm
, comm
);
156 static void add_child(PROC
*parent
, PROC
*child
)
161 new = xmalloc(sizeof(*new));
164 for (walk
= &parent
->children
; *walk
; walk
= &(*walk
)->next
) {
165 cmp
= strcmp((*walk
)->child
->comm
, child
->comm
);
168 if (cmp
== 0 && (*walk
)->child
->uid
> child
->uid
)
175 static void add_proc(const char *comm
, pid_t pid
, pid_t ppid
,
176 uid_t uid
/*, char isthread*/)
180 this = find_proc(pid
);
182 this = new_proc(comm
, pid
, uid
);
184 strcpy(this->comm
, comm
);
191 // this->flags |= PFLAG_THREAD;
193 parent
= find_proc(ppid
);
195 parent
= new_proc("?", ppid
, 0);
197 add_child(parent
, this);
198 this->parent
= parent
;
201 static int tree_equal(const PROC
*a
, const PROC
*b
)
203 const CHILD
*walk_a
, *walk_b
;
205 if (strcmp(a
->comm
, b
->comm
) != 0)
207 if ((option_mask32
/*& OPT_PID*/) && a
->pid
!= b
->pid
)
210 for (walk_a
= a
->children
, walk_b
= b
->children
;
212 walk_a
= walk_a
->next
, walk_b
= walk_b
->next
214 if (!tree_equal(walk_a
->child
, walk_b
->child
))
218 return !(walk_a
|| walk_b
);
221 static int out_args(const char *mystr
)
227 for (here
= mystr
; *here
; here
++) {
231 } else if (*here
>= ' ' && *here
< 0x7f) {
235 sprintf(tmpstr
, "\\%03o", (unsigned char) *here
);
245 dump_tree(PROC
*current
, int level
, int rep
, int leaf
, int last
, int closing
)
247 CHILD
*walk
, *next
, **scan
;
248 int lvl
, i
, add
, offset
, count
, comm_len
, first
;
249 char tmp
[sizeof(int)*3 + 4];
255 for (lvl
= 0; lvl
< level
; lvl
++) {
256 i
= G
.width
[lvl
] + 1;
260 if (lvl
== level
- 1) {
264 out_string(branch_2
);
267 if (G
.more
[lvl
+ 1]) {
278 add
+= sprintf(tmp
, "%d*[", rep
);
281 comm_len
= out_args(current
->comm
);
282 if (option_mask32
/*& OPT_PID*/) {
283 comm_len
+= sprintf(tmp
, "(%d)", (int)current
->pid
);
288 if (!current
->children
) {
293 ensure_buffer_capacity(level
);
294 G
.more
[level
] = !last
;
296 G
.width
[level
] = comm_len
+ G
.cur_x
- offset
+ add
;
297 if (G
.cur_x
>= G
.output_width
) {
298 //out_string(first_3); - why? it won't print anything
305 for (walk
= current
->children
; walk
; walk
= next
) {
310 if (!tree_equal(walk
->child
, (*scan
)->child
))
311 scan
= &(*scan
)->next
;
314 next
= (*scan
)->next
;
316 *scan
= (*scan
)->next
;
320 out_string(next
? first_3
: single_3
);
324 dump_tree(walk
->child
, level
+ 1, count
+ 1,
325 walk
== current
->children
, !next
,
326 closing
+ (count
? 1 : 0));
330 static void dump_by_user(PROC
*current
, uid_t uid
)
337 if (current
->uid
== uid
) {
340 dump_tree(current
, 0, 1, 1, 1, 0);
344 for (walk
= current
->children
; walk
; walk
= walk
->next
)
345 dump_by_user(walk
->child
, uid
);
348 #if ENABLE_FEATURE_SHOW_THREADS
349 static void handle_thread(const char *comm
, pid_t pid
, pid_t ppid
, uid_t uid
)
351 char threadname
[COMM_DISP_LEN
+ 1];
352 sprintf(threadname
, "{%.*s}", (int)sizeof(threadname
) - 3, comm
);
353 add_proc(threadname
, pid
, ppid
, uid
/*, 1*/);
357 static void mread_proc(void)
359 procps_status_t
*p
= NULL
;
361 int flags
= PSSCAN_COMM
| PSSCAN_PID
| PSSCAN_PPID
| PSSCAN_UIDGID
| PSSCAN_TASKS
;
363 while ((p
= procps_scan(p
, flags
)) != NULL
) {
364 #if ENABLE_FEATURE_SHOW_THREADS
365 if (p
->pid
!= p
->main_thread_pid
)
366 handle_thread(p
->comm
, p
->pid
, parent
, p
->uid
);
370 add_proc(p
->comm
, p
->pid
, p
->ppid
, p
->uid
/*, 0*/);
376 int pstree_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
377 int pstree_main(int argc UNUSED_PARAM
, char **argv
)
384 G
.output_width
= get_terminal_width(0);
386 opt_complementary
= "?1";
391 if (argv
[0][0] >= '0' && argv
[0][0] <= '9') {
392 pid
= xatoi(argv
[0]);
394 uid
= xuname2uid(argv
[0]);
401 dump_tree(find_proc(pid
), 0, 1, 1, 1, 0);
403 dump_by_user(find_proc(1), uid
);
405 bb_error_msg_and_die("no processes found");
409 if (ENABLE_FEATURE_CLEAN_UP
) {