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