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