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.43 (Berkeley) 07/01/91"; 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 if (kvm_openfiles(nlistf, memf, swapf) == -1) 222 err("kvm_openfiles: %s", kvm_geterr()); 223 224 if (!fmt) 225 parsefmt(dfmt); 226 227 if (!all && ttydev == NODEV && pid == -1) /* XXX - should be cleaner */ 228 uid = getuid(); 229 230 /* 231 * scan requested variables, noting what structures are needed, 232 * and adjusting header widths as appropiate. 233 */ 234 scanvars(); 235 /* 236 * get proc list 237 */ 238 if (uid != -1) { 239 what = KINFO_PROC_UID; 240 flag = uid; 241 } else if (ttydev != NODEV) { 242 what = KINFO_PROC_TTY; 243 flag = ttydev; 244 } else if (pid != -1) { 245 what = KINFO_PROC_PID; 246 flag = pid; 247 } else 248 what = KINFO_PROC_ALL; 249 /* 250 * select procs 251 */ 252 if ((nentries = kvm_getprocs(what, flag)) == -1) 253 err("%s", kvm_geterr()); 254 kinfo = malloc(nentries * sizeof(KINFO)); 255 if (kinfo == NULL) 256 err("%s", strerror(errno)); 257 for (nentries = 0; p = kvm_nextproc(); ++nentries) { 258 kinfo[nentries].ki_p = p; 259 kinfo[nentries].ki_e = kvm_geteproc(p); 260 if (needuser) 261 saveuser(&kinfo[nentries]); 262 } 263 /* 264 * print header 265 */ 266 printheader(); 267 if (nentries == 0) 268 exit(0); 269 /* 270 * sort proc list 271 */ 272 qsort((void *)kinfo, nentries, sizeof(KINFO), pscomp); 273 /* 274 * for each proc, call each variable output function. 275 */ 276 for (i = lineno = 0; i < nentries; i++) { 277 if (xflg == 0 && (kinfo[i].ki_e->e_tdev == NODEV || 278 (kinfo[i].ki_p->p_flag & SCTTY ) == 0)) 279 continue; 280 for (vent = vhead; vent; vent = vent->next) { 281 (*vent->var->oproc)(&kinfo[i], vent->var, vent->next); 282 if (vent->next != NULL) 283 (void) putchar(' '); 284 } 285 (void) putchar('\n'); 286 if (prtheader && lineno++ == prtheader-4) { 287 (void) putchar('\n'); 288 printheader(); 289 lineno = 0; 290 } 291 } 292 exit(eval); 293 } 294 295 scanvars() 296 { 297 register struct varent *vent; 298 register VAR *v; 299 register int i; 300 301 for (vent = vhead; vent; vent = vent->next) { 302 v = vent->var; 303 i = strlen(v->header); 304 if (v->width < i) 305 v->width = i; 306 totwidth += v->width + 1; /* +1 for space */ 307 if (v->flag & USER) 308 needuser = 1; 309 if (v->flag & COMM) 310 needcomm = 1; 311 } 312 totwidth--; 313 } 314 315 316 /* XXX - redo */ 317 saveuser(ki) 318 KINFO *ki; 319 { 320 register struct usave *usp; 321 register struct user *up; 322 323 if ((usp = calloc(1, sizeof(struct usave))) == NULL) 324 err("%s", strerror(errno)); 325 up = kvm_getu(ki->ki_p); 326 /* 327 * save arguments if needed 328 */ 329 ki->ki_args = needcomm ? strdup(kvm_getargs(ki->ki_p, up)) : NULL; 330 if (up != NULL) { 331 ki->ki_u = usp; 332 /* 333 * save important fields 334 */ 335 #ifdef NEWVM 336 usp->u_start = up->u_stats.p_start; 337 usp->u_ru = up->u_stats.p_ru; 338 usp->u_cru = up->u_stats.p_cru; 339 #else 340 usp->u_procp = up->u_procp; 341 usp->u_start = up->u_start; 342 usp->u_ru = up->u_ru; 343 usp->u_cru = up->u_cru; 344 usp->u_acflag = up->u_acflag; 345 #endif 346 } else 347 free(usp); 348 } 349 350 pscomp(k1, k2) 351 KINFO *k1, *k2; 352 { 353 int i; 354 #ifdef NEWVM 355 #define VSIZE(k) ((k)->ki_e->e_vm.vm_dsize + (k)->ki_e->e_vm.vm_ssize + \ 356 (k)->ki_e->e_vm.vm_tsize) 357 #else 358 #define VSIZE(k) ((k)->ki_p->p_dsize + (k)->ki_p->p_ssize + (k)->ki_e->e_xsize) 359 #endif 360 361 if (sortby == SORTCPU) 362 return (getpcpu(k2) - getpcpu(k1)); 363 if (sortby == SORTMEM) 364 return (VSIZE(k2) - VSIZE(k1)); 365 i = k1->ki_e->e_tdev - k2->ki_e->e_tdev; 366 if (i == 0) 367 i = k1->ki_p->p_pid - k2->ki_p->p_pid; 368 return (i); 369 } 370 371 /* 372 * ICK (all for getopt), would rather hide the ugliness 373 * here than taint the main code. 374 * 375 * ps foo -> ps -foo 376 * ps 34 -> ps -p34 377 * 378 * The old convention that 't' with no trailing tty arg means the users 379 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 380 * feature is available with the option 'T', which takes no argument. 381 */ 382 char * 383 kludge_oldps_options(s) 384 char *s; 385 { 386 size_t len; 387 char *newopts, *ns, *cp; 388 389 len = strlen(s); 390 if ((newopts = ns = malloc(len + 2)) == NULL) 391 err("%s", strerror(errno)); 392 /* 393 * options begin with '-' 394 */ 395 if (*s != '-') 396 *ns++ = '-'; /* add option flag */ 397 /* 398 * gaze to end of argv[1] 399 */ 400 cp = s + len - 1; 401 /* 402 * if last letter is a 't' flag with no argument (in the context 403 * of the oldps options -- option string NOT starting with a '-' -- 404 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 405 */ 406 if (*cp == 't' && *s != '-') 407 *cp = 'T'; 408 else { 409 /* 410 * otherwise check for trailing number, which *may* be a 411 * pid. 412 */ 413 while (cp >= s && isdigit(*cp)) 414 --cp; 415 } 416 cp++; 417 bcopy(s, ns, (size_t)(cp - s)); /* copy up to trailing number */ 418 ns += cp - s; 419 /* 420 * if there's a trailing number, and not a preceding 'p' (pid) or 421 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 422 */ 423 if (isdigit(*cp) && (cp == s || cp[-1] != 't' && cp[-1] != 'p' && 424 (cp - 1 == s || cp[-2] != 't'))) 425 *ns++ = 'p'; 426 (void) strcpy(ns, cp); /* and append the number */ 427 428 return (newopts); 429 } 430 431 #if __STDC__ 432 #include <stdarg.h> 433 #else 434 #include <varargs.h> 435 #endif 436 437 void 438 #if __STDC__ 439 err(const char *fmt, ...) 440 #else 441 err(fmt, va_alist) 442 char *fmt; 443 va_dcl 444 #endif 445 { 446 va_list ap; 447 #if __STDC__ 448 va_start(ap, fmt); 449 #else 450 va_start(ap); 451 #endif 452 (void)fprintf(stderr, "ps: "); 453 (void)vfprintf(stderr, fmt, ap); 454 va_end(ap); 455 (void)fprintf(stderr, "\n"); 456 exit(1); 457 /* NOTREACHED */ 458 } 459 460 usage() 461 { 462 (void) fprintf(stderr, 463 "usage: ps [-aChjlmrSTuvwx] [-O|o fmt] [-p pid] [-t tty]\n\t [-M core] [-N system] [-W swap]\n ps [-L]\n"); 464 exit(1); 465 } 466