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