1 /*- 2 * Copyright (c) 1990 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 char copyright[] = 10 "@(#) Copyright (c) 1990 The Regents of the University of California.\n\ 11 All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)ps.c 5.45 (Berkeley) 04/03/92"; 16 #endif /* not lint */ 17 18 #include <sys/param.h> 19 #include <sys/file.h> 20 #include <sys/user.h> 21 #include <sys/time.h> 22 #include <sys/resource.h> 23 #include <sys/proc.h> 24 #include <sys/stat.h> 25 #include <sys/ioctl.h> 26 #include <sys/kinfo.h> 27 #include <nlist.h> 28 #include <kvm.h> 29 #include <errno.h> 30 #include <unistd.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <paths.h> 35 #include "ps.h" 36 37 #ifdef SPPWAIT 38 #define NEWVM 39 #endif 40 41 KINFO *kinfo; 42 struct varent *vhead, *vtail; 43 44 int eval; /* exit value */ 45 int rawcpu; /* -C */ 46 int sumrusage; /* -S */ 47 int termwidth; /* width of screen (0 == infinity) */ 48 int totwidth; /* calculated width of requested variables */ 49 50 static int needuser, needcomm, needenv; 51 52 enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 53 54 uid_t getuid(); 55 char *ttyname(); 56 57 char dfmt[] = "pid tt state time command"; 58 char jfmt[] = "user pid ppid pgid sess jobc state tt time command"; 59 char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command"; 60 char o1[] = "pid"; 61 char o2[] = "tt state time command"; 62 char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command"; 63 char vfmt[] = 64 "pid state time sl re pagein vsz rss lim tsiz trs %cpu %mem command"; 65 66 kvm_t *kd; 67 68 main(argc, argv) 69 int argc; 70 char **argv; 71 { 72 extern char *optarg; 73 extern int optind; 74 register struct proc *p; 75 register struct kinfo_proc *kp; 76 register struct varent *vent; 77 int nentries; 78 register int i; 79 struct winsize ws; 80 dev_t ttydev; 81 int all, ch, flag, fmt, lineno, pid, prtheader, uid, what, xflg; 82 int pscomp(); 83 char *nlistf, *memf, *swapf; 84 char *kludge_oldps_options(); 85 char errbuf[80]; 86 87 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 88 ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 89 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || 90 ws.ws_col == 0) 91 termwidth = 79; 92 else 93 termwidth = ws.ws_col - 1; 94 95 if (argc > 1) 96 argv[1] = kludge_oldps_options(argv[1]); 97 98 fmt = 0; 99 all = xflg = 0; 100 pid = uid = -1; 101 ttydev = NODEV; 102 memf = nlistf = swapf = NULL; 103 while ((ch = getopt(argc, argv, 104 "aCeghjLlM:mN:O:o:p:rSTt:uvW:wx")) != EOF) 105 switch((char)ch) { 106 case 'a': 107 all = 1; 108 break; 109 case 'e': 110 /* XXX set ufmt */ 111 needenv = 1; 112 break; 113 case 'C': 114 rawcpu = 1; 115 break; 116 case 'g': 117 break; /* no-op */ 118 case 'h': 119 prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 120 break; 121 case 'j': 122 parsefmt(jfmt); 123 fmt = 1; 124 jfmt[0] = '\0'; 125 break; 126 case 'L': 127 showkey(); 128 exit(0); 129 case 'l': 130 parsefmt(lfmt); 131 fmt = 1; 132 lfmt[0] = '\0'; 133 break; 134 case 'M': 135 memf = optarg; 136 break; 137 case 'm': 138 sortby = SORTMEM; 139 break; 140 case 'N': 141 nlistf = optarg; 142 break; 143 case 'O': 144 parsefmt(o1); 145 parsefmt(optarg); 146 parsefmt(o2); 147 o1[0] = o2[0] = '\0'; 148 fmt = 1; 149 break; 150 case 'o': 151 parsefmt(optarg); 152 fmt = 1; 153 break; 154 case 'p': 155 pid = atoi(optarg); 156 xflg = 1; 157 break; 158 case 'r': 159 sortby = SORTCPU; 160 break; 161 case 'S': 162 sumrusage = 1; 163 break; 164 case 'T': 165 if ((optarg = ttyname(STDIN_FILENO)) == NULL) 166 err("stdin: not a terminal"); 167 /* FALLTHROUGH */ 168 case 't': { 169 char *ttypath; 170 struct stat stbuf; 171 char pathbuf[MAXPATHLEN]; 172 173 if (strcmp(optarg, "co") == 0) 174 ttypath = _PATH_CONSOLE; 175 else if (*optarg != '/') 176 (void) sprintf(ttypath = pathbuf, "%s%s", 177 _PATH_TTY, optarg); 178 else 179 ttypath = optarg; 180 if (stat(ttypath, &stbuf) == -1) 181 err("%s: %s", ttypath, strerror(errno)); 182 if (!S_ISCHR(stbuf.st_mode)) 183 err("%s: not a terminal", ttypath); 184 ttydev = stbuf.st_rdev; 185 break; 186 } 187 case 'u': 188 parsefmt(ufmt); 189 sortby = SORTCPU; 190 fmt = 1; 191 ufmt[0] = '\0'; 192 break; 193 case 'v': 194 parsefmt(vfmt); 195 sortby = SORTMEM; 196 fmt = 1; 197 vfmt[0] = '\0'; 198 break; 199 case 'W': 200 swapf = optarg; 201 break; 202 case 'w': 203 if (termwidth < 131) 204 termwidth = 131; 205 else 206 termwidth = UNLIMITED; 207 break; 208 case 'x': 209 xflg = 1; 210 break; 211 case '?': 212 default: 213 usage(); 214 } 215 argc -= optind; 216 argv += optind; 217 218 #define BACKWARD_COMPATIBILITY 219 #ifdef BACKWARD_COMPATIBILITY 220 if (*argv) { 221 222 nlistf = *argv; 223 if (*++argv) { 224 memf = *argv; 225 if (*++argv) 226 swapf = *argv; 227 } 228 } 229 #endif 230 /* 231 * Discard setgid privileges if not the running kernel so that bad 232 * guys can't print interesting stuff from kernel memory. 233 */ 234 if (nlistf != NULL || memf != NULL || swapf != NULL) 235 setgid(getgid()); 236 237 kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf); 238 if (kd == 0) 239 err("%s", errbuf); 240 241 if (!fmt) 242 parsefmt(dfmt); 243 244 if (!all && ttydev == NODEV && pid == -1) /* XXX - should be cleaner */ 245 uid = getuid(); 246 247 /* 248 * scan requested variables, noting what structures are needed, 249 * and adjusting header widths as appropiate. 250 */ 251 scanvars(); 252 /* 253 * get proc list 254 */ 255 if (uid != -1) { 256 what = KINFO_PROC_UID; 257 flag = uid; 258 } else if (ttydev != NODEV) { 259 what = KINFO_PROC_TTY; 260 flag = ttydev; 261 } else if (pid != -1) { 262 what = KINFO_PROC_PID; 263 flag = pid; 264 } else 265 what = KINFO_PROC_ALL; 266 /* 267 * select procs 268 */ 269 if ((kp = kvm_getprocs(kd, what, flag, &nentries)) == 0) 270 err("%s", kvm_geterr(kd)); 271 kinfo = malloc(nentries * sizeof(*kinfo)); 272 if (kinfo == NULL) 273 err("%s", strerror(errno)); 274 for (i = nentries; --i >= 0; ++kp) { 275 kinfo[i].ki_p = kp; 276 if (needuser) 277 saveuser(&kinfo[i]); 278 } 279 /* 280 * print header 281 */ 282 printheader(); 283 if (nentries == 0) 284 exit(0); 285 /* 286 * sort proc list 287 */ 288 qsort((void *)kinfo, nentries, sizeof(KINFO), pscomp); 289 /* 290 * for each proc, call each variable output function. 291 */ 292 for (i = lineno = 0; i < nentries; i++) { 293 if (xflg == 0 && (KI_EPROC(&kinfo[i])->e_tdev == NODEV || 294 (KI_PROC(&kinfo[i])->p_flag & SCTTY ) == 0)) 295 continue; 296 for (vent = vhead; vent; vent = vent->next) { 297 (*vent->var->oproc)(&kinfo[i], vent->var, vent->next); 298 if (vent->next != NULL) 299 (void) putchar(' '); 300 } 301 (void) putchar('\n'); 302 if (prtheader && lineno++ == prtheader-4) { 303 (void) putchar('\n'); 304 printheader(); 305 lineno = 0; 306 } 307 } 308 exit(eval); 309 } 310 311 scanvars() 312 { 313 register struct varent *vent; 314 register VAR *v; 315 register int i; 316 317 for (vent = vhead; vent; vent = vent->next) { 318 v = vent->var; 319 i = strlen(v->header); 320 if (v->width < i) 321 v->width = i; 322 totwidth += v->width + 1; /* +1 for space */ 323 if (v->flag & USER) 324 needuser = 1; 325 if (v->flag & COMM) 326 needcomm = 1; 327 } 328 totwidth--; 329 } 330 331 extern char *fmt_argv __P((char **, char *, int)); 332 333 static char * 334 fmt(fn, ki, comm, maxlen) 335 char **(*fn) __P((kvm_t *, const struct kinfo_proc *, int)); 336 KINFO *ki; 337 char *comm; 338 int maxlen; 339 { 340 register char *s; 341 342 s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen); 343 if (s == NULL) 344 err("%s", strerror(errno)); 345 return (s); 346 } 347 348 saveuser(ki) 349 KINFO *ki; 350 { 351 register struct usave *usp = &ki->ki_u; 352 struct pstats pstats; 353 extern char *fmt_argv(); 354 355 if (kvm_read(kd, (u_long)&KI_PROC(ki)->p_addr->u_stats, 356 (char *)&pstats, sizeof(pstats)) == sizeof(pstats)) { 357 /* 358 * The u-area might be swapped out, and we can't get 359 * at it because we have a crashdump and no swap. 360 * If it's here fill in these fields, otherwise, just 361 * leave them 0. 362 */ 363 usp->u_start = pstats.p_start; 364 usp->u_ru = pstats.p_ru; 365 usp->u_cru = pstats.p_cru; 366 usp->u_valid = 1; 367 } else 368 usp->u_valid = 0; 369 /* 370 * save arguments if needed 371 */ 372 if (needcomm) 373 ki->ki_args = fmt(kvm_getargv, ki, KI_PROC(ki)->p_comm, 374 MAXCOMLEN); 375 else 376 ki->ki_args = NULL; 377 if (needenv) 378 ki->ki_env = fmt(kvm_getenvv, ki, (char *)NULL, 0); 379 else 380 ki->ki_env = NULL; 381 } 382 383 pscomp(k1, k2) 384 KINFO *k1, *k2; 385 { 386 int i; 387 #ifdef NEWVM 388 #define VSIZE(k) (KI_EPROC(k)->e_vm.vm_dsize + KI_EPROC(k)->e_vm.vm_ssize + \ 389 KI_EPROC(k)->e_vm.vm_tsize) 390 #else 391 #define VSIZE(k) ((k)->ki_p->p_dsize + (k)->ki_p->p_ssize + (k)->ki_e->e_xsize) 392 #endif 393 394 if (sortby == SORTCPU) 395 return (getpcpu(k2) - getpcpu(k1)); 396 if (sortby == SORTMEM) 397 return (VSIZE(k2) - VSIZE(k1)); 398 i = KI_EPROC(k1)->e_tdev - KI_EPROC(k2)->e_tdev; 399 if (i == 0) 400 i = KI_PROC(k1)->p_pid - KI_PROC(k2)->p_pid; 401 return (i); 402 } 403 404 /* 405 * ICK (all for getopt), would rather hide the ugliness 406 * here than taint the main code. 407 * 408 * ps foo -> ps -foo 409 * ps 34 -> ps -p34 410 * 411 * The old convention that 't' with no trailing tty arg means the users 412 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 413 * feature is available with the option 'T', which takes no argument. 414 */ 415 char * 416 kludge_oldps_options(s) 417 char *s; 418 { 419 size_t len; 420 char *newopts, *ns, *cp; 421 422 len = strlen(s); 423 if ((newopts = ns = malloc(len + 2)) == NULL) 424 err("%s", strerror(errno)); 425 /* 426 * options begin with '-' 427 */ 428 if (*s != '-') 429 *ns++ = '-'; /* add option flag */ 430 /* 431 * gaze to end of argv[1] 432 */ 433 cp = s + len - 1; 434 /* 435 * if last letter is a 't' flag with no argument (in the context 436 * of the oldps options -- option string NOT starting with a '-' -- 437 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 438 */ 439 if (*cp == 't' && *s != '-') 440 *cp = 'T'; 441 else { 442 /* 443 * otherwise check for trailing number, which *may* be a 444 * pid. 445 */ 446 while (cp >= s && isdigit(*cp)) 447 --cp; 448 } 449 cp++; 450 bcopy(s, ns, (size_t)(cp - s)); /* copy up to trailing number */ 451 ns += cp - s; 452 /* 453 * if there's a trailing number, and not a preceding 'p' (pid) or 454 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 455 */ 456 if (isdigit(*cp) && (cp == s || cp[-1] != 't' && cp[-1] != 'p' && 457 (cp - 1 == s || cp[-2] != 't'))) 458 *ns++ = 'p'; 459 (void) strcpy(ns, cp); /* and append the number */ 460 461 return (newopts); 462 } 463 464 #if __STDC__ 465 #include <stdarg.h> 466 #else 467 #include <varargs.h> 468 #endif 469 470 void 471 #if __STDC__ 472 err(const char *fmt, ...) 473 #else 474 err(fmt, va_alist) 475 char *fmt; 476 va_dcl 477 #endif 478 { 479 va_list ap; 480 #if __STDC__ 481 va_start(ap, fmt); 482 #else 483 va_start(ap); 484 #endif 485 (void)fprintf(stderr, "ps: "); 486 (void)vfprintf(stderr, fmt, ap); 487 va_end(ap); 488 (void)fprintf(stderr, "\n"); 489 exit(1); 490 /* NOTREACHED */ 491 } 492 493 usage() 494 { 495 (void) fprintf(stderr, 496 "usage: ps [-aChjlmrSTuvwx] [-O|o fmt] [-p pid] [-t tty]\n\t [-M core] [-N system] [-W swap]\n ps [-L]\n"); 497 exit(1); 498 } 499