1 /* $NetBSD: ps.c,v 1.31 1999/10/15 20:39:52 jdolecek Exp $ */ 2 3 /*- 4 * Copyright (c) 1990, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 __COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\n\ 39 The Regents of the University of California. All rights reserved.\n"); 40 #endif /* not lint */ 41 42 #ifndef lint 43 #if 0 44 static char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94"; 45 #else 46 __RCSID("$NetBSD: ps.c,v 1.31 1999/10/15 20:39:52 jdolecek Exp $"); 47 #endif 48 #endif /* not lint */ 49 50 #include <sys/param.h> 51 #include <sys/user.h> 52 #include <sys/time.h> 53 #include <sys/resource.h> 54 #include <sys/proc.h> 55 #include <sys/stat.h> 56 #include <sys/ioctl.h> 57 #include <sys/sysctl.h> 58 59 #include <ctype.h> 60 #include <err.h> 61 #include <errno.h> 62 #include <fcntl.h> 63 #include <kvm.h> 64 #include <limits.h> 65 #include <nlist.h> 66 #include <paths.h> 67 #include <pwd.h> 68 #include <stdio.h> 69 #include <stdlib.h> 70 #include <string.h> 71 #include <unistd.h> 72 73 #include "ps.h" 74 75 #ifdef P_PPWAIT 76 #define NEWVM 77 #endif 78 79 KINFO *kinfo; 80 struct varent *vhead, *vtail; 81 82 int eval; /* exit value */ 83 int rawcpu; /* -C */ 84 int sumrusage; /* -S */ 85 int dontuseprocfs=0; /* -K */ 86 int termwidth; /* width of screen (0 == infinity) */ 87 int totwidth; /* calculated width of requested variables */ 88 89 int needuser, needcomm, needenv, commandonly, use_procfs; 90 91 enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 92 93 static KINFO *getkinfo_kvm __P((kvm_t *, int, int, int *, int)); 94 static char *kludge_oldps_options __P((char *)); 95 static int pscomp __P((const void *, const void *)); 96 static void saveuser __P((KINFO *)); 97 static void scanvars __P((void)); 98 static void usage __P((void)); 99 int main __P((int, char *[])); 100 101 char dfmt[] = "pid tt state time command"; 102 char jfmt[] = "user pid ppid pgid sess jobc state tt time command"; 103 char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command"; 104 char o1[] = "pid"; 105 char o2[] = "tt state time command"; 106 char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command"; 107 char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command"; 108 109 kvm_t *kd; 110 111 int 112 main(argc, argv) 113 int argc; 114 char *argv[]; 115 { 116 struct varent *vent; 117 struct winsize ws; 118 gid_t egid = getegid(); 119 int ch, flag, i, fmt, lineno, nentries; 120 int prtheader, wflag, what, xflg; 121 char *nlistf, *memf, *swapf, errbuf[_POSIX2_LINE_MAX]; 122 char *ttname; 123 124 (void)setegid(getgid()); 125 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 126 ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 127 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || 128 ws.ws_col == 0) 129 termwidth = 79; 130 else 131 termwidth = ws.ws_col - 1; 132 133 if (argc > 1) 134 argv[1] = kludge_oldps_options(argv[1]); 135 136 fmt = prtheader = wflag = xflg = 0; 137 what = KERN_PROC_UID; 138 flag = getuid(); 139 memf = nlistf = swapf = NULL; 140 while ((ch = getopt(argc, argv, 141 "acCeghjKLlM:mN:O:o:p:rSTt:U:uvW:wx")) != -1) 142 switch((char)ch) { 143 case 'a': 144 what = KERN_PROC_ALL; 145 flag = 0; 146 break; 147 case 'c': 148 commandonly = 1; 149 break; 150 case 'e': /* XXX set ufmt */ 151 needenv = 1; 152 break; 153 case 'C': 154 rawcpu = 1; 155 break; 156 case 'g': 157 break; /* no-op */ 158 case 'h': 159 prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 160 break; 161 case 'j': 162 parsefmt(jfmt); 163 fmt = 1; 164 jfmt[0] = '\0'; 165 break; 166 case 'K': 167 dontuseprocfs=1; 168 break; 169 case 'L': 170 showkey(); 171 exit(0); 172 /* NOTREACHED */ 173 case 'l': 174 parsefmt(lfmt); 175 fmt = 1; 176 lfmt[0] = '\0'; 177 break; 178 case 'M': 179 memf = optarg; 180 break; 181 case 'm': 182 sortby = SORTMEM; 183 break; 184 case 'N': 185 nlistf = optarg; 186 break; 187 case 'O': 188 parsefmt(o1); 189 parsefmt(optarg); 190 parsefmt(o2); 191 o1[0] = o2[0] = '\0'; 192 fmt = 1; 193 break; 194 case 'o': 195 parsefmt(optarg); 196 fmt = 1; 197 break; 198 case 'p': 199 what = KERN_PROC_PID; 200 flag = atol(optarg); 201 xflg = 1; 202 break; 203 case 'r': 204 sortby = SORTCPU; 205 break; 206 case 'S': 207 sumrusage = 1; 208 break; 209 case 'T': 210 if ((ttname = ttyname(STDIN_FILENO)) == NULL) 211 errx(1, "stdin: not a terminal"); 212 goto tty; 213 case 't': 214 ttname = optarg; 215 tty: { 216 struct stat sb; 217 char *ttypath, pathbuf[MAXPATHLEN]; 218 219 if (strcmp(ttname, "co") == 0) 220 ttypath = _PATH_CONSOLE; 221 else if (*ttname != '/') 222 (void)snprintf(ttypath = pathbuf, 223 sizeof(pathbuf), "%s%s", _PATH_TTY, ttname); 224 else 225 ttypath = ttname; 226 if (stat(ttypath, &sb) == -1) 227 err(1, "%s", ttypath); 228 if (!S_ISCHR(sb.st_mode)) 229 errx(1, "%s: not a terminal", ttypath); 230 what = KERN_PROC_TTY; 231 flag = sb.st_rdev; 232 break; 233 } 234 case 'U': 235 if (*optarg != '\0') { 236 struct passwd *pw; 237 char *ep; 238 239 what = KERN_PROC_UID; 240 pw = getpwnam(optarg); 241 if (pw == NULL) { 242 errno = 0; 243 flag = strtoul(optarg, &ep, 10); 244 if (errno) 245 err(1, "%s", optarg); 246 if (*ep != '\0') 247 errx(1, "%s: illegal user name", 248 optarg); 249 } else 250 flag = pw->pw_uid; 251 } 252 break; 253 case 'u': 254 parsefmt(ufmt); 255 sortby = SORTCPU; 256 fmt = 1; 257 ufmt[0] = '\0'; 258 break; 259 case 'v': 260 parsefmt(vfmt); 261 sortby = SORTMEM; 262 fmt = 1; 263 vfmt[0] = '\0'; 264 break; 265 case 'W': 266 swapf = optarg; 267 break; 268 case 'w': 269 if (wflag) 270 termwidth = UNLIMITED; 271 else if (termwidth < 131) 272 termwidth = 131; 273 wflag++; 274 break; 275 case 'x': 276 xflg = 1; 277 break; 278 case '?': 279 default: 280 usage(); 281 } 282 argc -= optind; 283 argv += optind; 284 285 #define BACKWARD_COMPATIBILITY 286 #ifdef BACKWARD_COMPATIBILITY 287 if (*argv) { 288 nlistf = *argv; 289 if (*++argv) { 290 memf = *argv; 291 if (*++argv) 292 swapf = *argv; 293 } 294 } 295 #endif 296 /* 297 * Discard setgid privileges. If not the running kernel, we toss 298 * them away totally so that bad guys can't print interesting stuff 299 * from kernel memory, otherwise switch back to kmem for the 300 * duration of the kvm_openfiles() call. 301 */ 302 if (nlistf != NULL || memf != NULL || swapf != NULL) 303 (void)setgid(getgid()); 304 else 305 (void)setegid(egid); 306 307 kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf); 308 if (kd == 0) { 309 if (dontuseprocfs) 310 errx(1, "%s", errbuf); 311 else { 312 warnx("kvm_openfiles: %s", errbuf); 313 fprintf(stderr, "ps: falling back to /proc-based lookup\n"); 314 } 315 } 316 317 if (nlistf == NULL && memf == NULL && swapf == NULL) 318 (void)setgid(getgid()); 319 320 if (!fmt) 321 parsefmt(dfmt); 322 323 /* 324 * scan requested variables, noting what structures are needed, 325 * and adjusting header widths as appropiate. 326 */ 327 scanvars(); 328 329 /* 330 * select procs 331 */ 332 if (!kd || !(kinfo = getkinfo_kvm(kd, what, flag, &nentries, needuser))) 333 { 334 /* If/when the /proc-based code is ripped out 335 * again, make sure all references to the -K 336 * option are also pulled (getopt(), usage(), 337 * man page). See the man page comments about 338 * this for more details. */ 339 /* sysctl() ought to provide some sort of 340 * always-working-but-minimal-functionality 341 * method of providing at least some of the 342 * process information. Unfortunately, such a 343 * change will require too much work to be put 344 * into 1.4. For now, enable this experimental 345 * /proc-based support instead (if /proc is 346 * mounted) to grab as much information as we can. 347 * The guts of emulating kvm_getprocs() is in 348 * the file procfs_ops.c. */ 349 if (kd) 350 warnx("%s.", kvm_geterr(kd)); 351 if (dontuseprocfs) { 352 exit(1); 353 } 354 /* procfs_getprocs supports all but the 355 * KERN_PROC_RUID flag. */ 356 kinfo = getkinfo_procfs(what, flag, &nentries); 357 if (kinfo == 0) { 358 errx(1, "fallback /proc-based lookup also failed. %s", 359 "Giving up..."); 360 } 361 fprintf(stderr, "%s%s", 362 "Warning: /proc does not provide ", 363 "valid data for all fields.\n"); 364 use_procfs = 1; 365 } 366 367 /* 368 * print header 369 */ 370 printheader(); 371 if (nentries == 0) 372 exit(0); 373 /* 374 * sort proc list 375 */ 376 qsort(kinfo, nentries, sizeof(KINFO), pscomp); 377 /* 378 * for each proc, call each variable output function. 379 */ 380 for (i = lineno = 0; i < nentries; i++) { 381 KINFO *ki = &kinfo[i]; 382 383 if (xflg == 0 && (KI_EPROC(ki)->e_tdev == NODEV || 384 (KI_PROC(ki)->p_flag & P_CONTROLT ) == 0)) 385 continue; 386 for (vent = vhead; vent; vent = vent->next) { 387 (vent->var->oproc)(ki, vent); 388 if (vent->next != NULL) 389 (void)putchar(' '); 390 } 391 (void)putchar('\n'); 392 if (prtheader && lineno++ == prtheader - 4) { 393 (void)putchar('\n'); 394 printheader(); 395 lineno = 0; 396 } 397 } 398 exit(eval); 399 /* NOTREACHED */ 400 } 401 402 static KINFO * 403 getkinfo_kvm(kd, what, flag, nentriesp, needuser) 404 kvm_t *kd; 405 int what, flag, *nentriesp, needuser; 406 { 407 struct kinfo_proc *kp; 408 KINFO *kinfo=NULL; 409 size_t i; 410 411 if ((kp = kvm_getprocs(kd, what, flag, nentriesp)) != 0) 412 { 413 if ((kinfo = malloc((*nentriesp) * sizeof(*kinfo))) == NULL) 414 err(1, "%s", ""); 415 for (i = (*nentriesp); i-- > 0; kp++) { 416 kinfo[i].ki_p = kp; 417 if (needuser) 418 saveuser(&kinfo[i]); 419 } 420 } 421 422 return (kinfo); 423 } 424 425 static void 426 scanvars() 427 { 428 struct varent *vent; 429 VAR *v; 430 int i; 431 432 for (vent = vhead; vent; vent = vent->next) { 433 v = vent->var; 434 i = strlen(v->header); 435 if (v->width < i) 436 v->width = i; 437 totwidth += v->width + 1; /* +1 for space */ 438 if (v->flag & USER) 439 needuser = 1; 440 if (v->flag & COMM) 441 needcomm = 1; 442 } 443 totwidth--; 444 } 445 446 static void 447 saveuser(ki) 448 KINFO *ki; 449 { 450 struct pstats pstats; 451 struct usave *usp; 452 453 usp = &ki->ki_u; 454 if (kvm_read(kd, (u_long)&KI_PROC(ki)->p_addr->u_stats, 455 (char *)&pstats, sizeof(pstats)) == sizeof(pstats)) { 456 /* 457 * The u-area might be swapped out, and we can't get 458 * at it because we have a crashdump and no swap. 459 * If it's here fill in these fields, otherwise, just 460 * leave them 0. 461 */ 462 usp->u_start = pstats.p_start; 463 usp->u_ru = pstats.p_ru; 464 usp->u_cru = pstats.p_cru; 465 usp->u_valid = 1; 466 } else 467 usp->u_valid = 0; 468 } 469 470 static int 471 pscomp(a, b) 472 const void *a, *b; 473 { 474 int i; 475 #ifdef NEWVM 476 #define VSIZE(k) (KI_EPROC(k)->e_vm.vm_dsize + KI_EPROC(k)->e_vm.vm_ssize + \ 477 KI_EPROC(k)->e_vm.vm_tsize) 478 #else 479 #define VSIZE(k) ((k)->ki_p->p_dsize + (k)->ki_p->p_ssize + (k)->ki_e->e_xsize) 480 #endif 481 482 if (sortby == SORTCPU) 483 return (getpcpu((KINFO *)b) - getpcpu((KINFO *)a)); 484 if (sortby == SORTMEM) 485 return (VSIZE((KINFO *)b) - VSIZE((KINFO *)a)); 486 i = KI_EPROC((KINFO *)a)->e_tdev - KI_EPROC((KINFO *)b)->e_tdev; 487 if (i == 0) 488 i = KI_PROC((KINFO *)a)->p_pid - KI_PROC((KINFO *)b)->p_pid; 489 return (i); 490 } 491 492 /* 493 * ICK (all for getopt), would rather hide the ugliness 494 * here than taint the main code. 495 * 496 * ps foo -> ps -foo 497 * ps 34 -> ps -p34 498 * 499 * The old convention that 't' with no trailing tty arg means the user's 500 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 501 * feature is available with the option 'T', which takes no argument. 502 */ 503 static char * 504 kludge_oldps_options(s) 505 char *s; 506 { 507 size_t len; 508 char *newopts, *ns, *cp; 509 510 len = strlen(s); 511 if ((newopts = ns = malloc(len + 3)) == NULL) 512 err(1, "%s", ""); 513 /* 514 * options begin with '-' 515 */ 516 if (*s != '-') 517 *ns++ = '-'; /* add option flag */ 518 /* 519 * gaze to end of argv[1] 520 */ 521 cp = s + len - 1; 522 /* 523 * if last letter is a 't' flag with no argument (in the context 524 * of the oldps options -- option string NOT starting with a '-' -- 525 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 526 */ 527 if (*cp == 't' && *s != '-') 528 *cp = 'T'; 529 else { 530 /* 531 * otherwise check for trailing number, which *may* be a 532 * pid. 533 */ 534 while (cp >= s && isdigit(*cp)) 535 --cp; 536 } 537 cp++; 538 memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */ 539 ns += cp - s; 540 /* 541 * if there's a trailing number, and not a preceding 'p' (pid) or 542 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 543 */ 544 if (isdigit(*cp) && 545 (cp == s || (cp[-1] != 'U' && cp[-1] != 't' && cp[-1] != 'p' && 546 (cp - 1 == s || cp[-2] != 't')))) 547 *ns++ = 'p'; 548 /* and append the number */ 549 (void)strcpy(ns, cp); /* XXX strcpy is safe */ 550 551 return (newopts); 552 } 553 554 static void 555 usage() 556 { 557 558 (void)fprintf(stderr, 559 "usage:\t%s\n\t %s\n\t%s\n", 560 "ps [-aChjKlmrSTuvwx] [-O|o fmt] [-p pid] [-t tty]", 561 "[-M core] [-N system] [-W swap]", 562 "ps [-L]"); 563 exit(1); 564 /* NOTREACHED */ 565 } 566