1 /* $NetBSD: ps.c,v 1.83 2015/06/16 22:31:08 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2000-2008 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Simon Burge. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Copyright (c) 1990, 1993, 1994 34 * The Regents of the University of California. All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 3. Neither the name of the University nor the names of its contributors 45 * may be used to endorse or promote products derived from this software 46 * without specific prior written permission. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 58 * SUCH DAMAGE. 59 */ 60 61 #include <sys/cdefs.h> 62 #ifndef lint 63 __COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\ 64 The Regents of the University of California. All rights reserved."); 65 #endif /* not lint */ 66 67 #ifndef lint 68 #if 0 69 static char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94"; 70 #else 71 __RCSID("$NetBSD: ps.c,v 1.83 2015/06/16 22:31:08 christos Exp $"); 72 #endif 73 #endif /* not lint */ 74 75 #include <sys/param.h> 76 #include <sys/time.h> 77 #include <sys/resource.h> 78 #include <sys/lwp.h> 79 #include <sys/proc.h> 80 #include <sys/stat.h> 81 #include <sys/ioctl.h> 82 #include <sys/sysctl.h> 83 84 #include <stddef.h> 85 #include <ctype.h> 86 #include <err.h> 87 #include <errno.h> 88 #include <fcntl.h> 89 #include <kvm.h> 90 #include <limits.h> 91 #include <locale.h> 92 #include <nlist.h> 93 #include <paths.h> 94 #include <pwd.h> 95 #include <stdio.h> 96 #include <stdlib.h> 97 #include <string.h> 98 #include <unistd.h> 99 100 #include "ps.h" 101 102 /* 103 * ARGOPTS must contain all option characters that take arguments 104 * (except for 't'!) - it is used in kludge_oldps_options() 105 */ 106 #define GETOPTSTR "aAcCeghjk:LlM:mN:O:o:p:rSsTt:U:uvW:wx" 107 #define ARGOPTS "kMNOopUW" 108 109 struct kinfo_proc2 *kinfo; 110 struct varlist displaylist = SIMPLEQ_HEAD_INITIALIZER(displaylist); 111 struct varlist sortlist = SIMPLEQ_HEAD_INITIALIZER(sortlist); 112 113 int eval; /* exit value */ 114 int rawcpu; /* -C */ 115 int sumrusage; /* -S */ 116 int termwidth; /* width of screen (0 == infinity) */ 117 int totwidth; /* calculated width of requested variables */ 118 119 int needcomm, needenv, commandonly; 120 uid_t myuid; 121 122 static struct kinfo_lwp 123 *pick_representative_lwp(struct kinfo_proc2 *, 124 struct kinfo_lwp *, int); 125 static struct kinfo_proc2 126 *getkinfo_kvm(kvm_t *, int, int, int *); 127 static char *kludge_oldps_options(char *); 128 static int pscomp(const void *, const void *); 129 static void scanvars(void); 130 __dead static void usage(void); 131 static int parsenum(const char *, const char *); 132 int main(int, char *[]); 133 134 char dfmt[] = "pid tt state time command"; 135 char jfmt[] = "user pid ppid pgid sess jobc state tt time command"; 136 char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command"; 137 char sfmt[] = "uid pid ppid cpu lid nlwp pri nice vsz rss wchan lstate tt " 138 "ltime command"; 139 char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command"; 140 char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command"; 141 142 const char *default_fmt = dfmt; 143 144 struct varent *Opos = NULL; /* -O flag inserts after this point */ 145 146 kvm_t *kd; 147 148 static long long 149 ttyname2dev(const char *ttname, int *xflg, int *what) 150 { 151 struct stat sb; 152 const char *ttypath; 153 char pathbuf[MAXPATHLEN]; 154 155 ttypath = NULL; 156 if (strcmp(ttname, "?") == 0) { 157 *xflg = 1; 158 return KERN_PROC_TTY_NODEV; 159 } 160 if (strcmp(ttname, "-") == 0) 161 return KERN_PROC_TTY_REVOKE; 162 163 if (strcmp(ttname, "co") == 0) 164 ttypath = _PATH_CONSOLE; 165 else if (strncmp(ttname, "pts/", 4) == 0 || 166 strncmp(ttname, "tty", 3) == 0) { 167 (void)snprintf(pathbuf, 168 sizeof(pathbuf), "%s%s", _PATH_DEV, ttname); 169 ttypath = pathbuf; 170 } else if (*ttname != '/') { 171 (void)snprintf(pathbuf, 172 sizeof(pathbuf), "%s%s", _PATH_TTY, ttname); 173 ttypath = pathbuf; 174 } else 175 ttypath = ttname; 176 *what = KERN_PROC_TTY; 177 if (stat(ttypath, &sb) == -1) { 178 devmajor_t pts; 179 int serrno; 180 181 serrno = errno; 182 pts = getdevmajor("pts", S_IFCHR); 183 if (pts != NODEVMAJOR && strncmp(ttname, "pts/", 4) == 0) { 184 int ptsminor = atoi(ttname + 4); 185 186 snprintf(pathbuf, sizeof(pathbuf), "pts/%d", ptsminor); 187 if (strcmp(pathbuf, ttname) == 0 && ptsminor >= 0) 188 return makedev(pts, ptsminor); 189 } 190 errno = serrno; 191 err(1, "%s", ttypath); 192 } 193 if (!S_ISCHR(sb.st_mode)) 194 errx(1, "%s: not a terminal", ttypath); 195 return sb.st_rdev; 196 } 197 198 int 199 main(int argc, char *argv[]) 200 { 201 struct varent *vent; 202 struct winsize ws; 203 struct kinfo_lwp *kl, *l; 204 int ch, i, j, fmt, lineno, nentries, nlwps; 205 long long flag; 206 int prtheader, wflag, what, xflg, showlwps; 207 char *nlistf, *memf, *swapf, errbuf[_POSIX2_LINE_MAX]; 208 char *ttname; 209 210 setprogname(argv[0]); 211 (void)setlocale(LC_ALL, ""); 212 213 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 214 ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && 215 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || 216 ws.ws_col == 0) 217 termwidth = 79; 218 else 219 termwidth = ws.ws_col - 1; 220 221 if (argc > 1) 222 argv[1] = kludge_oldps_options(argv[1]); 223 224 fmt = prtheader = wflag = xflg = showlwps = 0; 225 what = KERN_PROC_UID; 226 flag = myuid = getuid(); 227 memf = nlistf = swapf = NULL; 228 229 while ((ch = getopt(argc, argv, GETOPTSTR)) != -1) 230 switch((char)ch) { 231 case 'A': 232 /* "-A" shows all processes, like "-ax" */ 233 xflg = 1; 234 /*FALLTHROUGH*/ 235 case 'a': 236 what = KERN_PROC_ALL; 237 flag = 0; 238 break; 239 case 'c': 240 commandonly = 1; 241 break; 242 case 'e': /* XXX set ufmt */ 243 needenv = 1; 244 break; 245 case 'C': 246 rawcpu = 1; 247 break; 248 case 'g': 249 break; /* no-op */ 250 case 'h': 251 prtheader = ws.ws_row > 5 ? ws.ws_row : 22; 252 break; 253 case 'j': 254 parsefmt(jfmt); 255 fmt = 1; 256 jfmt[0] = '\0'; 257 break; 258 case 'k': 259 parsesort(optarg); 260 break; 261 case 'K': 262 break; /* no-op - was dontuseprocfs */ 263 case 'L': 264 showkey(); 265 exit(0); 266 /* NOTREACHED */ 267 case 'l': 268 parsefmt(lfmt); 269 fmt = 1; 270 lfmt[0] = '\0'; 271 break; 272 case 'M': 273 memf = optarg; 274 break; 275 case 'm': 276 parsesort("vsz"); 277 break; 278 case 'N': 279 nlistf = optarg; 280 break; 281 case 'O': 282 /* 283 * If this is not the first -O option, insert 284 * just after the previous one. 285 * 286 * If there is no format yet, start with the default 287 * format, and insert after the pid column. 288 * 289 * If there is already a format, insert after 290 * the pid column, or at the end if there's no 291 * pid column. 292 */ 293 if (!Opos) { 294 if (!fmt) 295 parsefmt(default_fmt); 296 Opos = varlist_find(&displaylist, "pid"); 297 } 298 parsefmt_insert(optarg, &Opos); 299 fmt = 1; 300 break; 301 case 'o': 302 parsefmt(optarg); 303 fmt = 1; 304 break; 305 case 'p': 306 what = KERN_PROC_PID; 307 flag = parsenum(optarg, "process id"); 308 xflg = 1; 309 break; 310 case 'r': 311 parsesort("%cpu"); 312 break; 313 case 'S': 314 sumrusage = 1; 315 break; 316 case 's': 317 /* -L was already taken... */ 318 showlwps = 1; 319 default_fmt = sfmt; 320 break; 321 case 'T': 322 if ((ttname = ttyname(STDIN_FILENO)) == NULL) 323 errx(1, "stdin: not a terminal"); 324 flag = ttyname2dev(ttname, &xflg, &what); 325 break; 326 case 't': 327 flag = ttyname2dev(optarg, &xflg, &what); 328 break; 329 case 'U': 330 if (*optarg != '\0') { 331 struct passwd *pw; 332 333 what = KERN_PROC_UID; 334 pw = getpwnam(optarg); 335 if (pw == NULL) { 336 flag = parsenum(optarg, "user name"); 337 } else 338 flag = pw->pw_uid; 339 } 340 break; 341 case 'u': 342 parsefmt(ufmt); 343 parsesort("%cpu"); 344 fmt = 1; 345 ufmt[0] = '\0'; 346 break; 347 case 'v': 348 parsefmt(vfmt); 349 parsesort("vsz"); 350 fmt = 1; 351 vfmt[0] = '\0'; 352 break; 353 case 'W': 354 swapf = optarg; 355 break; 356 case 'w': 357 if (wflag) 358 termwidth = UNLIMITED; 359 else if (termwidth < 131) 360 termwidth = 131; 361 wflag++; 362 break; 363 case 'x': 364 xflg = 1; 365 break; 366 case '?': 367 default: 368 usage(); 369 } 370 argc -= optind; 371 argv += optind; 372 373 #define BACKWARD_COMPATIBILITY 374 #ifdef BACKWARD_COMPATIBILITY 375 if (*argv) { 376 nlistf = *argv; 377 if (*++argv) { 378 memf = *argv; 379 if (*++argv) 380 swapf = *argv; 381 } 382 } 383 #endif 384 385 if (memf == NULL) { 386 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); 387 donlist_sysctl(); 388 } else 389 kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf); 390 391 if (kd == 0) 392 errx(1, "%s", errbuf); 393 394 if (!fmt) 395 parsefmt(default_fmt); 396 397 /* Add default sort criteria */ 398 parsesort("tdev,pid"); 399 SIMPLEQ_FOREACH(vent, &sortlist, next) { 400 if (vent->var->flag & LWP || vent->var->type == UNSPECIFIED) 401 warnx("Cannot sort on %s, sort key ignored", 402 vent->var->name); 403 } 404 405 /* 406 * scan requested variables, noting what structures are needed. 407 */ 408 scanvars(); 409 410 /* 411 * select procs 412 */ 413 if (!(kinfo = getkinfo_kvm(kd, what, flag, &nentries))) 414 err(1, "%s", kvm_geterr(kd)); 415 if (nentries == 0) { 416 printheader(); 417 exit(1); 418 } 419 /* 420 * sort proc list 421 */ 422 qsort(kinfo, nentries, sizeof(struct kinfo_proc2), pscomp); 423 /* 424 * For each proc, call each variable output function in 425 * "setwidth" mode to determine the widest element of 426 * the column. 427 */ 428 429 for (i = 0; i < nentries; i++) { 430 struct kinfo_proc2 *ki = &kinfo[i]; 431 432 if (xflg == 0 && (ki->p_tdev == (uint32_t)NODEV || 433 (ki->p_flag & P_CONTROLT) == 0)) 434 continue; 435 436 kl = kvm_getlwps(kd, ki->p_pid, ki->p_paddr, 437 sizeof(struct kinfo_lwp), &nlwps); 438 if (kl == 0) 439 nlwps = 0; 440 if (showlwps == 0) { 441 l = pick_representative_lwp(ki, kl, nlwps); 442 SIMPLEQ_FOREACH(vent, &displaylist, next) 443 OUTPUT(vent, ki, l, WIDTHMODE); 444 } else { 445 /* The printing is done with the loops 446 * reversed, but here we don't need that, 447 * and this improves the code locality a bit. 448 */ 449 SIMPLEQ_FOREACH(vent, &displaylist, next) 450 for (j = 0; j < nlwps; j++) 451 OUTPUT(vent, ki, &kl[j], WIDTHMODE); 452 } 453 } 454 /* 455 * Print header - AFTER determining process field widths. 456 * printheader() also adds up the total width of all 457 * fields the first time it's called. 458 */ 459 printheader(); 460 /* 461 * For each proc, call each variable output function in 462 * print mode. 463 */ 464 for (i = lineno = 0; i < nentries; i++) { 465 struct kinfo_proc2 *ki = &kinfo[i]; 466 467 if (xflg == 0 && (ki->p_tdev == (uint32_t)NODEV || 468 (ki->p_flag & P_CONTROLT ) == 0)) 469 continue; 470 kl = kvm_getlwps(kd, ki->p_pid, (u_long)ki->p_paddr, 471 sizeof(struct kinfo_lwp), &nlwps); 472 if (kl == 0) 473 nlwps = 0; 474 if (showlwps == 0) { 475 l = pick_representative_lwp(ki, kl, nlwps); 476 SIMPLEQ_FOREACH(vent, &displaylist, next) { 477 OUTPUT(vent, ki, l, PRINTMODE); 478 if (SIMPLEQ_NEXT(vent, next) != NULL) 479 (void)putchar(' '); 480 } 481 (void)putchar('\n'); 482 if (prtheader && lineno++ == prtheader - 4) { 483 (void)putchar('\n'); 484 printheader(); 485 lineno = 0; 486 } 487 } else { 488 for (j = 0; j < nlwps; j++) { 489 SIMPLEQ_FOREACH(vent, &displaylist, next) { 490 OUTPUT(vent, ki, &kl[j], PRINTMODE); 491 if (SIMPLEQ_NEXT(vent, next) != NULL) 492 (void)putchar(' '); 493 } 494 (void)putchar('\n'); 495 if (prtheader && lineno++ == prtheader - 4) { 496 (void)putchar('\n'); 497 printheader(); 498 lineno = 0; 499 } 500 } 501 } 502 } 503 exit(eval); 504 /* NOTREACHED */ 505 } 506 507 static struct kinfo_lwp * 508 pick_representative_lwp(struct kinfo_proc2 *ki, struct kinfo_lwp *kl, int nlwps) 509 { 510 int i, onproc, running, sleeping, stopped, suspended; 511 static struct kinfo_lwp zero_lwp; 512 513 if (kl == 0) 514 return &zero_lwp; 515 516 /* Trivial case: only one LWP */ 517 if (nlwps == 1) 518 return kl; 519 520 switch (ki->p_realstat) { 521 case SSTOP: 522 case SACTIVE: 523 /* Pick the most live LWP */ 524 onproc = running = sleeping = stopped = suspended = -1; 525 for (i = 0; i < nlwps; i++) { 526 switch (kl[i].l_stat) { 527 case LSONPROC: 528 onproc = i; 529 break; 530 case LSRUN: 531 running = i; 532 break; 533 case LSSLEEP: 534 sleeping = i; 535 break; 536 case LSSTOP: 537 stopped = i; 538 break; 539 case LSSUSPENDED: 540 suspended = i; 541 break; 542 } 543 } 544 if (onproc != -1) 545 return &kl[onproc]; 546 if (running != -1) 547 return &kl[running]; 548 if (sleeping != -1) 549 return &kl[sleeping]; 550 if (stopped != -1) 551 return &kl[stopped]; 552 if (suspended != -1) 553 return &kl[suspended]; 554 break; 555 case SZOMB: 556 /* First will do */ 557 return kl; 558 break; 559 } 560 /* Error condition! */ 561 warnx("Inconsistent LWP state for process %d", ki->p_pid); 562 return kl; 563 } 564 565 566 static struct kinfo_proc2 * 567 getkinfo_kvm(kvm_t *kdp, int what, int flag, int *nentriesp) 568 { 569 570 return (kvm_getproc2(kdp, what, flag, sizeof(struct kinfo_proc2), 571 nentriesp)); 572 } 573 574 static void 575 scanvars(void) 576 { 577 struct varent *vent; 578 VAR *v; 579 580 SIMPLEQ_FOREACH(vent, &displaylist, next) { 581 v = vent->var; 582 if (v->flag & COMM) { 583 needcomm = 1; 584 break; 585 } 586 } 587 } 588 589 static int 590 pscomp(const void *a, const void *b) 591 { 592 const struct kinfo_proc2 *ka = (const struct kinfo_proc2 *)a; 593 const struct kinfo_proc2 *kb = (const struct kinfo_proc2 *)b; 594 595 int i; 596 int64_t i64; 597 VAR *v; 598 struct varent *ve; 599 const sigset_t *sa, *sb; 600 601 #define V_SIZE(k) ((k)->p_vm_msize) 602 #define RDIFF_N(t, n) \ 603 if (((const t *)((const char *)ka + v->off))[n] > ((const t *)((const char *)kb + v->off))[n]) \ 604 return 1; \ 605 if (((const t *)((const char *)ka + v->off))[n] < ((const t *)((const char *)kb + v->off))[n]) \ 606 return -1; 607 608 #define RDIFF(type) RDIFF_N(type, 0); continue 609 610 SIMPLEQ_FOREACH(ve, &sortlist, next) { 611 v = ve->var; 612 if (v->flag & LWP) 613 /* LWP structure not available (yet) */ 614 continue; 615 /* Sort on pvar() fields, + a few others */ 616 switch (v->type) { 617 case CHAR: 618 RDIFF(char); 619 case UCHAR: 620 RDIFF(u_char); 621 case SHORT: 622 RDIFF(short); 623 case USHORT: 624 RDIFF(ushort); 625 case INT: 626 RDIFF(int); 627 case UINT: 628 RDIFF(uint); 629 case LONG: 630 RDIFF(long); 631 case ULONG: 632 RDIFF(ulong); 633 case INT32: 634 RDIFF(int32_t); 635 case UINT32: 636 RDIFF(uint32_t); 637 case SIGLIST: 638 sa = (const void *)((const char *)a + v->off); 639 sb = (const void *)((const char *)b + v->off); 640 i = 0; 641 do { 642 if (sa->__bits[i] > sb->__bits[i]) 643 return 1; 644 if (sa->__bits[i] < sb->__bits[i]) 645 return -1; 646 i++; 647 } while (i < (int)__arraycount(sa->__bits)); 648 continue; 649 case INT64: 650 RDIFF(int64_t); 651 case KPTR: 652 case KPTR24: 653 case UINT64: 654 RDIFF(uint64_t); 655 case TIMEVAL: 656 /* compare xxx_sec then xxx_usec */ 657 RDIFF_N(uint32_t, 0); 658 RDIFF_N(uint32_t, 1); 659 continue; 660 case CPUTIME: 661 i64 = ka->p_rtime_sec * 1000000 + ka->p_rtime_usec; 662 i64 -= kb->p_rtime_sec * 1000000 + kb->p_rtime_usec; 663 if (sumrusage) { 664 i64 += ka->p_uctime_sec * 1000000 665 + ka->p_uctime_usec; 666 i64 -= kb->p_uctime_sec * 1000000 667 + kb->p_uctime_usec; 668 } 669 if (i64 != 0) 670 return i64 > 0 ? 1 : -1; 671 continue; 672 case PCPU: 673 i = getpcpu(kb) - getpcpu(ka); 674 if (i != 0) 675 return i; 676 continue; 677 case VSIZE: 678 i = V_SIZE(kb) - V_SIZE(ka); 679 if (i != 0) 680 return i; 681 continue; 682 683 default: 684 /* Ignore everything else */ 685 break; 686 } 687 } 688 return 0; 689 690 #undef VSIZE 691 } 692 693 /* 694 * ICK (all for getopt), would rather hide the ugliness 695 * here than taint the main code. 696 * 697 * ps foo -> ps -foo 698 * ps 34 -> ps -p34 699 * 700 * The old convention that 't' with no trailing tty arg means the user's 701 * tty, is only supported if argv[1] doesn't begin with a '-'. This same 702 * feature is available with the option 'T', which takes no argument. 703 */ 704 static char * 705 kludge_oldps_options(char *s) 706 { 707 size_t len; 708 char *newopts, *ns, *cp; 709 710 len = strlen(s); 711 if ((newopts = ns = malloc(len + 3)) == NULL) 712 err(1, NULL); 713 /* 714 * options begin with '-' 715 */ 716 if (*s != '-') 717 *ns++ = '-'; /* add option flag */ 718 /* 719 * gaze to end of argv[1] 720 */ 721 cp = s + len - 1; 722 /* 723 * if the last letter is a 't' flag and there are no other option 724 * characters that take arguments (eg U, p, o) in the option 725 * string and the option string doesn't start with a '-' then 726 * convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). 727 */ 728 if (*cp == 't' && *s != '-' && strpbrk(s, ARGOPTS) == NULL) 729 *cp = 'T'; 730 else { 731 /* 732 * otherwise check for trailing number, which *may* be a 733 * pid. 734 */ 735 while (cp >= s && isdigit((unsigned char)*cp)) 736 --cp; 737 } 738 cp++; 739 memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */ 740 ns += cp - s; 741 /* 742 * if there's a trailing number, and not a preceding 'p' (pid) or 743 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. 744 */ 745 if (isdigit((unsigned char)*cp) && 746 (cp == s || (cp[-1] != 'U' && cp[-1] != 't' && cp[-1] != 'p' && 747 cp[-1] != '/' && (cp - 1 == s || cp[-2] != 't')))) 748 *ns++ = 'p'; 749 /* and append the number */ 750 (void)strcpy(ns, cp); /* XXX strcpy is safe here */ 751 752 return (newopts); 753 } 754 755 static int 756 parsenum(const char *str, const char *msg) 757 { 758 char *ep; 759 unsigned long ul; 760 761 ul = strtoul(str, &ep, 0); 762 763 if (*str == '\0' || *ep != '\0') 764 errx(1, "Invalid %s: `%s'", msg, str); 765 766 if (ul > INT_MAX) 767 errx(1, "Out of range %s: `%s'", msg, str); 768 769 return (int)ul; 770 } 771 772 static void 773 usage(void) 774 { 775 776 (void)fprintf(stderr, 777 "usage:\t%s\n\t %s\n\t%s\n", 778 "ps [-AaCcehjlmrSsTuvwx] [-k key] [-M core] [-N system] [-O fmt]", 779 "[-o fmt] [-p pid] [-t tty] [-U user] [-W swap]", 780 "ps -L"); 781 exit(1); 782 /* NOTREACHED */ 783 } 784