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)
38 char comm
[COMM_LEN
+ 1];
39 // char flags; - unused, delete?
42 struct child
*children
;
48 //#define PFLAG_THREAD 0x01
50 typedef struct child
{
59 #define single_3 "---"
63 /* 0-based. IOW: the number of chars we printed on current line */
65 unsigned output_width
;
67 /* The buffers will be dynamically increased in size as needed */
74 smallint dumped
; /* used by dump_by_user */
76 #define G (*ptr_to_globals)
77 #define INIT_G() do { \
78 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
83 * Allocates additional buffer space for width and more as needed.
84 * The first call will allocate the first buffer.
86 * bufindex the index that will be used after the call to this function.
88 static void ensure_buffer_capacity(int bufindex
)
90 if (bufindex
>= G
.capacity
) {
92 G
.width
= xrealloc(G
.width
, G
.capacity
* sizeof(G
.width
[0]));
93 G
.more
= xrealloc(G
.more
, G
.capacity
* sizeof(G
.more
[0]));
97 /* NB: this function is never called with "bad" chars
98 * (control chars or chars >= 0x7f)
100 static void out_char(char c
)
103 if (G
.cur_x
> G
.output_width
)
105 if (G
.cur_x
== G
.output_width
)
110 /* NB: this function is never called with "bad" chars
111 * (control chars or chars >= 0x7f)
113 static void out_string(const char *str
)
119 static void out_newline(void)
125 static PROC
*find_proc(pid_t pid
)
129 for (walk
= G
.list
; walk
; walk
= walk
->next
)
130 if (walk
->pid
== pid
)
136 static PROC
*new_proc(const char *comm
, pid_t pid
, uid_t uid
)
138 PROC
*new = xzalloc(sizeof(*new));
140 strcpy(new->comm
, comm
);
149 static void add_child(PROC
*parent
, PROC
*child
)
154 new = xmalloc(sizeof(*new));
157 for (walk
= &parent
->children
; *walk
; walk
= &(*walk
)->next
) {
158 cmp
= strcmp((*walk
)->child
->comm
, child
->comm
);
161 if (cmp
== 0 && (*walk
)->child
->uid
> child
->uid
)
168 static void add_proc(const char *comm
, pid_t pid
, pid_t ppid
,
169 uid_t uid
/*, char isthread*/)
173 this = find_proc(pid
);
175 this = new_proc(comm
, pid
, uid
);
177 strcpy(this->comm
, comm
);
184 // this->flags |= PFLAG_THREAD;
186 parent
= find_proc(ppid
);
188 parent
= new_proc("?", ppid
, 0);
190 add_child(parent
, this);
191 this->parent
= parent
;
194 static int tree_equal(const PROC
*a
, const PROC
*b
)
196 const CHILD
*walk_a
, *walk_b
;
198 if (strcmp(a
->comm
, b
->comm
) != 0)
200 if ((option_mask32
/*& OPT_PID*/) && a
->pid
!= b
->pid
)
203 for (walk_a
= a
->children
, walk_b
= b
->children
;
205 walk_a
= walk_a
->next
, walk_b
= walk_b
->next
207 if (!tree_equal(walk_a
->child
, walk_b
->child
))
211 return !(walk_a
|| walk_b
);
214 static int out_args(const char *mystr
)
220 for (here
= mystr
; *here
; here
++) {
224 } else if (*here
>= ' ' && *here
< 0x7f) {
228 sprintf(tmpstr
, "\\%03o", (unsigned char) *here
);
238 dump_tree(PROC
*current
, int level
, int rep
, int leaf
, int last
, int closing
)
240 CHILD
*walk
, *next
, **scan
;
241 int lvl
, i
, add
, offset
, count
, comm_len
, first
;
242 char tmp
[sizeof(int)*3 + 4];
248 for (lvl
= 0; lvl
< level
; lvl
++) {
249 i
= G
.width
[lvl
] + 1;
253 if (lvl
== level
- 1) {
257 out_string(branch_2
);
260 if (G
.more
[lvl
+ 1]) {
271 add
+= sprintf(tmp
, "%d*[", rep
);
274 comm_len
= out_args(current
->comm
);
275 if (option_mask32
/*& OPT_PID*/) {
276 comm_len
+= sprintf(tmp
, "(%d)", (int)current
->pid
);
281 if (!current
->children
) {
286 ensure_buffer_capacity(level
);
287 G
.more
[level
] = !last
;
289 G
.width
[level
] = comm_len
+ G
.cur_x
- offset
+ add
;
290 if (G
.cur_x
>= G
.output_width
) {
291 //out_string(first_3); - why? it won't print anything
298 for (walk
= current
->children
; walk
; walk
= next
) {
303 if (!tree_equal(walk
->child
, (*scan
)->child
))
304 scan
= &(*scan
)->next
;
307 next
= (*scan
)->next
;
309 *scan
= (*scan
)->next
;
313 out_string(next
? first_3
: single_3
);
317 dump_tree(walk
->child
, level
+ 1, count
+ 1,
318 walk
== current
->children
, !next
,
319 closing
+ (count
? 1 : 0));
323 static void dump_by_user(PROC
*current
, uid_t uid
)
330 if (current
->uid
== uid
) {
333 dump_tree(current
, 0, 1, 1, 1, 0);
337 for (walk
= current
->children
; walk
; walk
= walk
->next
)
338 dump_by_user(walk
->child
, uid
);
341 #if ENABLE_FEATURE_SHOW_THREADS
342 static void handle_thread(const char *comm
, pid_t pid
, pid_t ppid
, uid_t uid
)
344 char threadname
[COMM_LEN
+ 2];
345 sprintf(threadname
, "{%.*s}", COMM_LEN
- 2, comm
);
346 add_proc(threadname
, pid
, ppid
, uid
/*, 1*/);
350 static void mread_proc(void)
352 procps_status_t
*p
= NULL
;
354 int flags
= PSSCAN_COMM
| PSSCAN_PID
| PSSCAN_PPID
| PSSCAN_UIDGID
| PSSCAN_TASKS
;
356 while ((p
= procps_scan(p
, flags
)) != NULL
) {
357 #if ENABLE_FEATURE_SHOW_THREADS
358 if (p
->pid
!= p
->main_thread_pid
)
359 handle_thread(p
->comm
, p
->pid
, parent
, p
->uid
);
363 add_proc(p
->comm
, p
->pid
, p
->ppid
, p
->uid
/*, 0*/);
369 int pstree_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
370 int pstree_main(int argc UNUSED_PARAM
, char **argv
)
377 get_terminal_width_height(0, &G
.output_width
, NULL
);
379 opt_complementary
= "?1";
384 if (argv
[0][0] >= '0' && argv
[0][0] <= '9') {
385 pid
= xatoi(argv
[0]);
387 uid
= xuname2uid(argv
[0]);
394 dump_tree(find_proc(pid
), 0, 1, 1, 1, 0);
396 dump_by_user(find_proc(1), uid
);
398 bb_error_msg_and_die("no processes found");
402 if (ENABLE_FEATURE_CLEAN_UP
) {