ps.c 23 KB

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