1 /* $OpenBSD: ps.c,v 1.47 2009/10/27 23:59:22 deraadt Exp $ */ 2 /* $NetBSD: ps.c,v 1.15 1995/05/18 20:33:25 mycroft Exp $ */ 3 4 /*- 5 * Copyright (c) 1990, 1993, 1994 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/user.h> 35 #include <sys/time.h> 36 #include <sys/resource.h> 37 #include <sys/proc.h> 38 #include <sys/stat.h> 39 #include <sys/ioctl.h> 40 #include <sys/sysctl.h> 41 #include <sys/types.h> 42 43 #include <ctype.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <kvm.h> 48 #include <nlist.h> 49 #include <paths.h> 50 #include <pwd.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 #include <limits.h> 56 57 #include "ps.h" 58 59 extern char *__progname; 60 61 struct varent *vhead; 62 63 int eval; /* exit value */ 64 int rawcpu; /* -C */ 65 int sumrusage; /* -S */ 66 int termwidth; /* width of screen (0 == infinity) */ 67 int totwidth; /* calculated width of requested variables */ 68 69 int ncpu = 1; 70 71 int needcomm, needenv, neednlist, commandonly; 72 73 enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; 74 75 static char *kludge_oldps_options(char *); 76 static int pscomp(const void *, const void *); 77 static void scanvars(void); 78 static void usage(void); 79 80 char dfmt[] = "pid tt state time command"; 81 char jfmt[] = "user pid ppid pgid sess jobc state tt time command"; 82 char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command"; 83 char o1[] = "pid"; 84 char o2[] = "tt state time command"; 85 char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command"; 86 char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command"; 87 88 kvm_t *kd; 89 int kvm_sysctl_only; 90 91 int 92 main(int argc, char *argv[]) 93 { 94 struct kinfo_proc2 *kp, **kinfo; 95 struct varent *vent; 96 struct winsize ws; 97 struct passwd *pwd; 98 dev_t ttydev; 99 pid_t pid; 100 uid_t uid; 101 int all, ch, flag, i, fmt, lineno, nentries, mib[6]; 102 int prtheader, wflag, kflag, what, Uflag, xflg; 103 char *nlistf, *memf, *swapf, errbuf[_POSIX2_LINE_MAX]; 104 size_t size; 105 106 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 && 107 ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 && 108 ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || 109 ws.ws_col == 0) 110 termwidth = 79; 111 else 112 termwidth = ws.ws_col - 1; 113 114 if (argc > 1) 115 argv[1] = kludge_oldps_options(argv[1]); 116 117 all = fmt = prtheader = wflag = kflag = Uflag = xflg = 0; 118 pid = -1; 119 uid = 0; 120 ttydev = NODEV; 121 memf = nlistf = swapf = NULL; 122 while ((ch = getopt(argc, argv, 123 "acCeghjkLlM:mN:O:o:p:rSTt:U:uvW:wx")) != -1) 124 switch (ch) { 125 case 'a': 126 all = 1; 127 break; 128 case 'c': 129 commandonly = 1; 130 break; 131 case 'e': /* XXX set ufmt */ 132 needenv = 1; 133 break; 134 case 'C': 135 rawcpu = 1; 136 break; 137 case 'g': 138 break; /* no-op */ 139 case 'h': 140 prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 141 break; 142 case 'j': 143 parsefmt(jfmt); 144 fmt = 1; 145 jfmt[0] = '\0'; 146 break; 147 case 'k': 148 kflag++; 149 break; 150 case 'L': 151 showkey(); 152 exit(0); 153 case 'l': 154 parsefmt(lfmt); 155 fmt = 1; 156 lfmt[0] = '\0'; 157 break; 158 case 'M': 159 memf = optarg; 160 break; 161 case 'm': 162 sortby = SORTMEM; 163 break; 164 case 'N': 165 nlistf = optarg; 166 break; 167 case 'O': 168 parsefmt(o1); 169 parsefmt(optarg); 170 parsefmt(o2); 171 o1[0] = o2[0] = '\0'; 172 fmt = 1; 173 break; 174 case 'o': 175 parsefmt(optarg); 176 fmt = 1; 177 break; 178 case 'p': 179 pid = atol(optarg); 180 xflg = 1; 181 break; 182 case 'r': 183 sortby = SORTCPU; 184 break; 185 case 'S': 186 sumrusage = 1; 187 break; 188 case 'T': 189 if ((optarg = ttyname(STDIN_FILENO)) == NULL) 190 errx(1, "stdin: not a terminal"); 191 /* FALLTHROUGH */ 192 case 't': { 193 struct stat sb; 194 char *ttypath, pathbuf[MAXPATHLEN]; 195 196 if (strcmp(optarg, "co") == 0) 197 ttypath = _PATH_CONSOLE; 198 else if (*optarg != '/') 199 (void)snprintf(ttypath = pathbuf, 200 sizeof(pathbuf), "%s%s", _PATH_TTY, optarg); 201 else 202 ttypath = optarg; 203 if (stat(ttypath, &sb) == -1) 204 err(1, "%s", ttypath); 205 if (!S_ISCHR(sb.st_mode)) 206 errx(1, "%s: not a terminal", ttypath); 207 ttydev = sb.st_rdev; 208 break; 209 } 210 case 'U': 211 pwd = getpwnam(optarg); 212 if (pwd == NULL) 213 errx(1, "%s: no such user", optarg); 214 uid = pwd->pw_uid; 215 endpwent(); 216 Uflag = xflg = 1; 217 break; 218 case 'u': 219 parsefmt(ufmt); 220 sortby = SORTCPU; 221 fmt = 1; 222 ufmt[0] = '\0'; 223 break; 224 case 'v': 225 parsefmt(vfmt); 226 sortby = SORTMEM; 227 fmt = 1; 228 vfmt[0] = '\0'; 229 break; 230 case 'W': 231 swapf = optarg; 232 break; 233 case 'w': 234 if (wflag) 235 termwidth = UNLIMITED; 236 else if (termwidth < 131) 237 termwidth = 131; 238 wflag++; 239 break; 240 case 'x': 241 xflg = 1; 242 break; 243 default: 244 usage(); 245 } 246 argc -= optind; 247 argv += optind; 248 249 #define BACKWARD_COMPATIBILITY 250 #ifdef BACKWARD_COMPATIBILITY 251 if (*argv) { 252 nlistf = *argv; 253 if (*++argv) { 254 memf = *argv; 255 if (*++argv) 256 swapf = *argv; 257 } 258 } 259 #endif 260 261 if (nlistf == NULL && memf == NULL && swapf == NULL) { 262 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); 263 kvm_sysctl_only = 1; 264 } else { 265 kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf); 266 } 267 if (kd == NULL && (nlistf != NULL || memf != NULL || swapf != NULL)) 268 errx(1, "%s", errbuf); 269 270 if (!fmt) 271 parsefmt(dfmt); 272 273 /* XXX - should be cleaner */ 274 if (!all && ttydev == NODEV && pid == -1 && !Uflag) { 275 uid = getuid(); 276 Uflag = 1; 277 } 278 279 /* 280 * scan requested variables, noting what structures are needed, 281 * and adjusting header widths as appropriate. 282 */ 283 scanvars(); 284 285 if (neednlist && !nlistread) 286 (void) donlist(); 287 288 /* 289 * get proc list 290 */ 291 if (Uflag) { 292 what = KERN_PROC_UID; 293 flag = uid; 294 } else if (ttydev != NODEV) { 295 what = KERN_PROC_TTY; 296 flag = ttydev; 297 } else if (pid != -1) { 298 what = KERN_PROC_PID; 299 flag = pid; 300 } else if (kflag) { 301 what = KERN_PROC_KTHREAD; 302 flag = 0; 303 } else { 304 what = KERN_PROC_ALL; 305 flag = 0; 306 } 307 308 mib[0] = CTL_HW; 309 mib[1] = HW_NCPU; 310 size = sizeof(ncpu); 311 (void) sysctl(mib, 2, &ncpu, &size, NULL, 0); 312 313 /* 314 * select procs 315 */ 316 if (kd != NULL) { 317 kp = kvm_getproc2(kd, what, flag, sizeof(*kp), &nentries); 318 if (kp == NULL) 319 errx(1, "%s", kvm_geterr(kd)); 320 } else { 321 mib[0] = CTL_KERN; 322 mib[1] = KERN_PROC2; 323 mib[2] = what; 324 mib[3] = flag; 325 mib[4] = sizeof(struct kinfo_proc2); 326 mib[5] = 0; 327 retry: 328 if (sysctl(mib, 6, NULL, &size, NULL, 0) < 0) 329 err(1, "could not get kern.proc2 size"); 330 size = 5 * size / 4; /* extra slop */ 331 if ((kp = malloc(size)) == NULL) 332 err(1, 333 "failed to allocate memory for proc structures"); 334 mib[5] = (int)(size / sizeof(struct kinfo_proc2)); 335 if (sysctl(mib, 6, kp, &size, NULL, 0) < 0) { 336 if (errno == ENOMEM) { 337 free(kp); 338 goto retry; 339 } 340 err(1, "could not read kern.proc2"); 341 } 342 nentries = (int)(size / sizeof(struct kinfo_proc2)); 343 } 344 /* 345 * print header 346 */ 347 printheader(); 348 if (nentries == 0) 349 exit(1); 350 /* 351 * sort proc list, we convert from an array of structs to an array 352 * of pointers to make the sort cheaper. 353 */ 354 if ((kinfo = calloc(sizeof(*kinfo), nentries)) == NULL) 355 err(1, "failed to allocate memory for proc pointers"); 356 for (i = 0; i < nentries; i++) 357 kinfo[i] = &kp[i]; 358 qsort(kinfo, nentries, sizeof(*kinfo), pscomp); 359 /* 360 * for each proc, call each variable output function. 361 */ 362 for (i = lineno = 0; i < nentries; i++) { 363 if (xflg == 0 && ((int)kinfo[i]->p_tdev == NODEV || 364 (kinfo[i]->p_flag & P_CONTROLT ) == 0)) 365 continue; 366 for (vent = vhead; vent; vent = vent->next) { 367 (vent->var->oproc)(kinfo[i], vent); 368 if (vent->next != NULL) 369 (void)putchar(' '); 370 } 371 (void)putchar('\n'); 372 if (prtheader && lineno++ == prtheader - 4) { 373 (void)putchar('\n'); 374 printheader(); 375 lineno = 0; 376 } 377 } 378 exit(eval); 379 } 380 381 static void 382 scanvars(void) 383 { 384 struct varent *vent; 385 VAR *v; 386 int i; 387 388 for (vent = vhead; vent; vent = vent->next) { 389 v = vent->var; 390 i = strlen(v->header); 391 if (v->width < i) 392 v->width = i; 393 totwidth += v->width + 1; /* +1 for space */ 394 if (v->flag & COMM) 395 needcomm = 1; 396 if (v->flag & NLIST) 397 neednlist = 1; 398 } 399 totwidth--; 400 } 401 402 static int 403 pscomp(const void *v1, const void *v2) 404 { 405 const struct kinfo_proc2 *kp1 = *(const struct kinfo_proc2 **)v1; 406 const struct kinfo_proc2 *kp2 = *(const struct kinfo_proc2 **)v2; 407 int i; 408 #define VSIZE(k) ((k)->p_vm_dsize + (k)->p_vm_ssize + (k)->p_vm_tsize) 409 410 if (sortby == SORTCPU && (i = getpcpu(kp2) - getpcpu(kp1)) != 0) 411 return (i); 412 if (sortby == SORTMEM && (i = VSIZE(kp2) - VSIZE(kp1)) != 0) 413 return (i); 414 if ((i = kp1->p_tdev - kp2->p_tdev) == 0 && 415 (i = kp1->p_ustart_sec - kp2->p_ustart_sec) == 0) 416 i = kp1->p_ustart_usec - kp2->p_ustart_usec; 417 return (i); 418 } 419 420 /* 421 * ICK (all for getopt), would rather hide the ugliness 422 * here than taint the main code. 423 * 424 * ps foo -> ps -foo 425 * ps 34 -> ps -p34 426 * 427 * The old convention that 't' with no trailing tty arg means the users 428 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 429 * feature is available with the option 'T', which takes no argument. 430 */ 431 static char * 432 kludge_oldps_options(char *s) 433 { 434 size_t len; 435 char *newopts, *ns, *cp; 436 437 len = strlen(s); 438 if ((newopts = ns = malloc(2 + len + 1)) == NULL) 439 err(1, NULL); 440 /* 441 * options begin with '-' 442 */ 443 if (*s != '-') 444 *ns++ = '-'; /* add option flag */ 445 446 /* 447 * gaze to end of argv[1] 448 */ 449 cp = s + len - 1; 450 /* 451 * if last letter is a 't' flag with no argument (in the context 452 * of the oldps options -- option string NOT starting with a '-' -- 453 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 454 */ 455 if (*cp == 't' && *s != '-') 456 *cp = 'T'; 457 else { 458 /* 459 * otherwise check for trailing number, which *may* be a 460 * pid. 461 */ 462 while (cp >= s && isdigit(*cp)) 463 --cp; 464 } 465 cp++; 466 memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */ 467 ns += cp - s; 468 /* 469 * if there's a trailing number, and not a preceding 'p' (pid) or 470 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 471 */ 472 if (isdigit(*cp) && (cp == s || (cp[-1] != 't' && cp[-1] != 'p' && 473 (cp - 1 == s || cp[-2] != 't')))) 474 *ns++ = 'p'; 475 /* and append the number */ 476 (void)strlcpy(ns, cp, newopts + len + 3 - ns); 477 478 return (newopts); 479 } 480 481 static void 482 usage(void) 483 { 484 (void)fprintf(stderr, 485 "usage: %s [-aCcehjkLlmrSTuvwx] [-M core] [-N system] [-O fmt] [-o fmt] [-p pid]\n", 486 __progname); 487 (void)fprintf(stderr, 488 "%-*s[-t tty] [-U username] [-W swap]\n", (int)strlen(__progname) + 8, ""); 489 exit(1); 490 } 491