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