ps.c revision 127602
1145516Sdarrenr/*- 2145516Sdarrenr * Copyright (c) 1990, 1993, 1994 3145516Sdarrenr * The Regents of the University of California. All rights reserved. 4145516Sdarrenr * 5145516Sdarrenr * Redistribution and use in source and binary forms, with or without 6145516Sdarrenr * modification, are permitted provided that the following conditions 7145516Sdarrenr * are met: 8145516Sdarrenr * 1. Redistributions of source code must retain the above copyright 9145516Sdarrenr * notice, this list of conditions and the following disclaimer. 10172776Sdarrenr * 2. Redistributions in binary form must reproduce the above copyright 11145516Sdarrenr * notice, this list of conditions and the following disclaimer in the 12145516Sdarrenr * documentation and/or other materials provided with the distribution. 13145516Sdarrenr * 3. All advertising materials mentioning features or use of this software 14145516Sdarrenr * must display the following acknowledgement: 15145516Sdarrenr * This product includes software developed by the University of 16145516Sdarrenr * California, Berkeley and its contributors. 17145516Sdarrenr * 4. Neither the name of the University nor the names of its contributors 18145516Sdarrenr * may be used to endorse or promote products derived from this software 19145516Sdarrenr * without specific prior written permission. 20145516Sdarrenr * 21145516Sdarrenr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22145516Sdarrenr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23145516Sdarrenr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24145516Sdarrenr * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25145516Sdarrenr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26145516Sdarrenr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27145516Sdarrenr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28145516Sdarrenr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29145516Sdarrenr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30145516Sdarrenr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31145516Sdarrenr * SUCH DAMAGE. 32145516Sdarrenr * ------+---------+---------+-------- + --------+---------+---------+---------* 33145516Sdarrenr * Copyright (c) 2004 - Garance Alistair Drosehn <gad@FreeBSD.org>. 34145516Sdarrenr * All rights reserved. 35145516Sdarrenr * 36145516Sdarrenr * Significant modifications made to bring `ps' options somewhat closer 37145516Sdarrenr * to the standard for `ps' as described in SingleUnixSpec-v3. 38145516Sdarrenr * ------+---------+---------+-------- + --------+---------+---------+---------* 39145516Sdarrenr */ 40145516Sdarrenr 41145516Sdarrenr#ifndef lint 42145516Sdarrenrstatic const char copyright[] = 43145516Sdarrenr"@(#) Copyright (c) 1990, 1993, 1994\n\ 44145516Sdarrenr The Regents of the University of California. All rights reserved.\n"; 45145516Sdarrenr#endif /* not lint */ 46145516Sdarrenr 47145516Sdarrenr#if 0 48145516Sdarrenr#ifndef lint 49145516Sdarrenrstatic char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94"; 50145516Sdarrenr#endif /* not lint */ 51145516Sdarrenr#endif 52145516Sdarrenr 53145516Sdarrenr#include <sys/cdefs.h> 54145516Sdarrenr__FBSDID("$FreeBSD: head/bin/ps/ps.c 127602 2004-03-30 04:20:33Z gad $"); 55145516Sdarrenr 56145516Sdarrenr#include <sys/param.h> 57145516Sdarrenr#include <sys/proc.h> 58145516Sdarrenr#include <sys/user.h> 59145516Sdarrenr#include <sys/stat.h> 60170268Sdarrenr#include <sys/ioctl.h> 61170268Sdarrenr#include <sys/sysctl.h> 62170268Sdarrenr 63170268Sdarrenr#include <ctype.h> 64170268Sdarrenr#include <err.h> 65145516Sdarrenr#include <errno.h> 66145516Sdarrenr#include <fcntl.h> 67145516Sdarrenr#include <grp.h> 68145516Sdarrenr#include <kvm.h> 69170268Sdarrenr#include <limits.h> 70170268Sdarrenr#include <locale.h> 71170268Sdarrenr#include <paths.h> 72145516Sdarrenr#include <pwd.h> 73145516Sdarrenr#include <stdint.h> 74145516Sdarrenr#include <stdio.h> 75145516Sdarrenr#include <stdlib.h> 76145516Sdarrenr#include <string.h> 77145516Sdarrenr#include <unistd.h> 78145516Sdarrenr 79145516Sdarrenr#include "ps.h" 80145516Sdarrenr 81145516Sdarrenr#define W_SEP " \t" /* "Whitespace" list separators */ 82145516Sdarrenr#define T_SEP "," /* "Terminate-element" list separators */ 83145516Sdarrenr 84145516Sdarrenr#ifdef LAZY_PS 85145516Sdarrenr#define DEF_UREAD 0 86145516Sdarrenr#define OPT_LAZY_f "f" 87145516Sdarrenr#else 88145516Sdarrenr#define DEF_UREAD 1 /* Always do the more-expensive read. */ 89145516Sdarrenr#define OPT_LAZY_f /* I.e., the `-f' option is not added. */ 90145516Sdarrenr#endif 91145516Sdarrenr 92145516Sdarrenrint cflag; /* -c */ 93145516Sdarrenrint eval; /* Exit value */ 94145516Sdarrenrtime_t now; /* Current time(3) value */ 95145516Sdarrenrint rawcpu; /* -C */ 96145516Sdarrenrint sumrusage; /* -S */ 97145516Sdarrenrint termwidth; /* Width of the screen (0 == infinity). */ 98145516Sdarrenrint totwidth; /* Calculated-width of requested variables. */ 99145516Sdarrenr 100145516Sdarrenrstruct varent *vhead; 101145516Sdarrenr 102145516Sdarrenrstatic int forceuread = DEF_UREAD; /* Do extra work to get u-area. */ 103145516Sdarrenrstatic kvm_t *kd; 104145516Sdarrenrstatic KINFO *kinfo; 105145516Sdarrenrstatic int needcomm; /* -o "command" */ 106145516Sdarrenrstatic int needenv; /* -e */ 107145516Sdarrenrstatic int needuser; /* -o "user" */ 108145516Sdarrenrstatic int optfatal; /* Fatal error parsing some list-option. */ 109145516Sdarrenr 110145516Sdarrenrstatic enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 111145516Sdarrenr 112145516Sdarrenrstruct listinfo; 113145516Sdarrenrtypedef int addelem_rtn(struct listinfo *_inf, const char *_elem); 114145516Sdarrenr 115145516Sdarrenrstruct listinfo { 116145516Sdarrenr int count; 117145516Sdarrenr int maxcount; 118145516Sdarrenr int elemsize; 119145516Sdarrenr addelem_rtn *addelem; 120145516Sdarrenr const char *lname; 121145516Sdarrenr union { 122145516Sdarrenr gid_t *gids; 123151897Srwatson pid_t *pids; 124145516Sdarrenr dev_t *ttys; 125145516Sdarrenr uid_t *uids; 126145516Sdarrenr void *ptr; 127145516Sdarrenr }; 128145516Sdarrenr}; 129145516Sdarrenr 130145516Sdarrenrstatic int addelem_gid(struct listinfo *, const char *); 131145516Sdarrenrstatic int addelem_pid(struct listinfo *, const char *); 132145516Sdarrenrstatic int addelem_tty(struct listinfo *, const char *); 133145516Sdarrenrstatic int addelem_uid(struct listinfo *, const char *); 134145516Sdarrenrstatic void add_list(struct listinfo *, const char *); 135145516Sdarrenrstatic void dynsizevars(KINFO *); 136170268Sdarrenrstatic void *expand_list(struct listinfo *); 137145516Sdarrenrstatic const char * 138145516Sdarrenr fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int), 139145516Sdarrenr KINFO *, char *, int); 140145516Sdarrenrstatic void free_list(struct listinfo *); 141145516Sdarrenrstatic void init_list(struct listinfo *, addelem_rtn, int, const char *); 142145516Sdarrenrstatic char *kludge_oldps_options(char *); 143145516Sdarrenrstatic int pscomp(const void *, const void *); 144161356Sguidostatic void saveuser(KINFO *); 145145516Sdarrenrstatic void scanvars(void); 146145516Sdarrenrstatic void sizevars(void); 147145516Sdarrenrstatic void usage(void); 148145516Sdarrenr 149145516Sdarrenrstatic char dfmt[] = "pid,tt,state,time,command"; 150145516Sdarrenrstatic char jfmt[] = "user,pid,ppid,pgid,jobc,state,tt,time,command"; 151145516Sdarrenrstatic char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state," 152145516Sdarrenr "tt,time,command"; 153145516Sdarrenrstatic char o1[] = "pid"; 154145516Sdarrenrstatic char o2[] = "tt,state,time,command"; 155145516Sdarrenrstatic char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command"; 156145516Sdarrenrstatic char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz," 157145516Sdarrenr "%cpu,%mem,command"; 158145516Sdarrenrstatic char Zfmt[] = "label"; 159153876Sguido 160153876Sguido#define PS_ARGS "AaCc" OPT_LAZY_f "G:gHhjLlM:mN:O:o:p:rSTt:U:uvwXxZ" 161153876Sguido 162153876Sguidoint 163153876Sguidomain(int argc, char *argv[]) 164153876Sguido{ 165153876Sguido struct listinfo gidlist, pgrplist, pidlist; 166153876Sguido struct listinfo ruidlist, sesslist, ttylist, uidlist; 167153876Sguido struct kinfo_proc *kp; 168153876Sguido struct varent *vent; 169153876Sguido struct winsize ws; 170153876Sguido const char *cp, *nlistf, *memf; 171153876Sguido char *cols; 172145516Sdarrenr int all, ch, dropgid, elem, flag, _fmt, i, lineno; 173145516Sdarrenr int nentries, nocludge, nkept, nselectors; 174145516Sdarrenr int prtheader, showthreads, wflag, what, xkeep, xkeep_implied; 175145516Sdarrenr char errbuf[_POSIX2_LINE_MAX]; 176145516Sdarrenr 177145516Sdarrenr (void) setlocale(LC_ALL, ""); 178145516Sdarrenr time(&now); /* Used by routines in print.c. */ 179145516Sdarrenr 180145516Sdarrenr if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') 181145516Sdarrenr termwidth = atoi(cols); 182145516Sdarrenr else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 183145516Sdarrenr ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 184145516Sdarrenr ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || 185145516Sdarrenr ws.ws_col == 0) 186145516Sdarrenr termwidth = 79; 187145516Sdarrenr else 188145516Sdarrenr termwidth = ws.ws_col - 1; 189145516Sdarrenr 190145516Sdarrenr /* 191145516Sdarrenr * Don't apply a kludge if the first argument is an option taking an 192145516Sdarrenr * argument 193145516Sdarrenr */ 194145516Sdarrenr if (argc > 1) { 195145516Sdarrenr nocludge = 0; 196145516Sdarrenr if (argv[1][0] == '-') { 197145516Sdarrenr for (cp = PS_ARGS; *cp != '\0'; cp++) { 198145516Sdarrenr if (*cp != ':') 199145516Sdarrenr continue; 200145516Sdarrenr if (*(cp - 1) == argv[1][1]) { 201145516Sdarrenr nocludge = 1; 202145516Sdarrenr break; 203170268Sdarrenr } 204145516Sdarrenr } 205145516Sdarrenr } 206145516Sdarrenr if (nocludge == 0) 207145516Sdarrenr argv[1] = kludge_oldps_options(argv[1]); 208145516Sdarrenr } 209145516Sdarrenr 210145516Sdarrenr all = dropgid = _fmt = nselectors = optfatal = 0; 211145516Sdarrenr prtheader = showthreads = wflag = xkeep_implied = 0; 212145516Sdarrenr xkeep = -1; /* Neither -x nor -X. */ 213145516Sdarrenr init_list(&gidlist, addelem_gid, sizeof(gid_t), "group"); 214145516Sdarrenr init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group"); 215145516Sdarrenr init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id"); 216145516Sdarrenr init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser"); 217145516Sdarrenr init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id"); 218170268Sdarrenr init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty"); 219145516Sdarrenr init_list(&uidlist, addelem_uid, sizeof(uid_t), "user"); 220145516Sdarrenr memf = nlistf = _PATH_DEVNULL; 221145516Sdarrenr while ((ch = getopt(argc, argv, PS_ARGS)) != -1) 222145516Sdarrenr switch((char)ch) { 223145516Sdarrenr case 'A': 224145516Sdarrenr /* 225145516Sdarrenr * Exactly the same as `-ax'. This has been 226145516Sdarrenr * added for compatability with SUSv3, but for 227145516Sdarrenr * now it will not be described in the man page. 228145516Sdarrenr */ 229145516Sdarrenr nselectors++; 230145516Sdarrenr all = xkeep = 1; 231145516Sdarrenr break; 232161356Sguido case 'a': 233145516Sdarrenr nselectors++; 234145516Sdarrenr all = 1; 235145516Sdarrenr break; 236145516Sdarrenr case 'C': 237145516Sdarrenr rawcpu = 1; 238145516Sdarrenr break; 239145516Sdarrenr case 'c': 240145516Sdarrenr cflag = 1; 241145516Sdarrenr break; 242145516Sdarrenr case 'e': /* XXX set ufmt */ 243145516Sdarrenr needenv = 1; 244145516Sdarrenr break; 245145516Sdarrenr#ifdef LAZY_PS 246145516Sdarrenr case 'f': 247145516Sdarrenr if (getuid() == 0 || getgid() == 0) 248145516Sdarrenr forceuread = 1; 249145516Sdarrenr break; 250145516Sdarrenr#endif 251145516Sdarrenr case 'G': 252145516Sdarrenr add_list(&gidlist, optarg); 253145516Sdarrenr xkeep_implied = 1; 254170268Sdarrenr nselectors++; 255145516Sdarrenr break; 256145516Sdarrenr case 'g': 257145516Sdarrenr#if 0 258145516Sdarrenr /*- 259145516Sdarrenr * XXX - This SUSv3 behavior is still under debate 260145516Sdarrenr * since it conflicts with the (undocumented) 261145516Sdarrenr * `-g' option. So we skip it for now. 262145516Sdarrenr */ 263145516Sdarrenr add_list(&pgrplist, optarg); 264145516Sdarrenr xkeep_implied = 1; 265145516Sdarrenr nselectors++; 266145516Sdarrenr break; 267145516Sdarrenr#else 268145516Sdarrenr /* The historical BSD-ish (from SunOS) behavior. */ 269145516Sdarrenr break; /* no-op */ 270145516Sdarrenr#endif 271145516Sdarrenr case 'H': 272145516Sdarrenr showthreads = KERN_PROC_INC_THREAD; 273145516Sdarrenr break; 274145516Sdarrenr case 'h': 275145516Sdarrenr prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 276145516Sdarrenr break; 277145516Sdarrenr case 'j': 278145516Sdarrenr parsefmt(jfmt, 0); 279145516Sdarrenr _fmt = 1; 280145516Sdarrenr jfmt[0] = '\0'; 281145516Sdarrenr break; 282145516Sdarrenr case 'L': 283145516Sdarrenr showkey(); 284145516Sdarrenr exit(0); 285145516Sdarrenr case 'l': 286145516Sdarrenr parsefmt(lfmt, 0); 287145516Sdarrenr _fmt = 1; 288145516Sdarrenr lfmt[0] = '\0'; 289170268Sdarrenr break; 290145516Sdarrenr case 'M': 291145516Sdarrenr memf = optarg; 292145516Sdarrenr dropgid = 1; 293145516Sdarrenr break; 294145516Sdarrenr case 'm': 295145516Sdarrenr sortby = SORTMEM; 296145516Sdarrenr break; 297145516Sdarrenr case 'N': 298145516Sdarrenr nlistf = optarg; 299145516Sdarrenr dropgid = 1; 300145516Sdarrenr break; 301145516Sdarrenr case 'O': 302145516Sdarrenr parsefmt(o1, 1); 303145516Sdarrenr parsefmt(optarg, 1); 304145516Sdarrenr parsefmt(o2, 1); 305145516Sdarrenr o1[0] = o2[0] = '\0'; 306145516Sdarrenr _fmt = 1; 307170268Sdarrenr break; 308170268Sdarrenr case 'o': 309170268Sdarrenr parsefmt(optarg, 1); 310170268Sdarrenr _fmt = 1; 311170268Sdarrenr break; 312145516Sdarrenr case 'p': 313145516Sdarrenr add_list(&pidlist, optarg); 314170268Sdarrenr /* 315145516Sdarrenr * Note: `-p' does not *set* xkeep, but any values 316145516Sdarrenr * from pidlist are checked before xkeep is. That 317145516Sdarrenr * way they are always matched, even if the user 318145516Sdarrenr * specifies `-X'. 319145516Sdarrenr */ 320145516Sdarrenr nselectors++; 321145516Sdarrenr break; 322145516Sdarrenr#if 0 323145516Sdarrenr case 'R': 324145516Sdarrenr /*- 325145516Sdarrenr * XXX - This un-standard option is still under 326145516Sdarrenr * debate. This is what SUSv3 defines as 327145516Sdarrenr * the `-U' option, and while it would be 328170268Sdarrenr * nice to have, it could cause even more 329170268Sdarrenr * confusion to implement it as `-R'. 330145516Sdarrenr */ 331145516Sdarrenr add_list(&ruidlist, optarg); 332153876Sguido xkeep_implied = 1; 333145516Sdarrenr nselectors++; 334145516Sdarrenr break; 335145516Sdarrenr#endif 336145516Sdarrenr case 'r': 337145516Sdarrenr sortby = SORTCPU; 338145516Sdarrenr break; 339145516Sdarrenr case 'S': 340145516Sdarrenr sumrusage = 1; 341145516Sdarrenr break; 342145516Sdarrenr#if 0 343145516Sdarrenr case 's': 344173181Sdarrenr /*- 345145516Sdarrenr * XXX - This non-standard option is still under 346145516Sdarrenr * debate. This *is* supported on Solaris, 347145516Sdarrenr * Linux, and IRIX, but conflicts with `-s' 348145516Sdarrenr * on NetBSD and maybe some older BSD's. 349145516Sdarrenr */ 350145516Sdarrenr add_list(&sesslist, optarg); 351170268Sdarrenr xkeep_implied = 1; 352145516Sdarrenr nselectors++; 353145516Sdarrenr break; 354145516Sdarrenr#endif 355145516Sdarrenr case 'T': 356145516Sdarrenr if ((optarg = ttyname(STDIN_FILENO)) == NULL) 357145516Sdarrenr errx(1, "stdin: not a terminal"); 358161356Sguido /* FALLTHROUGH */ 359145516Sdarrenr case 't': 360145516Sdarrenr add_list(&ttylist, optarg); 361145516Sdarrenr xkeep_implied = 1; 362145516Sdarrenr nselectors++; 363145516Sdarrenr break; 364145516Sdarrenr case 'U': 365145516Sdarrenr /* This is what SUSv3 defines as the `-u' option. */ 366145516Sdarrenr add_list(&uidlist, optarg); 367145516Sdarrenr xkeep_implied = 1; 368145516Sdarrenr nselectors++; 369145516Sdarrenr break; 370145516Sdarrenr case 'u': 371145516Sdarrenr parsefmt(ufmt, 0); 372145516Sdarrenr sortby = SORTCPU; 373145516Sdarrenr _fmt = 1; 374145516Sdarrenr ufmt[0] = '\0'; 375145516Sdarrenr break; 376145516Sdarrenr case 'v': 377145516Sdarrenr parsefmt(vfmt, 0); 378145516Sdarrenr sortby = SORTMEM; 379145516Sdarrenr _fmt = 1; 380145516Sdarrenr vfmt[0] = '\0'; 381145516Sdarrenr break; 382145516Sdarrenr case 'w': 383145516Sdarrenr if (wflag) 384145516Sdarrenr termwidth = UNLIMITED; 385145516Sdarrenr else if (termwidth < 131) 386145516Sdarrenr termwidth = 131; 387145516Sdarrenr wflag++; 388145516Sdarrenr break; 389145516Sdarrenr case 'X': 390145516Sdarrenr /* 391145516Sdarrenr * Note that `-X' and `-x' are not standard "selector" 392145516Sdarrenr * options. For most selector-options, we check *all* 393145516Sdarrenr * processes to see if any are matched by the given 394145516Sdarrenr * value(s). After we have a set of all the matched 395145516Sdarrenr * processes, then `-X' and `-x' govern whether we 396145516Sdarrenr * modify that *matched* set for processes which do 397145516Sdarrenr * not have a controlling terminal. `-X' causes 398145516Sdarrenr * those processes to be deleted from the matched 399145516Sdarrenr * set, while `-x' causes them to be kept. 400145516Sdarrenr */ 401145516Sdarrenr xkeep = 0; 402145516Sdarrenr break; 403145516Sdarrenr case 'x': 404145516Sdarrenr xkeep = 1; 405145516Sdarrenr break; 406145516Sdarrenr case 'Z': 407145516Sdarrenr parsefmt(Zfmt, 0); 408145516Sdarrenr Zfmt[0] = '\0'; 409145516Sdarrenr break; 410145516Sdarrenr case '?': 411145516Sdarrenr default: 412145516Sdarrenr usage(); 413145516Sdarrenr } 414145516Sdarrenr argc -= optind; 415145516Sdarrenr argv += optind; 416145516Sdarrenr if (optfatal) 417145516Sdarrenr exit(1); /* Error messages already printed. */ 418145516Sdarrenr if (xkeep < 0) /* Neither -X nor -x was specified. */ 419145516Sdarrenr xkeep = xkeep_implied; 420145516Sdarrenr 421145516Sdarrenr#define BACKWARD_COMPATIBILITY 422145516Sdarrenr#ifdef BACKWARD_COMPATIBILITY 423145516Sdarrenr if (*argv) { 424145516Sdarrenr nlistf = *argv; 425145516Sdarrenr if (*++argv) 426145516Sdarrenr memf = *argv; 427145516Sdarrenr } 428145516Sdarrenr#endif 429145516Sdarrenr /* 430145516Sdarrenr * Discard setgid privileges if not the running kernel so that bad 431145516Sdarrenr * guys can't print interesting stuff from kernel memory. 432145516Sdarrenr */ 433145516Sdarrenr if (dropgid) { 434145516Sdarrenr setgid(getgid()); 435145516Sdarrenr setuid(getuid()); 436145516Sdarrenr } 437145516Sdarrenr 438145516Sdarrenr kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 439145516Sdarrenr if (kd == 0) 440145516Sdarrenr errx(1, "%s", errbuf); 441145516Sdarrenr 442145516Sdarrenr if (!_fmt) 443145516Sdarrenr parsefmt(dfmt, 0); 444145516Sdarrenr 445145516Sdarrenr if (nselectors == 0) { 446145516Sdarrenr uidlist.ptr = malloc(sizeof(uid_t)); 447145516Sdarrenr if (uidlist.ptr == NULL) 448145516Sdarrenr errx(1, "malloc failed"); 449145516Sdarrenr nselectors = 1; 450145516Sdarrenr uidlist.count = uidlist.maxcount = 1; 451145516Sdarrenr *uidlist.uids = getuid(); 452145516Sdarrenr } 453145516Sdarrenr 454145516Sdarrenr /* 455145516Sdarrenr * scan requested variables, noting what structures are needed, 456145516Sdarrenr * and adjusting header widths as appropriate. 457145516Sdarrenr */ 458145516Sdarrenr scanvars(); 459145516Sdarrenr 460145516Sdarrenr /* 461145516Sdarrenr * Get process list. If the user requested just one selector- 462145516Sdarrenr * option, then kvm_getprocs can be asked to return just those 463145516Sdarrenr * processes. Otherwise, have it return all processes, and 464145516Sdarrenr * then this routine will search that full list and select the 465145516Sdarrenr * processes which match any of the user's selector-options. 466145516Sdarrenr */ 467145516Sdarrenr what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC; 468145516Sdarrenr flag = 0; 469145516Sdarrenr if (nselectors == 1) { 470145516Sdarrenr /* XXX - Apparently there's no KERN_PROC_GID flag. */ 471145516Sdarrenr if (pgrplist.count == 1) { 472145516Sdarrenr what = KERN_PROC_PGRP | showthreads; 473145516Sdarrenr flag = *pgrplist.pids; 474145516Sdarrenr nselectors = 0; 475145516Sdarrenr } else if (pidlist.count == 1) { 476145516Sdarrenr what = KERN_PROC_PID | showthreads; 477145516Sdarrenr flag = *pidlist.pids; 478145516Sdarrenr nselectors = 0; 479145516Sdarrenr } else if (ruidlist.count == 1) { 480145516Sdarrenr what = KERN_PROC_RUID | showthreads; 481161356Sguido flag = *ruidlist.uids; 482145516Sdarrenr nselectors = 0; 483170268Sdarrenr#if 0 484170268Sdarrenr /*- 485170268Sdarrenr * XXX - KERN_PROC_SESSION causes error in kvm_getprocs? 486161356Sguido * For now, always do sid-matching in this routine. 487161356Sguido */ 488161356Sguido } else if (sesslist.count == 1) { 489145516Sdarrenr what = KERN_PROC_SESSION | showthreads; 490161356Sguido flag = *sesslist.pids; 491145516Sdarrenr nselectors = 0; 492145516Sdarrenr#endif 493145516Sdarrenr } else if (ttylist.count == 1) { 494145516Sdarrenr what = KERN_PROC_TTY | showthreads; 495161356Sguido flag = *ttylist.ttys; 496145516Sdarrenr nselectors = 0; 497145516Sdarrenr } else if (uidlist.count == 1) { 498145516Sdarrenr what = KERN_PROC_UID | showthreads; 499145516Sdarrenr flag = *uidlist.uids; 500145516Sdarrenr nselectors = 0; 501145516Sdarrenr } else if (all) { 502145516Sdarrenr /* No need for this routine to select processes. */ 503145516Sdarrenr nselectors = 0; 504145516Sdarrenr } 505145516Sdarrenr } 506145516Sdarrenr 507145516Sdarrenr /* 508145516Sdarrenr * select procs 509145516Sdarrenr */ 510145516Sdarrenr nentries = -1; 511145516Sdarrenr kp = kvm_getprocs(kd, what, flag, &nentries); 512145516Sdarrenr if ((kp == NULL && nentries > 0) || (kp != NULL && nentries < 0)) 513145516Sdarrenr errx(1, "%s", kvm_geterr(kd)); 514145516Sdarrenr nkept = 0; 515145516Sdarrenr if (nentries > 0) { 516145516Sdarrenr if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL) 517145516Sdarrenr errx(1, "malloc failed"); 518145516Sdarrenr for (i = nentries; --i >= 0; ++kp) { 519145516Sdarrenr /* 520145516Sdarrenr * If the user specified multiple selection-criteria, 521145516Sdarrenr * then keep any process matched by the inclusive OR 522170268Sdarrenr * of all the selection-criteria given. 523170268Sdarrenr */ 524170268Sdarrenr if (pidlist.count > 0) { 525145516Sdarrenr for (elem = 0; elem < pidlist.count; elem++) 526145516Sdarrenr if (kp->ki_pid == pidlist.pids[elem]) 527145516Sdarrenr goto keepit; 528145516Sdarrenr } 529145516Sdarrenr /* 530145516Sdarrenr * Note that we had to process pidlist before 531145516Sdarrenr * filtering out processes which do not have 532145516Sdarrenr * a controlling terminal. 533145516Sdarrenr */ 534145516Sdarrenr if (xkeep == 0) { 535145516Sdarrenr if ((kp->ki_tdev == NODEV || 536145516Sdarrenr (kp->ki_flag & P_CONTROLT) == 0)) 537145516Sdarrenr continue; 538145516Sdarrenr } 539145516Sdarrenr if (nselectors == 0) 540145516Sdarrenr goto keepit; 541145516Sdarrenr if (gidlist.count > 0) { 542145516Sdarrenr for (elem = 0; elem < gidlist.count; elem++) 543145516Sdarrenr if (kp->ki_rgid == gidlist.gids[elem]) 544145516Sdarrenr goto keepit; 545145516Sdarrenr } 546145516Sdarrenr if (pgrplist.count > 0) { 547145516Sdarrenr for (elem = 0; elem < pgrplist.count; elem++) 548145516Sdarrenr if (kp->ki_pgid == pgrplist.pids[elem]) 549145516Sdarrenr goto keepit; 550145516Sdarrenr } 551145516Sdarrenr if (ruidlist.count > 0) { 552145516Sdarrenr for (elem = 0; elem < ruidlist.count; elem++) 553145516Sdarrenr if (kp->ki_ruid == ruidlist.uids[elem]) 554145516Sdarrenr goto keepit; 555145516Sdarrenr } 556145516Sdarrenr if (sesslist.count > 0) { 557145516Sdarrenr for (elem = 0; elem < sesslist.count; elem++) 558145516Sdarrenr if (kp->ki_sid == sesslist.pids[elem]) 559145516Sdarrenr goto keepit; 560145516Sdarrenr } 561145516Sdarrenr if (ttylist.count > 0) { 562145516Sdarrenr for (elem = 0; elem < ttylist.count; elem++) 563145516Sdarrenr if (kp->ki_tdev == ttylist.ttys[elem]) 564145516Sdarrenr goto keepit; 565145516Sdarrenr } 566145516Sdarrenr if (uidlist.count > 0) { 567145516Sdarrenr for (elem = 0; elem < uidlist.count; elem++) 568145516Sdarrenr if (kp->ki_uid == uidlist.uids[elem]) 569145516Sdarrenr goto keepit; 570145516Sdarrenr } 571145516Sdarrenr /* 572145516Sdarrenr * This process did not match any of the user's 573145516Sdarrenr * selector-options, so skip the process. 574145516Sdarrenr */ 575145516Sdarrenr continue; 576145516Sdarrenr 577145516Sdarrenr keepit: 578145516Sdarrenr kinfo[nkept].ki_p = kp; 579145516Sdarrenr if (needuser) 580145516Sdarrenr saveuser(&kinfo[nkept]); 581145516Sdarrenr dynsizevars(&kinfo[nkept]); 582145516Sdarrenr nkept++; 583145516Sdarrenr } 584145516Sdarrenr } 585145516Sdarrenr 586145516Sdarrenr sizevars(); 587145516Sdarrenr 588145516Sdarrenr /* 589145516Sdarrenr * print header 590145516Sdarrenr */ 591145516Sdarrenr printheader(); 592145516Sdarrenr if (nkept == 0) 593145516Sdarrenr exit(1); 594145516Sdarrenr 595145516Sdarrenr /* 596145516Sdarrenr * sort proc list 597145516Sdarrenr */ 598145516Sdarrenr qsort(kinfo, nkept, sizeof(KINFO), pscomp); 599145516Sdarrenr /* 600145516Sdarrenr * For each process, call each variable output function. 601145516Sdarrenr */ 602145516Sdarrenr for (i = lineno = 0; i < nkept; i++) { 603145516Sdarrenr for (vent = vhead; vent; vent = vent->next) { 604145516Sdarrenr (vent->var->oproc)(&kinfo[i], vent); 605145516Sdarrenr if (vent->next != NULL) 606145516Sdarrenr (void)putchar(' '); 607145516Sdarrenr } 608145516Sdarrenr (void)putchar('\n'); 609145516Sdarrenr if (prtheader && lineno++ == prtheader - 4) { 610145516Sdarrenr (void)putchar('\n'); 611145516Sdarrenr printheader(); 612145516Sdarrenr lineno = 0; 613145516Sdarrenr } 614145516Sdarrenr } 615145516Sdarrenr free_list(&gidlist); 616145516Sdarrenr free_list(&pidlist); 617145516Sdarrenr free_list(&pgrplist); 618145516Sdarrenr free_list(&ruidlist); 619145516Sdarrenr free_list(&sesslist); 620145516Sdarrenr free_list(&ttylist); 621145516Sdarrenr free_list(&uidlist); 622145516Sdarrenr 623145516Sdarrenr exit(eval); 624145516Sdarrenr} 625145516Sdarrenr 626145516Sdarrenrstatic int 627145516Sdarrenraddelem_gid(struct listinfo *inf, const char *elem) 628145516Sdarrenr{ 629145516Sdarrenr struct group *grp; 630145516Sdarrenr const char *nameorID; 631145516Sdarrenr char *endp; 632145516Sdarrenr u_long bigtemp; 633145516Sdarrenr 634145516Sdarrenr if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) { 635145516Sdarrenr if (*elem == '\0') 636145516Sdarrenr warnx("Invalid (zero-length) %s name", inf->lname); 637145516Sdarrenr else 638145516Sdarrenr warnx("%s name too long: %s", inf->lname, elem); 639145516Sdarrenr optfatal = 1; 640145516Sdarrenr return (0); /* Do not add this value. */ 641145516Sdarrenr } 642145516Sdarrenr 643145516Sdarrenr /* 644145516Sdarrenr * SUSv3 states that `ps -G grouplist' should match "real-group 645145516Sdarrenr * ID numbers", and does not mention group-names. I do want to 646145516Sdarrenr * also support group-names, so this tries for a group-id first, 647145516Sdarrenr * and only tries for a name if that doesn't work. This is the 648145516Sdarrenr * opposite order of what is done in addelem_uid(), but in 649145516Sdarrenr * practice the order would only matter for group-names which 650145516Sdarrenr * are all-numeric. 651145516Sdarrenr */ 652145516Sdarrenr grp = NULL; 653145516Sdarrenr nameorID = "named"; 654145516Sdarrenr errno = 0; 655145516Sdarrenr bigtemp = strtoul(elem, &endp, 10); 656145516Sdarrenr if (errno == 0 && *endp == '\0' && bigtemp <= GID_MAX) { 657145516Sdarrenr nameorID = "name or ID matches"; 658145516Sdarrenr grp = getgrgid((gid_t)bigtemp); 659145516Sdarrenr } 660145516Sdarrenr if (grp == NULL) 661145516Sdarrenr grp = getgrnam(elem); 662145516Sdarrenr if (grp == NULL) { 663145516Sdarrenr warnx("No %s %s '%s'", inf->lname, nameorID, elem); 664145516Sdarrenr optfatal = 1; 665145516Sdarrenr return (0); /* Do not add this value. */ 666145516Sdarrenr } 667145516Sdarrenr 668145516Sdarrenr if (inf->count >= inf->maxcount) 669145516Sdarrenr expand_list(inf); 670145516Sdarrenr inf->gids[(inf->count)++] = grp->gr_gid; 671145516Sdarrenr return (1); 672145516Sdarrenr} 673145516Sdarrenr 674145516Sdarrenr#define BSD_PID_MAX 99999 /* Copy of PID_MAX from sys/proc.h. */ 675145516Sdarrenrstatic int 676145516Sdarrenraddelem_pid(struct listinfo *inf, const char *elem) 677145516Sdarrenr{ 678145516Sdarrenr char *endp; 679145516Sdarrenr long tempid; 680145516Sdarrenr 681145516Sdarrenr if (*elem == '\0') 682145516Sdarrenr tempid = 0L; 683145516Sdarrenr else { 684145516Sdarrenr errno = 0; 685145516Sdarrenr tempid = strtol(elem, &endp, 10); 686145516Sdarrenr if (*endp != '\0' || tempid < 0 || elem == endp) { 687145516Sdarrenr warnx("Invalid %s: %s", inf->lname, elem); 688145516Sdarrenr errno = ERANGE; 689145516Sdarrenr } else if (errno != 0 || tempid > BSD_PID_MAX) { 690145516Sdarrenr warnx("%s too large: %s", inf->lname, elem); 691145516Sdarrenr errno = ERANGE; 692145516Sdarrenr } 693145516Sdarrenr if (errno == ERANGE) { 694145516Sdarrenr optfatal = 1; 695145516Sdarrenr return (0); /* Do not add this value. */ 696145516Sdarrenr } 697145516Sdarrenr } 698145516Sdarrenr 699145516Sdarrenr if (inf->count >= inf->maxcount) 700145516Sdarrenr expand_list(inf); 701145516Sdarrenr inf->pids[(inf->count)++] = tempid; 702145516Sdarrenr return (1); 703145516Sdarrenr} 704145516Sdarrenr#undef BSD_PID_MAX 705145516Sdarrenr 706145516Sdarrenrstatic int 707145516Sdarrenraddelem_tty(struct listinfo *inf, const char *elem) 708145516Sdarrenr{ 709145516Sdarrenr const char *ttypath; 710145516Sdarrenr struct stat sb; 711145516Sdarrenr char pathbuf[PATH_MAX]; 712172776Sdarrenr 713145516Sdarrenr if (strcmp(elem, "co") == 0) 714145516Sdarrenr ttypath = strdup(_PATH_CONSOLE); 715145516Sdarrenr else if (*elem == '/') 716145516Sdarrenr ttypath = elem; 717145516Sdarrenr else { 718145516Sdarrenr strlcpy(pathbuf, _PATH_TTY, sizeof(pathbuf)); 719145516Sdarrenr strlcat(pathbuf, elem, sizeof(pathbuf)); 720145516Sdarrenr ttypath = pathbuf; 721145516Sdarrenr } 722145516Sdarrenr 723145516Sdarrenr if (stat(ttypath, &sb) == -1) { 724145516Sdarrenr warn("%s", ttypath); 725145516Sdarrenr optfatal = 1; 726145516Sdarrenr return (0); /* Do not add this value. */ 727145516Sdarrenr } 728145516Sdarrenr if (!S_ISCHR(sb.st_mode)) { 729145516Sdarrenr warn("%s: Not a terminal", ttypath); 730145516Sdarrenr optfatal = 1; 731145516Sdarrenr return (0); /* Do not add this value. */ 732145516Sdarrenr } 733145516Sdarrenr 734145516Sdarrenr if (inf->count >= inf->maxcount) 735145516Sdarrenr expand_list(inf); 736145516Sdarrenr inf->ttys[(inf->count)++] = sb.st_rdev; 737145516Sdarrenr return (1); 738145516Sdarrenr} 739145516Sdarrenr 740145516Sdarrenrstatic int 741145516Sdarrenraddelem_uid(struct listinfo *inf, const char *elem) 742145516Sdarrenr{ 743145516Sdarrenr struct passwd *pwd; 744145516Sdarrenr char *endp; 745145516Sdarrenr u_long bigtemp; 746145516Sdarrenr 747145516Sdarrenr if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) { 748145516Sdarrenr if (*elem == '\0') 749145516Sdarrenr warnx("Invalid (zero-length) %s name", inf->lname); 750145516Sdarrenr else 751145516Sdarrenr warnx("%s name too long: %s", inf->lname, elem); 752145516Sdarrenr optfatal = 1; 753145516Sdarrenr return (0); /* Do not add this value. */ 754145516Sdarrenr } 755145516Sdarrenr 756145516Sdarrenr pwd = getpwnam(elem); 757145516Sdarrenr if (pwd == NULL) { 758145516Sdarrenr errno = 0; 759145516Sdarrenr bigtemp = strtoul(elem, &endp, 10); 760145516Sdarrenr if (errno != 0 || *endp != '\0' || bigtemp > UID_MAX) 761145516Sdarrenr warnx("No %s named '%s'", inf->lname, elem); 762145516Sdarrenr else { 763145516Sdarrenr /* The string is all digits, so it might be a userID. */ 764145516Sdarrenr pwd = getpwuid((uid_t)bigtemp); 765145516Sdarrenr if (pwd == NULL) 766145516Sdarrenr warnx("No %s name or ID matches '%s'", 767145516Sdarrenr inf->lname, elem); 768145516Sdarrenr } 769145516Sdarrenr } 770145516Sdarrenr if (pwd == NULL) { 771145516Sdarrenr /* 772145516Sdarrenr * These used to be treated as minor warnings (and the 773145516Sdarrenr * option was simply ignored), but now they are fatal 774145516Sdarrenr * errors (and the command will be aborted). 775145516Sdarrenr */ 776145516Sdarrenr optfatal = 1; 777145516Sdarrenr return (0); /* Do not add this value. */ 778145516Sdarrenr } 779145516Sdarrenr 780145516Sdarrenr if (inf->count >= inf->maxcount) 781145516Sdarrenr expand_list(inf); 782145516Sdarrenr inf->uids[(inf->count)++] = pwd->pw_uid; 783145516Sdarrenr return (1); 784145516Sdarrenr} 785145516Sdarrenr 786145516Sdarrenrstatic void 787145516Sdarrenradd_list(struct listinfo *inf, const char *argp) 788145516Sdarrenr{ 789145516Sdarrenr const char *savep; 790145516Sdarrenr char *cp, *endp; 791145516Sdarrenr int toolong; 792145516Sdarrenr char elemcopy[PATH_MAX]; 793145516Sdarrenr 794145516Sdarrenr while (*argp != '\0') { 795145516Sdarrenr while (*argp != '\0' && strchr(W_SEP, *argp) != NULL) 796145516Sdarrenr argp++; 797145516Sdarrenr savep = argp; 798145516Sdarrenr toolong = 0; 799145516Sdarrenr cp = elemcopy; 800145516Sdarrenr if (strchr(T_SEP, *argp) == NULL) { 801145516Sdarrenr endp = elemcopy + sizeof(elemcopy) - 1; 802145516Sdarrenr while (*argp != '\0' && cp <= endp && 803145516Sdarrenr strchr(W_SEP T_SEP, *argp) == NULL) 804145516Sdarrenr *cp++ = *argp++; 805145516Sdarrenr if (cp > endp) 806145516Sdarrenr toolong = 1; 807145516Sdarrenr } 808145516Sdarrenr if (!toolong) { 809145516Sdarrenr *cp = '\0'; 810145516Sdarrenr#ifndef ADD_PS_LISTRESET 811145516Sdarrenr /* 812145516Sdarrenr * This is how the standard expects lists to 813145516Sdarrenr * be handled. 814145516Sdarrenr */ 815145516Sdarrenr inf->addelem(inf, elemcopy); 816145516Sdarrenr#else 817145516Sdarrenr /*- 818145516Sdarrenr * This would add a simple non-standard-but-convienent 819145516Sdarrenr * feature. 820145516Sdarrenr * 821145516Sdarrenr * XXX - The first time I tried to add this check, 822145516Sdarrenr * it increased the total size of `ps' by 3940 823145516Sdarrenr * bytes on i386! That's 12% of the entire 824145516Sdarrenr * program! The `ps.o' file grew by only about 825145516Sdarrenr * 40 bytes, but the final (stripped) executable 826145516Sdarrenr * in /bin/ps grew by 12%. I have not had time 827145516Sdarrenr * to investigate, so skip the feature for now. 828145516Sdarrenr */ 829145516Sdarrenr /* 830145516Sdarrenr * We now have a single element. Add it to the 831145516Sdarrenr * list, unless the element is ":". In that case, 832145516Sdarrenr * reset the list so previous entries are ignored. 833145516Sdarrenr */ 834145516Sdarrenr if (strcmp(elemcopy, ":") == 0) 835145516Sdarrenr inf->count = 0; 836145516Sdarrenr else 837145516Sdarrenr inf->addelem(inf, elemcopy); 838145516Sdarrenr#endif 839145516Sdarrenr } else { 840145516Sdarrenr /* 841145516Sdarrenr * The string is too long to copy. Find the end 842145516Sdarrenr * of the string to print out the warning message. 843145516Sdarrenr */ 844145516Sdarrenr while (*argp != '\0' && strchr(W_SEP T_SEP, 845145516Sdarrenr *argp) == NULL) 846145516Sdarrenr argp++; 847145516Sdarrenr warnx("Value too long: %.*s", (int)(argp - savep), 848145516Sdarrenr savep); 849145516Sdarrenr optfatal = 1; 850145516Sdarrenr } 851145516Sdarrenr /* 852145516Sdarrenr * Skip over any number of trailing whitespace characters, 853145516Sdarrenr * but only one (at most) trailing element-terminating 854145516Sdarrenr * character. 855145516Sdarrenr */ 856145516Sdarrenr while (*argp != '\0' && strchr(W_SEP, *argp) != NULL) 857145516Sdarrenr argp++; 858145516Sdarrenr if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) { 859145516Sdarrenr argp++; 860145516Sdarrenr /* Catch case where string ended with a comma. */ 861145516Sdarrenr if (*argp == '\0') 862145516Sdarrenr inf->addelem(inf, argp); 863145516Sdarrenr } 864145516Sdarrenr } 865145516Sdarrenr} 866145516Sdarrenr 867145516Sdarrenrstatic void * 868145516Sdarrenrexpand_list(struct listinfo *inf) 869145516Sdarrenr{ 870145516Sdarrenr void *newlist; 871145516Sdarrenr int newmax; 872145516Sdarrenr 873145516Sdarrenr newmax = (inf->maxcount + 1) << 1; 874145516Sdarrenr newlist = realloc(inf->ptr, newmax * inf->elemsize); 875145516Sdarrenr if (newlist == NULL) { 876145516Sdarrenr free(inf->ptr); 877145516Sdarrenr errx(1, "realloc to %d %ss failed", newmax, 878170268Sdarrenr inf->lname); 879145516Sdarrenr } 880145516Sdarrenr inf->maxcount = newmax; 881145516Sdarrenr inf->ptr = newlist; 882145516Sdarrenr 883145516Sdarrenr return (newlist); 884145516Sdarrenr} 885173181Sdarrenr 886173181Sdarrenrstatic void 887173181Sdarrenrfree_list(struct listinfo *inf) 888173181Sdarrenr{ 889145516Sdarrenr 890145516Sdarrenr inf->count = inf->elemsize = inf->maxcount = 0; 891145516Sdarrenr if (inf->ptr != NULL) 892145516Sdarrenr free(inf->ptr); 893145516Sdarrenr inf->addelem = NULL; 894145516Sdarrenr inf->lname = NULL; 895173181Sdarrenr inf->ptr = NULL; 896145516Sdarrenr} 897145516Sdarrenr 898145516Sdarrenrstatic void 899145516Sdarrenrinit_list(struct listinfo *inf, addelem_rtn artn, int elemsize, 900145516Sdarrenr const char *lname) 901145516Sdarrenr{ 902145516Sdarrenr 903145516Sdarrenr inf->count = inf->maxcount = 0; 904161356Sguido inf->elemsize = elemsize; 905161356Sguido inf->addelem = artn; 906145516Sdarrenr inf->lname = lname; 907145516Sdarrenr inf->ptr = NULL; 908145516Sdarrenr} 909145516Sdarrenr 910145516SdarrenrVARENT * 911145516Sdarrenrfind_varentry(VAR *v) 912145516Sdarrenr{ 913145516Sdarrenr struct varent *vent; 914145516Sdarrenr 915145516Sdarrenr for (vent = vhead; vent; vent = vent->next) { 916145516Sdarrenr if (strcmp(vent->var->name, v->name) == 0) 917145516Sdarrenr return vent; 918145516Sdarrenr } 919161356Sguido return NULL; 920161356Sguido} 921145516Sdarrenr 922145516Sdarrenrstatic void 923145516Sdarrenrscanvars(void) 924145516Sdarrenr{ 925145516Sdarrenr struct varent *vent; 926145516Sdarrenr VAR *v; 927161356Sguido 928145516Sdarrenr for (vent = vhead; vent; vent = vent->next) { 929145516Sdarrenr v = vent->var; 930145516Sdarrenr if (v->flag & DSIZ) { 931145516Sdarrenr v->dwidth = v->width; 932145516Sdarrenr v->width = 0; 933145516Sdarrenr } 934145516Sdarrenr if (v->flag & USER) 935145516Sdarrenr needuser = 1; 936145516Sdarrenr if (v->flag & COMM) 937145516Sdarrenr needcomm = 1; 938145516Sdarrenr } 939145516Sdarrenr} 940145516Sdarrenr 941145516Sdarrenrstatic void 942145516Sdarrenrdynsizevars(KINFO *ki) 943145516Sdarrenr{ 944145516Sdarrenr struct varent *vent; 945145516Sdarrenr VAR *v; 946145516Sdarrenr int i; 947145516Sdarrenr 948145516Sdarrenr for (vent = vhead; vent; vent = vent->next) { 949145516Sdarrenr v = vent->var; 950145516Sdarrenr if (!(v->flag & DSIZ)) 951145516Sdarrenr continue; 952145516Sdarrenr i = (v->sproc)( ki); 953145516Sdarrenr if (v->width < i) 954145516Sdarrenr v->width = i; 955145516Sdarrenr if (v->width > v->dwidth) 956145516Sdarrenr v->width = v->dwidth; 957145516Sdarrenr } 958145516Sdarrenr} 959145516Sdarrenr 960145516Sdarrenrstatic void 961145516Sdarrenrsizevars(void) 962145516Sdarrenr{ 963145516Sdarrenr struct varent *vent; 964145516Sdarrenr VAR *v; 965145516Sdarrenr int i; 966145516Sdarrenr 967145516Sdarrenr for (vent = vhead; vent; vent = vent->next) { 968145516Sdarrenr v = vent->var; 969161356Sguido i = strlen(vent->header); 970161356Sguido if (v->width < i) 971145516Sdarrenr v->width = i; 972145516Sdarrenr totwidth += v->width + 1; /* +1 for space */ 973178888Sjulian } 974145516Sdarrenr totwidth--; 975145516Sdarrenr} 976145516Sdarrenr 977145516Sdarrenrstatic const char * 978145516Sdarrenrfmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki, 979145516Sdarrenr char *comm, int maxlen) 980145516Sdarrenr{ 981145516Sdarrenr const char *s; 982145516Sdarrenr 983145516Sdarrenr s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen); 984145516Sdarrenr return (s); 985145516Sdarrenr} 986145516Sdarrenr 987145516Sdarrenr#define UREADOK(ki) (forceuread || (ki->ki_p->ki_sflag & PS_INMEM)) 988145516Sdarrenr 989145516Sdarrenrstatic void 990145516Sdarrenrsaveuser(KINFO *ki) 991145516Sdarrenr{ 992145516Sdarrenr 993170268Sdarrenr if (ki->ki_p->ki_sflag & PS_INMEM) { 994170268Sdarrenr /* 995170268Sdarrenr * The u-area might be swapped out, and we can't get 996145516Sdarrenr * at it because we have a crashdump and no swap. 997170268Sdarrenr * If it's here fill in these fields, otherwise, just 998145516Sdarrenr * leave them 0. 999145516Sdarrenr */ 1000145516Sdarrenr ki->ki_valid = 1; 1001145516Sdarrenr } else 1002145516Sdarrenr ki->ki_valid = 0; 1003145516Sdarrenr /* 1004145516Sdarrenr * save arguments if needed 1005145516Sdarrenr */ 1006170268Sdarrenr if (needcomm && (UREADOK(ki) || (ki->ki_p->ki_args != NULL))) { 1007170268Sdarrenr ki->ki_args = strdup(fmt(kvm_getargv, ki, ki->ki_p->ki_comm, 1008145516Sdarrenr MAXCOMLEN)); 1009145516Sdarrenr } else if (needcomm) { 1010145516Sdarrenr asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm); 1011145516Sdarrenr } else { 1012145516Sdarrenr ki->ki_args = NULL; 1013145516Sdarrenr } 1014145516Sdarrenr if (needenv && UREADOK(ki)) { 1015170268Sdarrenr ki->ki_env = strdup(fmt(kvm_getenvv, ki, (char *)NULL, 0)); 1016145516Sdarrenr } else if (needenv) { 1017145516Sdarrenr ki->ki_env = malloc(3); 1018145516Sdarrenr strcpy(ki->ki_env, "()"); 1019145516Sdarrenr } else { 1020173181Sdarrenr ki->ki_env = NULL; 1021145516Sdarrenr } 1022145516Sdarrenr} 1023145516Sdarrenr 1024145516Sdarrenrstatic int 1025145516Sdarrenrpscomp(const void *a, const void *b) 1026145516Sdarrenr{ 1027145516Sdarrenr const KINFO *ka, *kb; 1028145516Sdarrenr double cpua, cpub; 1029145516Sdarrenr segsz_t sizea, sizeb; 1030145516Sdarrenr 1031145516Sdarrenr ka = a; 1032145516Sdarrenr kb = b; 1033145516Sdarrenr /* SORTCPU and SORTMEM are sorted in descending order. */ 1034145516Sdarrenr if (sortby == SORTCPU) { 1035145516Sdarrenr cpua = getpcpu(ka); 1036145516Sdarrenr cpub = getpcpu(kb); 1037145516Sdarrenr if (cpua < cpub) 1038145516Sdarrenr return (1); 1039145516Sdarrenr if (cpua > cpub) 1040145516Sdarrenr return (-1); 1041145516Sdarrenr } 1042145516Sdarrenr if (sortby == SORTMEM) { 1043145516Sdarrenr sizea = ka->ki_p->ki_tsize + ka->ki_p->ki_dsize + 1044145516Sdarrenr ka->ki_p->ki_ssize; 1045145516Sdarrenr sizeb = kb->ki_p->ki_tsize + kb->ki_p->ki_dsize + 1046145516Sdarrenr kb->ki_p->ki_ssize; 1047145516Sdarrenr if (sizea < sizeb) 1048145516Sdarrenr return (1); 1049145516Sdarrenr if (sizea > sizeb) 1050145516Sdarrenr return (-1); 1051145516Sdarrenr } 1052145516Sdarrenr /* 1053145516Sdarrenr * TTY's are sorted in ascending order, except that all NODEV 1054145516Sdarrenr * processes come before all processes with a device. 1055145516Sdarrenr */ 1056145516Sdarrenr if (ka->ki_p->ki_tdev == NODEV && kb->ki_p->ki_tdev != NODEV) 1057145516Sdarrenr return (-1); 1058145516Sdarrenr if (ka->ki_p->ki_tdev != NODEV && kb->ki_p->ki_tdev == NODEV) 1059145516Sdarrenr return (1); 1060145516Sdarrenr if (ka->ki_p->ki_tdev < kb->ki_p->ki_tdev) 1061145516Sdarrenr return (-1); 1062145516Sdarrenr if (ka->ki_p->ki_tdev > kb->ki_p->ki_tdev) 1063145516Sdarrenr return (1); 1064145516Sdarrenr /* PID's are sorted in ascending order. */ 1065145516Sdarrenr if (ka->ki_p->ki_pid < kb->ki_p->ki_pid) 1066145516Sdarrenr return (-1); 1067145516Sdarrenr if (ka->ki_p->ki_pid > kb->ki_p->ki_pid) 1068145516Sdarrenr return (1); 1069145516Sdarrenr return (0); 1070145516Sdarrenr} 1071145516Sdarrenr 1072145516Sdarrenr/* 1073145516Sdarrenr * ICK (all for getopt), would rather hide the ugliness 1074145516Sdarrenr * here than taint the main code. 1075145516Sdarrenr * 1076145516Sdarrenr * ps foo -> ps -foo 1077145516Sdarrenr * ps 34 -> ps -p34 1078145516Sdarrenr * 1079145516Sdarrenr * The old convention that 't' with no trailing tty arg means the users 1080145516Sdarrenr * tty, is only supported if argv[1] doesn't begin with a '-'. This same 1081145516Sdarrenr * feature is available with the option 'T', which takes no argument. 1082145516Sdarrenr */ 1083145516Sdarrenrstatic char * 1084145516Sdarrenrkludge_oldps_options(char *s) 1085145516Sdarrenr{ 1086145516Sdarrenr int have_fmt; 1087145516Sdarrenr size_t len; 1088145516Sdarrenr char *newopts, *ns, *cp; 1089145516Sdarrenr 1090145516Sdarrenr /* 1091161356Sguido * If we have an 'o' option, then note it, since we don't want to do 1092145516Sdarrenr * some types of munging. 1093145516Sdarrenr */ 1094145516Sdarrenr have_fmt = index(s, 'o') != NULL; 1095145516Sdarrenr 1096145516Sdarrenr len = strlen(s); 1097145516Sdarrenr if ((newopts = ns = malloc(len + 2)) == NULL) 1098145516Sdarrenr errx(1, "malloc failed"); 1099145516Sdarrenr /* 1100145516Sdarrenr * options begin with '-' 1101145516Sdarrenr */ 1102145516Sdarrenr if (*s != '-') 1103145516Sdarrenr *ns++ = '-'; /* add option flag */ 1104145516Sdarrenr /* 1105145516Sdarrenr * gaze to end of argv[1] 1106145516Sdarrenr */ 1107145516Sdarrenr cp = s + len - 1; 1108145516Sdarrenr /* 1109145516Sdarrenr * if last letter is a 't' flag with no argument (in the context 1110145516Sdarrenr * of the oldps options -- option string NOT starting with a '-' -- 1111145516Sdarrenr * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 1112145516Sdarrenr * 1113145516Sdarrenr * However, if a flag accepting a string argument is found in the 1114145516Sdarrenr * option string, the remainder of the string is the argument to 1115145516Sdarrenr * that flag; do not modify that argument. 1116145516Sdarrenr */ 1117145516Sdarrenr if (strcspn(s, "MNOoU") == len && *cp == 't' && *s != '-') 1118145516Sdarrenr *cp = 'T'; 1119145516Sdarrenr else { 1120145516Sdarrenr /* 1121145516Sdarrenr * otherwise check for trailing number, which *may* be a 1122145516Sdarrenr * pid. 1123145516Sdarrenr */ 1124145516Sdarrenr while (cp >= s && isdigit(*cp)) 1125145516Sdarrenr --cp; 1126145516Sdarrenr } 1127145516Sdarrenr cp++; 1128145516Sdarrenr memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */ 1129145516Sdarrenr ns += cp - s; 1130161356Sguido /* 1131145516Sdarrenr * if there's a trailing number, and not a preceding 'p' (pid) or 1132145516Sdarrenr * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 1133145516Sdarrenr */ 1134145516Sdarrenr if (isdigit(*cp) && 1135145516Sdarrenr (cp == s || (cp[-1] != 't' && cp[-1] != 'p')) && 1136145516Sdarrenr (cp - 1 == s || cp[-2] != 't') && !have_fmt) 1137145516Sdarrenr *ns++ = 'p'; 1138145516Sdarrenr (void)strcpy(ns, cp); /* and append the number */ 1139145516Sdarrenr 1140145516Sdarrenr return (newopts); 1141145516Sdarrenr} 1142145516Sdarrenr 1143145516Sdarrenrstatic void 1144145516Sdarrenrusage(void) 1145145516Sdarrenr{ 1146145516Sdarrenr#define SINGLE_OPTS "[-aC" OPT_LAZY_f "HhjlmrSTuvwXxZ]" 1147145516Sdarrenr 1148145516Sdarrenr (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 1149145516Sdarrenr "usage: ps " SINGLE_OPTS " [-G gid[,gid]] [-O|o fmt]", 1150145516Sdarrenr " [-p pid[,pid]] [-t tty[,tty]] [-U user[,user]]", 1151145516Sdarrenr " [-M core] [-N system]", 1152145516Sdarrenr " ps [-L]"); 1153145516Sdarrenr exit(1); 1154145516Sdarrenr} 1155145516Sdarrenr