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