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