ps.c 23 KB

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