1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28#include "libbb.h"
29
30#define PROC_BASE "/proc"
31
32#define OPT_PID (1 << 0)
33
34struct child;
35
36#if ENABLE_FEATURE_SHOW_THREADS
37
38# define COMM_DISP_LEN (COMM_LEN + 2)
39#else
40# define COMM_DISP_LEN COMM_LEN
41#endif
42
43typedef struct proc {
44 char comm[COMM_DISP_LEN + 1];
45
46 pid_t pid;
47 uid_t uid;
48 struct child *children;
49 struct proc *parent;
50 struct proc *next;
51} PROC;
52
53
54
55
56typedef struct child {
57 PROC *child;
58 struct child *next;
59} CHILD;
60
61#define empty_2 " "
62#define branch_2 "|-"
63#define vert_2 "| "
64#define last_2 "`-"
65#define single_3 "---"
66#define first_3 "-+-"
67
68struct globals {
69
70 unsigned cur_x;
71 unsigned output_width;
72
73
74 unsigned capacity;
75 unsigned *width;
76 uint8_t *more;
77
78 PROC *list;
79
80 smallint dumped;
81};
82#define G (*ptr_to_globals)
83#define INIT_G() do { \
84 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
85} while (0)
86
87
88
89
90
91
92
93
94static void ensure_buffer_capacity(int bufindex)
95{
96 if (bufindex >= G.capacity) {
97 G.capacity += 0x100;
98 G.width = xrealloc(G.width, G.capacity * sizeof(G.width[0]));
99 G.more = xrealloc(G.more, G.capacity * sizeof(G.more[0]));
100 }
101}
102
103
104
105
106static void out_char(char c)
107{
108 G.cur_x++;
109 if (G.cur_x > G.output_width)
110 return;
111 if (G.cur_x == G.output_width)
112 c = '+';
113 putchar(c);
114}
115
116
117
118
119static void out_string(const char *str)
120{
121 while (*str)
122 out_char(*str++);
123}
124
125static void out_newline(void)
126{
127 putchar('\n');
128 G.cur_x = 0;
129}
130
131static PROC *find_proc(pid_t pid)
132{
133 PROC *walk;
134
135 for (walk = G.list; walk; walk = walk->next)
136 if (walk->pid == pid)
137 break;
138
139 return walk;
140}
141
142static PROC *new_proc(const char *comm, pid_t pid, uid_t uid)
143{
144 PROC *new = xzalloc(sizeof(*new));
145
146 strcpy(new->comm, comm);
147 new->pid = pid;
148 new->uid = uid;
149 new->next = G.list;
150
151 G.list = new;
152 return G.list;
153}
154
155static void add_child(PROC *parent, PROC *child)
156{
157 CHILD *new, **walk;
158 int cmp;
159
160 new = xmalloc(sizeof(*new));
161
162 new->child = child;
163 for (walk = &parent->children; *walk; walk = &(*walk)->next) {
164 cmp = strcmp((*walk)->child->comm, child->comm);
165 if (cmp > 0)
166 break;
167 if (cmp == 0 && (*walk)->child->uid > child->uid)
168 break;
169 }
170 new->next = *walk;
171 *walk = new;
172}
173
174static void add_proc(const char *comm, pid_t pid, pid_t ppid,
175 uid_t uid )
176{
177 PROC *this, *parent;
178
179 this = find_proc(pid);
180 if (!this)
181 this = new_proc(comm, pid, uid);
182 else {
183 strcpy(this->comm, comm);
184 this->uid = uid;
185 }
186
187 if (pid == ppid)
188 ppid = 0;
189
190
191
192 parent = find_proc(ppid);
193 if (!parent)
194 parent = new_proc("?", ppid, 0);
195
196 add_child(parent, this);
197 this->parent = parent;
198}
199
200static int tree_equal(const PROC *a, const PROC *b)
201{
202 const CHILD *walk_a, *walk_b;
203
204 if (strcmp(a->comm, b->comm) != 0)
205 return 0;
206 if ((option_mask32 ) && a->pid != b->pid)
207 return 0;
208
209 for (walk_a = a->children, walk_b = b->children;
210 walk_a && walk_b;
211 walk_a = walk_a->next, walk_b = walk_b->next
212 ) {
213 if (!tree_equal(walk_a->child, walk_b->child))
214 return 0;
215 }
216
217 return !(walk_a || walk_b);
218}
219
220static int out_args(const char *mystr)
221{
222 const char *here;
223 int strcount = 0;
224 char tmpstr[5];
225
226 for (here = mystr; *here; here++) {
227 if (*here == '\\') {
228 out_string("\\\\");
229 strcount += 2;
230 } else if (*here >= ' ' && *here < 0x7f) {
231 out_char(*here);
232 strcount++;
233 } else {
234 sprintf(tmpstr, "\\%03o", (unsigned char) *here);
235 out_string(tmpstr);
236 strcount += 4;
237 }
238 }
239
240 return strcount;
241}
242
243static void
244dump_tree(PROC *current, int level, int rep, int leaf, int last, int closing)
245{
246 CHILD *walk, *next, **scan;
247 int lvl, i, add, offset, count, comm_len, first;
248 char tmp[sizeof(int)*3 + 4];
249
250 if (!current)
251 return;
252
253 if (!leaf) {
254 for (lvl = 0; lvl < level; lvl++) {
255 i = G.width[lvl] + 1;
256 while (--i >= 0)
257 out_char(' ');
258
259 if (lvl == level - 1) {
260 if (last) {
261 out_string(last_2);
262 } else {
263 out_string(branch_2);
264 }
265 } else {
266 if (G.more[lvl + 1]) {
267 out_string(vert_2);
268 } else {
269 out_string(empty_2);
270 }
271 }
272 }
273 }
274
275 add = 0;
276 if (rep > 1) {
277 add += sprintf(tmp, "%d*[", rep);
278 out_string(tmp);
279 }
280 comm_len = out_args(current->comm);
281 if (option_mask32 ) {
282 comm_len += sprintf(tmp, "(%d)", (int)current->pid);
283 out_string(tmp);
284 }
285 offset = G.cur_x;
286
287 if (!current->children) {
288 while (closing--)
289 out_char(']');
290 out_newline();
291 }
292 ensure_buffer_capacity(level);
293 G.more[level] = !last;
294
295 G.width[level] = comm_len + G.cur_x - offset + add;
296 if (G.cur_x >= G.output_width) {
297
298
299 out_newline();
300 return;
301 }
302
303 first = 1;
304 for (walk = current->children; walk; walk = next) {
305 count = 0;
306 next = walk->next;
307 scan = &walk->next;
308 while (*scan) {
309 if (!tree_equal(walk->child, (*scan)->child))
310 scan = &(*scan)->next;
311 else {
312 if (next == *scan)
313 next = (*scan)->next;
314 count++;
315 *scan = (*scan)->next;
316 }
317 }
318 if (first) {
319 out_string(next ? first_3 : single_3);
320 first = 0;
321 }
322
323 dump_tree(walk->child, level + 1, count + 1,
324 walk == current->children, !next,
325 closing + (count ? 1 : 0));
326 }
327}
328
329static void dump_by_user(PROC *current, uid_t uid)
330{
331 const CHILD *walk;
332
333 if (!current)
334 return;
335
336 if (current->uid == uid) {
337 if (G.dumped)
338 putchar('\n');
339 dump_tree(current, 0, 1, 1, 1, 0);
340 G.dumped = 1;
341 return;
342 }
343 for (walk = current->children; walk; walk = walk->next)
344 dump_by_user(walk->child, uid);
345}
346
347#if ENABLE_FEATURE_SHOW_THREADS
348static void handle_thread(const char *comm, pid_t pid, pid_t ppid, uid_t uid)
349{
350 char threadname[COMM_DISP_LEN + 1];
351 sprintf(threadname, "{%.*s}", (int)sizeof(threadname) - 3, comm);
352 add_proc(threadname, pid, ppid, uid);
353}
354#endif
355
356static void mread_proc(void)
357{
358 procps_status_t *p = NULL;
359#if ENABLE_FEATURE_SHOW_THREADS
360 pid_t parent = 0;
361#endif
362 int flags = PSSCAN_COMM | PSSCAN_PID | PSSCAN_PPID | PSSCAN_UIDGID | PSSCAN_TASKS;
363
364 while ((p = procps_scan(p, flags)) != NULL) {
365#if ENABLE_FEATURE_SHOW_THREADS
366 if (p->pid != p->main_thread_pid)
367 handle_thread(p->comm, p->pid, parent, p->uid);
368 else
369#endif
370 {
371 add_proc(p->comm, p->pid, p->ppid, p->uid);
372#if ENABLE_FEATURE_SHOW_THREADS
373 parent = p->pid;
374#endif
375 }
376 }
377}
378
379int pstree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
380int pstree_main(int argc UNUSED_PARAM, char **argv)
381{
382 pid_t pid = 1;
383 long uid = 0;
384
385 INIT_G();
386
387 G.output_width = get_terminal_width(0);
388
389 getopt32(argv, "^" "p" "\0" "?1");
390 argv += optind;
391
392 if (argv[0]) {
393 if (argv[0][0] >= '0' && argv[0][0] <= '9') {
394 pid = xatoi(argv[0]);
395 } else {
396 uid = xuname2uid(argv[0]);
397 }
398 }
399
400 mread_proc();
401
402 if (!uid)
403 dump_tree(find_proc(pid), 0, 1, 1, 1, 0);
404 else {
405 dump_by_user(find_proc(1), uid);
406 if (!G.dumped) {
407 bb_simple_error_msg_and_die("no processes found");
408 }
409 }
410
411 if (ENABLE_FEATURE_CLEAN_UP) {
412 free(G.width);
413 free(G.more);
414 }
415 return 0;
416}
417