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