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.48 (Berkeley) 07/02/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 <ctype.h> 36 #include "ps.h" 37 38 #ifdef SPPWAIT 39 #define NEWVM 40 #endif 41 42 KINFO *kinfo; 43 struct varent *vhead, *vtail; 44 45 int eval; /* exit value */ 46 int rawcpu; /* -C */ 47 int sumrusage; /* -S */ 48 int termwidth; /* width of screen (0 == infinity) */ 49 int totwidth; /* calculated width of requested variables */ 50 51 static int needuser, needcomm, needenv; 52 53 enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 54 55 static char *fmt __P((char **(*)(kvm_t *, const struct kinfo_proc *, int), 56 KINFO *, char *, int)); 57 static char *kludge_oldps_options __P((char *)); 58 static int pscomp __P((const void *, const void *)); 59 static void saveuser __P((KINFO *)); 60 static void scanvars __P((void)); 61 static void usage __P((void)); 62 63 char dfmt[] = "pid tt state time command"; 64 char jfmt[] = "user pid ppid pgid sess jobc state tt time command"; 65 char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command"; 66 char o1[] = "pid"; 67 char o2[] = "tt state time command"; 68 char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command"; 69 char vfmt[] = 70 "pid state time sl re pagein vsz rss lim tsiz trs %cpu %mem command"; 71 72 kvm_t *kd; 73 74 int 75 main(argc, argv) 76 int argc; 77 char *argv[]; 78 { 79 register struct kinfo_proc *kp; 80 register struct varent *vent; 81 int nentries; 82 register int i; 83 struct winsize ws; 84 dev_t ttydev; 85 int all, ch, flag, fmt, lineno, pid, prtheader, uid, wflag, what, xflg; 86 char *nlistf, *memf, *swapf, errbuf[256]; 87 88 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 89 ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 90 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || 91 ws.ws_col == 0) 92 termwidth = 79; 93 else 94 termwidth = ws.ws_col - 1; 95 96 if (argc > 1) 97 argv[1] = kludge_oldps_options(argv[1]); 98 99 all = fmt = wflag = 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 struct stat sb; 170 char *ttypath, pathbuf[MAXPATHLEN]; 171 172 if (strcmp(optarg, "co") == 0) 173 ttypath = _PATH_CONSOLE; 174 else if (*optarg != '/') 175 (void)snprintf(ttypath = pathbuf, 176 sizeof(pathbuf), "%s%s", _PATH_TTY, optarg); 177 else 178 ttypath = optarg; 179 if (stat(ttypath, &sb) == -1) 180 err("%s: %s", ttypath, strerror(errno)); 181 if (!S_ISCHR(sb.st_mode)) 182 err("%s: not a terminal", ttypath); 183 ttydev = sb.st_rdev; 184 break; 185 } 186 case 'u': 187 parsefmt(ufmt); 188 sortby = SORTCPU; 189 fmt = 1; 190 ufmt[0] = '\0'; 191 break; 192 case 'v': 193 parsefmt(vfmt); 194 sortby = SORTMEM; 195 fmt = 1; 196 vfmt[0] = '\0'; 197 break; 198 case 'W': 199 swapf = optarg; 200 break; 201 case 'w': 202 if (wflag) 203 termwidth = UNLIMITED; 204 else if (termwidth < 131) 205 termwidth = 131; 206 wflag++; 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(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); 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 static void 312 scanvars() 313 { 314 register struct varent *vent; 315 register VAR *v; 316 register int i; 317 318 for (vent = vhead; vent; vent = vent->next) { 319 v = vent->var; 320 i = strlen(v->header); 321 if (v->width < i) 322 v->width = i; 323 totwidth += v->width + 1; /* +1 for space */ 324 if (v->flag & USER) 325 needuser = 1; 326 if (v->flag & COMM) 327 needcomm = 1; 328 } 329 totwidth--; 330 } 331 332 static char * 333 fmt(fn, ki, comm, maxlen) 334 char **(*fn) __P((kvm_t *, const struct kinfo_proc *, int)); 335 KINFO *ki; 336 char *comm; 337 int maxlen; 338 { 339 register char *s; 340 341 s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen); 342 if (s == NULL) 343 err("%s", strerror(errno)); 344 return (s); 345 } 346 347 static void 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 static int 384 pscomp(a, b) 385 const void *a, *b; 386 { 387 int i; 388 #ifdef NEWVM 389 #define VSIZE(k) (KI_EPROC(k)->e_vm.vm_dsize + KI_EPROC(k)->e_vm.vm_ssize + \ 390 KI_EPROC(k)->e_vm.vm_tsize) 391 #else 392 #define VSIZE(k) ((k)->ki_p->p_dsize + (k)->ki_p->p_ssize + (k)->ki_e->e_xsize) 393 #endif 394 395 if (sortby == SORTCPU) 396 return (getpcpu((KINFO *)b) - getpcpu((KINFO *)a)); 397 if (sortby == SORTMEM) 398 return (VSIZE((KINFO *)b) - VSIZE((KINFO *)a)); 399 i = KI_EPROC((KINFO *)a)->e_tdev - KI_EPROC((KINFO *)b)->e_tdev; 400 if (i == 0) 401 i = KI_PROC((KINFO *)a)->p_pid - KI_PROC((KINFO *)b)->p_pid; 402 return (i); 403 } 404 405 /* 406 * ICK (all for getopt), would rather hide the ugliness 407 * here than taint the main code. 408 * 409 * ps foo -> ps -foo 410 * ps 34 -> ps -p34 411 * 412 * The old convention that 't' with no trailing tty arg means the users 413 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 414 * feature is available with the option 'T', which takes no argument. 415 */ 416 static char * 417 kludge_oldps_options(s) 418 char *s; 419 { 420 size_t len; 421 char *newopts, *ns, *cp; 422 423 len = strlen(s); 424 if ((newopts = ns = malloc(len + 2)) == NULL) 425 err("%s", strerror(errno)); 426 /* 427 * options begin with '-' 428 */ 429 if (*s != '-') 430 *ns++ = '-'; /* add option flag */ 431 /* 432 * gaze to end of argv[1] 433 */ 434 cp = s + len - 1; 435 /* 436 * if last letter is a 't' flag with no argument (in the context 437 * of the oldps options -- option string NOT starting with a '-' -- 438 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 439 */ 440 if (*cp == 't' && *s != '-') 441 *cp = 'T'; 442 else { 443 /* 444 * otherwise check for trailing number, which *may* be a 445 * pid. 446 */ 447 while (cp >= s && isdigit(*cp)) 448 --cp; 449 } 450 cp++; 451 bcopy(s, ns, (size_t)(cp - s)); /* copy up to trailing number */ 452 ns += cp - s; 453 /* 454 * if there's a trailing number, and not a preceding 'p' (pid) or 455 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 456 */ 457 if (isdigit(*cp) && (cp == s || cp[-1] != 't' && cp[-1] != 'p' && 458 (cp - 1 == s || cp[-2] != 't'))) 459 *ns++ = 'p'; 460 (void)strcpy(ns, cp); /* and append the number */ 461 462 return (newopts); 463 } 464 465 #if __STDC__ 466 #include <stdarg.h> 467 #else 468 #include <varargs.h> 469 #endif 470 471 void 472 #if __STDC__ 473 err(const char *fmt, ...) 474 #else 475 err(fmt, va_alist) 476 char *fmt; 477 va_dcl 478 #endif 479 { 480 va_list ap; 481 #if __STDC__ 482 va_start(ap, fmt); 483 #else 484 va_start(ap); 485 #endif 486 (void)fprintf(stderr, "ps: "); 487 (void)vfprintf(stderr, fmt, ap); 488 va_end(ap); 489 (void)fprintf(stderr, "\n"); 490 exit(1); 491 /* NOTREACHED */ 492 } 493 494 static void 495 usage() 496 { 497 (void)fprintf(stderr, 498 "usage: ps [-aChjlmrSTuvwx] [-O|o fmt] [-p pid] [-t tty]\n\t [-M core] [-N system] [-W swap]\n ps [-L]\n"); 499 exit(1); 500 } 501