pstree.c 7.8 KB

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