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