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