ps.c 21 KB

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