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