ps.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  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 (12 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. // opt points to last spec in comma separated list.
  410. // This one can have =HEADER part.
  411. new = new_out_t();
  412. if (equal)
  413. *equal = '\0';
  414. *new = *find_out_spec(opt);
  415. if (!equal)
  416. break;
  417. *equal++ = '=';
  418. new->header = equal;
  419. comma = strchr(equal, ',');
  420. if (comma)
  421. *comma = '\0';
  422. // POSIX: the field widths shall be ... at least as wide as
  423. // the header text (default or overridden value).
  424. // If the header text is null, such as -o user=,
  425. // the field width shall be at least as wide as the
  426. // default header text
  427. if (new->header[0]) {
  428. new->width = strlen(new->header);
  429. }
  430. if (!comma)
  431. break;
  432. //*comma = ','; /* no, new->header should stay NUL-terminated */
  433. opt = comma + 1;
  434. }
  435. }
  436. static void alloc_line_buffer(void)
  437. {
  438. int i;
  439. int width = 0;
  440. for (i = 0; i < out_cnt; i++) {
  441. need_flags |= out[i].ps_flags;
  442. if (out[i].header[0]) {
  443. print_header = 1;
  444. }
  445. width += out[i].width + 1; /* "FIELD " */
  446. if ((int)(width - terminal_width) > 0) {
  447. /* The rest does not fit on the screen */
  448. //out[i].width -= (width - terminal_width - 1);
  449. out_cnt = i + 1;
  450. break;
  451. }
  452. }
  453. #if ENABLE_SELINUX
  454. if (!is_selinux_enabled())
  455. need_flags &= ~PSSCAN_CONTEXT;
  456. #endif
  457. buffer = xmalloc(width + 1); /* for trailing \0 */
  458. }
  459. static void format_header(void)
  460. {
  461. int i;
  462. ps_out_t* op;
  463. char *p;
  464. if (!print_header)
  465. return;
  466. p = buffer;
  467. i = 0;
  468. if (out_cnt) {
  469. while (1) {
  470. op = &out[i];
  471. if (++i == out_cnt) /* do not pad last field */
  472. break;
  473. p += sprintf(p, "%-*s ", op->width, op->header);
  474. }
  475. strcpy(p, op->header);
  476. }
  477. printf("%.*s\n", terminal_width, buffer);
  478. }
  479. static void format_process(const procps_status_t *ps)
  480. {
  481. int i, len;
  482. char *p = buffer;
  483. i = 0;
  484. if (out_cnt) while (1) {
  485. out[i].f(p, out[i].width, ps);
  486. // POSIX: Any field need not be meaningful in all
  487. // implementations. In such a case a hyphen ( '-' )
  488. // should be output in place of the field value.
  489. if (!p[0]) {
  490. p[0] = '-';
  491. p[1] = '\0';
  492. }
  493. len = strlen(p);
  494. p += len;
  495. len = out[i].width - len + 1;
  496. if (++i == out_cnt) /* do not pad last field */
  497. break;
  498. p += sprintf(p, "%*s", len, " "); /* " ", not "", to ensure separation of fields */
  499. }
  500. printf("%.*s\n", terminal_width, buffer);
  501. }
  502. #if ENABLE_SELINUX
  503. # define SELINUX_O_PREFIX "label,"
  504. # define DEFAULT_O_STR (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args")
  505. #else
  506. # define DEFAULT_O_STR ("pid,user" IF_FEATURE_PS_TIME(",time") ",args")
  507. #endif
  508. int ps_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  509. int ps_main(int argc UNUSED_PARAM, char **argv)
  510. {
  511. procps_status_t *p;
  512. llist_t* opt_o = NULL;
  513. char default_o[sizeof(DEFAULT_O_STR)];
  514. #if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS
  515. int opt;
  516. #endif
  517. enum {
  518. OPT_Z = (1 << 0),
  519. OPT_o = (1 << 1),
  520. OPT_a = (1 << 2),
  521. OPT_A = (1 << 3),
  522. OPT_d = (1 << 4),
  523. OPT_e = (1 << 5),
  524. OPT_f = (1 << 6),
  525. OPT_l = (1 << 7),
  526. OPT_T = (1 << 8) * ENABLE_FEATURE_SHOW_THREADS,
  527. };
  528. INIT_G();
  529. #if ENABLE_FEATURE_PS_TIME
  530. G.seconds_since_boot = get_uptime();
  531. # if ENABLE_FEATURE_PS_UNUSUAL_SYSTEMS || !defined(__linux__)
  532. G.kernel_HZ = bb_clk_tck(); /* this is sysconf(_SC_CLK_TCK) */
  533. # endif
  534. #endif
  535. // POSIX:
  536. // -a Write information for all processes associated with terminals
  537. // Implementations may omit session leaders from this list
  538. // -A Write information for all processes
  539. // -d Write information for all processes, except session leaders
  540. // -e Write information for all processes (equivalent to -A)
  541. // -f Generate a full listing
  542. // -l Generate a long listing
  543. // -o col1,col2,col3=header
  544. // Select which columns to display
  545. /* We allow (and ignore) most of the above. FIXME.
  546. * -T is picked for threads (POSIX hasn't standardized it).
  547. * procps v3.2.7 supports -T and shows tids as SPID column,
  548. * it also supports -L where it shows tids as LWP column.
  549. */
  550. #if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS
  551. opt =
  552. #endif
  553. getopt32(argv, "Zo:*aAdefl"IF_FEATURE_SHOW_THREADS("T"), &opt_o);
  554. if (opt_o) {
  555. do {
  556. parse_o(llist_pop(&opt_o));
  557. } while (opt_o);
  558. } else {
  559. /* Below: parse_o() needs char*, NOT const char*,
  560. * can't pass it constant string. Need to make a copy first.
  561. */
  562. #if ENABLE_SELINUX
  563. if (!(opt & OPT_Z) || !is_selinux_enabled()) {
  564. /* no -Z or no SELinux: do not show LABEL */
  565. strcpy(default_o, DEFAULT_O_STR + sizeof(SELINUX_O_PREFIX)-1);
  566. } else
  567. #endif
  568. {
  569. strcpy(default_o, DEFAULT_O_STR);
  570. }
  571. parse_o(default_o);
  572. }
  573. #if ENABLE_FEATURE_SHOW_THREADS
  574. if (opt & OPT_T)
  575. need_flags |= PSSCAN_TASKS;
  576. #endif
  577. /* Was INT_MAX, but some libc's go belly up with printf("%.*s")
  578. * and such large widths */
  579. terminal_width = MAX_WIDTH;
  580. if (isatty(1)) {
  581. terminal_width = get_terminal_width(0);
  582. if (--terminal_width > MAX_WIDTH)
  583. terminal_width = MAX_WIDTH;
  584. }
  585. alloc_line_buffer();
  586. format_header();
  587. p = NULL;
  588. while ((p = procps_scan(p, need_flags)) != NULL) {
  589. format_process(p);
  590. }
  591. return EXIT_SUCCESS;
  592. }
  593. #else /* !ENABLE_DESKTOP */
  594. int ps_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  595. int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
  596. {
  597. procps_status_t *p;
  598. int psscan_flags = PSSCAN_PID | PSSCAN_UIDGID
  599. | PSSCAN_STATE | PSSCAN_VSZ | PSSCAN_COMM;
  600. unsigned terminal_width IF_NOT_FEATURE_PS_WIDE(= 79);
  601. enum {
  602. OPT_Z = (1 << 0) * ENABLE_SELINUX,
  603. OPT_T = (1 << ENABLE_SELINUX) * ENABLE_FEATURE_SHOW_THREADS,
  604. OPT_l = (1 << ENABLE_SELINUX) * (1 << ENABLE_FEATURE_SHOW_THREADS) * ENABLE_FEATURE_PS_LONG,
  605. };
  606. #if ENABLE_FEATURE_PS_LONG
  607. time_t now = now; /* for compiler */
  608. unsigned long uptime = uptime;
  609. #endif
  610. /* If we support any options, parse argv */
  611. #if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_PS_WIDE || ENABLE_FEATURE_PS_LONG
  612. int opts = 0;
  613. # if ENABLE_FEATURE_PS_WIDE
  614. /* -w is a bit complicated */
  615. int w_count = 0;
  616. make_all_argv_opts(argv);
  617. opts = getopt32(argv, "^"
  618. IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")IF_FEATURE_PS_LONG("l")"w"
  619. "\0" "ww",
  620. &w_count
  621. );
  622. /* if w is given once, GNU ps sets the width to 132,
  623. * if w is given more than once, it is "unlimited"
  624. */
  625. if (w_count) {
  626. terminal_width = (w_count == 1) ? 132 : MAX_WIDTH;
  627. } else {
  628. terminal_width = get_terminal_width(0);
  629. /* Go one less... */
  630. if (--terminal_width > MAX_WIDTH)
  631. terminal_width = MAX_WIDTH;
  632. }
  633. # else
  634. /* -w is not supported, only -Z and/or -T */
  635. make_all_argv_opts(argv);
  636. opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")IF_FEATURE_PS_LONG("l"));
  637. # endif
  638. # if ENABLE_SELINUX
  639. if ((opts & OPT_Z) && is_selinux_enabled()) {
  640. psscan_flags = PSSCAN_PID | PSSCAN_CONTEXT
  641. | PSSCAN_STATE | PSSCAN_COMM;
  642. puts(" PID CONTEXT STAT COMMAND");
  643. } else
  644. # endif
  645. if (opts & OPT_l) {
  646. psscan_flags = PSSCAN_STATE | PSSCAN_UIDGID | PSSCAN_PID | PSSCAN_PPID
  647. | PSSCAN_TTY | PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_COMM
  648. | PSSCAN_VSZ | PSSCAN_RSS;
  649. /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
  650. * mandates for -l:
  651. * -F Flags (?)
  652. * S State
  653. * UID,PID,PPID
  654. * -C CPU usage
  655. * -PRI The priority of the process; higher numbers mean lower priority
  656. * -NI Nice value
  657. * -ADDR The address of the process (?)
  658. * SZ The size in blocks of the core image
  659. * -WCHAN The event for which the process is waiting or sleeping
  660. * TTY
  661. * TIME The cumulative execution time
  662. * CMD
  663. * We don't show fields marked with '-'.
  664. * We show VSZ and RSS instead of SZ.
  665. * We also show STIME (standard says that -f shows it, -l doesn't).
  666. */
  667. puts("S UID PID PPID VSZ RSS TTY STIME TIME CMD");
  668. # if ENABLE_FEATURE_PS_LONG
  669. now = time(NULL);
  670. uptime = get_uptime();
  671. # endif
  672. }
  673. else {
  674. puts(" PID USER VSZ STAT COMMAND");
  675. }
  676. if (opts & OPT_T) {
  677. psscan_flags |= PSSCAN_TASKS;
  678. }
  679. #endif
  680. p = NULL;
  681. while ((p = procps_scan(p, psscan_flags)) != NULL) {
  682. int len;
  683. #if ENABLE_SELINUX
  684. if (psscan_flags & PSSCAN_CONTEXT) {
  685. len = printf("%5u %-32.32s %s ",
  686. p->pid,
  687. p->context ? p->context : "unknown",
  688. p->state);
  689. } else
  690. #endif
  691. {
  692. char buf6[6];
  693. smart_ulltoa5(p->vsz, buf6, " mgtpezy")[0] = '\0';
  694. #if ENABLE_FEATURE_PS_LONG
  695. if (opts & OPT_l) {
  696. char bufr[6], stime_str[6];
  697. char tty[2 * sizeof(int)*3 + 2];
  698. char *endp;
  699. unsigned sut = (p->stime + p->utime) / 100;
  700. unsigned elapsed = uptime - (p->start_time / 100);
  701. time_t start = now - elapsed;
  702. struct tm *tm = localtime(&start);
  703. smart_ulltoa5(p->rss, bufr, " mgtpezy")[0] = '\0';
  704. if (p->tty_major == 136)
  705. /* It should be pts/N, not ptsN, but N > 9
  706. * will overflow field width...
  707. */
  708. endp = stpcpy(tty, "pts");
  709. else
  710. if (p->tty_major == 4) {
  711. endp = stpcpy(tty, "tty");
  712. if (p->tty_minor >= 64) {
  713. p->tty_minor -= 64;
  714. *endp++ = 'S';
  715. }
  716. }
  717. else
  718. endp = tty + sprintf(tty, "%d:", p->tty_major);
  719. strcpy(endp, utoa(p->tty_minor));
  720. strftime(stime_str, 6, (elapsed >= (24 * 60 * 60)) ? "%b%d" : "%H:%M", tm);
  721. stime_str[5] = '\0';
  722. // S UID PID PPID VSZ RSS TTY STIME TIME CMD
  723. len = printf("%c %5u %5u %5u %5s %5s %-5s %s %02u:%02u:%02u ",
  724. p->state[0], p->uid, p->pid, p->ppid, buf6, bufr, tty,
  725. stime_str, sut / 3600, (sut % 3600) / 60, sut % 60);
  726. } else
  727. #endif
  728. {
  729. const char *user = get_cached_username(p->uid);
  730. len = printf("%5u %-8.8s %s %s ",
  731. p->pid, user, buf6, p->state);
  732. }
  733. }
  734. {
  735. int sz = terminal_width - len;
  736. if (sz >= 0) {
  737. char buf[sz + 1];
  738. read_cmdline(buf, sz, p->pid, p->comm);
  739. puts(buf);
  740. }
  741. }
  742. }
  743. if (ENABLE_FEATURE_CLEAN_UP)
  744. clear_username_cache();
  745. return EXIT_SUCCESS;
  746. }
  747. #endif /* !ENABLE_DESKTOP */