source: MondoRescue/branches/3.3/mindi-busybox/procps/pstree.c@ 3837

Last change on this file since 3837 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 8.0 KB
Line 
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
35struct 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
44typedef 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
57typedef 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
69struct 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 */
95static 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 */
107static 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 */
120static void out_string(const char *str)
121{
122 while (*str)
123 out_char(*str++);
124}
125
126static void out_newline(void)
127{
128 putchar('\n');
129 G.cur_x = 0;
130}
131
132static 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
143static 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
156static 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
175static 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
201static 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
221static 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
244static void
245dump_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
330static 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
349static 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
357static 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
376int pstree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
377int pstree_main(int argc UNUSED_PARAM, char **argv)
378{
379 pid_t pid = 1;
380 long uid = 0;
381
382 INIT_G();
383
384 G.output_width = get_terminal_width(0);
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}
Note: See TracBrowser for help on using the repository browser.