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