Bump version to 1.32.0
[oweals/busybox.git] / procps / pstree.c
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 //config:config PSTREE
13 //config:       bool "pstree (9.3 kb)"
14 //config:       default y
15 //config:       help
16 //config:       Display a tree of processes.
17
18 //applet:IF_PSTREE(APPLET_NOEXEC(pstree, pstree, BB_DIR_USR_BIN, BB_SUID_DROP, pstree))
19
20 //kbuild:lib-$(CONFIG_PSTREE) += pstree.o
21
22 //usage:#define pstree_trivial_usage
23 //usage:        "[-p] [PID|USER]"
24 //usage:#define pstree_full_usage "\n\n"
25 //usage:       "Display process tree, optionally start from USER or PID\n"
26 //usage:     "\n        -p      Show pids"
27
28 #include "libbb.h"
29
30 #define PROC_BASE "/proc"
31
32 #define OPT_PID  (1 << 0)
33
34 struct child;
35
36 #if ENABLE_FEATURE_SHOW_THREADS
37 /* For threads, we add {...} around the comm, so we need two extra bytes */
38 # define COMM_DISP_LEN (COMM_LEN + 2)
39 #else
40 # define COMM_DISP_LEN COMM_LEN
41 #endif
42
43 typedef struct proc {
44         char comm[COMM_DISP_LEN + 1];
45 //      char flags; - unused, delete?
46         pid_t pid;
47         uid_t uid;
48         struct child *children;
49         struct proc *parent;
50         struct proc *next;
51 } PROC;
52
53 /* For flags above */
54 //#define PFLAG_THREAD  0x01
55
56 typedef 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
68 struct globals {
69         /* 0-based. IOW: the number of chars we printed on current line */
70         unsigned cur_x;
71         unsigned output_width;
72
73         /* The buffers will be dynamically increased in size as needed */
74         unsigned capacity;
75         unsigned *width;
76         uint8_t *more;
77
78         PROC *list;
79
80         smallint dumped; /* used by dump_by_user */
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  * Allocates additional buffer space for width and more as needed.
90  * The first call will allocate the first buffer.
91  *
92  * bufindex  the index that will be used after the call to this function.
93  */
94 static 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 /* NB: this function is never called with "bad" chars
104  * (control chars or chars >= 0x7f)
105  */
106 static 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 /* NB: this function is never called with "bad" chars
117  * (control chars or chars >= 0x7f)
118  */
119 static void out_string(const char *str)
120 {
121         while (*str)
122                 out_char(*str++);
123 }
124
125 static void out_newline(void)
126 {
127         putchar('\n');
128         G.cur_x = 0;
129 }
130
131 static 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
142 static 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
155 static 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
174 static void add_proc(const char *comm, pid_t pid, pid_t ppid,
175                         uid_t uid /*, char isthread*/)
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 //      if (isthread)
190 //              this->flags |= PFLAG_THREAD;
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
200 static 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 /*& OPT_PID*/) && 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
220 static 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
243 static void
244 dump_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 /*& OPT_PID*/) {
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                 //out_string(first_3); - why? it won't print anything
298                 //out_char('+');
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
329 static 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
348 static 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/*, 1*/);
353 }
354 #endif
355
356 static 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/*, 0*/);
372 #if ENABLE_FEATURE_SHOW_THREADS
373                         parent = p->pid;
374 #endif
375                 }
376         }
377 }
378
379 int pstree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
380 int 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 }