busybox/procps/pstree.c
<<
>>
Prefs
   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}
 415