11556Srgrimes/*- 21556Srgrimes * Copyright (c) 1990, 1993, 1994 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * Redistribution and use in source and binary forms, with or without 61556Srgrimes * modification, are permitted provided that the following conditions 71556Srgrimes * are met: 81556Srgrimes * 1. Redistributions of source code must retain the above copyright 91556Srgrimes * notice, this list of conditions and the following disclaimer. 101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111556Srgrimes * notice, this list of conditions and the following disclaimer in the 121556Srgrimes * documentation and/or other materials provided with the distribution. 131556Srgrimes * 4. Neither the name of the University nor the names of its contributors 141556Srgrimes * may be used to endorse or promote products derived from this software 151556Srgrimes * without specific prior written permission. 161556Srgrimes * 171556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271556Srgrimes * SUCH DAMAGE. 28127499Sgad * ------+---------+---------+-------- + --------+---------+---------+---------* 29127499Sgad * Copyright (c) 2004 - Garance Alistair Drosehn <gad@FreeBSD.org>. 30127499Sgad * All rights reserved. 31127499Sgad * 32127499Sgad * Significant modifications made to bring `ps' options somewhat closer 33127499Sgad * to the standard for `ps' as described in SingleUnixSpec-v3. 34127499Sgad * ------+---------+---------+-------- + --------+---------+---------+---------* 351556Srgrimes */ 361556Srgrimes 371556Srgrimes#ifndef lint 3890143Smarkmstatic const char copyright[] = 391556Srgrimes"@(#) Copyright (c) 1990, 1993, 1994\n\ 401556Srgrimes The Regents of the University of California. All rights reserved.\n"; 411556Srgrimes#endif /* not lint */ 421556Srgrimes 4390143Smarkm#if 0 441556Srgrimes#ifndef lint 4536049Scharnierstatic char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94"; 4690143Smarkm#endif /* not lint */ 4736049Scharnier#endif 48110391Scharnier 4999110Sobrien#include <sys/cdefs.h> 5099110Sobrien__FBSDID("$FreeBSD$"); 511556Srgrimes 521556Srgrimes#include <sys/param.h> 53266279Sbdrewery#include <sys/jail.h> 54127546Sgad#include <sys/proc.h> 553296Sdg#include <sys/user.h> 561556Srgrimes#include <sys/stat.h> 571556Srgrimes#include <sys/ioctl.h> 581556Srgrimes#include <sys/sysctl.h> 59137696Scsjp#include <sys/mount.h> 601556Srgrimes 611556Srgrimes#include <ctype.h> 621556Srgrimes#include <err.h> 63127149Sgad#include <errno.h> 641556Srgrimes#include <fcntl.h> 65127499Sgad#include <grp.h> 66266279Sbdrewery#include <jail.h> 671556Srgrimes#include <kvm.h> 6813514Smpp#include <limits.h> 6973367Sache#include <locale.h> 701556Srgrimes#include <paths.h> 7190143Smarkm#include <pwd.h> 721556Srgrimes#include <stdio.h> 731556Srgrimes#include <stdlib.h> 741556Srgrimes#include <string.h> 751556Srgrimes#include <unistd.h> 761556Srgrimes 771556Srgrimes#include "ps.h" 781556Srgrimes 79173492Sjhb#define _PATH_PTS "/dev/pts/" 80173492Sjhb 81127499Sgad#define W_SEP " \t" /* "Whitespace" list separators */ 82127499Sgad#define T_SEP "," /* "Terminate-element" list separators */ 8366377Sbrian 84127537Sgad#ifdef LAZY_PS 85127555Sgad#define DEF_UREAD 0 86127537Sgad#define OPT_LAZY_f "f" 87127537Sgad#else 88127555Sgad#define DEF_UREAD 1 /* Always do the more-expensive read. */ 89127537Sgad#define OPT_LAZY_f /* I.e., the `-f' option is not added. */ 90127537Sgad#endif 91127537Sgad 92129914Sgad/* 93129971Sgad * isdigit takes an `int', but expects values in the range of unsigned char. 94129971Sgad * This wrapper ensures that values from a 'char' end up in the correct range. 95129914Sgad */ 96129971Sgad#define isdigitch(Anychar) isdigit((u_char)(Anychar)) 97129914Sgad 98127537Sgadint cflag; /* -c */ 99127537Sgadint eval; /* Exit value */ 100127537Sgadtime_t now; /* Current time(3) value */ 101127537Sgadint rawcpu; /* -C */ 102127537Sgadint sumrusage; /* -S */ 103127537Sgadint termwidth; /* Width of the screen (0 == infinity). */ 104173004Sjulianint showthreads; /* will threads be shown? */ 105127537Sgad 106130999Sgadstruct velisthead varlist = STAILQ_HEAD_INITIALIZER(varlist); 1071556Srgrimes 108127537Sgadstatic int forceuread = DEF_UREAD; /* Do extra work to get u-area. */ 109127537Sgadstatic kvm_t *kd; 110127537Sgadstatic int needcomm; /* -o "command" */ 111127537Sgadstatic int needenv; /* -e */ 112127537Sgadstatic int needuser; /* -o "user" */ 113127537Sgadstatic int optfatal; /* Fatal error parsing some list-option. */ 114244154Spjdstatic int pid_max; /* kern.max_pid */ 1151556Srgrimes 116127537Sgadstatic enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 11797966Sjmallett 118127499Sgadstruct listinfo; 119127537Sgadtypedef int addelem_rtn(struct listinfo *_inf, const char *_elem); 120127499Sgad 121127499Sgadstruct listinfo { 122127499Sgad int count; 123127499Sgad int maxcount; 124127499Sgad int elemsize; 125127499Sgad addelem_rtn *addelem; 126127499Sgad const char *lname; 127127499Sgad union { 128127499Sgad gid_t *gids; 129266279Sbdrewery int *jids; 130127499Sgad pid_t *pids; 131127499Sgad dev_t *ttys; 132127499Sgad uid_t *uids; 133127499Sgad void *ptr; 134127823Sgad } l; 135127499Sgad}; 136127499Sgad 137127499Sgadstatic int addelem_gid(struct listinfo *, const char *); 138266279Sbdrewerystatic int addelem_jid(struct listinfo *, const char *); 139127499Sgadstatic int addelem_pid(struct listinfo *, const char *); 140127499Sgadstatic int addelem_tty(struct listinfo *, const char *); 141127499Sgadstatic int addelem_uid(struct listinfo *, const char *); 142127499Sgadstatic void add_list(struct listinfo *, const char *); 143192239Sbrianstatic void descendant_sort(KINFO *, int); 144225868Straszstatic void format_output(KINFO *); 145127499Sgadstatic void *expand_list(struct listinfo *); 146127598Sgadstatic const char * 147127598Sgad fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int), 148245610Sjhb KINFO *, char *, char *, int); 149127499Sgadstatic void free_list(struct listinfo *); 150127499Sgadstatic void init_list(struct listinfo *, addelem_rtn, int, const char *); 151129952Sgadstatic char *kludge_oldps_options(const char *, char *, const char *); 152127536Sgadstatic int pscomp(const void *, const void *); 153127536Sgadstatic void saveuser(KINFO *); 154127536Sgadstatic void scanvars(void); 155127536Sgadstatic void sizevars(void); 156244154Spjdstatic void pidmax_init(void); 157127536Sgadstatic void usage(void); 158127499Sgad 15997875Sjmallettstatic char dfmt[] = "pid,tt,state,time,command"; 160129635Sgadstatic char jfmt[] = "user,pid,ppid,pgid,sid,jobc,state,tt,time,command"; 161127538Sgadstatic char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state," 162127538Sgad "tt,time,command"; 16390143Smarkmstatic char o1[] = "pid"; 16497875Sjmallettstatic char o2[] = "tt,state,time,command"; 16597875Sjmallettstatic char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command"; 166127538Sgadstatic char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz," 167127538Sgad "%cpu,%mem,command"; 168105831Srwatsonstatic char Zfmt[] = "label"; 1691556Srgrimes 170266279Sbdrewery#define PS_ARGS "AaCcde" OPT_LAZY_f "G:gHhjJ:LlM:mN:O:o:p:rSTt:U:uvwXxZ" 17198494Ssobomax 1721556Srgrimesint 17390110Simpmain(int argc, char *argv[]) 1741556Srgrimes{ 175266279Sbdrewery struct listinfo gidlist, jidlist, pgrplist, pidlist; 176127499Sgad struct listinfo ruidlist, sesslist, ttylist, uidlist; 1771556Srgrimes struct kinfo_proc *kp; 178225868Strasz KINFO *kinfo = NULL, *next_KINFO; 179225868Strasz KINFO_STR *ks; 1801556Srgrimes struct varent *vent; 1811556Srgrimes struct winsize ws; 182225868Strasz const char *nlistf, *memf, *fmtstr, *str; 183127539Sgad char *cols; 184225868Strasz int all, ch, elem, flag, _fmt, i, lineno, linelen, left; 185192239Sbrian int descendancy, nentries, nkept, nselectors; 186173004Sjulian int prtheader, wflag, what, xkeep, xkeep_implied; 18790143Smarkm char errbuf[_POSIX2_LINE_MAX]; 1881556Srgrimes 18911809Sache (void) setlocale(LC_ALL, ""); 190127542Sgad time(&now); /* Used by routines in print.c. */ 19111809Sache 19297804Stjr if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') 19397804Stjr termwidth = atoi(cols); 19497804Stjr else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 1951556Srgrimes ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 1961556Srgrimes ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || 1971556Srgrimes ws.ws_col == 0) 1981556Srgrimes termwidth = 79; 1991556Srgrimes else 2001556Srgrimes termwidth = ws.ws_col - 1; 2011556Srgrimes 20298494Ssobomax /* 203129914Sgad * Hide a number of option-processing kludges in a separate routine, 204129914Sgad * to support some historical BSD behaviors, such as `ps axu'. 20598494Ssobomax */ 206129914Sgad if (argc > 1) 207129915Sgad argv[1] = kludge_oldps_options(PS_ARGS, argv[1], argv[2]); 2081556Srgrimes 209244154Spjd pidmax_init(); 210244154Spjd 211192239Sbrian all = descendancy = _fmt = nselectors = optfatal = 0; 212127542Sgad prtheader = showthreads = wflag = xkeep_implied = 0; 213127542Sgad xkeep = -1; /* Neither -x nor -X. */ 214127499Sgad init_list(&gidlist, addelem_gid, sizeof(gid_t), "group"); 215266279Sbdrewery init_list(&jidlist, addelem_jid, sizeof(int), "jail id"); 216127499Sgad init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group"); 217127499Sgad init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id"); 218127499Sgad init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser"); 219127499Sgad init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id"); 220127499Sgad init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty"); 221127499Sgad init_list(&uidlist, addelem_uid, sizeof(uid_t), "user"); 222203688Sbrucec memf = _PATH_DEVNULL; 223203688Sbrucec nlistf = NULL; 22498494Ssobomax while ((ch = getopt(argc, argv, PS_ARGS)) != -1) 225180596Skevlo switch (ch) { 226127499Sgad case 'A': 227127499Sgad /* 228127499Sgad * Exactly the same as `-ax'. This has been 229222178Suqs * added for compatibility with SUSv3, but for 230127499Sgad * now it will not be described in the man page. 231127499Sgad */ 232127499Sgad nselectors++; 233127499Sgad all = xkeep = 1; 234127499Sgad break; 2351556Srgrimes case 'a': 236127499Sgad nselectors++; 2371556Srgrimes all = 1; 2381556Srgrimes break; 23919068Speter case 'C': 24019068Speter rawcpu = 1; 24119068Speter break; 24219068Speter case 'c': 24319068Speter cflag = 1; 24419068Speter break; 245192239Sbrian case 'd': 246192239Sbrian descendancy = 1; 247192239Sbrian break; 2481556Srgrimes case 'e': /* XXX set ufmt */ 2491556Srgrimes needenv = 1; 2501556Srgrimes break; 251127506Sgad#ifdef LAZY_PS 252127506Sgad case 'f': 253127506Sgad if (getuid() == 0 || getgid() == 0) 254127542Sgad forceuread = 1; 255127506Sgad break; 256127506Sgad#endif 257127499Sgad case 'G': 258127499Sgad add_list(&gidlist, optarg); 259127499Sgad xkeep_implied = 1; 260127499Sgad nselectors++; 261127499Sgad break; 262127542Sgad case 'g': 263127499Sgad#if 0 264127597Sgad /*- 265127542Sgad * XXX - This SUSv3 behavior is still under debate 266127542Sgad * since it conflicts with the (undocumented) 267127542Sgad * `-g' option. So we skip it for now. 268127542Sgad */ 269127499Sgad add_list(&pgrplist, optarg); 270127499Sgad xkeep_implied = 1; 271127499Sgad nselectors++; 272127499Sgad break; 273127499Sgad#else 274127542Sgad /* The historical BSD-ish (from SunOS) behavior. */ 2751556Srgrimes break; /* no-op */ 276127499Sgad#endif 277116265Sscottl case 'H': 278126127Sdeischen showthreads = KERN_PROC_INC_THREAD; 279116265Sscottl break; 2801556Srgrimes case 'h': 2811556Srgrimes prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 2821556Srgrimes break; 283266279Sbdrewery case 'J': 284266279Sbdrewery add_list(&jidlist, optarg); 285266279Sbdrewery xkeep_implied = 1; 286266279Sbdrewery nselectors++; 287266279Sbdrewery break; 2881556Srgrimes case 'j': 289109502Sjmallett parsefmt(jfmt, 0); 29090143Smarkm _fmt = 1; 2911556Srgrimes jfmt[0] = '\0'; 2921556Srgrimes break; 2931556Srgrimes case 'L': 2941556Srgrimes showkey(); 2951556Srgrimes exit(0); 2961556Srgrimes case 'l': 297109502Sjmallett parsefmt(lfmt, 0); 29890143Smarkm _fmt = 1; 2991556Srgrimes lfmt[0] = '\0'; 3001556Srgrimes break; 3011556Srgrimes case 'M': 3021556Srgrimes memf = optarg; 3031556Srgrimes break; 3041556Srgrimes case 'm': 3051556Srgrimes sortby = SORTMEM; 3061556Srgrimes break; 3071556Srgrimes case 'N': 3081556Srgrimes nlistf = optarg; 3091556Srgrimes break; 3101556Srgrimes case 'O': 311109502Sjmallett parsefmt(o1, 1); 312109502Sjmallett parsefmt(optarg, 1); 313109502Sjmallett parsefmt(o2, 1); 3141556Srgrimes o1[0] = o2[0] = '\0'; 31590143Smarkm _fmt = 1; 3161556Srgrimes break; 3171556Srgrimes case 'o': 318109502Sjmallett parsefmt(optarg, 1); 31990143Smarkm _fmt = 1; 3201556Srgrimes break; 3211556Srgrimes case 'p': 322127499Sgad add_list(&pidlist, optarg); 323127499Sgad /* 324127499Sgad * Note: `-p' does not *set* xkeep, but any values 325127499Sgad * from pidlist are checked before xkeep is. That 326127499Sgad * way they are always matched, even if the user 327127499Sgad * specifies `-X'. 328127499Sgad */ 329127499Sgad nselectors++; 3301556Srgrimes break; 331127499Sgad#if 0 332127499Sgad case 'R': 333127597Sgad /*- 334127542Sgad * XXX - This un-standard option is still under 335127542Sgad * debate. This is what SUSv3 defines as 336127542Sgad * the `-U' option, and while it would be 337127542Sgad * nice to have, it could cause even more 338127542Sgad * confusion to implement it as `-R'. 339127542Sgad */ 340127499Sgad add_list(&ruidlist, optarg); 341127499Sgad xkeep_implied = 1; 342127499Sgad nselectors++; 343127499Sgad break; 344127499Sgad#endif 3451556Srgrimes case 'r': 3461556Srgrimes sortby = SORTCPU; 3471556Srgrimes break; 3481556Srgrimes case 'S': 3491556Srgrimes sumrusage = 1; 3501556Srgrimes break; 351127499Sgad#if 0 352127499Sgad case 's': 353127597Sgad /*- 354127542Sgad * XXX - This non-standard option is still under 355127542Sgad * debate. This *is* supported on Solaris, 356127542Sgad * Linux, and IRIX, but conflicts with `-s' 357127542Sgad * on NetBSD and maybe some older BSD's. 358127542Sgad */ 359127499Sgad add_list(&sesslist, optarg); 360127499Sgad xkeep_implied = 1; 361127499Sgad nselectors++; 362127499Sgad break; 363127499Sgad#endif 3641556Srgrimes case 'T': 3651556Srgrimes if ((optarg = ttyname(STDIN_FILENO)) == NULL) 3661556Srgrimes errx(1, "stdin: not a terminal"); 3671556Srgrimes /* FALLTHROUGH */ 368127499Sgad case 't': 369127499Sgad add_list(&ttylist, optarg); 370127499Sgad xkeep_implied = 1; 371127499Sgad nselectors++; 3721556Srgrimes break; 37313020Speter case 'U': 374127499Sgad /* This is what SUSv3 defines as the `-u' option. */ 375127499Sgad add_list(&uidlist, optarg); 376127499Sgad xkeep_implied = 1; 377127499Sgad nselectors++; 37813020Speter break; 3791556Srgrimes case 'u': 380109502Sjmallett parsefmt(ufmt, 0); 3811556Srgrimes sortby = SORTCPU; 38290143Smarkm _fmt = 1; 3831556Srgrimes ufmt[0] = '\0'; 3841556Srgrimes break; 3851556Srgrimes case 'v': 386109502Sjmallett parsefmt(vfmt, 0); 3871556Srgrimes sortby = SORTMEM; 38890143Smarkm _fmt = 1; 3891556Srgrimes vfmt[0] = '\0'; 3901556Srgrimes break; 3911556Srgrimes case 'w': 3921556Srgrimes if (wflag) 3931556Srgrimes termwidth = UNLIMITED; 3941556Srgrimes else if (termwidth < 131) 3951556Srgrimes termwidth = 131; 3961556Srgrimes wflag++; 3971556Srgrimes break; 398127499Sgad case 'X': 399127499Sgad /* 400127499Sgad * Note that `-X' and `-x' are not standard "selector" 401127499Sgad * options. For most selector-options, we check *all* 402127499Sgad * processes to see if any are matched by the given 403127499Sgad * value(s). After we have a set of all the matched 404127499Sgad * processes, then `-X' and `-x' govern whether we 405127499Sgad * modify that *matched* set for processes which do 406127499Sgad * not have a controlling terminal. `-X' causes 407127499Sgad * those processes to be deleted from the matched 408127499Sgad * set, while `-x' causes them to be kept. 409127499Sgad */ 410127499Sgad xkeep = 0; 411127499Sgad break; 4121556Srgrimes case 'x': 413127499Sgad xkeep = 1; 4141556Srgrimes break; 41586922Sgreen case 'Z': 416109502Sjmallett parsefmt(Zfmt, 0); 41786922Sgreen Zfmt[0] = '\0'; 41886922Sgreen break; 4191556Srgrimes case '?': 4201556Srgrimes default: 4211556Srgrimes usage(); 4221556Srgrimes } 4231556Srgrimes argc -= optind; 4241556Srgrimes argv += optind; 425129914Sgad 426129914Sgad /* 427129914Sgad * If there arguments after processing all the options, attempt 428129914Sgad * to treat them as a list of process ids. 429129914Sgad */ 430129914Sgad while (*argv) { 431129914Sgad if (!isdigitch(**argv)) 432129914Sgad break; 433129914Sgad add_list(&pidlist, *argv); 434129914Sgad argv++; 435129914Sgad } 436129914Sgad if (*argv) { 437129914Sgad fprintf(stderr, "%s: illegal argument: %s\n", 438129914Sgad getprogname(), *argv); 439129914Sgad usage(); 440129914Sgad } 441127499Sgad if (optfatal) 442127542Sgad exit(1); /* Error messages already printed. */ 443127542Sgad if (xkeep < 0) /* Neither -X nor -x was specified. */ 444127499Sgad xkeep = xkeep_implied; 445127499Sgad 44689909Sru kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 4471556Srgrimes if (kd == 0) 4481556Srgrimes errx(1, "%s", errbuf); 4491556Srgrimes 45090143Smarkm if (!_fmt) 451109502Sjmallett parsefmt(dfmt, 0); 4521556Srgrimes 453127499Sgad if (nselectors == 0) { 454127823Sgad uidlist.l.ptr = malloc(sizeof(uid_t)); 455127823Sgad if (uidlist.l.ptr == NULL) 45697877Sjmallett errx(1, "malloc failed"); 457127499Sgad nselectors = 1; 458127499Sgad uidlist.count = uidlist.maxcount = 1; 459127823Sgad *uidlist.l.uids = getuid(); 46066377Sbrian } 4611556Srgrimes 4621556Srgrimes /* 4631556Srgrimes * scan requested variables, noting what structures are needed, 46453170Skris * and adjusting header widths as appropriate. 4651556Srgrimes */ 4661556Srgrimes scanvars(); 467127499Sgad 4681556Srgrimes /* 469127499Sgad * Get process list. If the user requested just one selector- 470127499Sgad * option, then kvm_getprocs can be asked to return just those 471127499Sgad * processes. Otherwise, have it return all processes, and 472127499Sgad * then this routine will search that full list and select the 473127499Sgad * processes which match any of the user's selector-options. 4741556Srgrimes */ 475127499Sgad what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC; 476127499Sgad flag = 0; 477127499Sgad if (nselectors == 1) { 478129600Sgad if (gidlist.count == 1) { 479129600Sgad what = KERN_PROC_RGID | showthreads; 480129600Sgad flag = *gidlist.l.gids; 481129600Sgad nselectors = 0; 482129600Sgad } else if (pgrplist.count == 1) { 483127499Sgad what = KERN_PROC_PGRP | showthreads; 484127823Sgad flag = *pgrplist.l.pids; 485127499Sgad nselectors = 0; 486127499Sgad } else if (pidlist.count == 1) { 487127499Sgad what = KERN_PROC_PID | showthreads; 488127823Sgad flag = *pidlist.l.pids; 489127499Sgad nselectors = 0; 490127499Sgad } else if (ruidlist.count == 1) { 491127499Sgad what = KERN_PROC_RUID | showthreads; 492127823Sgad flag = *ruidlist.l.uids; 493127499Sgad nselectors = 0; 494127499Sgad } else if (sesslist.count == 1) { 495127499Sgad what = KERN_PROC_SESSION | showthreads; 496127823Sgad flag = *sesslist.l.pids; 497127499Sgad nselectors = 0; 498127499Sgad } else if (ttylist.count == 1) { 499127499Sgad what = KERN_PROC_TTY | showthreads; 500127823Sgad flag = *ttylist.l.ttys; 501127499Sgad nselectors = 0; 502127499Sgad } else if (uidlist.count == 1) { 503127499Sgad what = KERN_PROC_UID | showthreads; 504127823Sgad flag = *uidlist.l.uids; 505127499Sgad nselectors = 0; 506127499Sgad } else if (all) { 507127499Sgad /* No need for this routine to select processes. */ 508127499Sgad nselectors = 0; 509127499Sgad } 5101556Srgrimes } 511126127Sdeischen 5121556Srgrimes /* 5131556Srgrimes * select procs 5141556Srgrimes */ 515127499Sgad nentries = -1; 516127149Sgad kp = kvm_getprocs(kd, what, flag, &nentries); 517127544Sgad if ((kp == NULL && nentries > 0) || (kp != NULL && nentries < 0)) 5181556Srgrimes errx(1, "%s", kvm_geterr(kd)); 519127499Sgad nkept = 0; 520127149Sgad if (nentries > 0) { 521127149Sgad if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL) 522127149Sgad errx(1, "malloc failed"); 523127149Sgad for (i = nentries; --i >= 0; ++kp) { 524127499Sgad /* 525127499Sgad * If the user specified multiple selection-criteria, 526127499Sgad * then keep any process matched by the inclusive OR 527127499Sgad * of all the selection-criteria given. 528127499Sgad */ 529127499Sgad if (pidlist.count > 0) { 530127499Sgad for (elem = 0; elem < pidlist.count; elem++) 531127823Sgad if (kp->ki_pid == pidlist.l.pids[elem]) 532127499Sgad goto keepit; 533127499Sgad } 534127499Sgad /* 535127499Sgad * Note that we had to process pidlist before 536127499Sgad * filtering out processes which do not have 537127499Sgad * a controlling terminal. 538127499Sgad */ 539127499Sgad if (xkeep == 0) { 540127499Sgad if ((kp->ki_tdev == NODEV || 541127499Sgad (kp->ki_flag & P_CONTROLT) == 0)) 542127499Sgad continue; 543127499Sgad } 544127499Sgad if (nselectors == 0) 545127499Sgad goto keepit; 546127499Sgad if (gidlist.count > 0) { 547127499Sgad for (elem = 0; elem < gidlist.count; elem++) 548127823Sgad if (kp->ki_rgid == gidlist.l.gids[elem]) 549127499Sgad goto keepit; 550127499Sgad } 551266279Sbdrewery if (jidlist.count > 0) { 552266279Sbdrewery for (elem = 0; elem < jidlist.count; elem++) 553266279Sbdrewery if (kp->ki_jid == jidlist.l.jids[elem]) 554266279Sbdrewery goto keepit; 555266279Sbdrewery } 556127499Sgad if (pgrplist.count > 0) { 557127499Sgad for (elem = 0; elem < pgrplist.count; elem++) 558127823Sgad if (kp->ki_pgid == 559127823Sgad pgrplist.l.pids[elem]) 560127499Sgad goto keepit; 561127499Sgad } 562127499Sgad if (ruidlist.count > 0) { 563127499Sgad for (elem = 0; elem < ruidlist.count; elem++) 564127823Sgad if (kp->ki_ruid == 565127823Sgad ruidlist.l.uids[elem]) 566127499Sgad goto keepit; 567127499Sgad } 568127499Sgad if (sesslist.count > 0) { 569127499Sgad for (elem = 0; elem < sesslist.count; elem++) 570127823Sgad if (kp->ki_sid == sesslist.l.pids[elem]) 571127499Sgad goto keepit; 572127499Sgad } 573127499Sgad if (ttylist.count > 0) { 574127499Sgad for (elem = 0; elem < ttylist.count; elem++) 575127823Sgad if (kp->ki_tdev == ttylist.l.ttys[elem]) 576127499Sgad goto keepit; 577127499Sgad } 578127499Sgad if (uidlist.count > 0) { 579127499Sgad for (elem = 0; elem < uidlist.count; elem++) 580127823Sgad if (kp->ki_uid == uidlist.l.uids[elem]) 581127499Sgad goto keepit; 582127499Sgad } 583127499Sgad /* 584127499Sgad * This process did not match any of the user's 585127499Sgad * selector-options, so skip the process. 586127499Sgad */ 587127499Sgad continue; 588127499Sgad 589127499Sgad keepit: 590130816Sgad next_KINFO = &kinfo[nkept]; 591130816Sgad next_KINFO->ki_p = kp; 592192239Sbrian next_KINFO->ki_d.level = 0; 593192239Sbrian next_KINFO->ki_d.prefix = NULL; 594130816Sgad next_KINFO->ki_pcpu = getpcpu(next_KINFO); 595130816Sgad if (sortby == SORTMEM) 596130816Sgad next_KINFO->ki_memsize = kp->ki_tsize + 597130816Sgad kp->ki_dsize + kp->ki_ssize; 598127149Sgad if (needuser) 599130816Sgad saveuser(next_KINFO); 600127499Sgad nkept++; 601127149Sgad } 6021556Srgrimes } 60325271Sjkh 60425271Sjkh sizevars(); 60525271Sjkh 606225868Strasz if (nkept == 0) { 607225868Strasz printheader(); 60862803Swill exit(1); 609225868Strasz } 610127499Sgad 6111556Srgrimes /* 6121556Srgrimes * sort proc list 6131556Srgrimes */ 614127499Sgad qsort(kinfo, nkept, sizeof(KINFO), pscomp); 615192239Sbrian 6161556Srgrimes /* 617192239Sbrian * We want things in descendant order 618192239Sbrian */ 619192239Sbrian if (descendancy) 620192239Sbrian descendant_sort(kinfo, nkept); 621192239Sbrian 622225868Strasz 623192239Sbrian /* 624225868Strasz * Prepare formatted output. 6251556Srgrimes */ 626225868Strasz for (i = 0; i < nkept; i++) 627225868Strasz format_output(&kinfo[i]); 628225868Strasz 629225868Strasz /* 630225868Strasz * Print header. 631225868Strasz */ 632225868Strasz printheader(); 633225868Strasz 634225868Strasz /* 635225868Strasz * Output formatted lines. 636225868Strasz */ 637127499Sgad for (i = lineno = 0; i < nkept; i++) { 638225868Strasz linelen = 0; 639130999Sgad STAILQ_FOREACH(vent, &varlist, next_ve) { 640225868Strasz if (vent->var->flag & LJUST) 641225868Strasz fmtstr = "%-*s"; 642225868Strasz else 643225868Strasz fmtstr = "%*s"; 644225868Strasz 645225868Strasz ks = STAILQ_FIRST(&kinfo[i].ki_ks); 646225868Strasz STAILQ_REMOVE_HEAD(&kinfo[i].ki_ks, ks_next); 647229782Suqs /* Truncate rightmost column if necessary. */ 648225868Strasz if (STAILQ_NEXT(vent, next_ve) == NULL && 649225868Strasz termwidth != UNLIMITED && ks->ks_str != NULL) { 650225868Strasz left = termwidth - linelen; 651225868Strasz if (left > 0 && left < (int)strlen(ks->ks_str)) 652225868Strasz ks->ks_str[left] = '\0'; 653225868Strasz } 654225868Strasz str = ks->ks_str; 655225868Strasz if (str == NULL) 656225868Strasz str = "-"; 657225868Strasz /* No padding for the last column, if it's LJUST. */ 658225868Strasz if (STAILQ_NEXT(vent, next_ve) == NULL && 659225868Strasz vent->var->flag & LJUST) 660225868Strasz linelen += printf(fmtstr, 0, str); 661225868Strasz else 662225868Strasz linelen += printf(fmtstr, vent->var->width, str); 663225868Strasz 664225868Strasz if (ks->ks_str != NULL) { 665225868Strasz free(ks->ks_str); 666225868Strasz ks->ks_str = NULL; 667225868Strasz } 668225868Strasz free(ks); 669225868Strasz ks = NULL; 670225868Strasz 671225868Strasz if (STAILQ_NEXT(vent, next_ve) != NULL) { 6721556Srgrimes (void)putchar(' '); 673225868Strasz linelen++; 674225868Strasz } 6751556Srgrimes } 6761556Srgrimes (void)putchar('\n'); 6771556Srgrimes if (prtheader && lineno++ == prtheader - 4) { 6781556Srgrimes (void)putchar('\n'); 6791556Srgrimes printheader(); 6801556Srgrimes lineno = 0; 6811556Srgrimes } 6821556Srgrimes } 683127499Sgad free_list(&gidlist); 684266279Sbdrewery free_list(&jidlist); 685127499Sgad free_list(&pidlist); 686127499Sgad free_list(&pgrplist); 687127499Sgad free_list(&ruidlist); 688127499Sgad free_list(&sesslist); 689127499Sgad free_list(&ttylist); 690127499Sgad free_list(&uidlist); 691192239Sbrian for (i = 0; i < nkept; i++) 692192239Sbrian free(kinfo[i].ki_d.prefix); 693192239Sbrian free(kinfo); 69466377Sbrian 6951556Srgrimes exit(eval); 6961556Srgrimes} 6971556Srgrimes 698127499Sgadstatic int 699127499Sgadaddelem_gid(struct listinfo *inf, const char *elem) 700127499Sgad{ 701127499Sgad struct group *grp; 702127499Sgad const char *nameorID; 703127499Sgad char *endp; 704127602Sgad u_long bigtemp; 705127499Sgad 706127499Sgad if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) { 707127499Sgad if (*elem == '\0') 708127499Sgad warnx("Invalid (zero-length) %s name", inf->lname); 709127499Sgad else 710127499Sgad warnx("%s name too long: %s", inf->lname, elem); 711127499Sgad optfatal = 1; 712127542Sgad return (0); /* Do not add this value. */ 713127499Sgad } 714127499Sgad 715127499Sgad /* 716127499Sgad * SUSv3 states that `ps -G grouplist' should match "real-group 717127499Sgad * ID numbers", and does not mention group-names. I do want to 718127499Sgad * also support group-names, so this tries for a group-id first, 719127499Sgad * and only tries for a name if that doesn't work. This is the 720127499Sgad * opposite order of what is done in addelem_uid(), but in 721127499Sgad * practice the order would only matter for group-names which 722127499Sgad * are all-numeric. 723127499Sgad */ 724127499Sgad grp = NULL; 725127499Sgad nameorID = "named"; 726127499Sgad errno = 0; 727127602Sgad bigtemp = strtoul(elem, &endp, 10); 728127602Sgad if (errno == 0 && *endp == '\0' && bigtemp <= GID_MAX) { 729127499Sgad nameorID = "name or ID matches"; 730127602Sgad grp = getgrgid((gid_t)bigtemp); 731127499Sgad } 732127499Sgad if (grp == NULL) 733127499Sgad grp = getgrnam(elem); 734127499Sgad if (grp == NULL) { 735127499Sgad warnx("No %s %s '%s'", inf->lname, nameorID, elem); 736127499Sgad optfatal = 1; 737129967Sgad return (0); 738127499Sgad } 739127499Sgad if (inf->count >= inf->maxcount) 740127499Sgad expand_list(inf); 741127823Sgad inf->l.gids[(inf->count)++] = grp->gr_gid; 742127499Sgad return (1); 743127499Sgad} 744127499Sgad 745127499Sgadstatic int 746266279Sbdreweryaddelem_jid(struct listinfo *inf, const char *elem) 747266279Sbdrewery{ 748266279Sbdrewery int tempid; 749266279Sbdrewery 750266279Sbdrewery if (*elem == '\0') { 751266279Sbdrewery warnx("Invalid (zero-length) jail id"); 752266279Sbdrewery optfatal = 1; 753266279Sbdrewery return (0); /* Do not add this value. */ 754266279Sbdrewery } 755266279Sbdrewery 756266279Sbdrewery tempid = jail_getid(elem); 757266279Sbdrewery if (tempid < 0) { 758266279Sbdrewery warnx("Invalid %s: %s", inf->lname, elem); 759266279Sbdrewery optfatal = 1; 760266279Sbdrewery return (0); 761266279Sbdrewery } 762266279Sbdrewery 763266279Sbdrewery if (inf->count >= inf->maxcount) 764266279Sbdrewery expand_list(inf); 765266279Sbdrewery inf->l.jids[(inf->count)++] = tempid; 766266279Sbdrewery return (1); 767266279Sbdrewery} 768266279Sbdrewery 769266279Sbdrewerystatic int 770127499Sgadaddelem_pid(struct listinfo *inf, const char *elem) 771127149Sgad{ 772127539Sgad char *endp; 773127149Sgad long tempid; 774127149Sgad 775129917Sgad if (*elem == '\0') { 776129917Sgad warnx("Invalid (zero-length) process id"); 777129917Sgad optfatal = 1; 778129917Sgad return (0); /* Do not add this value. */ 779127149Sgad } 780127149Sgad 781129952Sgad errno = 0; 782129952Sgad tempid = strtol(elem, &endp, 10); 783129952Sgad if (*endp != '\0' || tempid < 0 || elem == endp) { 784129952Sgad warnx("Invalid %s: %s", inf->lname, elem); 785129952Sgad errno = ERANGE; 786244154Spjd } else if (errno != 0 || tempid > pid_max) { 787129952Sgad warnx("%s too large: %s", inf->lname, elem); 788129952Sgad errno = ERANGE; 789129952Sgad } 790129952Sgad if (errno == ERANGE) { 791129952Sgad optfatal = 1; 792129967Sgad return (0); 793129952Sgad } 794127499Sgad if (inf->count >= inf->maxcount) 795127499Sgad expand_list(inf); 796127823Sgad inf->l.pids[(inf->count)++] = tempid; 797127499Sgad return (1); 798127499Sgad} 799127149Sgad 800131010Sgad/*- 801131010Sgad * The user can specify a device via one of three formats: 802173492Sjhb * 1) fully qualified, e.g.: /dev/ttyp0 /dev/console /dev/pts/0 803173492Sjhb * 2) missing "/dev", e.g.: ttyp0 console pts/0 804173492Sjhb * 3) two-letters, e.g.: p0 co 0 805131010Sgad * (matching letters that would be seen in the "TT" column) 806131010Sgad */ 807127499Sgadstatic int 808127499Sgadaddelem_tty(struct listinfo *inf, const char *elem) 809127499Sgad{ 810127539Sgad const char *ttypath; 811127539Sgad struct stat sb; 812173492Sjhb char pathbuf[PATH_MAX], pathbuf2[PATH_MAX], pathbuf3[PATH_MAX]; 813127499Sgad 814131010Sgad ttypath = NULL; 815131209Sgad pathbuf2[0] = '\0'; 816173492Sjhb pathbuf3[0] = '\0'; 817131010Sgad switch (*elem) { 818131010Sgad case '/': 819127499Sgad ttypath = elem; 820131010Sgad break; 821131010Sgad case 'c': 822131010Sgad if (strcmp(elem, "co") == 0) { 823131010Sgad ttypath = _PATH_CONSOLE; 824131010Sgad break; 825131010Sgad } 826131010Sgad /* FALLTHROUGH */ 827131010Sgad default: 828131010Sgad strlcpy(pathbuf, _PATH_DEV, sizeof(pathbuf)); 829131010Sgad strlcat(pathbuf, elem, sizeof(pathbuf)); 830131010Sgad ttypath = pathbuf; 831131209Sgad if (strncmp(pathbuf, _PATH_TTY, strlen(_PATH_TTY)) == 0) 832131010Sgad break; 833173492Sjhb if (strncmp(pathbuf, _PATH_PTS, strlen(_PATH_PTS)) == 0) 834173492Sjhb break; 835131010Sgad if (strcmp(pathbuf, _PATH_CONSOLE) == 0) 836131010Sgad break; 837131209Sgad /* Check to see if /dev/tty${elem} exists */ 838131209Sgad strlcpy(pathbuf2, _PATH_TTY, sizeof(pathbuf2)); 839131209Sgad strlcat(pathbuf2, elem, sizeof(pathbuf2)); 840131209Sgad if (stat(pathbuf2, &sb) == 0 && S_ISCHR(sb.st_mode)) { 841131010Sgad /* No need to repeat stat() && S_ISCHR() checks */ 842192280Sbrian ttypath = NULL; 843131010Sgad break; 844131010Sgad } 845173492Sjhb /* Check to see if /dev/pts/${elem} exists */ 846173492Sjhb strlcpy(pathbuf3, _PATH_PTS, sizeof(pathbuf3)); 847173492Sjhb strlcat(pathbuf3, elem, sizeof(pathbuf3)); 848173492Sjhb if (stat(pathbuf3, &sb) == 0 && S_ISCHR(sb.st_mode)) { 849173492Sjhb /* No need to repeat stat() && S_ISCHR() checks */ 850192280Sbrian ttypath = NULL; 851173492Sjhb break; 852173492Sjhb } 853131010Sgad break; 854127499Sgad } 855131010Sgad if (ttypath) { 856131010Sgad if (stat(ttypath, &sb) == -1) { 857173492Sjhb if (pathbuf3[0] != '\0') 858173492Sjhb warn("%s, %s, and %s", pathbuf3, pathbuf2, 859173492Sjhb ttypath); 860131209Sgad else 861131209Sgad warn("%s", ttypath); 862131010Sgad optfatal = 1; 863131010Sgad return (0); 864131010Sgad } 865131010Sgad if (!S_ISCHR(sb.st_mode)) { 866173492Sjhb if (pathbuf3[0] != '\0') 867173492Sjhb warnx("%s, %s, and %s: Not a terminal", 868173492Sjhb pathbuf3, pathbuf2, ttypath); 869131209Sgad else 870131209Sgad warnx("%s: Not a terminal", ttypath); 871131010Sgad optfatal = 1; 872131010Sgad return (0); 873131010Sgad } 874127499Sgad } 875127499Sgad if (inf->count >= inf->maxcount) 876127499Sgad expand_list(inf); 877127823Sgad inf->l.ttys[(inf->count)++] = sb.st_rdev; 878127499Sgad return (1); 879127149Sgad} 880127149Sgad 881127499Sgadstatic int 882127499Sgadaddelem_uid(struct listinfo *inf, const char *elem) 88366377Sbrian{ 88466377Sbrian struct passwd *pwd; 885127539Sgad char *endp; 886127602Sgad u_long bigtemp; 88766377Sbrian 888127499Sgad if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) { 889127499Sgad if (*elem == '\0') 890127499Sgad warnx("Invalid (zero-length) %s name", inf->lname); 891127499Sgad else 892127499Sgad warnx("%s name too long: %s", inf->lname, elem); 893127499Sgad optfatal = 1; 894127542Sgad return (0); /* Do not add this value. */ 895127499Sgad } 89666377Sbrian 897127499Sgad pwd = getpwnam(elem); 898127499Sgad if (pwd == NULL) { 899127499Sgad errno = 0; 900127602Sgad bigtemp = strtoul(elem, &endp, 10); 901127602Sgad if (errno != 0 || *endp != '\0' || bigtemp > UID_MAX) 902127499Sgad warnx("No %s named '%s'", inf->lname, elem); 903127499Sgad else { 904127499Sgad /* The string is all digits, so it might be a userID. */ 905127602Sgad pwd = getpwuid((uid_t)bigtemp); 906127499Sgad if (pwd == NULL) 907127499Sgad warnx("No %s name or ID matches '%s'", 908127499Sgad inf->lname, elem); 90966377Sbrian } 910127499Sgad } 911127499Sgad if (pwd == NULL) { 912127509Sgad /* 913127509Sgad * These used to be treated as minor warnings (and the 914127509Sgad * option was simply ignored), but now they are fatal 915127509Sgad * errors (and the command will be aborted). 916127509Sgad */ 917127509Sgad optfatal = 1; 918129967Sgad return (0); 919127499Sgad } 920127499Sgad if (inf->count >= inf->maxcount) 921127499Sgad expand_list(inf); 922127823Sgad inf->l.uids[(inf->count)++] = pwd->pw_uid; 923127499Sgad return (1); 924127499Sgad} 925127499Sgad 926127499Sgadstatic void 927127499Sgadadd_list(struct listinfo *inf, const char *argp) 928127499Sgad{ 929127499Sgad const char *savep; 930127499Sgad char *cp, *endp; 931127499Sgad int toolong; 932127539Sgad char elemcopy[PATH_MAX]; 933127499Sgad 934239883Semaste if (*argp == '\0') 935239883Semaste inf->addelem(inf, argp); 936127499Sgad while (*argp != '\0') { 937127499Sgad while (*argp != '\0' && strchr(W_SEP, *argp) != NULL) 938127499Sgad argp++; 939127499Sgad savep = argp; 940127499Sgad toolong = 0; 941127499Sgad cp = elemcopy; 942127499Sgad if (strchr(T_SEP, *argp) == NULL) { 943127499Sgad endp = elemcopy + sizeof(elemcopy) - 1; 944127499Sgad while (*argp != '\0' && cp <= endp && 945127499Sgad strchr(W_SEP T_SEP, *argp) == NULL) 946127499Sgad *cp++ = *argp++; 947127499Sgad if (cp > endp) 948127499Sgad toolong = 1; 94966377Sbrian } 950127499Sgad if (!toolong) { 951127499Sgad *cp = '\0'; 952127542Sgad /* 953129953Sgad * Add this single element to the given list. 954127542Sgad */ 955127499Sgad inf->addelem(inf, elemcopy); 956127499Sgad } else { 957127499Sgad /* 958127499Sgad * The string is too long to copy. Find the end 959127499Sgad * of the string to print out the warning message. 960127499Sgad */ 961127499Sgad while (*argp != '\0' && strchr(W_SEP T_SEP, 962127499Sgad *argp) == NULL) 963127499Sgad argp++; 964127499Sgad warnx("Value too long: %.*s", (int)(argp - savep), 965127499Sgad savep); 966127499Sgad optfatal = 1; 96766377Sbrian } 968127499Sgad /* 969127499Sgad * Skip over any number of trailing whitespace characters, 970127499Sgad * but only one (at most) trailing element-terminating 971127499Sgad * character. 972127499Sgad */ 973127499Sgad while (*argp != '\0' && strchr(W_SEP, *argp) != NULL) 974127499Sgad argp++; 975127499Sgad if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) { 976127499Sgad argp++; 977127499Sgad /* Catch case where string ended with a comma. */ 978127499Sgad if (*argp == '\0') 979127499Sgad inf->addelem(inf, argp); 980127499Sgad } 98166377Sbrian } 982127499Sgad} 98366377Sbrian 984192239Sbrianstatic void 985192239Sbriandescendant_sort(KINFO *ki, int items) 986192239Sbrian{ 987192239Sbrian int dst, lvl, maxlvl, n, ndst, nsrc, siblings, src; 988192239Sbrian unsigned char *path; 989192239Sbrian KINFO kn; 990192239Sbrian 991192239Sbrian /* 992192239Sbrian * First, sort the entries by descendancy, tracking the descendancy 993192239Sbrian * depth in the ki_d.level field. 994192239Sbrian */ 995192239Sbrian src = 0; 996192239Sbrian maxlvl = 0; 997192239Sbrian while (src < items) { 998192239Sbrian if (ki[src].ki_d.level) { 999192239Sbrian src++; 1000192239Sbrian continue; 1001192239Sbrian } 1002192239Sbrian for (nsrc = 1; src + nsrc < items; nsrc++) 1003192239Sbrian if (!ki[src + nsrc].ki_d.level) 1004192239Sbrian break; 1005192239Sbrian 1006192239Sbrian for (dst = 0; dst < items; dst++) { 1007192239Sbrian if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_pid) 1008192239Sbrian continue; 1009192239Sbrian if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_ppid) 1010192239Sbrian break; 1011192239Sbrian } 1012192239Sbrian 1013192239Sbrian if (dst == items) { 1014192239Sbrian src += nsrc; 1015192239Sbrian continue; 1016192239Sbrian } 1017192239Sbrian 1018192239Sbrian for (ndst = 1; dst + ndst < items; ndst++) 1019192239Sbrian if (ki[dst + ndst].ki_d.level <= ki[dst].ki_d.level) 1020192239Sbrian break; 1021192239Sbrian 1022192239Sbrian for (n = src; n < src + nsrc; n++) { 1023192239Sbrian ki[n].ki_d.level += ki[dst].ki_d.level + 1; 1024192239Sbrian if (maxlvl < ki[n].ki_d.level) 1025192239Sbrian maxlvl = ki[n].ki_d.level; 1026192239Sbrian } 1027192239Sbrian 1028192239Sbrian while (nsrc) { 1029192239Sbrian if (src < dst) { 1030192239Sbrian kn = ki[src]; 1031192239Sbrian memmove(ki + src, ki + src + 1, 1032192239Sbrian (dst - src + ndst - 1) * sizeof *ki); 1033192239Sbrian ki[dst + ndst - 1] = kn; 1034192239Sbrian nsrc--; 1035192239Sbrian dst--; 1036192239Sbrian ndst++; 1037192239Sbrian } else if (src != dst + ndst) { 1038192239Sbrian kn = ki[src]; 1039192239Sbrian memmove(ki + dst + ndst + 1, ki + dst + ndst, 1040192239Sbrian (src - dst - ndst) * sizeof *ki); 1041192239Sbrian ki[dst + ndst] = kn; 1042192239Sbrian ndst++; 1043192239Sbrian nsrc--; 1044192239Sbrian src++; 1045192239Sbrian } else { 1046192239Sbrian ndst += nsrc; 1047192239Sbrian src += nsrc; 1048192239Sbrian nsrc = 0; 1049192239Sbrian } 1050192239Sbrian } 1051192239Sbrian } 1052192239Sbrian 1053192239Sbrian /* 1054192239Sbrian * Now populate ki_d.prefix (instead of ki_d.level) with the command 1055192239Sbrian * prefix used to show descendancies. 1056192239Sbrian */ 1057192239Sbrian path = malloc((maxlvl + 7) / 8); 1058192239Sbrian memset(path, '\0', (maxlvl + 7) / 8); 1059192239Sbrian for (src = 0; src < items; src++) { 1060192239Sbrian if ((lvl = ki[src].ki_d.level) == 0) { 1061192239Sbrian ki[src].ki_d.prefix = NULL; 1062192239Sbrian continue; 1063192239Sbrian } 1064192239Sbrian if ((ki[src].ki_d.prefix = malloc(lvl * 2 + 1)) == NULL) 1065192239Sbrian errx(1, "malloc failed"); 1066192239Sbrian for (n = 0; n < lvl - 2; n++) { 1067192239Sbrian ki[src].ki_d.prefix[n * 2] = 1068192239Sbrian path[n / 8] & 1 << (n % 8) ? '|' : ' '; 1069192239Sbrian ki[src].ki_d.prefix[n * 2 + 1] = ' '; 1070192239Sbrian } 1071192239Sbrian if (n == lvl - 2) { 1072192239Sbrian /* Have I any more siblings? */ 1073192239Sbrian for (siblings = 0, dst = src + 1; dst < items; dst++) { 1074192239Sbrian if (ki[dst].ki_d.level > lvl) 1075192239Sbrian continue; 1076192239Sbrian if (ki[dst].ki_d.level == lvl) 1077192239Sbrian siblings = 1; 1078192239Sbrian break; 1079192239Sbrian } 1080192239Sbrian if (siblings) 1081192239Sbrian path[n / 8] |= 1 << (n % 8); 1082192239Sbrian else 1083192239Sbrian path[n / 8] &= ~(1 << (n % 8)); 1084192239Sbrian ki[src].ki_d.prefix[n * 2] = siblings ? '|' : '`'; 1085192239Sbrian ki[src].ki_d.prefix[n * 2 + 1] = '-'; 1086192239Sbrian n++; 1087192239Sbrian } 1088192239Sbrian strcpy(ki[src].ki_d.prefix + n * 2, "- "); 1089192239Sbrian } 1090192239Sbrian free(path); 1091192239Sbrian} 1092192239Sbrian 1093127499Sgadstatic void * 1094127499Sgadexpand_list(struct listinfo *inf) 1095127499Sgad{ 1096127539Sgad void *newlist; 1097127499Sgad int newmax; 109866377Sbrian 1099127499Sgad newmax = (inf->maxcount + 1) << 1; 1100127823Sgad newlist = realloc(inf->l.ptr, newmax * inf->elemsize); 1101127499Sgad if (newlist == NULL) { 1102127823Sgad free(inf->l.ptr); 1103129967Sgad errx(1, "realloc to %d %ss failed", newmax, inf->lname); 1104127499Sgad } 1105127499Sgad inf->maxcount = newmax; 1106127823Sgad inf->l.ptr = newlist; 1107127499Sgad 1108127499Sgad return (newlist); 110966377Sbrian} 111066377Sbrian 1111127499Sgadstatic void 1112127499Sgadfree_list(struct listinfo *inf) 1113127499Sgad{ 1114127499Sgad 1115127499Sgad inf->count = inf->elemsize = inf->maxcount = 0; 1116127823Sgad if (inf->l.ptr != NULL) 1117127823Sgad free(inf->l.ptr); 1118127499Sgad inf->addelem = NULL; 1119127499Sgad inf->lname = NULL; 1120127823Sgad inf->l.ptr = NULL; 1121127499Sgad} 1122127499Sgad 1123127499Sgadstatic void 1124127499Sgadinit_list(struct listinfo *inf, addelem_rtn artn, int elemsize, 1125127499Sgad const char *lname) 1126127499Sgad{ 1127127499Sgad 1128127499Sgad inf->count = inf->maxcount = 0; 1129127499Sgad inf->elemsize = elemsize; 1130127499Sgad inf->addelem = artn; 1131127499Sgad inf->lname = lname; 1132127823Sgad inf->l.ptr = NULL; 1133127499Sgad} 1134127499Sgad 1135109502SjmallettVARENT * 1136109502Sjmallettfind_varentry(VAR *v) 1137109502Sjmallett{ 1138109502Sjmallett struct varent *vent; 1139109502Sjmallett 1140130999Sgad STAILQ_FOREACH(vent, &varlist, next_ve) { 1141109502Sjmallett if (strcmp(vent->var->name, v->name) == 0) 1142109502Sjmallett return vent; 1143109502Sjmallett } 1144109502Sjmallett return NULL; 1145109502Sjmallett} 1146109502Sjmallett 11471556Srgrimesstatic void 114890110Simpscanvars(void) 11491556Srgrimes{ 11501556Srgrimes struct varent *vent; 11511556Srgrimes VAR *v; 115225271Sjkh 1153130999Sgad STAILQ_FOREACH(vent, &varlist, next_ve) { 115425271Sjkh v = vent->var; 115525271Sjkh if (v->flag & USER) 115625271Sjkh needuser = 1; 115725271Sjkh if (v->flag & COMM) 115825271Sjkh needcomm = 1; 115925271Sjkh } 116025271Sjkh} 116125271Sjkh 116225271Sjkhstatic void 1163225868Straszformat_output(KINFO *ki) 116425271Sjkh{ 116525271Sjkh struct varent *vent; 116625271Sjkh VAR *v; 1167225868Strasz KINFO_STR *ks; 1168225868Strasz char *str; 1169225868Strasz int len; 11701556Srgrimes 1171225868Strasz STAILQ_INIT(&ki->ki_ks); 1172130999Sgad STAILQ_FOREACH(vent, &varlist, next_ve) { 11731556Srgrimes v = vent->var; 1174225868Strasz str = (v->oproc)(ki, vent); 1175225868Strasz ks = malloc(sizeof(*ks)); 1176225868Strasz if (ks == NULL) 1177225868Strasz errx(1, "malloc failed"); 1178225868Strasz ks->ks_str = str; 1179225868Strasz STAILQ_INSERT_TAIL(&ki->ki_ks, ks, ks_next); 1180225868Strasz if (str != NULL) { 1181225868Strasz len = strlen(str); 1182225868Strasz } else 1183225868Strasz len = 1; /* "-" */ 1184225868Strasz if (v->width < len) 1185225868Strasz v->width = len; 118625271Sjkh } 118725271Sjkh} 118825271Sjkh 118925271Sjkhstatic void 119090110Simpsizevars(void) 119125271Sjkh{ 119225271Sjkh struct varent *vent; 119325271Sjkh VAR *v; 119425271Sjkh int i; 119525271Sjkh 1196130999Sgad STAILQ_FOREACH(vent, &varlist, next_ve) { 119725271Sjkh v = vent->var; 1198109504Sjmallett i = strlen(vent->header); 11991556Srgrimes if (v->width < i) 12001556Srgrimes v->width = i; 12011556Srgrimes } 12021556Srgrimes} 12031556Srgrimes 120490143Smarkmstatic const char * 120590110Simpfmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki, 1206245610Sjhb char *comm, char *thread, int maxlen) 12071556Srgrimes{ 120890143Smarkm const char *s; 12091556Srgrimes 1210245610Sjhb s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, 1211245635Sjhb showthreads && ki->ki_p->ki_numthreads > 1 ? thread : NULL, maxlen); 12121556Srgrimes return (s); 12131556Srgrimes} 12141556Srgrimes 1215172207Sjeff#define UREADOK(ki) (forceuread || (ki->ki_p->ki_flag & P_INMEM)) 121631552Sdyson 12171556Srgrimesstatic void 121890110Simpsaveuser(KINFO *ki) 12191556Srgrimes{ 12201556Srgrimes 1221172207Sjeff if (ki->ki_p->ki_flag & P_INMEM) { 12221556Srgrimes /* 12231556Srgrimes * The u-area might be swapped out, and we can't get 12241556Srgrimes * at it because we have a crashdump and no swap. 12251556Srgrimes * If it's here fill in these fields, otherwise, just 12261556Srgrimes * leave them 0. 12271556Srgrimes */ 122869896Smckusick ki->ki_valid = 1; 12291556Srgrimes } else 123069896Smckusick ki->ki_valid = 0; 12311556Srgrimes /* 12321556Srgrimes * save arguments if needed 12331556Srgrimes */ 1234130991Sgad if (needcomm) { 1235130991Sgad if (ki->ki_p->ki_stat == SZOMB) 1236130991Sgad ki->ki_args = strdup("<defunct>"); 1237130991Sgad else if (UREADOK(ki) || (ki->ki_p->ki_args != NULL)) 1238130991Sgad ki->ki_args = strdup(fmt(kvm_getargv, ki, 1239245610Sjhb ki->ki_p->ki_comm, ki->ki_p->ki_tdname, MAXCOMLEN)); 1240130991Sgad else 1241130991Sgad asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm); 1242131024Sgad if (ki->ki_args == NULL) 1243130991Sgad errx(1, "malloc failed"); 124453276Speter } else { 124553276Speter ki->ki_args = NULL; 124653276Speter } 1247130991Sgad if (needenv) { 1248130991Sgad if (UREADOK(ki)) 1249130991Sgad ki->ki_env = strdup(fmt(kvm_getenvv, ki, 1250245610Sjhb (char *)NULL, (char *)NULL, 0)); 1251130991Sgad else 1252130991Sgad ki->ki_env = strdup("()"); 1253130991Sgad if (ki->ki_env == NULL) 1254130991Sgad errx(1, "malloc failed"); 125553276Speter } else { 125653276Speter ki->ki_env = NULL; 125753276Speter } 12581556Srgrimes} 12591556Srgrimes 1260130816Sgad/* A macro used to improve the readability of pscomp(). */ 1261130816Sgad#define DIFF_RETURN(a, b, field) do { \ 1262130816Sgad if ((a)->field != (b)->field) \ 1263130816Sgad return (((a)->field < (b)->field) ? -1 : 1); \ 1264130816Sgad} while (0) 1265130816Sgad 12661556Srgrimesstatic int 126790110Simppscomp(const void *a, const void *b) 12681556Srgrimes{ 1269127596Sgad const KINFO *ka, *kb; 12701556Srgrimes 1271127596Sgad ka = a; 1272127596Sgad kb = b; 1273127596Sgad /* SORTCPU and SORTMEM are sorted in descending order. */ 1274130816Sgad if (sortby == SORTCPU) 1275130816Sgad DIFF_RETURN(kb, ka, ki_pcpu); 1276130816Sgad if (sortby == SORTMEM) 1277130816Sgad DIFF_RETURN(kb, ka, ki_memsize); 1278127596Sgad /* 1279127596Sgad * TTY's are sorted in ascending order, except that all NODEV 1280127596Sgad * processes come before all processes with a device. 1281127596Sgad */ 1282130816Sgad if (ka->ki_p->ki_tdev != kb->ki_p->ki_tdev) { 1283130816Sgad if (ka->ki_p->ki_tdev == NODEV) 1284130816Sgad return (-1); 1285130816Sgad if (kb->ki_p->ki_tdev == NODEV) 1286130816Sgad return (1); 1287130816Sgad DIFF_RETURN(ka, kb, ki_p->ki_tdev); 1288130816Sgad } 1289130816Sgad 1290130972Sgad /* PID's and TID's (threads) are sorted in ascending order. */ 1291130816Sgad DIFF_RETURN(ka, kb, ki_p->ki_pid); 1292130972Sgad DIFF_RETURN(ka, kb, ki_p->ki_tid); 1293127596Sgad return (0); 12941556Srgrimes} 1295130816Sgad#undef DIFF_RETURN 12961556Srgrimes 12971556Srgrimes/* 12981556Srgrimes * ICK (all for getopt), would rather hide the ugliness 12991556Srgrimes * here than taint the main code. 13001556Srgrimes * 13011556Srgrimes * ps foo -> ps -foo 13021556Srgrimes * ps 34 -> ps -p34 13031556Srgrimes * 13041556Srgrimes * The old convention that 't' with no trailing tty arg means the users 13051556Srgrimes * tty, is only supported if argv[1] doesn't begin with a '-'. This same 13061556Srgrimes * feature is available with the option 'T', which takes no argument. 13071556Srgrimes */ 13081556Srgrimesstatic char * 1309129915Sgadkludge_oldps_options(const char *optlist, char *origval, const char *nextarg) 13101556Srgrimes{ 13111556Srgrimes size_t len; 1312129914Sgad char *argp, *cp, *newopts, *ns, *optp, *pidp; 13131556Srgrimes 1314102886Sjmallett /* 1315129914Sgad * See if the original value includes any option which takes an 1316129914Sgad * argument (and will thus use up the remainder of the string). 1317102886Sjmallett */ 1318129914Sgad argp = NULL; 1319129914Sgad if (optlist != NULL) { 1320129914Sgad for (cp = origval; *cp != '\0'; cp++) { 1321129914Sgad optp = strchr(optlist, *cp); 1322129914Sgad if ((optp != NULL) && *(optp + 1) == ':') { 1323129914Sgad argp = cp; 1324129914Sgad break; 1325129914Sgad } 1326129914Sgad } 1327129914Sgad } 1328129914Sgad if (argp != NULL && *origval == '-') 1329129914Sgad return (origval); 1330102886Sjmallett 13311556Srgrimes /* 13321556Srgrimes * if last letter is a 't' flag with no argument (in the context 13331556Srgrimes * of the oldps options -- option string NOT starting with a '-' -- 13341556Srgrimes * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 133581743Sbrian * 1336129634Sgad * However, if a flag accepting a string argument is found earlier 1337129634Sgad * in the option string (including a possible `t' flag), then the 1338129634Sgad * remainder of the string must be the argument to that flag; so 1339129914Sgad * do not modify that argument. Note that a trailing `t' would 1340129914Sgad * cause argp to be set, if argp was not already set by some 1341129914Sgad * earlier option. 13421556Srgrimes */ 1343129914Sgad len = strlen(origval); 1344129914Sgad cp = origval + len - 1; 1345129914Sgad pidp = NULL; 1346129915Sgad if (*cp == 't' && *origval != '-' && cp == argp) { 1347129915Sgad if (nextarg == NULL || *nextarg == '-' || isdigitch(*nextarg)) 1348129915Sgad *cp = 'T'; 1349129915Sgad } else if (argp == NULL) { 13501556Srgrimes /* 1351129914Sgad * The original value did not include any option which takes 1352129914Sgad * an argument (and that would include `p' and `t'), so check 1353129914Sgad * the value for trailing number, or comma-separated list of 1354129914Sgad * numbers, which we will treat as a pid request. 13551556Srgrimes */ 1356129914Sgad if (isdigitch(*cp)) { 1357129914Sgad while (cp >= origval && (*cp == ',' || isdigitch(*cp))) 1358129914Sgad --cp; 1359129914Sgad pidp = cp + 1; 1360129914Sgad } 13611556Srgrimes } 1362129914Sgad 13631556Srgrimes /* 1364129914Sgad * If nothing needs to be added to the string, then return 1365129914Sgad * the "original" (although possibly modified) value. 13661556Srgrimes */ 1367129914Sgad if (*origval == '-' && pidp == NULL) 1368129914Sgad return (origval); 1369129914Sgad 1370129914Sgad /* 1371129914Sgad * Create a copy of the string to add '-' and/or 'p' to the 1372129914Sgad * original value. 1373129914Sgad */ 1374129914Sgad if ((newopts = ns = malloc(len + 3)) == NULL) 1375129914Sgad errx(1, "malloc failed"); 1376129914Sgad 1377129914Sgad if (*origval != '-') 1378129914Sgad *ns++ = '-'; /* add option flag */ 1379129914Sgad 1380129914Sgad if (pidp == NULL) 1381129914Sgad strcpy(ns, origval); 1382129914Sgad else { 1383129914Sgad /* 1384129914Sgad * Copy everything before the pid string, add the `p', 1385129914Sgad * and then copy the pid string. 1386129914Sgad */ 1387129914Sgad len = pidp - origval; 1388129914Sgad memcpy(ns, origval, len); 1389129914Sgad ns += len; 13901556Srgrimes *ns++ = 'p'; 1391129914Sgad strcpy(ns, pidp); 1392129914Sgad } 13931556Srgrimes 13941556Srgrimes return (newopts); 13951556Srgrimes} 13961556Srgrimes 13971556Srgrimesstatic void 1398244154Spjdpidmax_init(void) 1399244154Spjd{ 1400244154Spjd size_t intsize; 1401244154Spjd 1402244154Spjd intsize = sizeof(pid_max); 1403244154Spjd if (sysctlbyname("kern.pid_max", &pid_max, &intsize, NULL, 0) < 0) { 1404244154Spjd warn("unable to read kern.pid_max"); 1405244154Spjd pid_max = 99999; 1406244154Spjd } 1407244154Spjd} 1408244154Spjd 1409244154Spjdstatic void 141090110Simpusage(void) 14111556Srgrimes{ 1412195830Sbrian#define SINGLE_OPTS "[-aCcde" OPT_LAZY_f "HhjlmrSTuvwXxZ]" 14131556Srgrimes 1414127499Sgad (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 1415141578Sru "usage: ps " SINGLE_OPTS " [-O fmt | -o fmt] [-G gid[,gid...]]", 1416266279Sbdrewery " [-J jid[,jid...]] [-M core] [-N system]", 1417141578Sru " [-p pid[,pid...]] [-t tty[,tty...]] [-U user[,user...]]", 141826465Scharnier " ps [-L]"); 14191556Srgrimes exit(1); 14201556Srgrimes} 1421