ps: fix "unused variable" warning; fix integer variable declared as char.
[platform/upstream/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 the GPL version 2, see the file LICENSE in this tarball.
10  */
11
12 #include "libbb.h"
13
14 #if ENABLE_DESKTOP
15
16 /* Print value to buf, max size+1 chars (including trailing '\0') */
17
18 static void func_user(char *buf, int size, const procps_status_t *ps)
19 {
20         safe_strncpy(buf, get_cached_username(ps->uid), size+1);
21 }
22
23 static void func_comm(char *buf, int size, const procps_status_t *ps)
24 {
25         safe_strncpy(buf, ps->comm, size+1);
26 }
27
28 static void func_args(char *buf, int size, const procps_status_t *ps)
29 {
30         read_cmdline(buf, size, ps->pid, ps->comm);
31 }
32
33 static void func_pid(char *buf, int size, const procps_status_t *ps)
34 {
35         sprintf(buf, "%*u", size, ps->pid);
36 }
37
38 static void func_ppid(char *buf, int size, const procps_status_t *ps)
39 {
40         sprintf(buf, "%*u", size, ps->ppid);
41 }
42
43 static void func_pgid(char *buf, int size, const procps_status_t *ps)
44 {
45         sprintf(buf, "%*u", size, ps->pgid);
46 }
47
48 static void put_u(char *buf, int size, unsigned u)
49 {
50         char buf5[5];
51         smart_ulltoa5( ((unsigned long long)u) << 10, buf5);
52         sprintf(buf, "%.*s", size, buf5);
53 }
54
55 static void func_vsz(char *buf, int size, const procps_status_t *ps)
56 {
57         put_u(buf, size, ps->vsz);
58 }
59
60 static void func_rss(char *buf, int size, const procps_status_t *ps)
61 {
62         put_u(buf, size, ps->rss);
63 }
64
65 static void func_tty(char *buf, int size, const procps_status_t *ps)
66 {
67         buf[0] = '?';
68         buf[1] = '\0';
69         if (ps->tty_major) /* tty field of "0" means "no tty" */
70                 snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor);
71 }
72
73 #if ENABLE_SELINUX
74 static void func_label(char *buf, int size, const procps_status_t *ps)
75 {
76         safe_strncpy(buf, ps->context ? ps->context : "unknown", size+1);
77 }
78 #endif
79
80 /*
81 static void func_nice(char *buf, int size, const procps_status_t *ps)
82 {
83         ps->???
84 }
85
86 static void func_etime(char *buf, int size, const procps_status_t *ps)
87 {
88         elapled time [[dd-]hh:]mm:ss
89 }
90
91 static void func_time(char *buf, int size, const procps_status_t *ps)
92 {
93         cumulative time [[dd-]hh:]mm:ss
94 }
95
96 static void func_pcpu(char *buf, int size, const procps_status_t *ps)
97 {
98 }
99 */
100
101 typedef struct {
102         uint16_t width;
103         char name[6];
104         const char *header;
105         void (*f)(char *buf, int size, const procps_status_t *ps);
106         int ps_flags;
107 } ps_out_t;
108
109 static const ps_out_t out_spec[] = {
110 // Mandated by POSIX:
111         { 8                  , "user"  ,"USER"   ,func_user  ,PSSCAN_UIDGID  },
112         { 16                 , "comm"  ,"COMMAND",func_comm  ,PSSCAN_COMM    },
113         { 256                , "args"  ,"COMMAND",func_args  ,PSSCAN_COMM    },
114         { 5                  , "pid"   ,"PID"    ,func_pid   ,PSSCAN_PID     },
115         { 5                  , "ppid"  ,"PPID"   ,func_ppid  ,PSSCAN_PPID    },
116         { 5                  , "pgid"  ,"PGID"   ,func_pgid  ,PSSCAN_PGID    },
117 //      { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_        },
118 //      { sizeof("GROUP"  )-1, "group" ,"GROUP"  ,func_group ,PSSCAN_UIDGID  },
119 //      { sizeof("NI"     )-1, "nice"  ,"NI"     ,func_nice  ,PSSCAN_        },
120 //      { sizeof("%CPU"   )-1, "pcpu"  ,"%CPU"   ,func_pcpu  ,PSSCAN_        },
121 //      { sizeof("RGROUP" )-1, "rgroup","RGROUP" ,func_rgroup,PSSCAN_UIDGID  },
122 //      { sizeof("RUSER"  )-1, "ruser" ,"RUSER"  ,func_ruser ,PSSCAN_UIDGID  },
123 //      { sizeof("TIME"   )-1, "time"  ,"TIME"   ,func_time  ,PSSCAN_        },
124         { 6                  , "tty"   ,"TT"     ,func_tty   ,PSSCAN_TTY     },
125         { 4                  , "vsz"   ,"VSZ"    ,func_vsz   ,PSSCAN_VSZ     },
126 // Not mandated by POSIX, but useful:
127         { 4                  , "rss"   ,"RSS"    ,func_rss   ,PSSCAN_RSS     },
128 #if ENABLE_SELINUX
129         { 35                 , "label" ,"LABEL"  ,func_label ,PSSCAN_CONTEXT },
130 #endif
131 };
132
133 #if ENABLE_SELINUX
134 #define SELINIX_O_PREFIX "label,"
135 #define DEFAULT_O_STR    SELINIX_O_PREFIX "pid,user" /* TODO: ,vsz,stat */ ",args"
136 #else
137 #define DEFAULT_O_STR    "pid,user" /* TODO: ,vsz,stat */ ",args"
138 #endif
139
140 struct globals {
141         ps_out_t* out;
142         int out_cnt;
143         int print_header;
144         int need_flags;
145         char *buffer;
146         unsigned terminal_width;
147         char default_o[sizeof(DEFAULT_O_STR)];
148 };
149 #define G (*(struct globals*)&bb_common_bufsiz1)
150 #define out            (G.out           )
151 #define out_cnt        (G.out_cnt       )
152 #define print_header   (G.print_header  )
153 #define need_flags     (G.need_flags    )
154 #define buffer         (G.buffer        )
155 #define terminal_width (G.terminal_width)
156 #define default_o      (G.default_o     )
157
158 static ps_out_t* new_out_t(void)
159 {
160         int i = out_cnt++;
161         out = xrealloc(out, out_cnt * sizeof(*out));
162         return &out[i];
163 }
164
165 static const ps_out_t* find_out_spec(const char *name)
166 {
167         int i;
168         for (i = 0; i < ARRAY_SIZE(out_spec); i++) {
169                 if (!strcmp(name, out_spec[i].name))
170                         return &out_spec[i];
171         }
172         bb_error_msg_and_die("bad -o argument '%s'", name);
173 }
174
175 static void parse_o(char* opt)
176 {
177         ps_out_t* new;
178         // POSIX: "-o is blank- or comma-separated list" (FIXME)
179         char *comma, *equal;
180         while (1) {
181                 comma = strchr(opt, ',');
182                 equal = strchr(opt, '=');
183                 if (comma && (!equal || equal > comma)) {
184                         *comma = '\0';
185                         *new_out_t() = *find_out_spec(opt);
186                         *comma = ',';
187                         opt = comma + 1;
188                         continue;
189                 }
190                 break;
191         }
192         // opt points to last spec in comma separated list.
193         // This one can have =HEADER part.
194         new = new_out_t();
195         if (equal)
196                 *equal = '\0';
197         *new = *find_out_spec(opt);
198         if (equal) {
199                 *equal = '=';
200                 new->header = equal + 1;
201                 // POSIX: the field widths shall be ... at least as wide as
202                 // the header text (default or overridden value).
203                 // If the header text is null, such as -o user=,
204                 // the field width shall be at least as wide as the
205                 // default header text
206                 if (new->header[0]) {
207                         new->width = strlen(new->header);
208                         print_header = 1;
209                 }
210         } else
211                 print_header = 1;
212 }
213
214 static void post_process(void)
215 {
216         int i;
217         int width = 0;
218         for (i = 0; i < out_cnt; i++) {
219                 need_flags |= out[i].ps_flags;
220                 if (out[i].header[0]) {
221                         print_header = 1;
222                 }
223                 width += out[i].width + 1; /* "FIELD " */
224         }
225 #if ENABLE_SELINUX
226         if (!is_selinux_enabled())
227                 need_flags &= ~PSSCAN_CONTEXT;
228 #endif
229         buffer = xmalloc(width + 1); /* for trailing \0 */
230 }
231
232 static void format_header(void)
233 {
234         int i;
235         ps_out_t* op;
236         char *p;
237
238         if (!print_header)
239                 return;
240         p = buffer;
241         i = 0;
242         if (out_cnt) {
243                 while (1) {
244                         op = &out[i];
245                         if (++i == out_cnt) /* do not pad last field */
246                                 break;
247                         p += sprintf(p, "%-*s ", op->width, op->header);
248                 }
249                 strcpy(p, op->header);
250         }
251         printf("%.*s\n", terminal_width, buffer);
252 }
253
254 static void format_process(const procps_status_t *ps)
255 {
256         int i, len;
257         char *p = buffer;
258         i = 0;
259         if (out_cnt) while (1) {
260                 out[i].f(p, out[i].width, ps);
261                 // POSIX: Any field need not be meaningful in all
262                 // implementations. In such a case a hyphen ( '-' )
263                 // should be output in place of the field value.
264                 if (!p[0]) {
265                         p[0] = '-';
266                         p[1] = '\0';
267                 }
268                 len = strlen(p);
269                 p += len;
270                 len = out[i].width - len + 1;
271                 if (++i == out_cnt) /* do not pad last field */
272                         break;
273                 p += sprintf(p, "%*s", len, "");
274         }
275         printf("%.*s\n", terminal_width, buffer);
276 }
277
278 int ps_main(int argc, char **argv);
279 int ps_main(int argc, char **argv)
280 {
281         procps_status_t *p;
282         llist_t* opt_o = NULL;
283         USE_SELINUX(int opt;)
284
285         // POSIX:
286         // -a  Write information for all processes associated with terminals
287         //     Implementations may omit session leaders from this list
288         // -A  Write information for all processes
289         // -d  Write information for all processes, except session leaders
290         // -e  Write information for all processes (equivalent to -A.)
291         // -f  Generate a full listing
292         // -l  Generate a long listing
293         // -o col1,col2,col3=header
294         //     Select which columns to display
295         /* We allow (and ignore) most of the above. FIXME */
296         opt_complementary = "o::";
297         USE_SELINUX(opt =) getopt32(argc, argv, "Zo:aAdefl", &opt_o);
298         if (opt_o) {
299                 do {
300                         parse_o(opt_o->data);
301                         opt_o = opt_o->link;
302                 } while (opt_o);
303         } else {
304                 /* Below: parse_o() needs char*, NOT const char*... */
305 #if ENABLE_SELINUX
306                 if (!(opt & 1) || !is_selinux_enabled()) {
307                         /* no -Z or no SELinux: do not show LABEL */
308                         strcpy(default_o, DEFAULT_O_STR + sizeof(SELINIX_O_PREFIX)-1);
309                 } else
310 #endif
311                 {
312                         strcpy(default_o, DEFAULT_O_STR);
313                 }
314                 parse_o(default_o);
315         }
316         post_process();
317
318         /* Was INT_MAX, but some libc's go belly up with printf("%.*s")
319          * and such large widths */
320         terminal_width = 30000;
321         if (isatty(1)) {
322                 get_terminal_width_height(1, &terminal_width, NULL);
323                 terminal_width--;
324         }
325         format_header();
326
327         p = NULL;
328         while ((p = procps_scan(p, need_flags))) {
329                 format_process(p);
330         }
331
332         return EXIT_SUCCESS;
333 }
334
335
336 #else /* !ENABLE_DESKTOP */
337
338
339 int ps_main(int argc, char **argv);
340 int ps_main(int argc, char **argv)
341 {
342         procps_status_t *p = NULL;
343         int len;
344         SKIP_SELINUX(const) int use_selinux = 0;
345         USE_SELINUX(int i;)
346 #if !ENABLE_FEATURE_PS_WIDE
347         enum { terminal_width = 79 };
348 #else
349         int terminal_width;
350         int w_count = 0;
351 #endif
352
353 #if ENABLE_FEATURE_PS_WIDE || ENABLE_SELINUX
354 #if ENABLE_FEATURE_PS_WIDE
355         opt_complementary = "-:ww";
356         USE_SELINUX(i =) getopt32(argc, argv, USE_SELINUX("Z") "w", &w_count);
357         /* if w is given once, GNU ps sets the width to 132,
358          * if w is given more than once, it is "unlimited"
359          */
360         if (w_count) {
361                 terminal_width = (w_count==1) ? 132 : INT_MAX;
362         } else {
363                 get_terminal_width_height(1, &terminal_width, NULL);
364                 /* Go one less... */
365                 terminal_width--;
366         }
367 #else /* only ENABLE_SELINUX */
368         i = getopt32(argc, argv, "Z");
369 #endif
370 #if ENABLE_SELINUX
371         if (i && is_selinux_enabled())
372                 use_selinux = PSSCAN_CONTEXT;
373 #endif
374 #endif /* ENABLE_FEATURE_PS_WIDE || ENABLE_SELINUX */
375
376         if (use_selinux)
377                 puts("  PID Context                          Stat Command");
378         else
379                 puts("  PID  Uid        VSZ Stat Command");
380
381         while ((p = procps_scan(p, 0
382                         | PSSCAN_PID
383                         | PSSCAN_UIDGID
384                         | PSSCAN_STATE
385                         | PSSCAN_VSZ
386                         | PSSCAN_COMM
387                         | use_selinux
388         ))) {
389 #if ENABLE_SELINUX
390                 if (use_selinux) {
391                         len = printf("%5u %-32s %s ",
392                                         p->pid,
393                                         p->context ? p->context : "unknown",
394                                         p->state);
395                 } else
396 #endif
397                 {
398                         const char *user = get_cached_username(p->uid);
399                         if (p->vsz == 0)
400                                 len = printf("%5u %-8s        %s ",
401                                         p->pid, user, p->state);
402                         else
403                                 len = printf("%5u %-8s %6u %s ",
404                                         p->pid, user, p->vsz, p->state);
405                 }
406
407                 {
408                         int sz = terminal_width - len;
409                         char buf[sz + 1];
410                         read_cmdline(buf, sz, p->pid, p->comm);
411                         puts(buf);
412                 }
413         }
414         if (ENABLE_FEATURE_CLEAN_UP)
415                 clear_username_cache();
416         return EXIT_SUCCESS;
417 }
418
419 #endif /* ENABLE_DESKTOP */