Contents of /trunk/mkinitrd-magellan/busybox/procps/ps.c
Parent Directory | Revision Log
Revision 816 -
(show annotations)
(download)
Fri Apr 24 18:33:46 2009 UTC (15 years, 5 months ago) by niro
File MIME type: text/plain
File size: 14199 byte(s)
Fri Apr 24 18:33:46 2009 UTC (15 years, 5 months ago) by niro
File MIME type: text/plain
File size: 14199 byte(s)
-updated to busybox-1.13.4
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 | /* Absolute maximum on output line length */ |
15 | enum { MAX_WIDTH = 2*1024 }; |
16 | |
17 | #if ENABLE_DESKTOP |
18 | |
19 | #include <sys/times.h> /* for times() */ |
20 | //#include <sys/sysinfo.h> /* for sysinfo() */ |
21 | #ifndef AT_CLKTCK |
22 | #define AT_CLKTCK 17 |
23 | #endif |
24 | |
25 | |
26 | #if ENABLE_SELINUX |
27 | #define SELINUX_O_PREFIX "label," |
28 | #define DEFAULT_O_STR (SELINUX_O_PREFIX "pid,user" USE_FEATURE_PS_TIME(",time") ",args") |
29 | #else |
30 | #define DEFAULT_O_STR ("pid,user" USE_FEATURE_PS_TIME(",time") ",args") |
31 | #endif |
32 | |
33 | typedef struct { |
34 | uint16_t width; |
35 | char name[6]; |
36 | const char *header; |
37 | void (*f)(char *buf, int size, const procps_status_t *ps); |
38 | int ps_flags; |
39 | } ps_out_t; |
40 | |
41 | struct globals { |
42 | ps_out_t* out; |
43 | int out_cnt; |
44 | int print_header; |
45 | int need_flags; |
46 | char *buffer; |
47 | unsigned terminal_width; |
48 | #if ENABLE_FEATURE_PS_TIME |
49 | unsigned kernel_HZ; |
50 | unsigned long long seconds_since_boot; |
51 | #endif |
52 | char default_o[sizeof(DEFAULT_O_STR)]; |
53 | }; |
54 | #define G (*(struct globals*)&bb_common_bufsiz1) |
55 | #define out (G.out ) |
56 | #define out_cnt (G.out_cnt ) |
57 | #define print_header (G.print_header ) |
58 | #define need_flags (G.need_flags ) |
59 | #define buffer (G.buffer ) |
60 | #define terminal_width (G.terminal_width ) |
61 | #define kernel_HZ (G.kernel_HZ ) |
62 | #define seconds_since_boot (G.seconds_since_boot) |
63 | #define default_o (G.default_o ) |
64 | |
65 | #if ENABLE_FEATURE_PS_TIME |
66 | /* for ELF executables, notes are pushed before environment and args */ |
67 | static ptrdiff_t find_elf_note(ptrdiff_t findme) |
68 | { |
69 | ptrdiff_t *ep = (ptrdiff_t *) environ; |
70 | |
71 | while (*ep++); |
72 | while (*ep) { |
73 | if (ep[0] == findme) { |
74 | return ep[1]; |
75 | } |
76 | ep += 2; |
77 | } |
78 | return -1; |
79 | } |
80 | |
81 | #if ENABLE_FEATURE_PS_UNUSUAL_SYSTEMS |
82 | static unsigned get_HZ_by_waiting(void) |
83 | { |
84 | struct timeval tv1, tv2; |
85 | unsigned t1, t2, r, hz; |
86 | unsigned cnt = cnt; /* for compiler */ |
87 | int diff; |
88 | |
89 | r = 0; |
90 | |
91 | /* Wait for times() to reach new tick */ |
92 | t1 = times(NULL); |
93 | do { |
94 | t2 = times(NULL); |
95 | } while (t2 == t1); |
96 | gettimeofday(&tv2, NULL); |
97 | |
98 | do { |
99 | t1 = t2; |
100 | tv1.tv_usec = tv2.tv_usec; |
101 | |
102 | /* Wait exactly one times() tick */ |
103 | do { |
104 | t2 = times(NULL); |
105 | } while (t2 == t1); |
106 | gettimeofday(&tv2, NULL); |
107 | |
108 | /* Calculate ticks per sec, rounding up to even */ |
109 | diff = tv2.tv_usec - tv1.tv_usec; |
110 | if (diff <= 0) diff += 1000000; |
111 | hz = 1000000u / (unsigned)diff; |
112 | hz = (hz+1) & ~1; |
113 | |
114 | /* Count how many same hz values we saw */ |
115 | if (r != hz) { |
116 | r = hz; |
117 | cnt = 0; |
118 | } |
119 | cnt++; |
120 | } while (cnt < 3); /* exit if saw 3 same values */ |
121 | |
122 | return r; |
123 | } |
124 | #else |
125 | static inline unsigned get_HZ_by_waiting(void) |
126 | { |
127 | /* Better method? */ |
128 | return 100; |
129 | } |
130 | #endif |
131 | |
132 | static unsigned get_kernel_HZ(void) |
133 | { |
134 | //char buf[64]; |
135 | struct sysinfo info; |
136 | |
137 | if (kernel_HZ) |
138 | return kernel_HZ; |
139 | |
140 | /* Works for ELF only, Linux 2.4.0+ */ |
141 | kernel_HZ = find_elf_note(AT_CLKTCK); |
142 | if (kernel_HZ == (unsigned)-1) |
143 | kernel_HZ = get_HZ_by_waiting(); |
144 | |
145 | //if (open_read_close("/proc/uptime", buf, sizeof(buf) <= 0) |
146 | // bb_perror_msg_and_die("cannot read %s", "/proc/uptime"); |
147 | //buf[sizeof(buf)-1] = '\0'; |
148 | ///sscanf(buf, "%llu", &seconds_since_boot); |
149 | sysinfo(&info); |
150 | seconds_since_boot = info.uptime; |
151 | |
152 | return kernel_HZ; |
153 | } |
154 | #endif |
155 | |
156 | /* Print value to buf, max size+1 chars (including trailing '\0') */ |
157 | |
158 | static void func_user(char *buf, int size, const procps_status_t *ps) |
159 | { |
160 | #if 1 |
161 | safe_strncpy(buf, get_cached_username(ps->uid), size+1); |
162 | #else |
163 | /* "compatible" version, but it's larger */ |
164 | /* procps 2.18 shows numeric UID if name overflows the field */ |
165 | /* TODO: get_cached_username() returns numeric string if |
166 | * user has no passwd record, we will display it |
167 | * left-justified here; too long usernames are shown |
168 | * as _right-justified_ IDs. Is it worth fixing? */ |
169 | const char *user = get_cached_username(ps->uid); |
170 | if (strlen(user) <= size) |
171 | safe_strncpy(buf, user, size+1); |
172 | else |
173 | sprintf(buf, "%*u", size, (unsigned)ps->uid); |
174 | #endif |
175 | } |
176 | |
177 | static void func_comm(char *buf, int size, const procps_status_t *ps) |
178 | { |
179 | safe_strncpy(buf, ps->comm, size+1); |
180 | } |
181 | |
182 | static void func_args(char *buf, int size, const procps_status_t *ps) |
183 | { |
184 | read_cmdline(buf, size, ps->pid, ps->comm); |
185 | } |
186 | |
187 | static void func_pid(char *buf, int size, const procps_status_t *ps) |
188 | { |
189 | sprintf(buf, "%*u", size, ps->pid); |
190 | } |
191 | |
192 | static void func_ppid(char *buf, int size, const procps_status_t *ps) |
193 | { |
194 | sprintf(buf, "%*u", size, ps->ppid); |
195 | } |
196 | |
197 | static void func_pgid(char *buf, int size, const procps_status_t *ps) |
198 | { |
199 | sprintf(buf, "%*u", size, ps->pgid); |
200 | } |
201 | |
202 | static void put_lu(char *buf, int size, unsigned long u) |
203 | { |
204 | char buf4[5]; |
205 | |
206 | /* see http://en.wikipedia.org/wiki/Tera */ |
207 | smart_ulltoa4(u, buf4, " mgtpezy"); |
208 | buf4[4] = '\0'; |
209 | sprintf(buf, "%.*s", size, buf4); |
210 | } |
211 | |
212 | static void func_vsz(char *buf, int size, const procps_status_t *ps) |
213 | { |
214 | put_lu(buf, size, ps->vsz); |
215 | } |
216 | |
217 | static void func_rss(char *buf, int size, const procps_status_t *ps) |
218 | { |
219 | put_lu(buf, size, ps->rss); |
220 | } |
221 | |
222 | static void func_tty(char *buf, int size, const procps_status_t *ps) |
223 | { |
224 | buf[0] = '?'; |
225 | buf[1] = '\0'; |
226 | if (ps->tty_major) /* tty field of "0" means "no tty" */ |
227 | snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor); |
228 | } |
229 | |
230 | #if ENABLE_FEATURE_PS_TIME |
231 | static void func_etime(char *buf, int size, const procps_status_t *ps) |
232 | { |
233 | /* elapsed time [[dd-]hh:]mm:ss; here only mm:ss */ |
234 | unsigned long mm; |
235 | unsigned ss; |
236 | |
237 | mm = ps->start_time / get_kernel_HZ(); |
238 | /* must be after get_kernel_HZ()! */ |
239 | mm = seconds_since_boot - mm; |
240 | ss = mm % 60; |
241 | mm /= 60; |
242 | snprintf(buf, size+1, "%3lu:%02u", mm, ss); |
243 | } |
244 | |
245 | static void func_time(char *buf, int size, const procps_status_t *ps) |
246 | { |
247 | /* cumulative time [[dd-]hh:]mm:ss; here only mm:ss */ |
248 | unsigned long mm; |
249 | unsigned ss; |
250 | |
251 | mm = (ps->utime + ps->stime) / get_kernel_HZ(); |
252 | ss = mm % 60; |
253 | mm /= 60; |
254 | snprintf(buf, size+1, "%3lu:%02u", mm, ss); |
255 | } |
256 | #endif |
257 | |
258 | #if ENABLE_SELINUX |
259 | static void func_label(char *buf, int size, const procps_status_t *ps) |
260 | { |
261 | safe_strncpy(buf, ps->context ? ps->context : "unknown", size+1); |
262 | } |
263 | #endif |
264 | |
265 | /* |
266 | static void func_nice(char *buf, int size, const procps_status_t *ps) |
267 | { |
268 | ps->??? |
269 | } |
270 | |
271 | static void func_pcpu(char *buf, int size, const procps_status_t *ps) |
272 | { |
273 | } |
274 | */ |
275 | |
276 | static const ps_out_t out_spec[] = { |
277 | // Mandated by POSIX: |
278 | { 8 , "user" ,"USER" ,func_user ,PSSCAN_UIDGID }, |
279 | { 16 , "comm" ,"COMMAND",func_comm ,PSSCAN_COMM }, |
280 | { 256 , "args" ,"COMMAND",func_args ,PSSCAN_COMM }, |
281 | { 5 , "pid" ,"PID" ,func_pid ,PSSCAN_PID }, |
282 | { 5 , "ppid" ,"PPID" ,func_ppid ,PSSCAN_PPID }, |
283 | { 5 , "pgid" ,"PGID" ,func_pgid ,PSSCAN_PGID }, |
284 | #if ENABLE_FEATURE_PS_TIME |
285 | { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_START_TIME }, |
286 | #endif |
287 | // { sizeof("GROUP" )-1, "group" ,"GROUP" ,func_group ,PSSCAN_UIDGID }, |
288 | // { sizeof("NI" )-1, "nice" ,"NI" ,func_nice ,PSSCAN_ }, |
289 | // { sizeof("%CPU" )-1, "pcpu" ,"%CPU" ,func_pcpu ,PSSCAN_ }, |
290 | // { sizeof("RGROUP" )-1, "rgroup","RGROUP" ,func_rgroup,PSSCAN_UIDGID }, |
291 | // { sizeof("RUSER" )-1, "ruser" ,"RUSER" ,func_ruser ,PSSCAN_UIDGID }, |
292 | #if ENABLE_FEATURE_PS_TIME |
293 | { 6 , "time" ,"TIME" ,func_time ,PSSCAN_STIME | PSSCAN_UTIME }, |
294 | #endif |
295 | { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY }, |
296 | { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ }, |
297 | // Not mandated by POSIX, but useful: |
298 | { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS }, |
299 | #if ENABLE_SELINUX |
300 | { 35 , "label" ,"LABEL" ,func_label ,PSSCAN_CONTEXT }, |
301 | #endif |
302 | }; |
303 | |
304 | static ps_out_t* new_out_t(void) |
305 | { |
306 | out = xrealloc_vector(out, 2, out_cnt); |
307 | return &out[out_cnt++]; |
308 | } |
309 | |
310 | static const ps_out_t* find_out_spec(const char *name) |
311 | { |
312 | unsigned i; |
313 | for (i = 0; i < ARRAY_SIZE(out_spec); i++) { |
314 | if (!strcmp(name, out_spec[i].name)) |
315 | return &out_spec[i]; |
316 | } |
317 | bb_error_msg_and_die("bad -o argument '%s'", name); |
318 | } |
319 | |
320 | static void parse_o(char* opt) |
321 | { |
322 | ps_out_t* new; |
323 | // POSIX: "-o is blank- or comma-separated list" (FIXME) |
324 | char *comma, *equal; |
325 | while (1) { |
326 | comma = strchr(opt, ','); |
327 | equal = strchr(opt, '='); |
328 | if (comma && (!equal || equal > comma)) { |
329 | *comma = '\0'; |
330 | *new_out_t() = *find_out_spec(opt); |
331 | *comma = ','; |
332 | opt = comma + 1; |
333 | continue; |
334 | } |
335 | break; |
336 | } |
337 | // opt points to last spec in comma separated list. |
338 | // This one can have =HEADER part. |
339 | new = new_out_t(); |
340 | if (equal) |
341 | *equal = '\0'; |
342 | *new = *find_out_spec(opt); |
343 | if (equal) { |
344 | *equal = '='; |
345 | new->header = equal + 1; |
346 | // POSIX: the field widths shall be ... at least as wide as |
347 | // the header text (default or overridden value). |
348 | // If the header text is null, such as -o user=, |
349 | // the field width shall be at least as wide as the |
350 | // default header text |
351 | if (new->header[0]) { |
352 | new->width = strlen(new->header); |
353 | print_header = 1; |
354 | } |
355 | } else |
356 | print_header = 1; |
357 | } |
358 | |
359 | static void post_process(void) |
360 | { |
361 | int i; |
362 | int width = 0; |
363 | for (i = 0; i < out_cnt; i++) { |
364 | need_flags |= out[i].ps_flags; |
365 | if (out[i].header[0]) { |
366 | print_header = 1; |
367 | } |
368 | width += out[i].width + 1; /* "FIELD " */ |
369 | } |
370 | #if ENABLE_SELINUX |
371 | if (!is_selinux_enabled()) |
372 | need_flags &= ~PSSCAN_CONTEXT; |
373 | #endif |
374 | buffer = xmalloc(width + 1); /* for trailing \0 */ |
375 | } |
376 | |
377 | static void format_header(void) |
378 | { |
379 | int i; |
380 | ps_out_t* op; |
381 | char *p; |
382 | |
383 | if (!print_header) |
384 | return; |
385 | p = buffer; |
386 | i = 0; |
387 | if (out_cnt) { |
388 | while (1) { |
389 | op = &out[i]; |
390 | if (++i == out_cnt) /* do not pad last field */ |
391 | break; |
392 | p += sprintf(p, "%-*s ", op->width, op->header); |
393 | } |
394 | strcpy(p, op->header); |
395 | } |
396 | printf("%.*s\n", terminal_width, buffer); |
397 | } |
398 | |
399 | static void format_process(const procps_status_t *ps) |
400 | { |
401 | int i, len; |
402 | char *p = buffer; |
403 | i = 0; |
404 | if (out_cnt) while (1) { |
405 | out[i].f(p, out[i].width, ps); |
406 | // POSIX: Any field need not be meaningful in all |
407 | // implementations. In such a case a hyphen ( '-' ) |
408 | // should be output in place of the field value. |
409 | if (!p[0]) { |
410 | p[0] = '-'; |
411 | p[1] = '\0'; |
412 | } |
413 | len = strlen(p); |
414 | p += len; |
415 | len = out[i].width - len + 1; |
416 | if (++i == out_cnt) /* do not pad last field */ |
417 | break; |
418 | p += sprintf(p, "%*s", len, ""); |
419 | } |
420 | printf("%.*s\n", terminal_width, buffer); |
421 | } |
422 | |
423 | int ps_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
424 | int ps_main(int argc UNUSED_PARAM, char **argv) |
425 | { |
426 | procps_status_t *p; |
427 | llist_t* opt_o = NULL; |
428 | USE_SELINUX(int opt;) |
429 | |
430 | // POSIX: |
431 | // -a Write information for all processes associated with terminals |
432 | // Implementations may omit session leaders from this list |
433 | // -A Write information for all processes |
434 | // -d Write information for all processes, except session leaders |
435 | // -e Write information for all processes (equivalent to -A.) |
436 | // -f Generate a full listing |
437 | // -l Generate a long listing |
438 | // -o col1,col2,col3=header |
439 | // Select which columns to display |
440 | /* We allow (and ignore) most of the above. FIXME */ |
441 | opt_complementary = "o::"; |
442 | USE_SELINUX(opt =) getopt32(argv, "Zo:aAdefl", &opt_o); |
443 | if (opt_o) { |
444 | do { |
445 | parse_o(llist_pop(&opt_o)); |
446 | } while (opt_o); |
447 | } else { |
448 | /* Below: parse_o() needs char*, NOT const char*... */ |
449 | #if ENABLE_SELINUX |
450 | if (!(opt & 1) || !is_selinux_enabled()) { |
451 | /* no -Z or no SELinux: do not show LABEL */ |
452 | strcpy(default_o, DEFAULT_O_STR + sizeof(SELINUX_O_PREFIX)-1); |
453 | } else |
454 | #endif |
455 | { |
456 | strcpy(default_o, DEFAULT_O_STR); |
457 | } |
458 | parse_o(default_o); |
459 | } |
460 | post_process(); |
461 | |
462 | /* Was INT_MAX, but some libc's go belly up with printf("%.*s") |
463 | * and such large widths */ |
464 | terminal_width = MAX_WIDTH; |
465 | if (isatty(1)) { |
466 | get_terminal_width_height(0, &terminal_width, NULL); |
467 | if (--terminal_width > MAX_WIDTH) |
468 | terminal_width = MAX_WIDTH; |
469 | } |
470 | format_header(); |
471 | |
472 | p = NULL; |
473 | while ((p = procps_scan(p, need_flags))) { |
474 | format_process(p); |
475 | } |
476 | |
477 | return EXIT_SUCCESS; |
478 | } |
479 | |
480 | |
481 | #else /* !ENABLE_DESKTOP */ |
482 | |
483 | |
484 | int ps_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
485 | int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
486 | { |
487 | procps_status_t *p = NULL; |
488 | int len; |
489 | SKIP_SELINUX(const) int use_selinux = 0; |
490 | USE_SELINUX(int i;) |
491 | #if !ENABLE_FEATURE_PS_WIDE |
492 | enum { terminal_width = 79 }; |
493 | #else |
494 | unsigned terminal_width; |
495 | int w_count = 0; |
496 | #endif |
497 | |
498 | #if ENABLE_FEATURE_PS_WIDE || ENABLE_SELINUX |
499 | #if ENABLE_FEATURE_PS_WIDE |
500 | opt_complementary = "-:ww"; |
501 | USE_SELINUX(i =) getopt32(argv, USE_SELINUX("Z") "w", &w_count); |
502 | /* if w is given once, GNU ps sets the width to 132, |
503 | * if w is given more than once, it is "unlimited" |
504 | */ |
505 | if (w_count) { |
506 | terminal_width = (w_count==1) ? 132 : MAX_WIDTH; |
507 | } else { |
508 | get_terminal_width_height(0, &terminal_width, NULL); |
509 | /* Go one less... */ |
510 | if (--terminal_width > MAX_WIDTH) |
511 | terminal_width = MAX_WIDTH; |
512 | } |
513 | #else /* only ENABLE_SELINUX */ |
514 | i = getopt32(argv, "Z"); |
515 | #endif |
516 | #if ENABLE_SELINUX |
517 | if ((i & 1) && is_selinux_enabled()) |
518 | use_selinux = PSSCAN_CONTEXT; |
519 | #endif |
520 | #endif /* ENABLE_FEATURE_PS_WIDE || ENABLE_SELINUX */ |
521 | |
522 | if (use_selinux) |
523 | puts(" PID CONTEXT STAT COMMAND"); |
524 | else |
525 | puts(" PID USER VSZ STAT COMMAND"); |
526 | |
527 | while ((p = procps_scan(p, 0 |
528 | | PSSCAN_PID |
529 | | PSSCAN_UIDGID |
530 | | PSSCAN_STATE |
531 | | PSSCAN_VSZ |
532 | | PSSCAN_COMM |
533 | | use_selinux |
534 | ))) { |
535 | #if ENABLE_SELINUX |
536 | if (use_selinux) { |
537 | len = printf("%5u %-32.32s %s ", |
538 | p->pid, |
539 | p->context ? p->context : "unknown", |
540 | p->state); |
541 | } else |
542 | #endif |
543 | { |
544 | const char *user = get_cached_username(p->uid); |
545 | //if (p->vsz == 0) |
546 | // len = printf("%5u %-8.8s %s ", |
547 | // p->pid, user, p->state); |
548 | //else |
549 | { |
550 | char buf6[6]; |
551 | smart_ulltoa5(p->vsz, buf6, " mgtpezy"); |
552 | buf6[5] = '\0'; |
553 | len = printf("%5u %-8.8s %s %s ", |
554 | p->pid, user, buf6, p->state); |
555 | } |
556 | } |
557 | |
558 | { |
559 | int sz = terminal_width - len; |
560 | char buf[sz + 1]; |
561 | read_cmdline(buf, sz, p->pid, p->comm); |
562 | puts(buf); |
563 | } |
564 | } |
565 | if (ENABLE_FEATURE_CLEAN_UP) |
566 | clear_username_cache(); |
567 | return EXIT_SUCCESS; |
568 | } |
569 | |
570 | #endif /* ENABLE_DESKTOP */ |