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