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