1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright 2012, Joyent, Inc. All rights reserved. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 /* 41 * ps -- print things about processes. 42 */ 43 44 #define _SYSCALL32 45 46 #include <stdio.h> 47 #include <ctype.h> 48 #include <string.h> 49 #include <errno.h> 50 #include <fcntl.h> 51 #include <pwd.h> 52 #include <sys/types.h> 53 #include <sys/stat.h> 54 #include <sys/mkdev.h> 55 #include <unistd.h> 56 #include <stdlib.h> 57 #include <limits.h> 58 #include <dirent.h> 59 #include <procfs.h> 60 #include <sys/param.h> 61 #include <sys/ttold.h> 62 #include <libelf.h> 63 #include <gelf.h> 64 #include <locale.h> 65 #include <wctype.h> 66 #include <stdarg.h> 67 #include <sys/proc.h> 68 #include <priv_utils.h> 69 #include <zone.h> 70 71 #define NTTYS 2 /* max ttys that can be specified with the -t option */ 72 /* only one tty can be specified with SunOS ps */ 73 #define SIZ 30 /* max processes that can be specified with -p and -g */ 74 #define ARGSIZ 30 /* size of buffer holding args for -t, -p, -u options */ 75 76 #define FSTYPE_MAX 8 77 78 struct psent { 79 psinfo_t *psinfo; 80 char *psargs; 81 int found; 82 }; 83 84 static int tplen, maxlen, twidth; 85 static char hdr[81]; 86 static struct winsize win; 87 88 static int retcode = 1; 89 static int lflg; /* long format */ 90 static int uflg; /* user-oriented output */ 91 static int aflg; /* Display all processes */ 92 static int eflg; /* Display environment as well as arguments */ 93 static int gflg; /* Display process group leaders */ 94 static int tflg; /* Processes running on specific terminals */ 95 static int rflg; /* Running processes only flag */ 96 static int Sflg; /* Accumulated time plus all reaped children */ 97 static int xflg; /* Include processes with no controlling tty */ 98 static int cflg; /* Display command name */ 99 static int vflg; /* Virtual memory-oriented output */ 100 static int nflg; /* Numerical output */ 101 static int pflg; /* Specific process id passed as argument */ 102 static int Uflg; /* Update private database, ups_data */ 103 static int errflg; 104 105 static char *gettty(); 106 static char argbuf[ARGSIZ]; 107 static char *parg; 108 static char *p1; /* points to successive option arguments */ 109 static uid_t my_uid; 110 static char stdbuf[BUFSIZ]; 111 112 static int ndev; /* number of devices */ 113 static int maxdev; /* number of devl structures allocated */ 114 115 #define DNINCR 100 116 #define DNSIZE 14 117 static struct devl { /* device list */ 118 char dname[DNSIZE]; /* device name */ 119 dev_t ddev; /* device number */ 120 } *devl; 121 122 static struct tty { 123 char *tname; 124 dev_t tdev; 125 } tty[NTTYS]; /* for t option */ 126 static int ntty = 0; 127 static pid_t pidsave; 128 static int pidwidth; 129 130 static char *procdir = "/proc"; /* standard /proc directory */ 131 static void usage(); /* print usage message and quit */ 132 static void getarg(void); 133 static void prtime(timestruc_t st); 134 static void przom(psinfo_t *psinfo); 135 static int num(char *); 136 static int preadargs(int, psinfo_t *, char *); 137 static int preadenvs(int, psinfo_t *, char *); 138 static int prcom(int, psinfo_t *, char *); 139 static int namencnt(char *, int, int); 140 static int pscompare(const void *, const void *); 141 static char *err_string(int); 142 143 extern int scrwidth(wchar_t); /* header file? */ 144 145 int 146 ucbmain(int argc, char **argv) 147 { 148 psinfo_t info; /* process information structure from /proc */ 149 char *psargs = NULL; /* pointer to buffer for -w and -ww options */ 150 char *svpsargs = NULL; 151 struct psent *psent; 152 int entsize; 153 int nent; 154 pid_t maxpid; 155 156 struct tty *ttyp = tty; 157 char *tmp; 158 char *p; 159 int c; 160 pid_t pid; /* pid: process id */ 161 pid_t ppid; /* ppid: parent process id */ 162 int i, found; 163 164 size_t size; 165 166 DIR *dirp; 167 struct dirent *dentp; 168 char psname[100]; 169 char asname[100]; 170 size_t len; 171 172 (void) setlocale(LC_ALL, ""); 173 174 my_uid = getuid(); 175 176 /* 177 * This program needs the proc_owner privilege 178 */ 179 (void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_PROC_OWNER, 180 (char *)NULL); 181 182 /* 183 * calculate width of pid fields based on configured MAXPID 184 * (must be at least 5 to retain output format compatibility) 185 */ 186 maxpid = (pid_t)sysconf(_SC_MAXPID); 187 pidwidth = 1; 188 while ((maxpid /= 10) > 0) 189 ++pidwidth; 190 pidwidth = pidwidth < 5 ? 5 : pidwidth; 191 192 if (ioctl(1, TIOCGWINSZ, &win) == -1) 193 twidth = 80; 194 else 195 twidth = (win.ws_col == 0 ? 80 : win.ws_col); 196 197 /* add the '-' for BSD compatibility */ 198 if (argc > 1) { 199 if (argv[1][0] != '-' && !isdigit(argv[1][0])) { 200 len = strlen(argv[1]) + 2; 201 tmp = malloc(len); 202 if (tmp != NULL) { 203 (void) snprintf(tmp, len, "%s%s", "-", argv[1]); 204 argv[1] = tmp; 205 } 206 } 207 } 208 209 setbuf(stdout, stdbuf); 210 while ((c = getopt(argc, argv, "lcaengrSt:xuvwU")) != EOF) 211 switch (c) { 212 case 'g': 213 gflg++; /* include process group leaders */ 214 break; 215 case 'c': /* display internal command name */ 216 cflg++; 217 break; 218 case 'r': /* restrict output to running processes */ 219 rflg++; 220 break; 221 case 'S': /* display time by process and all reaped children */ 222 Sflg++; 223 break; 224 case 'x': /* process w/o controlling tty */ 225 xflg++; 226 break; 227 case 'l': /* long listing */ 228 lflg++; 229 uflg = vflg = 0; 230 break; 231 case 'u': /* user-oriented output */ 232 uflg++; 233 lflg = vflg = 0; 234 break; 235 case 'U': /* update private database ups_data */ 236 Uflg++; 237 break; 238 case 'w': /* increase display width */ 239 if (twidth < 132) 240 twidth = 132; 241 else /* second w option */ 242 twidth = NCARGS; 243 break; 244 case 'v': /* display virtual memory format */ 245 vflg++; 246 lflg = uflg = 0; 247 break; 248 case 'a': 249 /* 250 * display all processes except process group 251 * leaders and processes w/o controlling tty 252 */ 253 aflg++; 254 gflg++; 255 break; 256 case 'e': 257 /* Display environment along with aguments. */ 258 eflg++; 259 break; 260 case 'n': /* Display numerical output */ 261 nflg++; 262 break; 263 case 't': /* restrict output to named terminal */ 264 #define TSZ 30 265 tflg++; 266 gflg++; 267 xflg = 0; 268 269 p1 = optarg; 270 do { /* only loop through once (NTTYS = 2) */ 271 parg = argbuf; 272 if (ntty >= NTTYS-1) 273 break; 274 getarg(); 275 if ((p = malloc(TSZ+1)) == NULL) { 276 (void) fprintf(stderr, 277 "ps: no memory\n"); 278 exit(1); 279 } 280 p[0] = '\0'; 281 size = TSZ; 282 if (isdigit(*parg)) { 283 (void) strcpy(p, "tty"); 284 size -= 3; 285 } 286 287 (void) strncat(p, parg, size); 288 ttyp->tdev = PRNODEV; 289 if (parg && *parg == '?') 290 xflg++; 291 else { 292 char nambuf[TSZ+6]; /* for /dev/+\0 */ 293 struct stat64 s; 294 (void) strcpy(nambuf, "/dev/"); 295 (void) strcat(nambuf, p); 296 if (stat64(nambuf, &s) == 0) 297 ttyp->tdev = s.st_rdev; 298 } 299 ttyp++->tname = p; 300 ntty++; 301 } while (*p1); 302 break; 303 default: /* error on ? */ 304 errflg++; 305 break; 306 } 307 308 if (errflg) 309 usage(); 310 311 if (optind + 1 < argc) { /* more than one additional argument */ 312 (void) fprintf(stderr, "ps: too many arguments\n"); 313 usage(); 314 } 315 316 /* 317 * The -U option is obsolete. Attempts to use it cause ps to exit 318 * without printing anything. 319 */ 320 if (Uflg) 321 exit(0); 322 323 if (optind < argc) { /* user specified a specific proc id */ 324 pflg++; 325 p1 = argv[optind]; 326 parg = argbuf; 327 getarg(); 328 if (!num(parg)) { 329 (void) fprintf(stderr, 330 "ps: %s is an invalid non-numeric argument for a process id\n", parg); 331 usage(); 332 } 333 pidsave = (pid_t)atol(parg); 334 aflg = rflg = xflg = 0; 335 gflg++; 336 } 337 338 if (tflg) 339 ttyp->tname = NULL; 340 341 /* allocate an initial guess for the number of processes */ 342 entsize = 1024; 343 psent = malloc(entsize * sizeof (struct psent)); 344 if (psent == NULL) { 345 (void) fprintf(stderr, "ps: no memory\n"); 346 exit(1); 347 } 348 nent = 0; /* no active entries yet */ 349 350 if (lflg) { 351 (void) sprintf(hdr, 352 " F UID%*s%*s %%C PRI NI SZ RSS " 353 "WCHAN S TT TIME COMMAND", pidwidth + 1, "PID", 354 pidwidth + 1, "PPID"); 355 } else if (uflg) { 356 if (nflg) 357 (void) sprintf(hdr, 358 " UID%*s %%CPU %%MEM SZ RSS " 359 "TT S START TIME COMMAND", 360 pidwidth + 1, "PID"); 361 else 362 (void) sprintf(hdr, 363 "USER %*s %%CPU %%MEM SZ RSS " 364 "TT S START TIME COMMAND", 365 pidwidth + 1, "PID"); 366 } else if (vflg) { 367 (void) sprintf(hdr, 368 "%*s TT S TIME SIZE RSS %%CPU %%MEM " 369 "COMMAND", pidwidth + 1, "PID"); 370 } else 371 (void) sprintf(hdr, "%*s TT S TIME COMMAND", 372 pidwidth + 1, "PID"); 373 374 twidth = twidth - strlen(hdr) + 6; 375 (void) printf("%s\n", hdr); 376 377 if (twidth > PRARGSZ && (psargs = malloc(twidth)) == NULL) { 378 (void) fprintf(stderr, "ps: no memory\n"); 379 exit(1); 380 } 381 svpsargs = psargs; 382 383 /* 384 * Determine which processes to print info about by searching 385 * the /proc directory and looking at each process. 386 */ 387 if ((dirp = opendir(procdir)) == NULL) { 388 (void) fprintf(stderr, "ps: cannot open PROC directory %s\n", 389 procdir); 390 exit(1); 391 } 392 393 /* for each active process --- */ 394 while ((dentp = readdir(dirp)) != NULL) { 395 int psfd; /* file descriptor for /proc/nnnnn/psinfo */ 396 int asfd; /* file descriptor for /proc/nnnnn/as */ 397 int n; 398 399 if (dentp->d_name[0] == '.') /* skip . and .. */ 400 continue; 401 n = snprintf(psname, sizeof (psname), "%s/%s/psinfo", 402 procdir, dentp->d_name); 403 if (n < 0 || n >= sizeof (psname)) 404 exit(1); 405 406 n = snprintf(asname, sizeof (asname), "%s/%s/as", 407 procdir, dentp->d_name); 408 if (n < 0 || n >= sizeof (psname)) 409 exit(1); 410 411 retry: 412 if ((psfd = open(psname, O_RDONLY)) == -1) 413 continue; 414 asfd = -1; 415 if (psargs != NULL || eflg) { 416 417 /* now we need the proc_owner privilege */ 418 (void) __priv_bracket(PRIV_ON); 419 420 asfd = open(asname, O_RDONLY); 421 422 /* drop proc_owner privilege after open */ 423 (void) __priv_bracket(PRIV_OFF); 424 } 425 426 /* 427 * Get the info structure for the process 428 */ 429 if (read(psfd, &info, sizeof (info)) != sizeof (info)) { 430 int saverr = errno; 431 432 (void) close(psfd); 433 if (asfd > 0) 434 (void) close(asfd); 435 if (saverr == EAGAIN) 436 goto retry; 437 if (saverr != ENOENT) 438 (void) fprintf(stderr, "ps: read() on %s: %s\n", 439 psname, err_string(saverr)); 440 continue; 441 } 442 (void) close(psfd); 443 444 found = 0; 445 if (info.pr_lwp.pr_state == 0) /* can't happen? */ 446 goto closeit; 447 pid = info.pr_pid; 448 ppid = info.pr_ppid; 449 450 /* Display only process from command line */ 451 if (pflg) { /* pid in arg list */ 452 if (pidsave == pid) 453 found++; 454 else 455 goto closeit; 456 } 457 458 /* 459 * Omit "uninteresting" processes unless 'g' option. 460 */ 461 if ((ppid == 1) && !(gflg)) 462 goto closeit; 463 464 /* 465 * Omit non-running processes for 'r' option 466 */ 467 if (rflg && 468 !(info.pr_lwp.pr_sname == 'O' || 469 info.pr_lwp.pr_sname == 'R')) 470 goto closeit; 471 472 if (!found && !tflg && !aflg && info.pr_euid != my_uid) 473 goto closeit; 474 475 /* 476 * Read the args for the -w and -ww cases 477 */ 478 if (asfd > 0) { 479 if ((psargs != NULL && 480 preadargs(asfd, &info, psargs) == -1) || 481 (eflg && preadenvs(asfd, &info, psargs) == -1)) { 482 int saverr = errno; 483 484 (void) close(asfd); 485 if (saverr == EAGAIN) 486 goto retry; 487 if (saverr != ENOENT) 488 (void) fprintf(stderr, 489 "ps: read() on %s: %s\n", 490 asname, err_string(saverr)); 491 continue; 492 } 493 } else { 494 psargs = info.pr_psargs; 495 } 496 497 if (nent >= entsize) { 498 entsize *= 2; 499 psent = (struct psent *)realloc((char *)psent, 500 entsize * sizeof (struct psent)); 501 if (psent == NULL) { 502 (void) fprintf(stderr, "ps: no memory\n"); 503 exit(1); 504 } 505 } 506 if ((psent[nent].psinfo = malloc(sizeof (psinfo_t))) 507 == NULL) { 508 (void) fprintf(stderr, "ps: no memory\n"); 509 exit(1); 510 } 511 *psent[nent].psinfo = info; 512 if (psargs == NULL) 513 psent[nent].psargs = NULL; 514 else { 515 if ((psent[nent].psargs = malloc(strlen(psargs)+1)) 516 == NULL) { 517 (void) fprintf(stderr, "ps: no memory\n"); 518 exit(1); 519 } 520 (void) strcpy(psent[nent].psargs, psargs); 521 } 522 psent[nent].found = found; 523 nent++; 524 closeit: 525 if (asfd > 0) 526 (void) close(asfd); 527 psargs = svpsargs; 528 } 529 530 /* revert to non-privileged user */ 531 (void) __priv_relinquish(); 532 533 (void) closedir(dirp); 534 535 qsort((char *)psent, nent, sizeof (psent[0]), pscompare); 536 537 for (i = 0; i < nent; i++) { 538 struct psent *pp = &psent[i]; 539 if (prcom(pp->found, pp->psinfo, pp->psargs)) { 540 (void) printf("\n"); 541 retcode = 0; 542 } 543 } 544 545 return (retcode); 546 } 547 548 static void 549 usage(void) /* print usage message and quit */ 550 { 551 static char usage1[] = "ps [ -aceglnrSuUvwx ] [ -t term ] [ num ]"; 552 553 (void) fprintf(stderr, "usage: %s\n", usage1); 554 exit(1); 555 } 556 557 /* 558 * Read the process arguments from the process. 559 * This allows >PRARGSZ characters of arguments to be displayed but, 560 * unlike pr_psargs[], the process may have changed them. 561 */ 562 #define NARG 100 563 static int 564 preadargs(int pfd, psinfo_t *psinfo, char *psargs) 565 { 566 off_t argvoff = (off_t)psinfo->pr_argv; 567 size_t len; 568 char *psa = psargs; 569 int bsize = twidth; 570 int narg = NARG; 571 off_t argv[NARG]; 572 off_t argoff; 573 off_t nextargoff; 574 int i; 575 #ifdef _LP64 576 caddr32_t argv32[NARG]; 577 int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64); 578 #endif 579 580 if (psinfo->pr_nlwp == 0 || 581 strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0) 582 goto out; 583 584 (void) memset(psa, 0, bsize--); 585 nextargoff = 0; 586 errno = EIO; 587 while (bsize > 0) { 588 if (narg == NARG) { 589 (void) memset(argv, 0, sizeof (argv)); 590 #ifdef _LP64 591 if (is32) { 592 if ((i = pread(pfd, argv32, sizeof (argv32), 593 argvoff)) <= 0) { 594 if (i == 0 || errno == EIO) 595 break; 596 return (-1); 597 } 598 for (i = 0; i < NARG; i++) 599 argv[i] = argv32[i]; 600 } else 601 #endif 602 if ((i = pread(pfd, argv, sizeof (argv), 603 argvoff)) <= 0) { 604 if (i == 0 || errno == EIO) 605 break; 606 return (-1); 607 } 608 narg = 0; 609 } 610 if ((argoff = argv[narg++]) == 0) 611 break; 612 if (argoff != nextargoff && 613 (i = pread(pfd, psa, bsize, argoff)) <= 0) { 614 if (i == 0 || errno == EIO) 615 break; 616 return (-1); 617 } 618 len = strlen(psa); 619 psa += len; 620 *psa++ = ' '; 621 bsize -= len + 1; 622 nextargoff = argoff + len + 1; 623 #ifdef _LP64 624 argvoff += is32? sizeof (caddr32_t) : sizeof (caddr_t); 625 #else 626 argvoff += sizeof (caddr_t); 627 #endif 628 } 629 while (psa > psargs && isspace(*(psa-1))) 630 psa--; 631 632 out: 633 *psa = '\0'; 634 if (strlen(psinfo->pr_psargs) > strlen(psargs)) 635 (void) strcpy(psargs, psinfo->pr_psargs); 636 637 return (0); 638 } 639 640 /* 641 * Read environment variables from the process. 642 * Append them to psargs if there is room. 643 */ 644 static int 645 preadenvs(int pfd, psinfo_t *psinfo, char *psargs) 646 { 647 off_t envpoff = (off_t)psinfo->pr_envp; 648 int len; 649 char *psa; 650 char *psainit; 651 int bsize; 652 int nenv = NARG; 653 off_t envp[NARG]; 654 off_t envoff; 655 off_t nextenvoff; 656 int i; 657 #ifdef _LP64 658 caddr32_t envp32[NARG]; 659 int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64); 660 #endif 661 662 psainit = psa = (psargs != NULL)? psargs : psinfo->pr_psargs; 663 len = strlen(psa); 664 psa += len; 665 bsize = twidth - len - 1; 666 667 if (bsize <= 0 || psinfo->pr_nlwp == 0 || 668 strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0) 669 return (0); 670 671 nextenvoff = 0; 672 errno = EIO; 673 while (bsize > 0) { 674 if (nenv == NARG) { 675 (void) memset(envp, 0, sizeof (envp)); 676 #ifdef _LP64 677 if (is32) { 678 if ((i = pread(pfd, envp32, sizeof (envp32), 679 envpoff)) <= 0) { 680 if (i == 0 || errno == EIO) 681 break; 682 return (-1); 683 } 684 for (i = 0; i < NARG; i++) 685 envp[i] = envp32[i]; 686 } else 687 #endif 688 if ((i = pread(pfd, envp, sizeof (envp), 689 envpoff)) <= 0) { 690 if (i == 0 || errno == EIO) 691 break; 692 return (-1); 693 } 694 nenv = 0; 695 } 696 if ((envoff = envp[nenv++]) == 0) 697 break; 698 if (envoff != nextenvoff && 699 (i = pread(pfd, psa+1, bsize, envoff)) <= 0) { 700 if (i == 0 || errno == EIO) 701 break; 702 return (-1); 703 } 704 *psa++ = ' '; 705 len = strlen(psa); 706 psa += len; 707 bsize -= len + 1; 708 nextenvoff = envoff + len + 1; 709 #ifdef _LP64 710 envpoff += is32? sizeof (caddr32_t) : sizeof (caddr_t); 711 #else 712 envpoff += sizeof (caddr_t); 713 #endif 714 } 715 while (psa > psainit && isspace(*(psa-1))) 716 psa--; 717 *psa = '\0'; 718 719 return (0); 720 } 721 722 /* 723 * getarg() finds the next argument in list and copies arg into argbuf. 724 * p1 first pts to arg passed back from getopt routine. p1 is then 725 * bumped to next character that is not a comma or blank -- p1 NULL 726 * indicates end of list. 727 */ 728 729 static void 730 getarg() 731 { 732 char *parga; 733 int c; 734 735 while ((c = *p1) != '\0' && (c == ',' || isspace(c))) 736 p1++; 737 738 parga = argbuf; 739 while ((c = *p1) != '\0' && c != ',' && !isspace(c)) { 740 if (parga < argbuf + ARGSIZ - 1) 741 *parga++ = c; 742 p1++; 743 } 744 *parga = '\0'; 745 746 while ((c = *p1) != '\0' && (c == ',' || isspace(c))) 747 p1++; 748 } 749 750 static char * 751 devlookup(dev_t ddev) 752 { 753 struct devl *dp; 754 int i; 755 756 for (dp = devl, i = 0; i < ndev; dp++, i++) { 757 if (dp->ddev == ddev) 758 return (dp->dname); 759 } 760 return (NULL); 761 } 762 763 static char * 764 devadd(char *name, dev_t ddev) 765 { 766 struct devl *dp; 767 int leng, start, i; 768 769 if (ndev == maxdev) { 770 maxdev += DNINCR; 771 devl = realloc(devl, maxdev * sizeof (struct devl)); 772 if (devl == NULL) { 773 (void) fprintf(stderr, 774 "ps: not enough memory for %d devices\n", maxdev); 775 exit(1); 776 } 777 } 778 dp = &devl[ndev++]; 779 780 dp->ddev = ddev; 781 if (name == NULL) { 782 (void) strcpy(dp->dname, "??"); 783 return (dp->dname); 784 } 785 786 leng = strlen(name); 787 /* Strip off /dev/ */ 788 if (leng < DNSIZE + 4) 789 (void) strcpy(dp->dname, &name[5]); 790 else { 791 start = leng - (DNSIZE - 1); 792 793 for (i = start; i < leng && name[i] != '/'; i++) 794 ; 795 if (i == leng) 796 (void) strlcpy(dp->dname, &name[start], DNSIZE); 797 else 798 (void) strlcpy(dp->dname, &name[i+1], DNSIZE); 799 } 800 return (dp->dname); 801 } 802 803 /* 804 * gettty returns the user's tty number or ? if none. 805 */ 806 static char * 807 gettty(psinfo_t *psinfo) 808 { 809 extern char *_ttyname_dev(dev_t, char *, size_t); 810 static zoneid_t zid = -1; 811 char devname[TTYNAME_MAX]; 812 char *retval; 813 814 if (zid == -1) 815 zid = getzoneid(); 816 817 if (psinfo->pr_ttydev == PRNODEV || psinfo->pr_zoneid != zid) 818 return ("?"); 819 820 if ((retval = devlookup(psinfo->pr_ttydev)) != NULL) 821 return (retval); 822 823 retval = _ttyname_dev(psinfo->pr_ttydev, devname, sizeof (devname)); 824 825 return (devadd(retval, psinfo->pr_ttydev)); 826 } 827 828 /* 829 * Print percent from 16-bit binary fraction [0 .. 1] 830 * Round up .01 to .1 to indicate some small percentage (the 0x7000 below). 831 */ 832 static void 833 prtpct(ushort_t pct) 834 { 835 uint_t value = pct; /* need 32 bits to compute with */ 836 837 value = ((value * 1000) + 0x7000) >> 15; /* [0 .. 1000] */ 838 (void) printf("%3u.%u", value / 10, value % 10); 839 } 840 841 /* 842 * Print info about the process. 843 */ 844 static int 845 prcom(int found, psinfo_t *psinfo, char *psargs) 846 { 847 char *cp; 848 char *tp; 849 char *psa; 850 long tm; 851 int i, wcnt, length; 852 wchar_t wchar; 853 struct tty *ttyp; 854 855 /* 856 * If process is zombie, call print routine and return. 857 */ 858 if (psinfo->pr_nlwp == 0) { 859 if (tflg && !found) 860 return (0); 861 else { 862 przom(psinfo); 863 return (1); 864 } 865 } 866 867 /* 868 * Get current terminal. If none ("?") and 'a' is set, don't print 869 * info. If 't' is set, check if term is in list of desired terminals 870 * and print it if it is. 871 */ 872 i = 0; 873 tp = gettty(psinfo); 874 875 if (*tp == '?' && !found && !xflg) 876 return (0); 877 878 if (!(*tp == '?' && aflg) && tflg && !found) { 879 int match = 0; 880 char *other = NULL; 881 for (ttyp = tty; ttyp->tname != NULL; ttyp++) { 882 /* 883 * Look for a name match 884 */ 885 if (strcmp(tp, ttyp->tname) == 0) { 886 match = 1; 887 break; 888 } 889 /* 890 * Look for same device under different names. 891 */ 892 if ((other == NULL) && 893 (psinfo->pr_ttydev == ttyp->tdev)) 894 other = ttyp->tname; 895 } 896 if (!match) { 897 if (other == NULL) 898 return (0); 899 tp = other; 900 } 901 } 902 903 if (lflg) 904 (void) printf("%2x", psinfo->pr_flag & 0377); 905 if (uflg) { 906 if (!nflg) { 907 struct passwd *pwd; 908 909 if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) 910 /* USER */ 911 (void) printf("%-8.8s", pwd->pw_name); 912 else 913 /* UID */ 914 (void) printf(" %7.7d", (int)psinfo->pr_euid); 915 } else { 916 (void) printf(" %5d", (int)psinfo->pr_euid); /* UID */ 917 } 918 } else if (lflg) 919 (void) printf(" %5d", (int)psinfo->pr_euid); /* UID */ 920 921 (void) printf("%*d", pidwidth + 1, (int)psinfo->pr_pid); /* PID */ 922 if (lflg) 923 (void) printf("%*d", pidwidth + 1, 924 (int)psinfo->pr_ppid); /* PPID */ 925 if (lflg) 926 (void) printf("%3d", psinfo->pr_lwp.pr_cpu & 0377); /* CP */ 927 if (uflg) { 928 prtpct(psinfo->pr_pctcpu); /* %CPU */ 929 prtpct(psinfo->pr_pctmem); /* %MEM */ 930 } 931 if (lflg) { 932 (void) printf("%4d", psinfo->pr_lwp.pr_pri); /* PRI */ 933 (void) printf("%3d", psinfo->pr_lwp.pr_nice); /* NICE */ 934 } 935 if (lflg || uflg) { 936 if (psinfo->pr_flag & SSYS) /* SZ */ 937 (void) printf(" 0"); 938 else if (psinfo->pr_size) 939 (void) printf(" %4lu", (ulong_t)psinfo->pr_size); 940 else 941 (void) printf(" ?"); 942 if (psinfo->pr_flag & SSYS) /* RSS */ 943 (void) printf(" 0"); 944 else if (psinfo->pr_rssize) 945 (void) printf(" %4lu", (ulong_t)psinfo->pr_rssize); 946 else 947 (void) printf(" ?"); 948 } 949 if (lflg) { /* WCHAN */ 950 if (psinfo->pr_lwp.pr_sname != 'S') { 951 (void) printf(" "); 952 } else if (psinfo->pr_lwp.pr_wchan) { 953 (void) printf(" %+8.8lx", 954 (ulong_t)psinfo->pr_lwp.pr_wchan); 955 } else { 956 (void) printf(" ?"); 957 } 958 } 959 if ((tplen = strlen(tp)) > 9) 960 maxlen = twidth - tplen + 9; 961 else 962 maxlen = twidth; 963 964 if (!lflg) 965 (void) printf(" %-8.14s", tp); /* TTY */ 966 (void) printf(" %c", psinfo->pr_lwp.pr_sname); /* STATE */ 967 if (lflg) 968 (void) printf(" %-8.14s", tp); /* TTY */ 969 if (uflg) 970 prtime(psinfo->pr_start); /* START */ 971 972 /* time just for process */ 973 tm = psinfo->pr_time.tv_sec; 974 if (Sflg) { /* calculate time for process and all reaped children */ 975 tm += psinfo->pr_ctime.tv_sec; 976 if (psinfo->pr_time.tv_nsec + psinfo->pr_ctime.tv_nsec 977 >= 1000000000) 978 tm += 1; 979 } 980 981 (void) printf(" %2ld:%.2ld", tm / 60, tm % 60); /* TIME */ 982 983 if (vflg) { 984 if (psinfo->pr_flag & SSYS) /* SZ */ 985 (void) printf(" 0"); 986 else if (psinfo->pr_size) 987 (void) printf("%5lu", (ulong_t)psinfo->pr_size); 988 else 989 (void) printf(" ?"); 990 if (psinfo->pr_flag & SSYS) /* SZ */ 991 (void) printf(" 0"); 992 else if (psinfo->pr_rssize) 993 (void) printf("%5lu", (ulong_t)psinfo->pr_rssize); 994 else 995 (void) printf(" ?"); 996 prtpct(psinfo->pr_pctcpu); /* %CPU */ 997 prtpct(psinfo->pr_pctmem); /* %MEM */ 998 } 999 if (cflg) { /* CMD */ 1000 wcnt = namencnt(psinfo->pr_fname, 16, maxlen); 1001 (void) printf(" %.*s", wcnt, psinfo->pr_fname); 1002 return (1); 1003 } 1004 /* 1005 * PRARGSZ == length of cmd arg string. 1006 */ 1007 if (psargs == NULL) { 1008 psa = &psinfo->pr_psargs[0]; 1009 i = PRARGSZ; 1010 tp = &psinfo->pr_psargs[PRARGSZ]; 1011 } else { 1012 psa = psargs; 1013 i = strlen(psargs); 1014 tp = psa + i; 1015 } 1016 1017 for (cp = psa; cp < tp; /* empty */) { 1018 if (*cp == 0) 1019 break; 1020 length = mbtowc(&wchar, cp, MB_LEN_MAX); 1021 if (length < 0 || !iswprint(wchar)) { 1022 (void) printf(" [ %.16s ]", psinfo->pr_fname); 1023 return (1); 1024 } 1025 cp += length; 1026 } 1027 wcnt = namencnt(psa, i, maxlen); 1028 #if 0 1029 /* dumps core on really long strings */ 1030 (void) printf(" %.*s", wcnt, psa); 1031 #else 1032 (void) putchar(' '); 1033 (void) fwrite(psa, 1, wcnt, stdout); 1034 #endif 1035 return (1); 1036 } 1037 1038 /* 1039 * Print starting time of process unless process started more than 24 hours 1040 * ago, in which case the date is printed. 1041 */ 1042 static void 1043 prtime(timestruc_t st) 1044 { 1045 char sttim[26]; 1046 static time_t tim = 0L; 1047 time_t starttime; 1048 1049 if (tim == 0L) 1050 tim = time((time_t *)0); 1051 starttime = st.tv_sec; 1052 if (tim - starttime > 24*60*60) { 1053 (void) strftime(sttim, sizeof (sttim), "%b %d", 1054 localtime(&starttime)); 1055 } else { 1056 (void) strftime(sttim, sizeof (sttim), "%H:%M:%S", 1057 localtime(&starttime)); 1058 } 1059 (void) printf("%9.9s", sttim); 1060 } 1061 1062 static void 1063 przom(psinfo_t *psinfo) 1064 { 1065 long tm; 1066 1067 if (lflg) 1068 (void) printf("%2x", psinfo->pr_flag & 0377); 1069 if (uflg) { 1070 struct passwd *pwd; 1071 1072 if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) 1073 (void) printf("%-8.8s", pwd->pw_name); /* USER */ 1074 else 1075 (void) printf(" %7.7d", (int)psinfo->pr_euid); /* UID */ 1076 } else if (lflg) 1077 (void) printf(" %5d", (int)psinfo->pr_euid); /* UID */ 1078 1079 (void) printf("%*d", pidwidth + 1, (int)psinfo->pr_pid); /* PID */ 1080 if (lflg) 1081 (void) printf("%*d", pidwidth + 1, 1082 (int)psinfo->pr_ppid); /* PPID */ 1083 if (lflg) 1084 (void) printf(" 0"); /* CP */ 1085 if (uflg) { 1086 prtpct(0); /* %CPU */ 1087 prtpct(0); /* %MEM */ 1088 } 1089 if (lflg) { 1090 (void) printf("%4d", psinfo->pr_lwp.pr_pri); /* PRI */ 1091 (void) printf(" "); /* NICE */ 1092 } 1093 if (lflg || uflg) { 1094 (void) printf(" 0"); /* SZ */ 1095 (void) printf(" 0"); /* RSS */ 1096 } 1097 if (lflg) 1098 (void) printf(" "); /* WCHAN */ 1099 (void) printf(" "); /* TTY */ 1100 (void) printf("%c", psinfo->pr_lwp.pr_sname); /* STATE */ 1101 if (uflg) 1102 (void) printf(" "); /* START */ 1103 1104 /* time just for process */ 1105 tm = psinfo->pr_time.tv_sec; 1106 if (Sflg) { /* calculate time for process and all reaped children */ 1107 tm += psinfo->pr_ctime.tv_sec; 1108 if (psinfo->pr_time.tv_nsec + psinfo->pr_ctime.tv_nsec 1109 >= 1000000000) 1110 tm += 1; 1111 } 1112 (void) printf(" %2ld:%.2ld", tm / 60, tm % 60); /* TIME */ 1113 1114 if (vflg) { 1115 (void) printf(" 0"); /* SZ */ 1116 (void) printf(" 0"); /* RSS */ 1117 prtpct(0); /* %CPU */ 1118 prtpct(0); /* %MEM */ 1119 } 1120 (void) printf(" %.*s", maxlen, " <defunct>"); 1121 } 1122 1123 /* 1124 * Returns true iff string is all numeric. 1125 */ 1126 static int 1127 num(char *s) 1128 { 1129 int c; 1130 1131 if (s == NULL) 1132 return (0); 1133 c = *s; 1134 do { 1135 if (!isdigit(c)) 1136 return (0); 1137 } while ((c = *++s) != '\0'); 1138 return (1); 1139 } 1140 1141 /* 1142 * Function to compute the number of printable bytes in a multibyte 1143 * command string ("internationalization"). 1144 */ 1145 static int 1146 namencnt(char *cmd, int eucsize, int scrsize) 1147 { 1148 int eucwcnt = 0, scrwcnt = 0; 1149 int neucsz, nscrsz; 1150 wchar_t wchar; 1151 1152 while (*cmd != '\0') { 1153 if ((neucsz = mbtowc(&wchar, cmd, MB_LEN_MAX)) < 0) 1154 return (8); /* default to use for illegal chars */ 1155 if ((nscrsz = scrwidth(wchar)) == 0) 1156 return (8); 1157 if (eucwcnt + neucsz > eucsize || scrwcnt + nscrsz > scrsize) 1158 break; 1159 eucwcnt += neucsz; 1160 scrwcnt += nscrsz; 1161 cmd += neucsz; 1162 } 1163 return (eucwcnt); 1164 } 1165 1166 static int 1167 pscompare(const void *v1, const void *v2) 1168 { 1169 const struct psent *p1 = v1; 1170 const struct psent *p2 = v2; 1171 int i; 1172 1173 if (uflg) 1174 i = p2->psinfo->pr_pctcpu - p1->psinfo->pr_pctcpu; 1175 else if (vflg) 1176 i = p2->psinfo->pr_rssize - p1->psinfo->pr_rssize; 1177 else 1178 i = p1->psinfo->pr_ttydev - p2->psinfo->pr_ttydev; 1179 if (i == 0) 1180 i = p1->psinfo->pr_pid - p2->psinfo->pr_pid; 1181 return (i); 1182 } 1183 1184 static char * 1185 err_string(int err) 1186 { 1187 static char buf[32]; 1188 char *str = strerror(err); 1189 1190 if (str == NULL) 1191 (void) sprintf(str = buf, "Errno #%d", err); 1192 1193 return (str); 1194 } 1195