1 /* $OpenBSD: ps.c,v 1.76 2019/12/16 19:21:16 guenther Exp $ */ 2 /* $NetBSD: ps.c,v 1.15 1995/05/18 20:33:25 mycroft Exp $ */ 3 4 /*- 5 * Copyright (c) 1990, 1993, 1994 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> /* MAXCOMLEN NODEV */ 34 #include <sys/types.h> 35 #include <sys/sysctl.h> 36 #include <sys/time.h> 37 #include <sys/resource.h> 38 #include <sys/proc.h> 39 #include <sys/stat.h> 40 #include <sys/ioctl.h> 41 42 #include <ctype.h> 43 #include <err.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <kvm.h> 47 #include <locale.h> 48 #include <nlist.h> 49 #include <paths.h> 50 #include <pwd.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 #include <limits.h> 56 57 #include "ps.h" 58 59 extern char *__progname; 60 61 struct varent *vhead; 62 63 int eval; /* exit value */ 64 int sumrusage; /* -S */ 65 int termwidth; /* width of screen (0 == infinity) */ 66 int totwidth; /* calculated width of requested variables */ 67 68 int needcomm, needenv, neednlist, commandonly; 69 70 enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 71 72 static char *kludge_oldps_options(char *); 73 static int pscomp(const void *, const void *); 74 static void scanvars(void); 75 static void usage(void); 76 77 char dfmt[] = "pid tt state time command"; 78 char tfmt[] = "pid tid tt state time command"; 79 char jfmt[] = "user pid ppid pgid sess jobc state tt time command"; 80 char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command"; 81 char o1[] = "pid"; 82 char o2[] = "tt state time command"; 83 char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command"; 84 char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command"; 85 86 kvm_t *kd; 87 int kvm_sysctl_only; 88 89 int 90 main(int argc, char *argv[]) 91 { 92 struct kinfo_proc *kp, **kinfo; 93 struct varent *vent; 94 struct winsize ws; 95 dev_t ttydev; 96 pid_t pid; 97 uid_t uid; 98 int all, ch, flag, i, fmt, lineno, nentries; 99 int prtheader, showthreads, wflag, kflag, what, Uflag, xflg; 100 char *nlistf, *memf, *swapf, *cols, errbuf[_POSIX2_LINE_MAX]; 101 102 setlocale(LC_CTYPE, ""); 103 104 termwidth = 0; 105 if ((cols = getenv("COLUMNS")) != NULL) 106 termwidth = strtonum(cols, 1, INT_MAX, NULL); 107 if (termwidth == 0 && 108 (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 || 109 ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == 0 || 110 ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) && 111 ws.ws_col > 0) 112 termwidth = ws.ws_col - 1; 113 if (termwidth == 0) 114 termwidth = 79; 115 116 if (argc > 1) 117 argv[1] = kludge_oldps_options(argv[1]); 118 119 all = fmt = prtheader = showthreads = wflag = kflag = Uflag = xflg = 0; 120 pid = -1; 121 uid = 0; 122 ttydev = NODEV; 123 memf = nlistf = swapf = NULL; 124 while ((ch = getopt(argc, argv, 125 "AaCcegHhjkLlM:mN:O:o:p:rSTt:U:uvW:wx")) != -1) 126 switch (ch) { 127 case 'A': 128 all = 1; 129 xflg = 1; 130 break; 131 case 'a': 132 all = 1; 133 break; 134 case 'C': 135 break; /* no-op */ 136 case 'c': 137 commandonly = 1; 138 break; 139 case 'e': /* XXX set ufmt */ 140 needenv = 1; 141 break; 142 case 'g': 143 break; /* no-op */ 144 case 'H': 145 showthreads = 1; 146 break; 147 case 'h': 148 prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 149 break; 150 case 'j': 151 parsefmt(jfmt); 152 fmt = 1; 153 jfmt[0] = '\0'; 154 break; 155 case 'k': 156 kflag = 1; 157 break; 158 case 'L': 159 showkey(); 160 exit(0); 161 case 'l': 162 parsefmt(lfmt); 163 fmt = 1; 164 lfmt[0] = '\0'; 165 break; 166 case 'M': 167 memf = optarg; 168 break; 169 case 'm': 170 sortby = SORTMEM; 171 break; 172 case 'N': 173 nlistf = optarg; 174 break; 175 case 'O': 176 parsefmt(o1); 177 parsefmt(optarg); 178 parsefmt(o2); 179 o1[0] = o2[0] = '\0'; 180 fmt = 1; 181 break; 182 case 'o': 183 parsefmt(optarg); 184 fmt = 1; 185 break; 186 case 'p': 187 pid = atol(optarg); 188 xflg = 1; 189 break; 190 case 'r': 191 sortby = SORTCPU; 192 break; 193 case 'S': 194 sumrusage = 1; 195 break; 196 case 'T': 197 if ((optarg = ttyname(STDIN_FILENO)) == NULL) 198 errx(1, "stdin: not a terminal"); 199 /* FALLTHROUGH */ 200 case 't': { 201 struct stat sb; 202 char *ttypath, pathbuf[PATH_MAX]; 203 204 if (strcmp(optarg, "co") == 0) 205 ttypath = _PATH_CONSOLE; 206 else if (*optarg != '/') { 207 int r = snprintf(pathbuf, sizeof(pathbuf), "%s%s", 208 _PATH_TTY, optarg); 209 if (r < 0 || r > sizeof(pathbuf)) 210 errx(1, "%s: too long\n", optarg); 211 ttypath = pathbuf; 212 } else 213 ttypath = optarg; 214 if (stat(ttypath, &sb) == -1) 215 err(1, "%s", ttypath); 216 if (!S_ISCHR(sb.st_mode)) 217 errx(1, "%s: not a terminal", ttypath); 218 ttydev = sb.st_rdev; 219 break; 220 } 221 case 'U': 222 if (uid_from_user(optarg, &uid) == -1) 223 errx(1, "%s: no such user", optarg); 224 Uflag = xflg = 1; 225 break; 226 case 'u': 227 parsefmt(ufmt); 228 sortby = SORTCPU; 229 fmt = 1; 230 ufmt[0] = '\0'; 231 break; 232 case 'v': 233 parsefmt(vfmt); 234 sortby = SORTMEM; 235 fmt = 1; 236 vfmt[0] = '\0'; 237 break; 238 case 'W': 239 swapf = optarg; 240 break; 241 case 'w': 242 if (wflag) 243 termwidth = UNLIMITED; 244 else if (termwidth < 131) 245 termwidth = 131; 246 wflag = 1; 247 break; 248 case 'x': 249 xflg = 1; 250 break; 251 default: 252 usage(); 253 } 254 argc -= optind; 255 argv += optind; 256 257 #define BACKWARD_COMPATIBILITY 258 #ifdef BACKWARD_COMPATIBILITY 259 if (*argv) { 260 nlistf = *argv; 261 if (*++argv) { 262 memf = *argv; 263 if (*++argv) 264 swapf = *argv; 265 } 266 } 267 #endif 268 269 if (nlistf == NULL && memf == NULL && swapf == NULL) { 270 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); 271 kvm_sysctl_only = 1; 272 } else { 273 kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf); 274 } 275 if (kd == NULL) 276 errx(1, "%s", errbuf); 277 278 if (unveil(_PATH_DEVDB, "r") == -1 && errno != ENOENT) 279 err(1, "unveil"); 280 if (unveil(_PATH_DEV, "r") == -1 && errno != ENOENT) 281 err(1, "unveil"); 282 if (swapf) 283 if (unveil(swapf, "r") == -1) 284 err(1, "unveil"); 285 if (nlistf) 286 if (unveil(nlistf, "r") == -1) 287 err(1, "unveil"); 288 if (memf) 289 if (unveil(memf, "r") == -1) 290 err(1, "unveil"); 291 if (pledge("stdio rpath getpw ps", NULL) == -1) 292 err(1, "pledge"); 293 294 if (!fmt) { 295 if (showthreads) 296 parsefmt(tfmt); 297 else 298 parsefmt(dfmt); 299 } 300 301 /* XXX - should be cleaner */ 302 if (!all && ttydev == NODEV && pid == -1 && !Uflag) { 303 uid = getuid(); 304 Uflag = 1; 305 } 306 307 /* 308 * scan requested variables, noting what structures are needed, 309 * and adjusting header widths as appropriate. 310 */ 311 scanvars(); 312 313 if (neednlist && !nlistread) 314 (void) donlist(); 315 316 /* 317 * get proc list 318 */ 319 if (Uflag) { 320 what = KERN_PROC_UID; 321 flag = uid; 322 } else if (ttydev != NODEV) { 323 what = KERN_PROC_TTY; 324 flag = ttydev; 325 } else if (pid != -1) { 326 what = KERN_PROC_PID; 327 flag = pid; 328 } else if (kflag) { 329 what = KERN_PROC_KTHREAD; 330 flag = 0; 331 } else { 332 what = KERN_PROC_ALL; 333 flag = 0; 334 } 335 if (showthreads) 336 what |= KERN_PROC_SHOW_THREADS; 337 338 /* 339 * select procs 340 */ 341 kp = kvm_getprocs(kd, what, flag, sizeof(*kp), &nentries); 342 if (kp == NULL) 343 errx(1, "%s", kvm_geterr(kd)); 344 345 /* 346 * print header 347 */ 348 printheader(); 349 if (nentries == 0) 350 exit(1); 351 /* 352 * sort proc list, we convert from an array of structs to an array 353 * of pointers to make the sort cheaper. 354 */ 355 if ((kinfo = reallocarray(NULL, nentries, sizeof(*kinfo))) == NULL) 356 err(1, "failed to allocate memory for proc pointers"); 357 for (i = 0; i < nentries; i++) 358 kinfo[i] = &kp[i]; 359 qsort(kinfo, nentries, sizeof(*kinfo), pscomp); 360 /* 361 * for each proc, call each variable output function. 362 */ 363 for (i = lineno = 0; i < nentries; i++) { 364 if (xflg == 0 && ((int)kinfo[i]->p_tdev == NODEV || 365 (kinfo[i]->p_psflags & PS_CONTROLT ) == 0)) 366 continue; 367 if (showthreads && kinfo[i]->p_tid == -1) 368 continue; 369 for (vent = vhead; vent; vent = vent->next) { 370 (vent->var->oproc)(kinfo[i], vent); 371 if (vent->next != NULL) 372 (void)putchar(' '); 373 } 374 (void)putchar('\n'); 375 if (prtheader && lineno++ == prtheader - 4) { 376 (void)putchar('\n'); 377 printheader(); 378 lineno = 0; 379 } 380 } 381 exit(eval); 382 } 383 384 static void 385 scanvars(void) 386 { 387 struct varent *vent; 388 VAR *v; 389 int i; 390 391 for (vent = vhead; vent; vent = vent->next) { 392 v = vent->var; 393 i = strlen(v->header); 394 if (v->width < i) 395 v->width = i; 396 totwidth += v->width + 1; /* +1 for space */ 397 if (v->flag & COMM) 398 needcomm = 1; 399 if (v->flag & NLIST) 400 neednlist = 1; 401 } 402 totwidth--; 403 } 404 405 static int 406 pscomp(const void *v1, const void *v2) 407 { 408 const struct kinfo_proc *kp1 = *(const struct kinfo_proc **)v1; 409 const struct kinfo_proc *kp2 = *(const struct kinfo_proc **)v2; 410 int i; 411 #define VSIZE(k) ((k)->p_vm_dsize + (k)->p_vm_ssize + (k)->p_vm_tsize) 412 413 if (sortby == SORTCPU && (i = getpcpu(kp2) - getpcpu(kp1)) != 0) 414 return (i); 415 if (sortby == SORTMEM && (i = VSIZE(kp2) - VSIZE(kp1)) != 0) 416 return (i); 417 if ((i = kp1->p_tdev - kp2->p_tdev) == 0 && 418 (i = kp1->p_ustart_sec - kp2->p_ustart_sec) == 0) 419 i = kp1->p_ustart_usec - kp2->p_ustart_usec; 420 return (i); 421 } 422 423 /* 424 * ICK (all for getopt), would rather hide the ugliness 425 * here than taint the main code. 426 * 427 * ps foo -> ps -foo 428 * ps 34 -> ps -p34 429 * 430 * The old convention that 't' with no trailing tty arg means the users 431 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 432 * feature is available with the option 'T', which takes no argument. 433 */ 434 static char * 435 kludge_oldps_options(char *s) 436 { 437 size_t len; 438 char *newopts, *ns, *cp; 439 440 len = strlen(s); 441 if ((newopts = ns = malloc(2 + len + 1)) == NULL) 442 err(1, NULL); 443 /* 444 * options begin with '-' 445 */ 446 if (*s != '-') 447 *ns++ = '-'; /* add option flag */ 448 449 /* 450 * gaze to end of argv[1] 451 */ 452 cp = s + len - 1; 453 /* 454 * if last letter is a 't' flag with no argument (in the context 455 * of the oldps options -- option string NOT starting with a '-' -- 456 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 457 */ 458 if (*cp == 't' && *s != '-') 459 *cp = 'T'; 460 else { 461 /* 462 * otherwise check for trailing number, which *may* be a 463 * pid. 464 */ 465 while (cp >= s && isdigit((unsigned char)*cp)) 466 --cp; 467 } 468 cp++; 469 memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */ 470 ns += cp - s; 471 /* 472 * if there's a trailing number, and not a preceding 'p' (pid) or 473 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 474 */ 475 if (isdigit((unsigned char)*cp) && 476 (cp == s || (cp[-1] != 't' && cp[-1] != 'p' && 477 (cp - 1 == s || cp[-2] != 't')))) 478 *ns++ = 'p'; 479 /* and append the number */ 480 (void)strlcpy(ns, cp, newopts + len + 3 - ns); 481 482 return (newopts); 483 } 484 485 static void 486 usage(void) 487 { 488 (void)fprintf(stderr, 489 "usage: %s [-AaceHhjkLlmrSTuvwx] [-M core] [-N system] [-O fmt] [-o fmt] [-p pid]\n", 490 __progname); 491 (void)fprintf(stderr, 492 "%-*s[-t tty] [-U username] [-W swap]\n", (int)strlen(__progname) + 8, ""); 493 exit(1); 494 } 495