fix breakage caused by 'remove "Options:" string from help texts' commit
[oweals/busybox.git] / procps / ps.c
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
12 //usage:#if ENABLE_DESKTOP
13 //usage:
14 //usage:#define ps_trivial_usage
15 //usage:       "[-o COL1,COL2=HEADER]" IF_FEATURE_SHOW_THREADS(" [-T]")
16 //usage:#define ps_full_usage "\n\n"
17 //usage:       "Show list of processes\n"
18 //usage:     "\n        -o COL1,COL2=HEADER     Select columns for display"
19 //usage:        IF_FEATURE_SHOW_THREADS(
20 //usage:     "\n        -T                      Show threads"
21 //usage:        )
22 //usage:
23 //usage:#else /* !ENABLE_DESKTOP */
24 //usage:
25 //usage:#if !ENABLE_SELINUX && !ENABLE_FEATURE_PS_WIDE
26 //usage:#define USAGE_PS "\nThis version of ps accepts no options"
27 //usage:#else
28 //usage:#define USAGE_PS ""
29 //usage:#endif
30 //usage:
31 //usage:#define ps_trivial_usage
32 //usage:       ""
33 //usage:#define ps_full_usage "\n\n"
34 //usage:       "Show list of processes\n"
35 //usage:        USAGE_PS
36 //usage:        IF_SELINUX(
37 //usage:     "\n        -Z      Show selinux context"
38 //usage:        )
39 //usage:        IF_FEATURE_PS_WIDE(
40 //usage:     "\n        w       Wide output"
41 //usage:        )
42 //usage:
43 //usage:#endif /* ENABLE_DESKTOP */
44 //usage:
45 //usage:#define ps_example_usage
46 //usage:       "$ ps\n"
47 //usage:       "  PID  Uid      Gid State Command\n"
48 //usage:       "    1 root     root     S init\n"
49 //usage:       "    2 root     root     S [kflushd]\n"
50 //usage:       "    3 root     root     S [kupdate]\n"
51 //usage:       "    4 root     root     S [kpiod]\n"
52 //usage:       "    5 root     root     S [kswapd]\n"
53 //usage:       "  742 andersen andersen S [bash]\n"
54 //usage:       "  743 andersen andersen S -bash\n"
55 //usage:       "  745 root     root     S [getty]\n"
56 //usage:       " 2990 andersen andersen R ps\n"
57
58 #include "libbb.h"
59
60 /* Absolute maximum on output line length */
61 enum { MAX_WIDTH = 2*1024 };
62
63 #if ENABLE_DESKTOP
64
65 #include <sys/times.h> /* for times() */
66 #ifndef AT_CLKTCK
67 # define AT_CLKTCK 17
68 #endif
69
70 /* TODO:
71  * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
72  * specifies (for XSI-conformant systems) following default columns
73  * (l and f mark columns shown with -l and -f respectively):
74  * F     l   Flags (octal and additive) associated with the process (??)
75  * S     l   The state of the process
76  * UID   f,l The user ID; the login name is printed with -f
77  * PID       The process ID
78  * PPID  f,l The parent process
79  * C     f,l Processor utilization
80  * PRI   l   The priority of the process; higher numbers mean lower priority
81  * NI    l   Nice value
82  * ADDR  l   The address of the process
83  * SZ    l   The size in blocks of the core image of the process
84  * WCHAN l   The event for which the process is waiting or sleeping
85  * STIME f   Starting time of the process
86  * TTY       The controlling terminal for the process
87  * TIME      The cumulative execution time for the process
88  * CMD       The command name; the full command line is shown with -f
89  */
90 #if ENABLE_SELINUX
91 # define SELINUX_O_PREFIX "label,"
92 # define DEFAULT_O_STR    (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args")
93 #else
94 # define DEFAULT_O_STR    ("pid,user" IF_FEATURE_PS_TIME(",time") ",args")
95 #endif
96
97 typedef struct {
98         uint16_t width;
99         char name6[6];
100         const char *header;
101         void (*f)(char *buf, int size, const procps_status_t *ps);
102         int ps_flags;
103 } ps_out_t;
104
105 struct globals {
106         ps_out_t* out;
107         int out_cnt;
108         int print_header;
109         int need_flags;
110         char *buffer;
111         unsigned terminal_width;
112 #if ENABLE_FEATURE_PS_TIME
113         unsigned kernel_HZ;
114         unsigned long long seconds_since_boot;
115 #endif
116         char default_o[sizeof(DEFAULT_O_STR)];
117 } FIX_ALIASING;
118 #define G (*(struct globals*)&bb_common_bufsiz1)
119 #define out                (G.out               )
120 #define out_cnt            (G.out_cnt           )
121 #define print_header       (G.print_header      )
122 #define need_flags         (G.need_flags        )
123 #define buffer             (G.buffer            )
124 #define terminal_width     (G.terminal_width    )
125 #define kernel_HZ          (G.kernel_HZ         )
126 #define seconds_since_boot (G.seconds_since_boot)
127 #define default_o          (G.default_o         )
128 #define INIT_G() do { } while (0)
129
130 #if ENABLE_FEATURE_PS_TIME
131 /* for ELF executables, notes are pushed before environment and args */
132 static ptrdiff_t find_elf_note(ptrdiff_t findme)
133 {
134         ptrdiff_t *ep = (ptrdiff_t *) environ;
135
136         while (*ep++)
137                 continue;
138         while (*ep) {
139                 if (ep[0] == findme) {
140                         return ep[1];
141                 }
142                 ep += 2;
143         }
144         return -1;
145 }
146
147 #if ENABLE_FEATURE_PS_UNUSUAL_SYSTEMS
148 static unsigned get_HZ_by_waiting(void)
149 {
150         struct timeval tv1, tv2;
151         unsigned t1, t2, r, hz;
152         unsigned cnt = cnt; /* for compiler */
153         int diff;
154
155         r = 0;
156
157         /* Wait for times() to reach new tick */
158         t1 = times(NULL);
159         do {
160                 t2 = times(NULL);
161         } while (t2 == t1);
162         gettimeofday(&tv2, NULL);
163
164         do {
165                 t1 = t2;
166                 tv1.tv_usec = tv2.tv_usec;
167
168                 /* Wait exactly one times() tick */
169                 do {
170                         t2 = times(NULL);
171                 } while (t2 == t1);
172                 gettimeofday(&tv2, NULL);
173
174                 /* Calculate ticks per sec, rounding up to even */
175                 diff = tv2.tv_usec - tv1.tv_usec;
176                 if (diff <= 0) diff += 1000000;
177                 hz = 1000000u / (unsigned)diff;
178                 hz = (hz+1) & ~1;
179
180                 /* Count how many same hz values we saw */
181                 if (r != hz) {
182                         r = hz;
183                         cnt = 0;
184                 }
185                 cnt++;
186         } while (cnt < 3); /* exit if saw 3 same values */
187
188         return r;
189 }
190 #else
191 static inline unsigned get_HZ_by_waiting(void)
192 {
193         /* Better method? */
194         return 100;
195 }
196 #endif
197
198 static unsigned get_kernel_HZ(void)
199 {
200         //char buf[64];
201         struct sysinfo info;
202
203         if (kernel_HZ)
204                 return kernel_HZ;
205
206         /* Works for ELF only, Linux 2.4.0+ */
207         kernel_HZ = find_elf_note(AT_CLKTCK);
208         if (kernel_HZ == (unsigned)-1)
209                 kernel_HZ = get_HZ_by_waiting();
210
211         //if (open_read_close("/proc/uptime", buf, sizeof(buf)) <= 0)
212         //      bb_perror_msg_and_die("can't read %s", "/proc/uptime");
213         //buf[sizeof(buf)-1] = '\0';
214         ///sscanf(buf, "%llu", &seconds_since_boot);
215         sysinfo(&info);
216         seconds_since_boot = info.uptime;
217
218         return kernel_HZ;
219 }
220 #endif
221
222 /* Print value to buf, max size+1 chars (including trailing '\0') */
223
224 static void func_user(char *buf, int size, const procps_status_t *ps)
225 {
226 #if 1
227         safe_strncpy(buf, get_cached_username(ps->uid), size+1);
228 #else
229         /* "compatible" version, but it's larger */
230         /* procps 2.18 shows numeric UID if name overflows the field */
231         /* TODO: get_cached_username() returns numeric string if
232          * user has no passwd record, we will display it
233          * left-justified here; too long usernames are shown
234          * as _right-justified_ IDs. Is it worth fixing? */
235         const char *user = get_cached_username(ps->uid);
236         if (strlen(user) <= size)
237                 safe_strncpy(buf, user, size+1);
238         else
239                 sprintf(buf, "%*u", size, (unsigned)ps->uid);
240 #endif
241 }
242
243 static void func_group(char *buf, int size, const procps_status_t *ps)
244 {
245         safe_strncpy(buf, get_cached_groupname(ps->gid), size+1);
246 }
247
248 static void func_comm(char *buf, int size, const procps_status_t *ps)
249 {
250         safe_strncpy(buf, ps->comm, size+1);
251 }
252
253 static void func_stat(char *buf, int size, const procps_status_t *ps)
254 {
255         safe_strncpy(buf, ps->state, size+1);
256 }
257
258 static void func_args(char *buf, int size, const procps_status_t *ps)
259 {
260         read_cmdline(buf, size+1, ps->pid, ps->comm);
261 }
262
263 static void func_pid(char *buf, int size, const procps_status_t *ps)
264 {
265         sprintf(buf, "%*u", size, ps->pid);
266 }
267
268 static void func_ppid(char *buf, int size, const procps_status_t *ps)
269 {
270         sprintf(buf, "%*u", size, ps->ppid);
271 }
272
273 static void func_pgid(char *buf, int size, const procps_status_t *ps)
274 {
275         sprintf(buf, "%*u", size, ps->pgid);
276 }
277
278 static void put_lu(char *buf, int size, unsigned long u)
279 {
280         char buf4[5];
281
282         /* see http://en.wikipedia.org/wiki/Tera */
283         smart_ulltoa4(u, buf4, " mgtpezy");
284         buf4[4] = '\0';
285         sprintf(buf, "%.*s", size, buf4);
286 }
287
288 static void func_vsz(char *buf, int size, const procps_status_t *ps)
289 {
290         put_lu(buf, size, ps->vsz);
291 }
292
293 static void func_rss(char *buf, int size, const procps_status_t *ps)
294 {
295         put_lu(buf, size, ps->rss);
296 }
297
298 static void func_tty(char *buf, int size, const procps_status_t *ps)
299 {
300         buf[0] = '?';
301         buf[1] = '\0';
302         if (ps->tty_major) /* tty field of "0" means "no tty" */
303                 snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor);
304 }
305
306 #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
307
308 static void func_rgroup(char *buf, int size, const procps_status_t *ps)
309 {
310         safe_strncpy(buf, get_cached_groupname(ps->rgid), size+1);
311 }
312
313 static void func_ruser(char *buf, int size, const procps_status_t *ps)
314 {
315         safe_strncpy(buf, get_cached_username(ps->ruid), size+1);
316 }
317
318 static void func_nice(char *buf, int size, const procps_status_t *ps)
319 {
320         sprintf(buf, "%*d", size, ps->niceness);
321 }
322
323 #endif
324
325 #if ENABLE_FEATURE_PS_TIME
326
327 static void func_etime(char *buf, int size, const procps_status_t *ps)
328 {
329         /* elapsed time [[dd-]hh:]mm:ss; here only mm:ss */
330         unsigned long mm;
331         unsigned ss;
332
333         mm = ps->start_time / get_kernel_HZ();
334         /* must be after get_kernel_HZ()! */
335         mm = seconds_since_boot - mm;
336         ss = mm % 60;
337         mm /= 60;
338         snprintf(buf, size+1, "%3lu:%02u", mm, ss);
339 }
340
341 static void func_time(char *buf, int size, const procps_status_t *ps)
342 {
343         /* cumulative time [[dd-]hh:]mm:ss; here only mm:ss */
344         unsigned long mm;
345         unsigned ss;
346
347         mm = (ps->utime + ps->stime) / get_kernel_HZ();
348         ss = mm % 60;
349         mm /= 60;
350         snprintf(buf, size+1, "%3lu:%02u", mm, ss);
351 }
352
353 #endif
354
355 #if ENABLE_SELINUX
356 static void func_label(char *buf, int size, const procps_status_t *ps)
357 {
358         safe_strncpy(buf, ps->context ? ps->context : "unknown", size+1);
359 }
360 #endif
361
362 /*
363 static void func_nice(char *buf, int size, const procps_status_t *ps)
364 {
365         ps->???
366 }
367
368 static void func_pcpu(char *buf, int size, const procps_status_t *ps)
369 {
370 }
371 */
372
373 static const ps_out_t out_spec[] = {
374 /* Mandated by http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html: */
375         { 8                  , "user"  ,"USER"   ,func_user  ,PSSCAN_UIDGID  },
376         { 8                  , "group" ,"GROUP"  ,func_group ,PSSCAN_UIDGID  },
377         { 16                 , "comm"  ,"COMMAND",func_comm  ,PSSCAN_COMM    },
378         { MAX_WIDTH          , "args"  ,"COMMAND",func_args  ,PSSCAN_COMM    },
379         { 5                  , "pid"   ,"PID"    ,func_pid   ,PSSCAN_PID     },
380         { 5                  , "ppid"  ,"PPID"   ,func_ppid  ,PSSCAN_PPID    },
381         { 5                  , "pgid"  ,"PGID"   ,func_pgid  ,PSSCAN_PGID    },
382 #if ENABLE_FEATURE_PS_TIME
383         { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_START_TIME },
384 #endif
385 #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
386         { 5                  , "nice"  ,"NI"     ,func_nice  ,PSSCAN_NICE    },
387         { 8                  , "rgroup","RGROUP" ,func_rgroup,PSSCAN_RUIDGID },
388         { 8                  , "ruser" ,"RUSER"  ,func_ruser ,PSSCAN_RUIDGID },
389 //      { 5                  , "pcpu"  ,"%CPU"   ,func_pcpu  ,PSSCAN_        },
390 #endif
391 #if ENABLE_FEATURE_PS_TIME
392         { 6                  , "time"  ,"TIME"   ,func_time  ,PSSCAN_STIME | PSSCAN_UTIME },
393 #endif
394         { 6                  , "tty"   ,"TT"     ,func_tty   ,PSSCAN_TTY     },
395         { 4                  , "vsz"   ,"VSZ"    ,func_vsz   ,PSSCAN_VSZ     },
396 /* Not mandated, but useful: */
397         { 4                  , "stat"  ,"STAT"   ,func_stat  ,PSSCAN_STAT    },
398         { 4                  , "rss"   ,"RSS"    ,func_rss   ,PSSCAN_RSS     },
399 #if ENABLE_SELINUX
400         { 35                 , "label" ,"LABEL"  ,func_label ,PSSCAN_CONTEXT },
401 #endif
402 };
403
404 static ps_out_t* new_out_t(void)
405 {
406         out = xrealloc_vector(out, 2, out_cnt);
407         return &out[out_cnt++];
408 }
409
410 static const ps_out_t* find_out_spec(const char *name)
411 {
412         unsigned i;
413         char buf[ARRAY_SIZE(out_spec)*7 + 1];
414         char *p = buf;
415
416         for (i = 0; i < ARRAY_SIZE(out_spec); i++) {
417                 if (strncmp(name, out_spec[i].name6, 6) == 0)
418                         return &out_spec[i];
419                 p += sprintf(p, "%.6s,", out_spec[i].name6);
420         }
421         p[-1] = '\0';
422         bb_error_msg_and_die("bad -o argument '%s', supported arguments: %s", name, buf);
423 }
424
425 static void parse_o(char* opt)
426 {
427         ps_out_t* new;
428         // POSIX: "-o is blank- or comma-separated list" (FIXME)
429         char *comma, *equal;
430         while (1) {
431                 comma = strchr(opt, ',');
432                 equal = strchr(opt, '=');
433                 if (comma && (!equal || equal > comma)) {
434                         *comma = '\0';
435                         *new_out_t() = *find_out_spec(opt);
436                         *comma = ',';
437                         opt = comma + 1;
438                         continue;
439                 }
440                 break;
441         }
442         // opt points to last spec in comma separated list.
443         // This one can have =HEADER part.
444         new = new_out_t();
445         if (equal)
446                 *equal = '\0';
447         *new = *find_out_spec(opt);
448         if (equal) {
449                 *equal = '=';
450                 new->header = equal + 1;
451                 // POSIX: the field widths shall be ... at least as wide as
452                 // the header text (default or overridden value).
453                 // If the header text is null, such as -o user=,
454                 // the field width shall be at least as wide as the
455                 // default header text
456                 if (new->header[0]) {
457                         new->width = strlen(new->header);
458                         print_header = 1;
459                 }
460         } else
461                 print_header = 1;
462 }
463
464 static void alloc_line_buffer(void)
465 {
466         int i;
467         int width = 0;
468         for (i = 0; i < out_cnt; i++) {
469                 need_flags |= out[i].ps_flags;
470                 if (out[i].header[0]) {
471                         print_header = 1;
472                 }
473                 width += out[i].width + 1; /* "FIELD " */
474                 if ((int)(width - terminal_width) > 0) {
475                         /* The rest does not fit on the screen */
476                         //out[i].width -= (width - terminal_width - 1);
477                         out_cnt = i + 1;
478                         break;
479                 }
480         }
481 #if ENABLE_SELINUX
482         if (!is_selinux_enabled())
483                 need_flags &= ~PSSCAN_CONTEXT;
484 #endif
485         buffer = xmalloc(width + 1); /* for trailing \0 */
486 }
487
488 static void format_header(void)
489 {
490         int i;
491         ps_out_t* op;
492         char *p;
493
494         if (!print_header)
495                 return;
496         p = buffer;
497         i = 0;
498         if (out_cnt) {
499                 while (1) {
500                         op = &out[i];
501                         if (++i == out_cnt) /* do not pad last field */
502                                 break;
503                         p += sprintf(p, "%-*s ", op->width, op->header);
504                 }
505                 strcpy(p, op->header);
506         }
507         printf("%.*s\n", terminal_width, buffer);
508 }
509
510 static void format_process(const procps_status_t *ps)
511 {
512         int i, len;
513         char *p = buffer;
514         i = 0;
515         if (out_cnt) while (1) {
516                 out[i].f(p, out[i].width, ps);
517                 // POSIX: Any field need not be meaningful in all
518                 // implementations. In such a case a hyphen ( '-' )
519                 // should be output in place of the field value.
520                 if (!p[0]) {
521                         p[0] = '-';
522                         p[1] = '\0';
523                 }
524                 len = strlen(p);
525                 p += len;
526                 len = out[i].width - len + 1;
527                 if (++i == out_cnt) /* do not pad last field */
528                         break;
529                 p += sprintf(p, "%*s", len, "");
530         }
531         printf("%.*s\n", terminal_width, buffer);
532 }
533
534 int ps_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
535 int ps_main(int argc UNUSED_PARAM, char **argv)
536 {
537         procps_status_t *p;
538         llist_t* opt_o = NULL;
539         int opt;
540         enum {
541                 OPT_Z = (1 << 0),
542                 OPT_o = (1 << 1),
543                 OPT_a = (1 << 2),
544                 OPT_A = (1 << 3),
545                 OPT_d = (1 << 4),
546                 OPT_e = (1 << 5),
547                 OPT_f = (1 << 6),
548                 OPT_l = (1 << 7),
549                 OPT_T = (1 << 8) * ENABLE_FEATURE_SHOW_THREADS,
550         };
551
552         INIT_G();
553
554         // POSIX:
555         // -a  Write information for all processes associated with terminals
556         //     Implementations may omit session leaders from this list
557         // -A  Write information for all processes
558         // -d  Write information for all processes, except session leaders
559         // -e  Write information for all processes (equivalent to -A)
560         // -f  Generate a full listing
561         // -l  Generate a long listing
562         // -o col1,col2,col3=header
563         //     Select which columns to display
564         /* We allow (and ignore) most of the above. FIXME.
565          * -T is picked for threads (POSIX hasn't it standardized).
566          * procps v3.2.7 supports -T and shows tids as SPID column,
567          * it also supports -L where it shows tids as LWP column.
568          */
569         opt_complementary = "o::";
570         opt = getopt32(argv, "Zo:aAdefl"IF_FEATURE_SHOW_THREADS("T"), &opt_o);
571         if (opt_o) {
572                 do {
573                         parse_o(llist_pop(&opt_o));
574                 } while (opt_o);
575         } else {
576                 /* Below: parse_o() needs char*, NOT const char*... */
577 #if ENABLE_SELINUX
578                 if (!(opt & OPT_Z) || !is_selinux_enabled()) {
579                         /* no -Z or no SELinux: do not show LABEL */
580                         strcpy(default_o, DEFAULT_O_STR + sizeof(SELINUX_O_PREFIX)-1);
581                 } else
582 #endif
583                 {
584                         strcpy(default_o, DEFAULT_O_STR);
585                 }
586                 parse_o(default_o);
587         }
588 #if ENABLE_FEATURE_SHOW_THREADS
589         if (opt & OPT_T)
590                 need_flags |= PSSCAN_TASKS;
591 #endif
592
593         /* Was INT_MAX, but some libc's go belly up with printf("%.*s")
594          * and such large widths */
595         terminal_width = MAX_WIDTH;
596         if (isatty(1)) {
597                 get_terminal_width_height(0, &terminal_width, NULL);
598                 if (--terminal_width > MAX_WIDTH)
599                         terminal_width = MAX_WIDTH;
600         }
601         alloc_line_buffer();
602         format_header();
603
604         p = NULL;
605         while ((p = procps_scan(p, need_flags)) != NULL) {
606                 format_process(p);
607         }
608
609         return EXIT_SUCCESS;
610 }
611
612
613 #else /* !ENABLE_DESKTOP */
614
615
616 int ps_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
617 int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
618 {
619         procps_status_t *p;
620         int psscan_flags = PSSCAN_PID | PSSCAN_UIDGID
621                         | PSSCAN_STATE | PSSCAN_VSZ | PSSCAN_COMM;
622         unsigned terminal_width IF_NOT_FEATURE_PS_WIDE(= 79);
623         enum {
624                 OPT_Z = (1 << 0) * ENABLE_SELINUX,
625                 OPT_T = (1 << ENABLE_SELINUX) * ENABLE_FEATURE_SHOW_THREADS,
626         };
627         int opts = 0;
628         /* If we support any options, parse argv */
629 #if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_PS_WIDE
630 # if ENABLE_FEATURE_PS_WIDE
631         /* -w is a bit complicated */
632         int w_count = 0;
633         opt_complementary = "-:ww";
634         opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")"w", &w_count);
635         /* if w is given once, GNU ps sets the width to 132,
636          * if w is given more than once, it is "unlimited"
637          */
638         if (w_count) {
639                 terminal_width = (w_count == 1) ? 132 : MAX_WIDTH;
640         } else {
641                 get_terminal_width_height(0, &terminal_width, NULL);
642                 /* Go one less... */
643                 if (--terminal_width > MAX_WIDTH)
644                         terminal_width = MAX_WIDTH;
645         }
646 # else
647         /* -w is not supported, only -Z and/or -T */
648         opt_complementary = "-";
649         opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T"));
650 # endif
651 #endif
652
653 #if ENABLE_SELINUX
654         if ((opts & OPT_Z) && is_selinux_enabled()) {
655                 psscan_flags = PSSCAN_PID | PSSCAN_CONTEXT
656                                 | PSSCAN_STATE | PSSCAN_COMM;
657                 puts("  PID CONTEXT                          STAT COMMAND");
658         } else
659 #endif
660         {
661                 puts("  PID USER       VSZ STAT COMMAND");
662         }
663         if (opts & OPT_T) {
664                 psscan_flags |= PSSCAN_TASKS;
665         }
666
667         p = NULL;
668         while ((p = procps_scan(p, psscan_flags)) != NULL) {
669                 int len;
670 #if ENABLE_SELINUX
671                 if (psscan_flags & PSSCAN_CONTEXT) {
672                         len = printf("%5u %-32.32s %s  ",
673                                         p->pid,
674                                         p->context ? p->context : "unknown",
675                                         p->state);
676                 } else
677 #endif
678                 {
679                         const char *user = get_cached_username(p->uid);
680                         //if (p->vsz == 0)
681                         //      len = printf("%5u %-8.8s        %s ",
682                         //              p->pid, user, p->state);
683                         //else
684                         {
685                                 char buf6[6];
686                                 smart_ulltoa5(p->vsz, buf6, " mgtpezy");
687                                 buf6[5] = '\0';
688                                 len = printf("%5u %-8.8s %s %s  ",
689                                         p->pid, user, buf6, p->state);
690                         }
691                 }
692
693                 {
694                         int sz = terminal_width - len;
695                         char buf[sz + 1];
696                         read_cmdline(buf, sz, p->pid, p->comm);
697                         puts(buf);
698                 }
699         }
700         if (ENABLE_FEATURE_CLEAN_UP)
701                 clear_username_cache();
702         return EXIT_SUCCESS;
703 }
704
705 #endif /* !ENABLE_DESKTOP */