123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- /*
- * pstree.c - display process tree
- *
- * Copyright (C) 1993-2002 Werner Almesberger
- * Copyright (C) 2002-2009 Craig Small
- * Copyright (C) 2010 Lauri Kasanen
- *
- * Based on pstree (PSmisc) 22.13.
- *
- * Licensed under GPLv2, see file LICENSE in this source tree.
- */
- //config:config PSTREE
- //config: bool "pstree (9.4 kb)"
- //config: default y
- //config: help
- //config: Display a tree of processes.
- //applet:IF_PSTREE(APPLET_NOEXEC(pstree, pstree, BB_DIR_USR_BIN, BB_SUID_DROP, pstree))
- //kbuild:lib-$(CONFIG_PSTREE) += pstree.o
- //usage:#define pstree_trivial_usage
- //usage: "[-p] [PID|USER]"
- //usage:#define pstree_full_usage "\n\n"
- //usage: "Display process tree, optionally start from USER or PID\n"
- //usage: "\n -p Show pids"
- #include "libbb.h"
- #define PROC_BASE "/proc"
- #define OPT_PID (1 << 0)
- struct child;
- #if ENABLE_FEATURE_SHOW_THREADS
- /* For threads, we add {...} around the comm, so we need two extra bytes */
- # define COMM_DISP_LEN (COMM_LEN + 2)
- #else
- # define COMM_DISP_LEN COMM_LEN
- #endif
- typedef struct proc {
- char comm[COMM_DISP_LEN + 1];
- // char flags; - unused, delete?
- pid_t pid;
- uid_t uid;
- struct child *children;
- struct proc *parent;
- struct proc *next;
- } PROC;
- /* For flags above */
- //#define PFLAG_THREAD 0x01
- typedef struct child {
- PROC *child;
- struct child *next;
- } CHILD;
- #define empty_2 " "
- #define branch_2 "|-"
- #define vert_2 "| "
- #define last_2 "`-"
- #define single_3 "---"
- #define first_3 "-+-"
- struct globals {
- /* 0-based. IOW: the number of chars we printed on current line */
- unsigned cur_x;
- unsigned output_width;
- /* The buffers will be dynamically increased in size as needed */
- unsigned capacity;
- unsigned *width;
- uint8_t *more;
- PROC *list;
- smallint dumped; /* used by dump_by_user */
- };
- #define G (*ptr_to_globals)
- #define INIT_G() do { \
- SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
- } while (0)
- /*
- * Allocates additional buffer space for width and more as needed.
- * The first call will allocate the first buffer.
- *
- * bufindex the index that will be used after the call to this function.
- */
- static void ensure_buffer_capacity(int bufindex)
- {
- if (bufindex >= G.capacity) {
- G.capacity += 0x100;
- G.width = xrealloc(G.width, G.capacity * sizeof(G.width[0]));
- G.more = xrealloc(G.more, G.capacity * sizeof(G.more[0]));
- }
- }
- /* NB: this function is never called with "bad" chars
- * (control chars or chars >= 0x7f)
- */
- static void out_char(char c)
- {
- G.cur_x++;
- if (G.cur_x > G.output_width)
- return;
- if (G.cur_x == G.output_width)
- c = '+';
- putchar(c);
- }
- /* NB: this function is never called with "bad" chars
- * (control chars or chars >= 0x7f)
- */
- static void out_string(const char *str)
- {
- while (*str)
- out_char(*str++);
- }
- static void out_newline(void)
- {
- putchar('\n');
- G.cur_x = 0;
- }
- static PROC *find_proc(pid_t pid)
- {
- PROC *walk;
- for (walk = G.list; walk; walk = walk->next)
- if (walk->pid == pid)
- break;
- return walk;
- }
- static PROC *new_proc(const char *comm, pid_t pid, uid_t uid)
- {
- PROC *new = xzalloc(sizeof(*new));
- strcpy(new->comm, comm);
- new->pid = pid;
- new->uid = uid;
- new->next = G.list;
- G.list = new;
- return G.list;
- }
- static void add_child(PROC *parent, PROC *child)
- {
- CHILD *new, **walk;
- int cmp;
- new = xmalloc(sizeof(*new));
- new->child = child;
- for (walk = &parent->children; *walk; walk = &(*walk)->next) {
- cmp = strcmp((*walk)->child->comm, child->comm);
- if (cmp > 0)
- break;
- if (cmp == 0 && (*walk)->child->uid > child->uid)
- break;
- }
- new->next = *walk;
- *walk = new;
- }
- static void add_proc(const char *comm, pid_t pid, pid_t ppid,
- uid_t uid /*, char isthread*/)
- {
- PROC *this, *parent;
- this = find_proc(pid);
- if (!this)
- this = new_proc(comm, pid, uid);
- else {
- strcpy(this->comm, comm);
- this->uid = uid;
- }
- if (pid == ppid)
- ppid = 0;
- // if (isthread)
- // this->flags |= PFLAG_THREAD;
- parent = find_proc(ppid);
- if (!parent)
- parent = new_proc("?", ppid, 0);
- add_child(parent, this);
- this->parent = parent;
- }
- static int tree_equal(const PROC *a, const PROC *b)
- {
- const CHILD *walk_a, *walk_b;
- if (strcmp(a->comm, b->comm) != 0)
- return 0;
- if ((option_mask32 /*& OPT_PID*/) && a->pid != b->pid)
- return 0;
- for (walk_a = a->children, walk_b = b->children;
- walk_a && walk_b;
- walk_a = walk_a->next, walk_b = walk_b->next
- ) {
- if (!tree_equal(walk_a->child, walk_b->child))
- return 0;
- }
- return !(walk_a || walk_b);
- }
- static int out_args(const char *mystr)
- {
- const char *here;
- int strcount = 0;
- char tmpstr[5];
- for (here = mystr; *here; here++) {
- if (*here == '\\') {
- out_string("\\\\");
- strcount += 2;
- } else if (*here >= ' ' && *here < 0x7f) {
- out_char(*here);
- strcount++;
- } else {
- sprintf(tmpstr, "\\%03o", (unsigned char) *here);
- out_string(tmpstr);
- strcount += 4;
- }
- }
- return strcount;
- }
- static void
- dump_tree(PROC *current, int level, int rep, int leaf, int last, int closing)
- {
- CHILD *walk, *next, **scan;
- int lvl, i, add, offset, count, comm_len, first;
- char tmp[sizeof(int)*3 + 4];
- if (!current)
- return;
- if (!leaf) {
- for (lvl = 0; lvl < level; lvl++) {
- i = G.width[lvl] + 1;
- while (--i >= 0)
- out_char(' ');
- if (lvl == level - 1) {
- if (last) {
- out_string(last_2);
- } else {
- out_string(branch_2);
- }
- } else {
- if (G.more[lvl + 1]) {
- out_string(vert_2);
- } else {
- out_string(empty_2);
- }
- }
- }
- }
- add = 0;
- if (rep > 1) {
- add += sprintf(tmp, "%d*[", rep);
- out_string(tmp);
- }
- comm_len = out_args(current->comm);
- if (option_mask32 /*& OPT_PID*/) {
- comm_len += sprintf(tmp, "(%d)", (int)current->pid);
- out_string(tmp);
- }
- offset = G.cur_x;
- if (!current->children) {
- while (closing--)
- out_char(']');
- out_newline();
- }
- ensure_buffer_capacity(level);
- G.more[level] = !last;
- G.width[level] = comm_len + G.cur_x - offset + add;
- if (G.cur_x >= G.output_width) {
- //out_string(first_3); - why? it won't print anything
- //out_char('+');
- out_newline();
- return;
- }
- first = 1;
- for (walk = current->children; walk; walk = next) {
- count = 0;
- next = walk->next;
- scan = &walk->next;
- while (*scan) {
- if (!tree_equal(walk->child, (*scan)->child))
- scan = &(*scan)->next;
- else {
- if (next == *scan)
- next = (*scan)->next;
- count++;
- *scan = (*scan)->next;
- }
- }
- if (first) {
- out_string(next ? first_3 : single_3);
- first = 0;
- }
- dump_tree(walk->child, level + 1, count + 1,
- walk == current->children, !next,
- closing + (count ? 1 : 0));
- }
- }
- static void dump_by_user(PROC *current, uid_t uid)
- {
- const CHILD *walk;
- if (!current)
- return;
- if (current->uid == uid) {
- if (G.dumped)
- putchar('\n');
- dump_tree(current, 0, 1, 1, 1, 0);
- G.dumped = 1;
- return;
- }
- for (walk = current->children; walk; walk = walk->next)
- dump_by_user(walk->child, uid);
- }
- #if ENABLE_FEATURE_SHOW_THREADS
- static void handle_thread(const char *comm, pid_t pid, pid_t ppid, uid_t uid)
- {
- char threadname[COMM_DISP_LEN + 1];
- sprintf(threadname, "{%.*s}", (int)sizeof(threadname) - 3, comm);
- add_proc(threadname, pid, ppid, uid/*, 1*/);
- }
- #endif
- static void mread_proc(void)
- {
- procps_status_t *p = NULL;
- #if ENABLE_FEATURE_SHOW_THREADS
- pid_t parent = 0;
- #endif
- int flags = PSSCAN_COMM | PSSCAN_PID | PSSCAN_PPID | PSSCAN_UIDGID | PSSCAN_TASKS;
- while ((p = procps_scan(p, flags)) != NULL) {
- #if ENABLE_FEATURE_SHOW_THREADS
- if (p->pid != p->main_thread_pid)
- handle_thread(p->comm, p->pid, parent, p->uid);
- else
- #endif
- {
- add_proc(p->comm, p->pid, p->ppid, p->uid/*, 0*/);
- #if ENABLE_FEATURE_SHOW_THREADS
- parent = p->pid;
- #endif
- }
- }
- }
- int pstree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
- int pstree_main(int argc UNUSED_PARAM, char **argv)
- {
- pid_t pid = 1;
- long uid = 0;
- INIT_G();
- G.output_width = get_terminal_width(0);
- getopt32(argv, "^" "p" "\0" "?1");
- argv += optind;
- if (argv[0]) {
- if (argv[0][0] >= '0' && argv[0][0] <= '9') {
- pid = xatoi(argv[0]);
- } else {
- uid = xuname2uid(argv[0]);
- }
- }
- mread_proc();
- if (!uid)
- dump_tree(find_proc(pid), 0, 1, 1, 1, 0);
- else {
- dump_by_user(find_proc(1), uid);
- if (!G.dumped) {
- bb_simple_error_msg_and_die("no processes found");
- }
- }
- if (ENABLE_FEATURE_CLEAN_UP) {
- free(G.width);
- free(G.more);
- }
- return 0;
- }
|