ps.c 23 KB

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