busybox/procps/ps.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini ps implementation(s) for busybox
   4 *
   5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
   6 * Fix for SELinux Support:(c)2007 Hiroshi Shinji <shiroshi@my.email.ne.jp>
   7 *                         (c)2007 Yuichi Nakamura <ynakam@hitachisoft.jp>
   8 *
   9 * Licensed under GPLv2, see file LICENSE in this source tree.
  10 */
  11//config:config PS
  12//config:       bool "ps (11 kb)"
  13//config:       default y
  14//config:       help
  15//config:       ps gives a snapshot of the current processes.
  16//config:
  17//config:config FEATURE_PS_WIDE
  18//config:       bool "Enable wide output (-w)"
  19//config:       default y
  20//config:       depends on (PS || MINIPS) && !DESKTOP
  21//config:       help
  22//config:       Support argument 'w' for wide output.
  23//config:       If given once, 132 chars are printed, and if given more
  24//config:       than once, the length is unlimited.
  25//config:
  26//config:config FEATURE_PS_LONG
  27//config:       bool "Enable long output (-l)"
  28//config:       default y
  29//config:       depends on (PS || MINIPS) && !DESKTOP
  30//config:       help
  31//config:       Support argument 'l' for long output.
  32//config:       Adds fields PPID, RSS, START, TIME & TTY
  33//config:
  34//config:config FEATURE_PS_TIME
  35//config:       bool "Enable -o time and -o etime specifiers"
  36//config:       default y
  37//config:       depends on (PS || MINIPS) && DESKTOP
  38//config:
  39//config:config FEATURE_PS_UNUSUAL_SYSTEMS
  40//config:       bool "Support Linux prior to 2.4.0 and non-ELF systems"
  41//config:       default n
  42//config:       depends on FEATURE_PS_TIME
  43//config:       help
  44//config:       Include support for measuring HZ on old kernels and non-ELF systems
  45//config:       (if you are on Linux 2.4.0+ and use ELF, you don't need this)
  46//config:
  47//config:config FEATURE_PS_ADDITIONAL_COLUMNS
  48//config:       bool "Enable -o rgroup, -o ruser, -o nice specifiers"
  49//config:       default y
  50//config:       depends on (PS || MINIPS) && DESKTOP
  51
  52//                 APPLET_NOEXEC:name    main location    suid_type     help
  53//applet:IF_PS(    APPLET_NOEXEC(ps,     ps,  BB_DIR_BIN, BB_SUID_DROP, ps))
  54//applet:IF_MINIPS(APPLET_NOEXEC(minips, ps,  BB_DIR_BIN, BB_SUID_DROP, ps))
  55
  56//kbuild:lib-$(CONFIG_PS) += ps.o
  57//kbuild:lib-$(CONFIG_MINIPS) += ps.o
  58
  59//usage:#if ENABLE_DESKTOP
  60//usage:
  61//usage:#define ps_trivial_usage
  62//usage:       "[-o COL1,COL2=HEADER]" IF_FEATURE_SHOW_THREADS(" [-T]")
  63//usage:#define ps_full_usage "\n\n"
  64//usage:       "Show list of processes\n"
  65//usage:     "\n        -o COL1,COL2=HEADER     Select columns for display"
  66//usage:        IF_FEATURE_SHOW_THREADS(
  67//usage:     "\n        -T                      Show threads"
  68//usage:        )
  69//usage:
  70//usage:#else /* !ENABLE_DESKTOP */
  71//usage:
  72//usage:#if !ENABLE_SELINUX && !ENABLE_FEATURE_PS_WIDE
  73//usage:#define USAGE_PS "\nThis version of ps accepts no options"
  74//usage:#else
  75//usage:#define USAGE_PS ""
  76//usage:#endif
  77//usage:
  78//usage:#define ps_trivial_usage
  79//usage:       ""
  80//usage:#define ps_full_usage "\n\n"
  81//usage:       "Show list of processes\n"
  82//usage:        USAGE_PS
  83//usage:        IF_SELINUX(
  84//usage:     "\n        -Z      Show selinux context"
  85//usage:        )
  86//usage:        IF_FEATURE_PS_WIDE(
  87//usage:     "\n        w       Wide output"
  88//usage:        )
  89//usage:        IF_FEATURE_PS_LONG(
  90//usage:     "\n        l       Long output"
  91//usage:        )
  92//usage:        IF_FEATURE_SHOW_THREADS(
  93//usage:     "\n        T       Show threads"
  94//usage:        )
  95//usage:
  96//usage:#endif /* ENABLE_DESKTOP */
  97//usage:
  98//usage:#define ps_example_usage
  99//usage:       "$ ps\n"
 100//usage:       "  PID  Uid      Gid State Command\n"
 101//usage:       "    1 root     root     S init\n"
 102//usage:       "    2 root     root     S [kflushd]\n"
 103//usage:       "    3 root     root     S [kupdate]\n"
 104//usage:       "    4 root     root     S [kpiod]\n"
 105//usage:       "    5 root     root     S [kswapd]\n"
 106//usage:       "  742 andersen andersen S [bash]\n"
 107//usage:       "  743 andersen andersen S -bash\n"
 108//usage:       "  745 root     root     S [getty]\n"
 109//usage:       " 2990 andersen andersen R ps\n"
 110
 111#include "libbb.h"
 112#include "common_bufsiz.h"
 113#ifdef __linux__
 114# include <sys/sysinfo.h>
 115#endif
 116
 117/* Absolute maximum on output line length */
 118enum { MAX_WIDTH = 2*1024 };
 119
 120#if ENABLE_FEATURE_PS_TIME || ENABLE_FEATURE_PS_LONG
 121static unsigned long get_uptime(void)
 122{
 123#ifdef __linux__
 124        struct sysinfo info;
 125        if (sysinfo(&info) < 0)
 126                return 0;
 127        return info.uptime;
 128#elif 1
 129        unsigned long uptime;
 130        char buf[sizeof(uptime)*3 + 2];
 131        /* /proc/uptime is "UPTIME_SEC.NN IDLE_SEC.NN\n"
 132         * (where IDLE is cumulative over all CPUs)
 133         */
 134        if (open_read_close("/proc/uptime", buf, sizeof(buf)) <= 0)
 135                bb_perror_msg_and_die("can't read '%s'", "/proc/uptime");
 136        buf[sizeof(buf)-1] = '\0';
 137        sscanf(buf, "%lu", &uptime);
 138        return uptime;
 139#else
 140        struct timespec ts;
 141        if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
 142                return 0;
 143        return ts.tv_sec;
 144#endif
 145}
 146#endif
 147
 148#if ENABLE_DESKTOP
 149/* TODO:
 150 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
 151 * specifies (for XSI-conformant systems) following default columns
 152 * (l and f mark columns shown with -l and -f respectively):
 153 * F     l   Flags (octal and additive) associated with the process (??)
 154 * S     l   The state of the process
 155 * UID   f,l The user ID; the login name is printed with -f
 156 * PID       The process ID
 157 * PPID  f,l The parent process
 158 * C     f,l Processor utilization
 159 * PRI   l   The priority of the process; higher numbers mean lower priority
 160 * NI    l   Nice value
 161 * ADDR  l   The address of the process
 162 * SZ    l   The size in blocks of the core image of the process
 163 * WCHAN l   The event for which the process is waiting or sleeping
 164 * STIME f   Starting time of the process
 165 * TTY       The controlling terminal for the process
 166 * TIME      The cumulative execution time for the process
 167 * CMD       The command name; the full command line is shown with -f
 168 */
 169typedef struct {
 170        uint16_t width;
 171        char name6[6];
 172        const char *header;
 173        void (*f)(char *buf, int size, const procps_status_t *ps);
 174        int ps_flags;
 175} ps_out_t;
 176
 177struct globals {
 178        ps_out_t* out;
 179        int out_cnt;
 180        int print_header;
 181        int need_flags;
 182        char *buffer;
 183        unsigned terminal_width;
 184#if ENABLE_FEATURE_PS_TIME
 185# if ENABLE_FEATURE_PS_UNUSUAL_SYSTEMS || !defined(__linux__)
 186        unsigned kernel_HZ;
 187# endif
 188        unsigned long seconds_since_boot;
 189#endif
 190} FIX_ALIASING;
 191#define G (*(struct globals*)bb_common_bufsiz1)
 192#define out                (G.out               )
 193#define out_cnt            (G.out_cnt           )
 194#define print_header       (G.print_header      )
 195#define need_flags         (G.need_flags        )
 196#define buffer             (G.buffer            )
 197#define terminal_width     (G.terminal_width    )
 198#define INIT_G() do { setup_common_bufsiz(); } while (0)
 199
 200#if ENABLE_FEATURE_PS_TIME
 201# if ENABLE_FEATURE_PS_UNUSUAL_SYSTEMS || !defined(__linux__)
 202#  define get_kernel_HZ() (G.kernel_HZ)
 203# else
 204    /* non-ancient Linux standardized on 100 for "times" freq */
 205#  define get_kernel_HZ() ((unsigned)100)
 206# endif
 207#endif
 208
 209/* Print value to buf, max size+1 chars (including trailing '\0') */
 210
 211static void func_user(char *buf, int size, const procps_status_t *ps)
 212{
 213#if 1
 214        safe_strncpy(buf, get_cached_username(ps->uid), size+1);
 215#else
 216        /* "compatible" version, but it's larger */
 217        /* procps 2.18 shows numeric UID if name overflows the field */
 218        /* TODO: get_cached_username() returns numeric string if
 219         * user has no passwd record, we will display it
 220         * left-justified here; too long usernames are shown
 221         * as _right-justified_ IDs. Is it worth fixing? */
 222        const char *user = get_cached_username(ps->uid);
 223        if (strlen(user) <= size)
 224                safe_strncpy(buf, user, size+1);
 225        else
 226                sprintf(buf, "%*u", size, (unsigned)ps->uid);
 227#endif
 228}
 229
 230static void func_group(char *buf, int size, const procps_status_t *ps)
 231{
 232        safe_strncpy(buf, get_cached_groupname(ps->gid), size+1);
 233}
 234
 235static void func_comm(char *buf, int size, const procps_status_t *ps)
 236{
 237        safe_strncpy(buf, ps->comm, size+1);
 238}
 239
 240static void func_state(char *buf, int size, const procps_status_t *ps)
 241{
 242        safe_strncpy(buf, ps->state, size+1);
 243}
 244
 245static void func_args(char *buf, int size, const procps_status_t *ps)
 246{
 247        read_cmdline(buf, size+1, ps->pid, ps->comm);
 248}
 249
 250static void func_pid(char *buf, int size, const procps_status_t *ps)
 251{
 252        sprintf(buf, "%*u", size, ps->pid);
 253}
 254
 255static void func_ppid(char *buf, int size, const procps_status_t *ps)
 256{
 257        sprintf(buf, "%*u", size, ps->ppid);
 258}
 259
 260static void func_pgid(char *buf, int size, const procps_status_t *ps)
 261{
 262        sprintf(buf, "%*u", size, ps->pgid);
 263}
 264
 265static void func_sid(char *buf, int size, const procps_status_t *ps)
 266{
 267        sprintf(buf, "%*u", size, ps->sid);
 268}
 269
 270static void put_lu(char *buf, int size, unsigned long u)
 271{
 272        char buf4[5];
 273
 274        /* see http://en.wikipedia.org/wiki/Tera */
 275        smart_ulltoa4(u, buf4, " mgtpezy")[0] = '\0';
 276        sprintf(buf, "%.*s", size, buf4);
 277}
 278
 279static void func_vsz(char *buf, int size, const procps_status_t *ps)
 280{
 281        put_lu(buf, size, ps->vsz);
 282}
 283
 284static void func_rss(char *buf, int size, const procps_status_t *ps)
 285{
 286        put_lu(buf, size, ps->rss);
 287}
 288
 289static void func_tty(char *buf, int size, const procps_status_t *ps)
 290{
 291        buf[0] = '?';
 292        buf[1] = '\0';
 293        if (ps->tty_major) /* tty field of "0" means "no tty" */
 294                snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor);
 295}
 296
 297#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
 298static void func_rgroup(char *buf, int size, const procps_status_t *ps)
 299{
 300        safe_strncpy(buf, get_cached_groupname(ps->rgid), size+1);
 301}
 302static void func_ruser(char *buf, int size, const procps_status_t *ps)
 303{
 304        safe_strncpy(buf, get_cached_username(ps->ruid), size+1);
 305}
 306static void func_nice(char *buf, int size, const procps_status_t *ps)
 307{
 308        sprintf(buf, "%*d", size, ps->niceness);
 309}
 310#endif
 311
 312#if ENABLE_FEATURE_PS_TIME
 313static void format_time(char *buf, int size, unsigned long tt)
 314{
 315        unsigned ff;
 316
 317        /* Used to show "14453:50" if tt is large. Ugly.
 318         * procps-ng 3.3.10 uses "[[dd-]hh:]mm:ss" format.
 319         * TODO: switch to that?
 320         */
 321
 322        /* Formatting for 5-char TIME column.
 323         * NB: "size" is not always 5: ELAPSED is wider (7),
 324         * not taking advantage of that (yet?).
 325         */
 326        ff = tt % 60;
 327        tt /= 60;
 328        if (tt < 60) {
 329                snprintf(buf, size+1, "%2u:%02u", (unsigned)tt, ff);
 330                return;
 331        }
 332        ff = tt % 60;
 333        tt /= 60;
 334        if (tt < 24) {
 335                snprintf(buf, size+1, "%2uh%02u", (unsigned)tt, ff);
 336                return;
 337        }
 338        ff = tt % 24;
 339        tt /= 24;
 340        if (tt < 100) {
 341                snprintf(buf, size+1, "%2ud%02u", (unsigned)tt, ff);
 342                return;
 343        }
 344        snprintf(buf, size+1, "%4lud", tt);
 345}
 346static void func_etime(char *buf, int size, const procps_status_t *ps)
 347{
 348        /* elapsed time [[dd-]hh:]mm:ss; here only mm:ss */
 349        unsigned long mm;
 350
 351        mm = ps->start_time / get_kernel_HZ();
 352        mm = G.seconds_since_boot - mm;
 353        format_time(buf, size, mm);
 354}
 355static void func_time(char *buf, int size, const procps_status_t *ps)
 356{
 357        /* cumulative time [[dd-]hh:]mm:ss; here only mm:ss */
 358        unsigned long mm;
 359
 360        mm = (ps->utime + ps->stime) / get_kernel_HZ();
 361        format_time(buf, size, mm);
 362}
 363#endif
 364
 365#if ENABLE_SELINUX
 366static void func_label(char *buf, int size, const procps_status_t *ps)
 367{
 368        safe_strncpy(buf, ps->context ? ps->context : "unknown", size+1);
 369}
 370#endif
 371
 372/*
 373static void func_pcpu(char *buf, int size, const procps_status_t *ps)
 374{
 375}
 376*/
 377
 378static const ps_out_t out_spec[] ALIGN_PTR = {
 379/* Mandated by http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html: */
 380        { 8                  , "user"  ,"USER"   ,func_user  ,PSSCAN_UIDGID  },
 381        { 8                  , "group" ,"GROUP"  ,func_group ,PSSCAN_UIDGID  },
 382        { 16                 , "comm"  ,"COMMAND",func_comm  ,PSSCAN_COMM    },
 383        { MAX_WIDTH          , "args"  ,"COMMAND",func_args  ,PSSCAN_COMM    },
 384        { 5                  , "pid"   ,"PID"    ,func_pid   ,PSSCAN_PID     },
 385        { 5                  , "ppid"  ,"PPID"   ,func_ppid  ,PSSCAN_PPID    },
 386        { 5                  , "pgid"  ,"PGID"   ,func_pgid  ,PSSCAN_PGID    },
 387#if ENABLE_FEATURE_PS_TIME
 388        { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_START_TIME },
 389#endif
 390#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
 391        { 5                  , "nice"  ,"NI"     ,func_nice  ,PSSCAN_NICE    },
 392        { 8                  , "rgroup","RGROUP" ,func_rgroup,PSSCAN_RUIDGID },
 393        { 8                  , "ruser" ,"RUSER"  ,func_ruser ,PSSCAN_RUIDGID },
 394//      { 5                  , "pcpu"  ,"%CPU"   ,func_pcpu  ,PSSCAN_        },
 395#endif
 396#if ENABLE_FEATURE_PS_TIME
 397        { 5                  , "time"  ,"TIME"   ,func_time  ,PSSCAN_STIME | PSSCAN_UTIME },
 398#endif
 399        { 6                  , "tty"   ,"TT"     ,func_tty   ,PSSCAN_TTY     },
 400        { 4                  , "vsz"   ,"VSZ"    ,func_vsz   ,PSSCAN_VSZ     },
 401/* Not mandated, but useful: */
 402        { 5                  , "sid"   ,"SID"    ,func_sid   ,PSSCAN_SID     },
 403        { 4                  , "stat"  ,"STAT"   ,func_state ,PSSCAN_STATE   },
 404        { 4                  , "rss"   ,"RSS"    ,func_rss   ,PSSCAN_RSS     },
 405#if ENABLE_SELINUX
 406        { 35                 , "label" ,"LABEL"  ,func_label ,PSSCAN_CONTEXT },
 407#endif
 408};
 409
 410static ps_out_t* new_out_t(void)
 411{
 412        out = xrealloc_vector(out, 2, out_cnt);
 413        return &out[out_cnt++];
 414}
 415
 416static const ps_out_t* find_out_spec(const char *name)
 417{
 418        unsigned i;
 419        char buf[ARRAY_SIZE(out_spec)*7 + 1];
 420        char *p = buf;
 421
 422        for (i = 0; i < ARRAY_SIZE(out_spec); i++) {
 423                if (strncmp(name, out_spec[i].name6, 6) == 0)
 424                        return &out_spec[i];
 425                p += sprintf(p, "%.6s,", out_spec[i].name6);
 426        }
 427        p[-1] = '\0';
 428        bb_error_msg_and_die("bad -o argument '%s', supported arguments: %s", name, buf);
 429}
 430
 431static void parse_o(char* opt)
 432{
 433        ps_out_t* new;
 434        // POSIX: "-o is blank- or comma-separated list" (FIXME)
 435        char *comma, *equal;
 436        while (1) {
 437                comma = strchr(opt, ',');
 438                equal = strchr(opt, '=');
 439                if (comma && (!equal || equal > comma)) {
 440                        *comma = '\0';
 441                        *new_out_t() = *find_out_spec(opt);
 442                        *comma = ',';
 443                        opt = comma + 1;
 444                        continue;
 445                }
 446                // opt points to last spec in comma separated list.
 447                // This one can have =HEADER part.
 448                new = new_out_t();
 449                if (equal)
 450                        *equal = '\0';
 451                *new = *find_out_spec(opt);
 452                if (!equal)
 453                        break;
 454                *equal++ = '=';
 455                new->header = equal;
 456                comma = strchr(equal, ',');
 457                if (comma)
 458                        *comma = '\0';
 459                // POSIX: the field widths shall be ... at least as wide as
 460                // the header text (default or overridden value).
 461                // If the header text is null, such as -o user=,
 462                // the field width shall be at least as wide as the
 463                // default header text
 464                if (new->header[0]) {
 465                        new->width = strlen(new->header);
 466                }
 467                if (!comma)
 468                        break;
 469                //*comma = ','; /* no, new->header should stay NUL-terminated */
 470                opt = comma + 1;
 471        }
 472}
 473
 474static void alloc_line_buffer(void)
 475{
 476        int i;
 477        int width = 0;
 478        for (i = 0; i < out_cnt; i++) {
 479                need_flags |= out[i].ps_flags;
 480                if (out[i].header[0]) {
 481                        print_header = 1;
 482                }
 483                width += out[i].width + 1; /* "FIELD " */
 484                if ((int)(width - terminal_width) > 0) {
 485                        /* The rest does not fit on the screen */
 486                        //out[i].width -= (width - terminal_width - 1);
 487                        out_cnt = i + 1;
 488                        break;
 489                }
 490        }
 491#if ENABLE_SELINUX
 492        if (!is_selinux_enabled())
 493                need_flags &= ~PSSCAN_CONTEXT;
 494#endif
 495        buffer = xmalloc(width + 1); /* for trailing \0 */
 496}
 497
 498static void format_header(void)
 499{
 500        int i;
 501        ps_out_t* op;
 502        char *p;
 503
 504        if (!print_header)
 505                return;
 506        p = buffer;
 507        i = 0;
 508        if (out_cnt) {
 509                while (1) {
 510                        op = &out[i];
 511                        if (++i == out_cnt) /* do not pad last field */
 512                                break;
 513                        p += sprintf(p, "%-*s ", op->width, op->header);
 514                }
 515                strcpy(p, op->header);
 516        }
 517        printf("%.*s\n", terminal_width, buffer);
 518}
 519
 520static void format_process(const procps_status_t *ps)
 521{
 522        int i, len;
 523        char *p = buffer;
 524        i = 0;
 525        if (out_cnt) while (1) {
 526                out[i].f(p, out[i].width, ps);
 527                // POSIX: Any field need not be meaningful in all
 528                // implementations. In such a case a hyphen ( '-' )
 529                // should be output in place of the field value.
 530                if (!p[0]) {
 531                        p[0] = '-';
 532                        p[1] = '\0';
 533                }
 534                len = strlen(p);
 535                p += len;
 536                len = out[i].width - len + 1;
 537                if (++i == out_cnt) /* do not pad last field */
 538                        break;
 539                p += sprintf(p, "%*s", len, " "); /* " ", not "", to ensure separation of fields */
 540        }
 541        printf("%.*s\n", terminal_width, buffer);
 542}
 543
 544#if ENABLE_SELINUX
 545# define SELINUX_O_PREFIX "label,"
 546# define DEFAULT_O_STR    (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args")
 547#else
 548# define DEFAULT_O_STR    ("pid,user" IF_FEATURE_PS_TIME(",time") ",args")
 549#endif
 550
 551int ps_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 552int ps_main(int argc UNUSED_PARAM, char **argv)
 553{
 554        procps_status_t *p;
 555        llist_t* opt_o = NULL;
 556        char default_o[sizeof(DEFAULT_O_STR)];
 557#if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS
 558        int opt;
 559#endif
 560        enum {
 561                OPT_Z = (1 << 0),
 562                OPT_o = (1 << 1),
 563                OPT_a = (1 << 2),
 564                OPT_A = (1 << 3),
 565                OPT_d = (1 << 4),
 566                OPT_e = (1 << 5),
 567                OPT_f = (1 << 6),
 568                OPT_l = (1 << 7),
 569                OPT_T = (1 << 8) * ENABLE_FEATURE_SHOW_THREADS,
 570        };
 571
 572        INIT_G();
 573#if ENABLE_FEATURE_PS_TIME
 574        G.seconds_since_boot = get_uptime();
 575# if ENABLE_FEATURE_PS_UNUSUAL_SYSTEMS || !defined(__linux__)
 576        G.kernel_HZ = bb_clk_tck(); /* this is sysconf(_SC_CLK_TCK) */
 577# endif
 578#endif
 579
 580        // POSIX:
 581        // -a  Write information for all processes associated with terminals
 582        //     Implementations may omit session leaders from this list
 583        // -A  Write information for all processes
 584        // -d  Write information for all processes, except session leaders
 585        // -e  Write information for all processes (equivalent to -A)
 586        // -f  Generate a full listing
 587        // -l  Generate a long listing
 588        // -o col1,col2,col3=header
 589        //     Select which columns to display
 590        /* We allow (and ignore) most of the above. FIXME.
 591         * -T is picked for threads (POSIX hasn't standardized it).
 592         * procps v3.2.7 supports -T and shows tids as SPID column,
 593         * it also supports -L where it shows tids as LWP column.
 594         */
 595#if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS
 596        opt =
 597#endif
 598                getopt32(argv, "Zo:*aAdefl"IF_FEATURE_SHOW_THREADS("T"), &opt_o);
 599
 600        if (opt_o) {
 601                do {
 602                        parse_o(llist_pop(&opt_o));
 603                } while (opt_o);
 604        } else {
 605                /* Below: parse_o() needs char*, NOT const char*,
 606                 * can't pass it constant string. Need to make a copy first.
 607                 */
 608#if ENABLE_SELINUX
 609                if (!(opt & OPT_Z) || !is_selinux_enabled()) {
 610                        /* no -Z or no SELinux: do not show LABEL */
 611                        strcpy(default_o, DEFAULT_O_STR + sizeof(SELINUX_O_PREFIX)-1);
 612                } else
 613#endif
 614                {
 615                        strcpy(default_o, DEFAULT_O_STR);
 616                }
 617                parse_o(default_o);
 618        }
 619#if ENABLE_FEATURE_SHOW_THREADS
 620        if (opt & OPT_T)
 621                need_flags |= PSSCAN_TASKS;
 622#endif
 623
 624        /* Was INT_MAX, but some libc's go belly up with printf("%.*s")
 625         * and such large widths */
 626        terminal_width = MAX_WIDTH;
 627        if (isatty(1)) {
 628                terminal_width = get_terminal_width(0);
 629                if (--terminal_width > MAX_WIDTH)
 630                        terminal_width = MAX_WIDTH;
 631        }
 632        alloc_line_buffer();
 633        format_header();
 634
 635        p = NULL;
 636        while ((p = procps_scan(p, need_flags)) != NULL) {
 637                format_process(p);
 638        }
 639
 640        return EXIT_SUCCESS;
 641}
 642
 643
 644#else /* !ENABLE_DESKTOP */
 645
 646
 647int ps_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 648int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 649{
 650        procps_status_t *p;
 651        int psscan_flags = PSSCAN_PID | PSSCAN_UIDGID
 652                        | PSSCAN_STATE | PSSCAN_VSZ | PSSCAN_COMM;
 653        unsigned terminal_width IF_NOT_FEATURE_PS_WIDE(= 79);
 654        enum {
 655                OPT_Z = (1 << 0) * ENABLE_SELINUX,
 656                OPT_T = (1 << ENABLE_SELINUX) * ENABLE_FEATURE_SHOW_THREADS,
 657                OPT_l = (1 << ENABLE_SELINUX) * (1 << ENABLE_FEATURE_SHOW_THREADS) * ENABLE_FEATURE_PS_LONG,
 658        };
 659#if ENABLE_FEATURE_PS_LONG
 660        time_t now = now; /* for compiler */
 661        unsigned long uptime = uptime;
 662#endif
 663        /* If we support any options, parse argv */
 664#if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_PS_WIDE || ENABLE_FEATURE_PS_LONG
 665        int opts = 0;
 666# if ENABLE_FEATURE_PS_WIDE
 667        /* -w is a bit complicated */
 668        int w_count = 0;
 669        make_all_argv_opts(argv);
 670        opts = getopt32(argv, "^"
 671                IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")IF_FEATURE_PS_LONG("l")"w"
 672                "\0" "ww",
 673                &w_count
 674        );
 675        /* if w is given once, GNU ps sets the width to 132,
 676         * if w is given more than once, it is "unlimited"
 677         */
 678        if (w_count) {
 679                terminal_width = (w_count == 1) ? 132 : MAX_WIDTH;
 680        } else {
 681                terminal_width = get_terminal_width(0);
 682                /* Go one less... */
 683                if (--terminal_width > MAX_WIDTH)
 684                        terminal_width = MAX_WIDTH;
 685        }
 686# else
 687        /* -w is not supported, only -Z and/or -T */
 688        make_all_argv_opts(argv);
 689        opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")IF_FEATURE_PS_LONG("l"));
 690# endif
 691
 692# if ENABLE_SELINUX
 693        if ((opts & OPT_Z) && is_selinux_enabled()) {
 694                psscan_flags = PSSCAN_PID | PSSCAN_CONTEXT
 695                                | PSSCAN_STATE | PSSCAN_COMM;
 696                puts("  PID CONTEXT                          STAT COMMAND");
 697        } else
 698# endif
 699        if (opts & OPT_l) {
 700                psscan_flags = PSSCAN_STATE | PSSCAN_UIDGID | PSSCAN_PID | PSSCAN_PPID
 701                        | PSSCAN_TTY | PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_COMM
 702                        | PSSCAN_VSZ | PSSCAN_RSS;
 703/* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
 704 * mandates for -l:
 705 * -F     Flags (?)
 706 * S      State
 707 * UID,PID,PPID
 708 * -C     CPU usage
 709 * -PRI   The priority of the process; higher numbers mean lower priority
 710 * -NI    Nice value
 711 * -ADDR  The address of the process (?)
 712 * SZ     The size in blocks of the core image
 713 * -WCHAN The event for which the process is waiting or sleeping
 714 * TTY
 715 * TIME   The cumulative execution time
 716 * CMD
 717 * We don't show fields marked with '-'.
 718 * We show VSZ and RSS instead of SZ.
 719 * We also show STIME (standard says that -f shows it, -l doesn't).
 720 */
 721                puts("S   UID   PID  PPID   VSZ   RSS TTY   STIME TIME     CMD");
 722# if ENABLE_FEATURE_PS_LONG
 723                now = time(NULL);
 724                uptime = get_uptime();
 725# endif
 726        }
 727        else {
 728                puts("  PID USER       VSZ STAT COMMAND");
 729        }
 730        if (opts & OPT_T) {
 731                psscan_flags |= PSSCAN_TASKS;
 732        }
 733#endif
 734
 735        p = NULL;
 736        while ((p = procps_scan(p, psscan_flags)) != NULL) {
 737                int len;
 738#if ENABLE_SELINUX
 739                if (psscan_flags & PSSCAN_CONTEXT) {
 740                        len = printf("%5u %-32.32s %s  ",
 741                                        p->pid,
 742                                        p->context ? p->context : "unknown",
 743                                        p->state);
 744                } else
 745#endif
 746                {
 747                        char buf6[6];
 748                        smart_ulltoa5(p->vsz, buf6, " mgtpezy")[0] = '\0';
 749#if ENABLE_FEATURE_PS_LONG
 750                        if (opts & OPT_l) {
 751                                char bufr[6], stime_str[6];
 752                                char tty[2 * sizeof(int)*3 + 2];
 753                                char *endp;
 754                                unsigned sut = (p->stime + p->utime) / 100;
 755                                unsigned elapsed = uptime - (p->start_time / 100);
 756                                time_t start = now - elapsed;
 757                                struct tm *tm = localtime(&start);
 758
 759                                smart_ulltoa5(p->rss, bufr, " mgtpezy")[0] = '\0';
 760
 761                                if (p->tty_major == 136)
 762                                        /* It should be pts/N, not ptsN, but N > 9
 763                                         * will overflow field width...
 764                                         */
 765                                        endp = stpcpy(tty, "pts");
 766                                else
 767                                if (p->tty_major == 4) {
 768                                        endp = stpcpy(tty, "tty");
 769                                        if (p->tty_minor >= 64) {
 770                                                p->tty_minor -= 64;
 771                                                *endp++ = 'S';
 772                                        }
 773                                }
 774                                else
 775                                        endp = tty + sprintf(tty, "%d:", p->tty_major);
 776                                strcpy(endp, utoa(p->tty_minor));
 777
 778                                strftime(stime_str, 6, (elapsed >= (24 * 60 * 60)) ? "%b%d" : "%H:%M", tm);
 779                                stime_str[5] = '\0';
 780                                //            S  UID PID PPID VSZ RSS TTY STIME TIME        CMD
 781                                len = printf("%c %5u %5u %5u %5s %5s %-5s %s %02u:%02u:%02u ",
 782                                        p->state[0], p->uid, p->pid, p->ppid, buf6, bufr, tty,
 783                                        stime_str, sut / 3600, (sut % 3600) / 60, sut % 60);
 784                        } else
 785#endif
 786                        {
 787                                const char *user = get_cached_username(p->uid);
 788                                len = printf("%5u %-8.8s %s %s  ",
 789                                        p->pid, user, buf6, p->state);
 790                        }
 791                }
 792
 793                {
 794                        int sz = terminal_width - len;
 795                        if (sz >= 0) {
 796                                char buf[sz + 1];
 797                                read_cmdline(buf, sz, p->pid, p->comm);
 798                                puts(buf);
 799                        }
 800                }
 801        }
 802        if (ENABLE_FEATURE_CLEAN_UP)
 803                clear_username_cache();
 804        return EXIT_SUCCESS;
 805}
 806
 807#endif /* !ENABLE_DESKTOP */
 808