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