PageRenderTime 113ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/bin/ps/ps.c

https://bitbucket.org/evzijst/freebsd
C | 1386 lines | 1033 code | 101 blank | 252 comment | 360 complexity | a71782e31ed288b62cb9b1a3f80c0924 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0, AGPL-1.0, LGPL-2.1, GPL-2.0, BSD-2-Clause, 0BSD, MPL-2.0-no-copyleft-exception
  1. /*-
  2. * Copyright (c) 1990, 1993, 1994
  3. * The Regents of the University of California. All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 4. Neither the name of the University nor the names of its contributors
  14. * may be used to endorse or promote products derived from this software
  15. * without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  18. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  21. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27. * SUCH DAMAGE.
  28. * ------+---------+---------+-------- + --------+---------+---------+---------*
  29. * Copyright (c) 2004 - Garance Alistair Drosehn <gad@FreeBSD.org>.
  30. * All rights reserved.
  31. *
  32. * Significant modifications made to bring `ps' options somewhat closer
  33. * to the standard for `ps' as described in SingleUnixSpec-v3.
  34. * ------+---------+---------+-------- + --------+---------+---------+---------*
  35. */
  36. #ifndef lint
  37. static const char copyright[] =
  38. "@(#) Copyright (c) 1990, 1993, 1994\n\
  39. The Regents of the University of California. All rights reserved.\n";
  40. #endif /* not lint */
  41. #if 0
  42. #ifndef lint
  43. static char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94";
  44. #endif /* not lint */
  45. #endif
  46. #include <sys/cdefs.h>
  47. __FBSDID("$FreeBSD$");
  48. #include <sys/param.h>
  49. #include <sys/proc.h>
  50. #include <sys/user.h>
  51. #include <sys/stat.h>
  52. #include <sys/ioctl.h>
  53. #include <sys/sysctl.h>
  54. #include <sys/mount.h>
  55. #include <ctype.h>
  56. #include <err.h>
  57. #include <errno.h>
  58. #include <fcntl.h>
  59. #include <grp.h>
  60. #include <kvm.h>
  61. #include <limits.h>
  62. #include <locale.h>
  63. #include <paths.h>
  64. #include <pwd.h>
  65. #include <stdio.h>
  66. #include <stdlib.h>
  67. #include <string.h>
  68. #include <unistd.h>
  69. #include "ps.h"
  70. #define _PATH_PTS "/dev/pts/"
  71. #define W_SEP " \t" /* "Whitespace" list separators */
  72. #define T_SEP "," /* "Terminate-element" list separators */
  73. #ifdef LAZY_PS
  74. #define DEF_UREAD 0
  75. #define OPT_LAZY_f "f"
  76. #else
  77. #define DEF_UREAD 1 /* Always do the more-expensive read. */
  78. #define OPT_LAZY_f /* I.e., the `-f' option is not added. */
  79. #endif
  80. /*
  81. * isdigit takes an `int', but expects values in the range of unsigned char.
  82. * This wrapper ensures that values from a 'char' end up in the correct range.
  83. */
  84. #define isdigitch(Anychar) isdigit((u_char)(Anychar))
  85. int cflag; /* -c */
  86. int eval; /* Exit value */
  87. time_t now; /* Current time(3) value */
  88. int rawcpu; /* -C */
  89. int sumrusage; /* -S */
  90. int termwidth; /* Width of the screen (0 == infinity). */
  91. int showthreads; /* will threads be shown? */
  92. struct velisthead varlist = STAILQ_HEAD_INITIALIZER(varlist);
  93. static int forceuread = DEF_UREAD; /* Do extra work to get u-area. */
  94. static kvm_t *kd;
  95. static int needcomm; /* -o "command" */
  96. static int needenv; /* -e */
  97. static int needuser; /* -o "user" */
  98. static int optfatal; /* Fatal error parsing some list-option. */
  99. static enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
  100. struct listinfo;
  101. typedef int addelem_rtn(struct listinfo *_inf, const char *_elem);
  102. struct listinfo {
  103. int count;
  104. int maxcount;
  105. int elemsize;
  106. addelem_rtn *addelem;
  107. const char *lname;
  108. union {
  109. gid_t *gids;
  110. pid_t *pids;
  111. dev_t *ttys;
  112. uid_t *uids;
  113. void *ptr;
  114. } l;
  115. };
  116. static int check_procfs(void);
  117. static int addelem_gid(struct listinfo *, const char *);
  118. static int addelem_pid(struct listinfo *, const char *);
  119. static int addelem_tty(struct listinfo *, const char *);
  120. static int addelem_uid(struct listinfo *, const char *);
  121. static void add_list(struct listinfo *, const char *);
  122. static void descendant_sort(KINFO *, int);
  123. static void format_output(KINFO *);
  124. static void *expand_list(struct listinfo *);
  125. static const char *
  126. fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int),
  127. KINFO *, char *, int);
  128. static void free_list(struct listinfo *);
  129. static void init_list(struct listinfo *, addelem_rtn, int, const char *);
  130. static char *kludge_oldps_options(const char *, char *, const char *);
  131. static int pscomp(const void *, const void *);
  132. static void saveuser(KINFO *);
  133. static void scanvars(void);
  134. static void sizevars(void);
  135. static void usage(void);
  136. static char dfmt[] = "pid,tt,state,time,command";
  137. static char jfmt[] = "user,pid,ppid,pgid,sid,jobc,state,tt,time,command";
  138. static char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state,"
  139. "tt,time,command";
  140. static char o1[] = "pid";
  141. static char o2[] = "tt,state,time,command";
  142. static char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command";
  143. static char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz,"
  144. "%cpu,%mem,command";
  145. static char Zfmt[] = "label";
  146. #define PS_ARGS "AaCcde" OPT_LAZY_f "G:gHhjLlM:mN:O:o:p:rSTt:U:uvwXxZ"
  147. int
  148. main(int argc, char *argv[])
  149. {
  150. struct listinfo gidlist, pgrplist, pidlist;
  151. struct listinfo ruidlist, sesslist, ttylist, uidlist;
  152. struct kinfo_proc *kp;
  153. KINFO *kinfo = NULL, *next_KINFO;
  154. KINFO_STR *ks;
  155. struct varent *vent;
  156. struct winsize ws;
  157. const char *nlistf, *memf, *fmtstr, *str;
  158. char *cols;
  159. int all, ch, elem, flag, _fmt, i, lineno, linelen, left;
  160. int descendancy, nentries, nkept, nselectors;
  161. int prtheader, wflag, what, xkeep, xkeep_implied;
  162. char errbuf[_POSIX2_LINE_MAX];
  163. (void) setlocale(LC_ALL, "");
  164. time(&now); /* Used by routines in print.c. */
  165. if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0')
  166. termwidth = atoi(cols);
  167. else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
  168. ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
  169. ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) ||
  170. ws.ws_col == 0)
  171. termwidth = 79;
  172. else
  173. termwidth = ws.ws_col - 1;
  174. /*
  175. * Hide a number of option-processing kludges in a separate routine,
  176. * to support some historical BSD behaviors, such as `ps axu'.
  177. */
  178. if (argc > 1)
  179. argv[1] = kludge_oldps_options(PS_ARGS, argv[1], argv[2]);
  180. all = descendancy = _fmt = nselectors = optfatal = 0;
  181. prtheader = showthreads = wflag = xkeep_implied = 0;
  182. xkeep = -1; /* Neither -x nor -X. */
  183. init_list(&gidlist, addelem_gid, sizeof(gid_t), "group");
  184. init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group");
  185. init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id");
  186. init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser");
  187. init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id");
  188. init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty");
  189. init_list(&uidlist, addelem_uid, sizeof(uid_t), "user");
  190. memf = _PATH_DEVNULL;
  191. nlistf = NULL;
  192. while ((ch = getopt(argc, argv, PS_ARGS)) != -1)
  193. switch (ch) {
  194. case 'A':
  195. /*
  196. * Exactly the same as `-ax'. This has been
  197. * added for compatibility with SUSv3, but for
  198. * now it will not be described in the man page.
  199. */
  200. nselectors++;
  201. all = xkeep = 1;
  202. break;
  203. case 'a':
  204. nselectors++;
  205. all = 1;
  206. break;
  207. case 'C':
  208. rawcpu = 1;
  209. break;
  210. case 'c':
  211. cflag = 1;
  212. break;
  213. case 'd':
  214. descendancy = 1;
  215. break;
  216. case 'e': /* XXX set ufmt */
  217. needenv = 1;
  218. break;
  219. #ifdef LAZY_PS
  220. case 'f':
  221. if (getuid() == 0 || getgid() == 0)
  222. forceuread = 1;
  223. break;
  224. #endif
  225. case 'G':
  226. add_list(&gidlist, optarg);
  227. xkeep_implied = 1;
  228. nselectors++;
  229. break;
  230. case 'g':
  231. #if 0
  232. /*-
  233. * XXX - This SUSv3 behavior is still under debate
  234. * since it conflicts with the (undocumented)
  235. * `-g' option. So we skip it for now.
  236. */
  237. add_list(&pgrplist, optarg);
  238. xkeep_implied = 1;
  239. nselectors++;
  240. break;
  241. #else
  242. /* The historical BSD-ish (from SunOS) behavior. */
  243. break; /* no-op */
  244. #endif
  245. case 'H':
  246. showthreads = KERN_PROC_INC_THREAD;
  247. break;
  248. case 'h':
  249. prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
  250. break;
  251. case 'j':
  252. parsefmt(jfmt, 0);
  253. _fmt = 1;
  254. jfmt[0] = '\0';
  255. break;
  256. case 'L':
  257. showkey();
  258. exit(0);
  259. case 'l':
  260. parsefmt(lfmt, 0);
  261. _fmt = 1;
  262. lfmt[0] = '\0';
  263. break;
  264. case 'M':
  265. memf = optarg;
  266. break;
  267. case 'm':
  268. sortby = SORTMEM;
  269. break;
  270. case 'N':
  271. nlistf = optarg;
  272. break;
  273. case 'O':
  274. parsefmt(o1, 1);
  275. parsefmt(optarg, 1);
  276. parsefmt(o2, 1);
  277. o1[0] = o2[0] = '\0';
  278. _fmt = 1;
  279. break;
  280. case 'o':
  281. parsefmt(optarg, 1);
  282. _fmt = 1;
  283. break;
  284. case 'p':
  285. add_list(&pidlist, optarg);
  286. /*
  287. * Note: `-p' does not *set* xkeep, but any values
  288. * from pidlist are checked before xkeep is. That
  289. * way they are always matched, even if the user
  290. * specifies `-X'.
  291. */
  292. nselectors++;
  293. break;
  294. #if 0
  295. case 'R':
  296. /*-
  297. * XXX - This un-standard option is still under
  298. * debate. This is what SUSv3 defines as
  299. * the `-U' option, and while it would be
  300. * nice to have, it could cause even more
  301. * confusion to implement it as `-R'.
  302. */
  303. add_list(&ruidlist, optarg);
  304. xkeep_implied = 1;
  305. nselectors++;
  306. break;
  307. #endif
  308. case 'r':
  309. sortby = SORTCPU;
  310. break;
  311. case 'S':
  312. sumrusage = 1;
  313. break;
  314. #if 0
  315. case 's':
  316. /*-
  317. * XXX - This non-standard option is still under
  318. * debate. This *is* supported on Solaris,
  319. * Linux, and IRIX, but conflicts with `-s'
  320. * on NetBSD and maybe some older BSD's.
  321. */
  322. add_list(&sesslist, optarg);
  323. xkeep_implied = 1;
  324. nselectors++;
  325. break;
  326. #endif
  327. case 'T':
  328. if ((optarg = ttyname(STDIN_FILENO)) == NULL)
  329. errx(1, "stdin: not a terminal");
  330. /* FALLTHROUGH */
  331. case 't':
  332. add_list(&ttylist, optarg);
  333. xkeep_implied = 1;
  334. nselectors++;
  335. break;
  336. case 'U':
  337. /* This is what SUSv3 defines as the `-u' option. */
  338. add_list(&uidlist, optarg);
  339. xkeep_implied = 1;
  340. nselectors++;
  341. break;
  342. case 'u':
  343. parsefmt(ufmt, 0);
  344. sortby = SORTCPU;
  345. _fmt = 1;
  346. ufmt[0] = '\0';
  347. break;
  348. case 'v':
  349. parsefmt(vfmt, 0);
  350. sortby = SORTMEM;
  351. _fmt = 1;
  352. vfmt[0] = '\0';
  353. break;
  354. case 'w':
  355. if (wflag)
  356. termwidth = UNLIMITED;
  357. else if (termwidth < 131)
  358. termwidth = 131;
  359. wflag++;
  360. break;
  361. case 'X':
  362. /*
  363. * Note that `-X' and `-x' are not standard "selector"
  364. * options. For most selector-options, we check *all*
  365. * processes to see if any are matched by the given
  366. * value(s). After we have a set of all the matched
  367. * processes, then `-X' and `-x' govern whether we
  368. * modify that *matched* set for processes which do
  369. * not have a controlling terminal. `-X' causes
  370. * those processes to be deleted from the matched
  371. * set, while `-x' causes them to be kept.
  372. */
  373. xkeep = 0;
  374. break;
  375. case 'x':
  376. xkeep = 1;
  377. break;
  378. case 'Z':
  379. parsefmt(Zfmt, 0);
  380. Zfmt[0] = '\0';
  381. break;
  382. case '?':
  383. default:
  384. usage();
  385. }
  386. argc -= optind;
  387. argv += optind;
  388. /*
  389. * If the user specified ps -e then they want a copy of the process
  390. * environment kvm_getenvv(3) attempts to open /proc/<pid>/mem.
  391. * Check to make sure that procfs is mounted on /proc, otherwise
  392. * print a warning informing the user that output will be incomplete.
  393. */
  394. if (needenv == 1 && check_procfs() == 0)
  395. warnx("Process environment requires procfs(5)");
  396. /*
  397. * If there arguments after processing all the options, attempt
  398. * to treat them as a list of process ids.
  399. */
  400. while (*argv) {
  401. if (!isdigitch(**argv))
  402. break;
  403. add_list(&pidlist, *argv);
  404. argv++;
  405. }
  406. if (*argv) {
  407. fprintf(stderr, "%s: illegal argument: %s\n",
  408. getprogname(), *argv);
  409. usage();
  410. }
  411. if (optfatal)
  412. exit(1); /* Error messages already printed. */
  413. if (xkeep < 0) /* Neither -X nor -x was specified. */
  414. xkeep = xkeep_implied;
  415. kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
  416. if (kd == 0)
  417. errx(1, "%s", errbuf);
  418. if (!_fmt)
  419. parsefmt(dfmt, 0);
  420. if (nselectors == 0) {
  421. uidlist.l.ptr = malloc(sizeof(uid_t));
  422. if (uidlist.l.ptr == NULL)
  423. errx(1, "malloc failed");
  424. nselectors = 1;
  425. uidlist.count = uidlist.maxcount = 1;
  426. *uidlist.l.uids = getuid();
  427. }
  428. /*
  429. * scan requested variables, noting what structures are needed,
  430. * and adjusting header widths as appropriate.
  431. */
  432. scanvars();
  433. /*
  434. * Get process list. If the user requested just one selector-
  435. * option, then kvm_getprocs can be asked to return just those
  436. * processes. Otherwise, have it return all processes, and
  437. * then this routine will search that full list and select the
  438. * processes which match any of the user's selector-options.
  439. */
  440. what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC;
  441. flag = 0;
  442. if (nselectors == 1) {
  443. if (gidlist.count == 1) {
  444. what = KERN_PROC_RGID | showthreads;
  445. flag = *gidlist.l.gids;
  446. nselectors = 0;
  447. } else if (pgrplist.count == 1) {
  448. what = KERN_PROC_PGRP | showthreads;
  449. flag = *pgrplist.l.pids;
  450. nselectors = 0;
  451. } else if (pidlist.count == 1) {
  452. what = KERN_PROC_PID | showthreads;
  453. flag = *pidlist.l.pids;
  454. nselectors = 0;
  455. } else if (ruidlist.count == 1) {
  456. what = KERN_PROC_RUID | showthreads;
  457. flag = *ruidlist.l.uids;
  458. nselectors = 0;
  459. } else if (sesslist.count == 1) {
  460. what = KERN_PROC_SESSION | showthreads;
  461. flag = *sesslist.l.pids;
  462. nselectors = 0;
  463. } else if (ttylist.count == 1) {
  464. what = KERN_PROC_TTY | showthreads;
  465. flag = *ttylist.l.ttys;
  466. nselectors = 0;
  467. } else if (uidlist.count == 1) {
  468. what = KERN_PROC_UID | showthreads;
  469. flag = *uidlist.l.uids;
  470. nselectors = 0;
  471. } else if (all) {
  472. /* No need for this routine to select processes. */
  473. nselectors = 0;
  474. }
  475. }
  476. /*
  477. * select procs
  478. */
  479. nentries = -1;
  480. kp = kvm_getprocs(kd, what, flag, &nentries);
  481. if ((kp == NULL && nentries > 0) || (kp != NULL && nentries < 0))
  482. errx(1, "%s", kvm_geterr(kd));
  483. nkept = 0;
  484. if (nentries > 0) {
  485. if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
  486. errx(1, "malloc failed");
  487. for (i = nentries; --i >= 0; ++kp) {
  488. /*
  489. * If the user specified multiple selection-criteria,
  490. * then keep any process matched by the inclusive OR
  491. * of all the selection-criteria given.
  492. */
  493. if (pidlist.count > 0) {
  494. for (elem = 0; elem < pidlist.count; elem++)
  495. if (kp->ki_pid == pidlist.l.pids[elem])
  496. goto keepit;
  497. }
  498. /*
  499. * Note that we had to process pidlist before
  500. * filtering out processes which do not have
  501. * a controlling terminal.
  502. */
  503. if (xkeep == 0) {
  504. if ((kp->ki_tdev == NODEV ||
  505. (kp->ki_flag & P_CONTROLT) == 0))
  506. continue;
  507. }
  508. if (nselectors == 0)
  509. goto keepit;
  510. if (gidlist.count > 0) {
  511. for (elem = 0; elem < gidlist.count; elem++)
  512. if (kp->ki_rgid == gidlist.l.gids[elem])
  513. goto keepit;
  514. }
  515. if (pgrplist.count > 0) {
  516. for (elem = 0; elem < pgrplist.count; elem++)
  517. if (kp->ki_pgid ==
  518. pgrplist.l.pids[elem])
  519. goto keepit;
  520. }
  521. if (ruidlist.count > 0) {
  522. for (elem = 0; elem < ruidlist.count; elem++)
  523. if (kp->ki_ruid ==
  524. ruidlist.l.uids[elem])
  525. goto keepit;
  526. }
  527. if (sesslist.count > 0) {
  528. for (elem = 0; elem < sesslist.count; elem++)
  529. if (kp->ki_sid == sesslist.l.pids[elem])
  530. goto keepit;
  531. }
  532. if (ttylist.count > 0) {
  533. for (elem = 0; elem < ttylist.count; elem++)
  534. if (kp->ki_tdev == ttylist.l.ttys[elem])
  535. goto keepit;
  536. }
  537. if (uidlist.count > 0) {
  538. for (elem = 0; elem < uidlist.count; elem++)
  539. if (kp->ki_uid == uidlist.l.uids[elem])
  540. goto keepit;
  541. }
  542. /*
  543. * This process did not match any of the user's
  544. * selector-options, so skip the process.
  545. */
  546. continue;
  547. keepit:
  548. next_KINFO = &kinfo[nkept];
  549. next_KINFO->ki_p = kp;
  550. next_KINFO->ki_d.level = 0;
  551. next_KINFO->ki_d.prefix = NULL;
  552. next_KINFO->ki_pcpu = getpcpu(next_KINFO);
  553. if (sortby == SORTMEM)
  554. next_KINFO->ki_memsize = kp->ki_tsize +
  555. kp->ki_dsize + kp->ki_ssize;
  556. if (needuser)
  557. saveuser(next_KINFO);
  558. nkept++;
  559. }
  560. }
  561. sizevars();
  562. if (nkept == 0) {
  563. printheader();
  564. exit(1);
  565. }
  566. /*
  567. * sort proc list
  568. */
  569. qsort(kinfo, nkept, sizeof(KINFO), pscomp);
  570. /*
  571. * We want things in descendant order
  572. */
  573. if (descendancy)
  574. descendant_sort(kinfo, nkept);
  575. /*
  576. * Prepare formatted output.
  577. */
  578. for (i = 0; i < nkept; i++)
  579. format_output(&kinfo[i]);
  580. /*
  581. * Print header.
  582. */
  583. printheader();
  584. /*
  585. * Output formatted lines.
  586. */
  587. for (i = lineno = 0; i < nkept; i++) {
  588. linelen = 0;
  589. STAILQ_FOREACH(vent, &varlist, next_ve) {
  590. if (vent->var->flag & LJUST)
  591. fmtstr = "%-*s";
  592. else
  593. fmtstr = "%*s";
  594. ks = STAILQ_FIRST(&kinfo[i].ki_ks);
  595. STAILQ_REMOVE_HEAD(&kinfo[i].ki_ks, ks_next);
  596. /* Truncate rightmost column if neccessary. */
  597. if (STAILQ_NEXT(vent, next_ve) == NULL &&
  598. termwidth != UNLIMITED && ks->ks_str != NULL) {
  599. left = termwidth - linelen;
  600. if (left > 0 && left < (int)strlen(ks->ks_str))
  601. ks->ks_str[left] = '\0';
  602. }
  603. str = ks->ks_str;
  604. if (str == NULL)
  605. str = "-";
  606. /* No padding for the last column, if it's LJUST. */
  607. if (STAILQ_NEXT(vent, next_ve) == NULL &&
  608. vent->var->flag & LJUST)
  609. linelen += printf(fmtstr, 0, str);
  610. else
  611. linelen += printf(fmtstr, vent->var->width, str);
  612. if (ks->ks_str != NULL) {
  613. free(ks->ks_str);
  614. ks->ks_str = NULL;
  615. }
  616. free(ks);
  617. ks = NULL;
  618. if (STAILQ_NEXT(vent, next_ve) != NULL) {
  619. (void)putchar(' ');
  620. linelen++;
  621. }
  622. }
  623. (void)putchar('\n');
  624. if (prtheader && lineno++ == prtheader - 4) {
  625. (void)putchar('\n');
  626. printheader();
  627. lineno = 0;
  628. }
  629. }
  630. free_list(&gidlist);
  631. free_list(&pidlist);
  632. free_list(&pgrplist);
  633. free_list(&ruidlist);
  634. free_list(&sesslist);
  635. free_list(&ttylist);
  636. free_list(&uidlist);
  637. for (i = 0; i < nkept; i++)
  638. free(kinfo[i].ki_d.prefix);
  639. free(kinfo);
  640. exit(eval);
  641. }
  642. static int
  643. addelem_gid(struct listinfo *inf, const char *elem)
  644. {
  645. struct group *grp;
  646. const char *nameorID;
  647. char *endp;
  648. u_long bigtemp;
  649. if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
  650. if (*elem == '\0')
  651. warnx("Invalid (zero-length) %s name", inf->lname);
  652. else
  653. warnx("%s name too long: %s", inf->lname, elem);
  654. optfatal = 1;
  655. return (0); /* Do not add this value. */
  656. }
  657. /*
  658. * SUSv3 states that `ps -G grouplist' should match "real-group
  659. * ID numbers", and does not mention group-names. I do want to
  660. * also support group-names, so this tries for a group-id first,
  661. * and only tries for a name if that doesn't work. This is the
  662. * opposite order of what is done in addelem_uid(), but in
  663. * practice the order would only matter for group-names which
  664. * are all-numeric.
  665. */
  666. grp = NULL;
  667. nameorID = "named";
  668. errno = 0;
  669. bigtemp = strtoul(elem, &endp, 10);
  670. if (errno == 0 && *endp == '\0' && bigtemp <= GID_MAX) {
  671. nameorID = "name or ID matches";
  672. grp = getgrgid((gid_t)bigtemp);
  673. }
  674. if (grp == NULL)
  675. grp = getgrnam(elem);
  676. if (grp == NULL) {
  677. warnx("No %s %s '%s'", inf->lname, nameorID, elem);
  678. optfatal = 1;
  679. return (0);
  680. }
  681. if (inf->count >= inf->maxcount)
  682. expand_list(inf);
  683. inf->l.gids[(inf->count)++] = grp->gr_gid;
  684. return (1);
  685. }
  686. #define BSD_PID_MAX 99999 /* Copy of PID_MAX from sys/proc.h. */
  687. static int
  688. addelem_pid(struct listinfo *inf, const char *elem)
  689. {
  690. char *endp;
  691. long tempid;
  692. if (*elem == '\0') {
  693. warnx("Invalid (zero-length) process id");
  694. optfatal = 1;
  695. return (0); /* Do not add this value. */
  696. }
  697. errno = 0;
  698. tempid = strtol(elem, &endp, 10);
  699. if (*endp != '\0' || tempid < 0 || elem == endp) {
  700. warnx("Invalid %s: %s", inf->lname, elem);
  701. errno = ERANGE;
  702. } else if (errno != 0 || tempid > BSD_PID_MAX) {
  703. warnx("%s too large: %s", inf->lname, elem);
  704. errno = ERANGE;
  705. }
  706. if (errno == ERANGE) {
  707. optfatal = 1;
  708. return (0);
  709. }
  710. if (inf->count >= inf->maxcount)
  711. expand_list(inf);
  712. inf->l.pids[(inf->count)++] = tempid;
  713. return (1);
  714. }
  715. #undef BSD_PID_MAX
  716. /*-
  717. * The user can specify a device via one of three formats:
  718. * 1) fully qualified, e.g.: /dev/ttyp0 /dev/console /dev/pts/0
  719. * 2) missing "/dev", e.g.: ttyp0 console pts/0
  720. * 3) two-letters, e.g.: p0 co 0
  721. * (matching letters that would be seen in the "TT" column)
  722. */
  723. static int
  724. addelem_tty(struct listinfo *inf, const char *elem)
  725. {
  726. const char *ttypath;
  727. struct stat sb;
  728. char pathbuf[PATH_MAX], pathbuf2[PATH_MAX], pathbuf3[PATH_MAX];
  729. ttypath = NULL;
  730. pathbuf2[0] = '\0';
  731. pathbuf3[0] = '\0';
  732. switch (*elem) {
  733. case '/':
  734. ttypath = elem;
  735. break;
  736. case 'c':
  737. if (strcmp(elem, "co") == 0) {
  738. ttypath = _PATH_CONSOLE;
  739. break;
  740. }
  741. /* FALLTHROUGH */
  742. default:
  743. strlcpy(pathbuf, _PATH_DEV, sizeof(pathbuf));
  744. strlcat(pathbuf, elem, sizeof(pathbuf));
  745. ttypath = pathbuf;
  746. if (strncmp(pathbuf, _PATH_TTY, strlen(_PATH_TTY)) == 0)
  747. break;
  748. if (strncmp(pathbuf, _PATH_PTS, strlen(_PATH_PTS)) == 0)
  749. break;
  750. if (strcmp(pathbuf, _PATH_CONSOLE) == 0)
  751. break;
  752. /* Check to see if /dev/tty${elem} exists */
  753. strlcpy(pathbuf2, _PATH_TTY, sizeof(pathbuf2));
  754. strlcat(pathbuf2, elem, sizeof(pathbuf2));
  755. if (stat(pathbuf2, &sb) == 0 && S_ISCHR(sb.st_mode)) {
  756. /* No need to repeat stat() && S_ISCHR() checks */
  757. ttypath = NULL;
  758. break;
  759. }
  760. /* Check to see if /dev/pts/${elem} exists */
  761. strlcpy(pathbuf3, _PATH_PTS, sizeof(pathbuf3));
  762. strlcat(pathbuf3, elem, sizeof(pathbuf3));
  763. if (stat(pathbuf3, &sb) == 0 && S_ISCHR(sb.st_mode)) {
  764. /* No need to repeat stat() && S_ISCHR() checks */
  765. ttypath = NULL;
  766. break;
  767. }
  768. break;
  769. }
  770. if (ttypath) {
  771. if (stat(ttypath, &sb) == -1) {
  772. if (pathbuf3[0] != '\0')
  773. warn("%s, %s, and %s", pathbuf3, pathbuf2,
  774. ttypath);
  775. else
  776. warn("%s", ttypath);
  777. optfatal = 1;
  778. return (0);
  779. }
  780. if (!S_ISCHR(sb.st_mode)) {
  781. if (pathbuf3[0] != '\0')
  782. warnx("%s, %s, and %s: Not a terminal",
  783. pathbuf3, pathbuf2, ttypath);
  784. else
  785. warnx("%s: Not a terminal", ttypath);
  786. optfatal = 1;
  787. return (0);
  788. }
  789. }
  790. if (inf->count >= inf->maxcount)
  791. expand_list(inf);
  792. inf->l.ttys[(inf->count)++] = sb.st_rdev;
  793. return (1);
  794. }
  795. static int
  796. addelem_uid(struct listinfo *inf, const char *elem)
  797. {
  798. struct passwd *pwd;
  799. char *endp;
  800. u_long bigtemp;
  801. if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
  802. if (*elem == '\0')
  803. warnx("Invalid (zero-length) %s name", inf->lname);
  804. else
  805. warnx("%s name too long: %s", inf->lname, elem);
  806. optfatal = 1;
  807. return (0); /* Do not add this value. */
  808. }
  809. pwd = getpwnam(elem);
  810. if (pwd == NULL) {
  811. errno = 0;
  812. bigtemp = strtoul(elem, &endp, 10);
  813. if (errno != 0 || *endp != '\0' || bigtemp > UID_MAX)
  814. warnx("No %s named '%s'", inf->lname, elem);
  815. else {
  816. /* The string is all digits, so it might be a userID. */
  817. pwd = getpwuid((uid_t)bigtemp);
  818. if (pwd == NULL)
  819. warnx("No %s name or ID matches '%s'",
  820. inf->lname, elem);
  821. }
  822. }
  823. if (pwd == NULL) {
  824. /*
  825. * These used to be treated as minor warnings (and the
  826. * option was simply ignored), but now they are fatal
  827. * errors (and the command will be aborted).
  828. */
  829. optfatal = 1;
  830. return (0);
  831. }
  832. if (inf->count >= inf->maxcount)
  833. expand_list(inf);
  834. inf->l.uids[(inf->count)++] = pwd->pw_uid;
  835. return (1);
  836. }
  837. static void
  838. add_list(struct listinfo *inf, const char *argp)
  839. {
  840. const char *savep;
  841. char *cp, *endp;
  842. int toolong;
  843. char elemcopy[PATH_MAX];
  844. if (*argp == 0)
  845. inf->addelem(inf, elemcopy);
  846. while (*argp != '\0') {
  847. while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
  848. argp++;
  849. savep = argp;
  850. toolong = 0;
  851. cp = elemcopy;
  852. if (strchr(T_SEP, *argp) == NULL) {
  853. endp = elemcopy + sizeof(elemcopy) - 1;
  854. while (*argp != '\0' && cp <= endp &&
  855. strchr(W_SEP T_SEP, *argp) == NULL)
  856. *cp++ = *argp++;
  857. if (cp > endp)
  858. toolong = 1;
  859. }
  860. if (!toolong) {
  861. *cp = '\0';
  862. /*
  863. * Add this single element to the given list.
  864. */
  865. inf->addelem(inf, elemcopy);
  866. } else {
  867. /*
  868. * The string is too long to copy. Find the end
  869. * of the string to print out the warning message.
  870. */
  871. while (*argp != '\0' && strchr(W_SEP T_SEP,
  872. *argp) == NULL)
  873. argp++;
  874. warnx("Value too long: %.*s", (int)(argp - savep),
  875. savep);
  876. optfatal = 1;
  877. }
  878. /*
  879. * Skip over any number of trailing whitespace characters,
  880. * but only one (at most) trailing element-terminating
  881. * character.
  882. */
  883. while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
  884. argp++;
  885. if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) {
  886. argp++;
  887. /* Catch case where string ended with a comma. */
  888. if (*argp == '\0')
  889. inf->addelem(inf, argp);
  890. }
  891. }
  892. }
  893. static void
  894. descendant_sort(KINFO *ki, int items)
  895. {
  896. int dst, lvl, maxlvl, n, ndst, nsrc, siblings, src;
  897. unsigned char *path;
  898. KINFO kn;
  899. /*
  900. * First, sort the entries by descendancy, tracking the descendancy
  901. * depth in the ki_d.level field.
  902. */
  903. src = 0;
  904. maxlvl = 0;
  905. while (src < items) {
  906. if (ki[src].ki_d.level) {
  907. src++;
  908. continue;
  909. }
  910. for (nsrc = 1; src + nsrc < items; nsrc++)
  911. if (!ki[src + nsrc].ki_d.level)
  912. break;
  913. for (dst = 0; dst < items; dst++) {
  914. if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_pid)
  915. continue;
  916. if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_ppid)
  917. break;
  918. }
  919. if (dst == items) {
  920. src += nsrc;
  921. continue;
  922. }
  923. for (ndst = 1; dst + ndst < items; ndst++)
  924. if (ki[dst + ndst].ki_d.level <= ki[dst].ki_d.level)
  925. break;
  926. for (n = src; n < src + nsrc; n++) {
  927. ki[n].ki_d.level += ki[dst].ki_d.level + 1;
  928. if (maxlvl < ki[n].ki_d.level)
  929. maxlvl = ki[n].ki_d.level;
  930. }
  931. while (nsrc) {
  932. if (src < dst) {
  933. kn = ki[src];
  934. memmove(ki + src, ki + src + 1,
  935. (dst - src + ndst - 1) * sizeof *ki);
  936. ki[dst + ndst - 1] = kn;
  937. nsrc--;
  938. dst--;
  939. ndst++;
  940. } else if (src != dst + ndst) {
  941. kn = ki[src];
  942. memmove(ki + dst + ndst + 1, ki + dst + ndst,
  943. (src - dst - ndst) * sizeof *ki);
  944. ki[dst + ndst] = kn;
  945. ndst++;
  946. nsrc--;
  947. src++;
  948. } else {
  949. ndst += nsrc;
  950. src += nsrc;
  951. nsrc = 0;
  952. }
  953. }
  954. }
  955. /*
  956. * Now populate ki_d.prefix (instead of ki_d.level) with the command
  957. * prefix used to show descendancies.
  958. */
  959. path = malloc((maxlvl + 7) / 8);
  960. memset(path, '\0', (maxlvl + 7) / 8);
  961. for (src = 0; src < items; src++) {
  962. if ((lvl = ki[src].ki_d.level) == 0) {
  963. ki[src].ki_d.prefix = NULL;
  964. continue;
  965. }
  966. if ((ki[src].ki_d.prefix = malloc(lvl * 2 + 1)) == NULL)
  967. errx(1, "malloc failed");
  968. for (n = 0; n < lvl - 2; n++) {
  969. ki[src].ki_d.prefix[n * 2] =
  970. path[n / 8] & 1 << (n % 8) ? '|' : ' ';
  971. ki[src].ki_d.prefix[n * 2 + 1] = ' ';
  972. }
  973. if (n == lvl - 2) {
  974. /* Have I any more siblings? */
  975. for (siblings = 0, dst = src + 1; dst < items; dst++) {
  976. if (ki[dst].ki_d.level > lvl)
  977. continue;
  978. if (ki[dst].ki_d.level == lvl)
  979. siblings = 1;
  980. break;
  981. }
  982. if (siblings)
  983. path[n / 8] |= 1 << (n % 8);
  984. else
  985. path[n / 8] &= ~(1 << (n % 8));
  986. ki[src].ki_d.prefix[n * 2] = siblings ? '|' : '`';
  987. ki[src].ki_d.prefix[n * 2 + 1] = '-';
  988. n++;
  989. }
  990. strcpy(ki[src].ki_d.prefix + n * 2, "- ");
  991. }
  992. free(path);
  993. }
  994. static void *
  995. expand_list(struct listinfo *inf)
  996. {
  997. void *newlist;
  998. int newmax;
  999. newmax = (inf->maxcount + 1) << 1;
  1000. newlist = realloc(inf->l.ptr, newmax * inf->elemsize);
  1001. if (newlist == NULL) {
  1002. free(inf->l.ptr);
  1003. errx(1, "realloc to %d %ss failed", newmax, inf->lname);
  1004. }
  1005. inf->maxcount = newmax;
  1006. inf->l.ptr = newlist;
  1007. return (newlist);
  1008. }
  1009. static void
  1010. free_list(struct listinfo *inf)
  1011. {
  1012. inf->count = inf->elemsize = inf->maxcount = 0;
  1013. if (inf->l.ptr != NULL)
  1014. free(inf->l.ptr);
  1015. inf->addelem = NULL;
  1016. inf->lname = NULL;
  1017. inf->l.ptr = NULL;
  1018. }
  1019. static void
  1020. init_list(struct listinfo *inf, addelem_rtn artn, int elemsize,
  1021. const char *lname)
  1022. {
  1023. inf->count = inf->maxcount = 0;
  1024. inf->elemsize = elemsize;
  1025. inf->addelem = artn;
  1026. inf->lname = lname;
  1027. inf->l.ptr = NULL;
  1028. }
  1029. VARENT *
  1030. find_varentry(VAR *v)
  1031. {
  1032. struct varent *vent;
  1033. STAILQ_FOREACH(vent, &varlist, next_ve) {
  1034. if (strcmp(vent->var->name, v->name) == 0)
  1035. return vent;
  1036. }
  1037. return NULL;
  1038. }
  1039. static void
  1040. scanvars(void)
  1041. {
  1042. struct varent *vent;
  1043. VAR *v;
  1044. STAILQ_FOREACH(vent, &varlist, next_ve) {
  1045. v = vent->var;
  1046. if (v->flag & USER)
  1047. needuser = 1;
  1048. if (v->flag & COMM)
  1049. needcomm = 1;
  1050. }
  1051. }
  1052. static void
  1053. format_output(KINFO *ki)
  1054. {
  1055. struct varent *vent;
  1056. VAR *v;
  1057. KINFO_STR *ks;
  1058. char *str;
  1059. int len;
  1060. STAILQ_INIT(&ki->ki_ks);
  1061. STAILQ_FOREACH(vent, &varlist, next_ve) {
  1062. v = vent->var;
  1063. str = (v->oproc)(ki, vent);
  1064. ks = malloc(sizeof(*ks));
  1065. if (ks == NULL)
  1066. errx(1, "malloc failed");
  1067. ks->ks_str = str;
  1068. STAILQ_INSERT_TAIL(&ki->ki_ks, ks, ks_next);
  1069. if (str != NULL) {
  1070. len = strlen(str);
  1071. } else
  1072. len = 1; /* "-" */
  1073. if (v->width < len)
  1074. v->width = len;
  1075. }
  1076. }
  1077. static void
  1078. sizevars(void)
  1079. {
  1080. struct varent *vent;
  1081. VAR *v;
  1082. int i;
  1083. STAILQ_FOREACH(vent, &varlist, next_ve) {
  1084. v = vent->var;
  1085. i = strlen(vent->header);
  1086. if (v->width < i)
  1087. v->width = i;
  1088. }
  1089. }
  1090. static const char *
  1091. fmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki,
  1092. char *comm, int maxlen)
  1093. {
  1094. const char *s;
  1095. s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen);
  1096. return (s);
  1097. }
  1098. #define UREADOK(ki) (forceuread || (ki->ki_p->ki_flag & P_INMEM))
  1099. static void
  1100. saveuser(KINFO *ki)
  1101. {
  1102. if (ki->ki_p->ki_flag & P_INMEM) {
  1103. /*
  1104. * The u-area might be swapped out, and we can't get
  1105. * at it because we have a crashdump and no swap.
  1106. * If it's here fill in these fields, otherwise, just
  1107. * leave them 0.
  1108. */
  1109. ki->ki_valid = 1;
  1110. } else
  1111. ki->ki_valid = 0;
  1112. /*
  1113. * save arguments if needed
  1114. */
  1115. if (needcomm) {
  1116. if (ki->ki_p->ki_stat == SZOMB)
  1117. ki->ki_args = strdup("<defunct>");
  1118. else if (UREADOK(ki) || (ki->ki_p->ki_args != NULL))
  1119. ki->ki_args = strdup(fmt(kvm_getargv, ki,
  1120. ki->ki_p->ki_comm, MAXCOMLEN));
  1121. else
  1122. asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm);
  1123. if (ki->ki_args == NULL)
  1124. errx(1, "malloc failed");
  1125. } else {
  1126. ki->ki_args = NULL;
  1127. }
  1128. if (needenv) {
  1129. if (UREADOK(ki))
  1130. ki->ki_env = strdup(fmt(kvm_getenvv, ki,
  1131. (char *)NULL, 0));
  1132. else
  1133. ki->ki_env = strdup("()");
  1134. if (ki->ki_env == NULL)
  1135. errx(1, "malloc failed");
  1136. } else {
  1137. ki->ki_env = NULL;
  1138. }
  1139. }
  1140. /* A macro used to improve the readability of pscomp(). */
  1141. #define DIFF_RETURN(a, b, field) do { \
  1142. if ((a)->field != (b)->field) \
  1143. return (((a)->field < (b)->field) ? -1 : 1); \
  1144. } while (0)
  1145. static int
  1146. pscomp(const void *a, const void *b)
  1147. {
  1148. const KINFO *ka, *kb;
  1149. ka = a;
  1150. kb = b;
  1151. /* SORTCPU and SORTMEM are sorted in descending order. */
  1152. if (sortby == SORTCPU)
  1153. DIFF_RETURN(kb, ka, ki_pcpu);
  1154. if (sortby == SORTMEM)
  1155. DIFF_RETURN(kb, ka, ki_memsize);
  1156. /*
  1157. * TTY's are sorted in ascending order, except that all NODEV
  1158. * processes come before all processes with a device.
  1159. */
  1160. if (ka->ki_p->ki_tdev != kb->ki_p->ki_tdev) {
  1161. if (ka->ki_p->ki_tdev == NODEV)
  1162. return (-1);
  1163. if (kb->ki_p->ki_tdev == NODEV)
  1164. return (1);
  1165. DIFF_RETURN(ka, kb, ki_p->ki_tdev);
  1166. }
  1167. /* PID's and TID's (threads) are sorted in ascending order. */
  1168. DIFF_RETURN(ka, kb, ki_p->ki_pid);
  1169. DIFF_RETURN(ka, kb, ki_p->ki_tid);
  1170. return (0);
  1171. }
  1172. #undef DIFF_RETURN
  1173. /*
  1174. * ICK (all for getopt), would rather hide the ugliness
  1175. * here than taint the main code.
  1176. *
  1177. * ps foo -> ps -foo
  1178. * ps 34 -> ps -p34
  1179. *
  1180. * The old convention that 't' with no trailing tty arg means the users
  1181. * tty, is only supported if argv[1] doesn't begin with a '-'. This same
  1182. * feature is available with the option 'T', which takes no argument.
  1183. */
  1184. static char *
  1185. kludge_oldps_options(const char *optlist, char *origval, const char *nextarg)
  1186. {
  1187. size_t len;
  1188. char *argp, *cp, *newopts, *ns, *optp, *pidp;
  1189. /*
  1190. * See if the original value includes any option which takes an
  1191. * argument (and will thus use up the remainder of the string).
  1192. */
  1193. argp = NULL;
  1194. if (optlist != NULL) {
  1195. for (cp = origval; *cp != '\0'; cp++) {
  1196. optp = strchr(optlist, *cp);
  1197. if ((optp != NULL) && *(optp + 1) == ':') {
  1198. argp = cp;
  1199. break;
  1200. }
  1201. }
  1202. }
  1203. if (argp != NULL && *origval == '-')
  1204. return (origval);
  1205. /*
  1206. * if last letter is a 't' flag with no argument (in the context
  1207. * of the oldps options -- option string NOT starting with a '-' --
  1208. * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
  1209. *
  1210. * However, if a flag accepting a string argument is found earlier
  1211. * in the option string (including a possible `t' flag), then the
  1212. * remainder of the string must be the argument to that flag; so
  1213. * do not modify that argument. Note that a trailing `t' would
  1214. * cause argp to be set, if argp was not already set by some
  1215. * earlier option.
  1216. */
  1217. len = strlen(origval);
  1218. cp = origval + len - 1;
  1219. pidp = NULL;
  1220. if (*cp == 't' && *origval != '-' && cp == argp) {
  1221. if (nextarg == NULL || *nextarg == '-' || isdigitch(*nextarg))
  1222. *cp = 'T';
  1223. } else if (argp == NULL) {
  1224. /*
  1225. * The original value did not include any option which takes
  1226. * an argument (and that would include `p' and `t'), so check
  1227. * the value for trailing number, or comma-separated list of
  1228. * numbers, which we will treat as a pid request.
  1229. */
  1230. if (isdigitch(*cp)) {
  1231. while (cp >= origval && (*cp == ',' || isdigitch(*cp)))
  1232. --cp;
  1233. pidp = cp + 1;
  1234. }
  1235. }
  1236. /*
  1237. * If nothing needs to be added to the string, then return
  1238. * the "original" (although possibly modified) value.
  1239. */
  1240. if (*origval == '-' && pidp == NULL)
  1241. return (origval);
  1242. /*
  1243. * Create a copy of the string to add '-' and/or 'p' to the
  1244. * original value.
  1245. */
  1246. if ((newopts = ns = malloc(len + 3)) == NULL)
  1247. errx(1, "malloc failed");
  1248. if (*origval != '-')
  1249. *ns++ = '-'; /* add option flag */
  1250. if (pidp == NULL)
  1251. strcpy(ns, origval);
  1252. else {
  1253. /*
  1254. * Copy everything before the pid string, add the `p',
  1255. * and then copy the pid string.
  1256. */
  1257. len = pidp - origval;
  1258. memcpy(ns, origval, len);
  1259. ns += len;
  1260. *ns++ = 'p';
  1261. strcpy(ns, pidp);
  1262. }
  1263. return (newopts);
  1264. }
  1265. static int
  1266. check_procfs(void)
  1267. {
  1268. struct statfs mnt;
  1269. if (statfs("/proc", &mnt) < 0)
  1270. return (0);
  1271. if (strcmp(mnt.f_fstypename, "procfs") != 0)
  1272. return (0);
  1273. return (1);
  1274. }
  1275. static void
  1276. usage(void)
  1277. {
  1278. #define SINGLE_OPTS "[-aCcde" OPT_LAZY_f "HhjlmrSTuvwXxZ]"
  1279. (void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
  1280. "usage: ps " SINGLE_OPTS " [-O fmt | -o fmt] [-G gid[,gid...]]",
  1281. " [-M core] [-N system]",
  1282. " [-p pid[,pid...]] [-t tty[,tty...]] [-U user[,user...]]",
  1283. " ps [-L]");
  1284. exit(1);
  1285. }