pstree.c 8.1 KB

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