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 /* 23 * Copyright (c) 2013 Gary Mills 24 * 25 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28 29 /* 30 * Copyright (c) 2018, Joyent, Inc. 31 */ 32 33 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 34 /* All Rights Reserved */ 35 36 /* 37 * ps -- print things about processes. 38 */ 39 #include <stdio.h> 40 #include <ctype.h> 41 #include <string.h> 42 #include <errno.h> 43 #include <fcntl.h> 44 #include <pwd.h> 45 #include <grp.h> 46 #include <sys/types.h> 47 #include <sys/stat.h> 48 #include <sys/mkdev.h> 49 #include <unistd.h> 50 #include <stdlib.h> 51 #include <limits.h> 52 #include <dirent.h> 53 #include <sys/signal.h> 54 #include <sys/fault.h> 55 #include <sys/syscall.h> 56 #include <sys/time.h> 57 #include <procfs.h> 58 #include <locale.h> 59 #include <wctype.h> 60 #include <wchar.h> 61 #include <libw.h> 62 #include <stdarg.h> 63 #include <sys/proc.h> 64 #include <sys/pset.h> 65 #include <project.h> 66 #include <zone.h> 67 68 #define min(a, b) ((a) > (b) ? (b) : (a)) 69 #define max(a, b) ((a) < (b) ? (b) : (a)) 70 71 #define NTTYS 20 /* initial size of table for -t option */ 72 #define SIZ 30 /* initial size of tables for -p, -s, -g, -h and -z */ 73 74 /* 75 * Size of buffer holding args for t, p, s, g, u, U, G, z options. 76 * Set to ZONENAME_MAX, the minimum value needed to allow any 77 * zone to be specified. 78 */ 79 #define ARGSIZ ZONENAME_MAX 80 81 /* Max chars in a user/group name or printed u/g id */ 82 #define MAXUGNAME (LOGNAME_MAX+2) 83 84 /* Structure for storing user or group info */ 85 struct ugdata { 86 id_t id; /* numeric user-id or group-id */ 87 char name[MAXUGNAME+1]; /* user/group name, null terminated */ 88 }; 89 90 struct ughead { 91 size_t size; /* number of ugdata structs allocated */ 92 size_t nent; /* number of active entries */ 93 struct ugdata *ent; /* pointer to array of actual entries */ 94 }; 95 96 enum fname { /* enumeration of field names */ 97 F_USER, /* effective user of the process */ 98 F_RUSER, /* real user of the process */ 99 F_GROUP, /* effective group of the process */ 100 F_RGROUP, /* real group of the process */ 101 F_UID, /* numeric effective uid of the process */ 102 F_RUID, /* numeric real uid of the process */ 103 F_GID, /* numeric effective gid of the process */ 104 F_RGID, /* numeric real gid of the process */ 105 F_PID, /* process id */ 106 F_PPID, /* parent process id */ 107 F_PGID, /* process group id */ 108 F_SID, /* session id */ 109 F_PSR, /* bound processor */ 110 F_LWP, /* lwp-id */ 111 F_LWPNAME, /* lwp name */ 112 F_NLWP, /* number of lwps */ 113 F_OPRI, /* old priority (obsolete) */ 114 F_PRI, /* new priority */ 115 F_F, /* process flags */ 116 F_S, /* letter indicating the state */ 117 F_C, /* processor utilization (obsolete) */ 118 F_PCPU, /* percent of recently used cpu time */ 119 F_PMEM, /* percent of physical memory used (rss) */ 120 F_OSZ, /* virtual size of the process in pages */ 121 F_VSZ, /* virtual size of the process in kilobytes */ 122 F_RSS, /* resident set size of the process in kilobytes */ 123 F_NICE, /* "nice" value of the process */ 124 F_CLASS, /* scheduler class */ 125 F_STIME, /* start time of the process, hh:mm:ss or Month Day */ 126 F_ETIME, /* elapsed time of the process, [[dd-]hh:]mm:ss */ 127 F_TIME, /* cpu time of the process, [[dd-]hh:]mm:ss */ 128 F_TTY, /* name of the controlling terminal */ 129 F_ADDR, /* address of the process (obsolete) */ 130 F_WCHAN, /* wait channel (sleep condition variable) */ 131 F_FNAME, /* file name of command */ 132 F_COMM, /* name of command (argv[0] value) */ 133 F_ARGS, /* name of command plus all its arguments */ 134 F_TASKID, /* task id */ 135 F_PROJID, /* project id */ 136 F_PROJECT, /* project name of the process */ 137 F_PSET, /* bound processor set */ 138 F_ZONE, /* zone name */ 139 F_ZONEID, /* zone id */ 140 F_CTID, /* process contract id */ 141 F_LGRP, /* process home lgroup */ 142 F_DMODEL /* process data model */ 143 }; 144 145 struct field { 146 struct field *next; /* linked list */ 147 int fname; /* field index */ 148 const char *header; /* header to use */ 149 int width; /* width of field */ 150 }; 151 152 static struct field *fields = NULL; /* fields selected via -o */ 153 static struct field *last_field = NULL; 154 static int do_header = 0; 155 static struct timeval now; 156 157 /* array of defined fields, in fname order */ 158 struct def_field { 159 const char *fname; 160 const char *header; 161 int width; 162 int minwidth; 163 }; 164 165 static struct def_field fname[] = { 166 /* fname header width minwidth */ 167 { "user", "USER", 8, 8 }, 168 { "ruser", "RUSER", 8, 8 }, 169 { "group", "GROUP", 8, 8 }, 170 { "rgroup", "RGROUP", 8, 8 }, 171 { "uid", "UID", 5, 5 }, 172 { "ruid", "RUID", 5, 5 }, 173 { "gid", "GID", 5, 5 }, 174 { "rgid", "RGID", 5, 5 }, 175 { "pid", "PID", 5, 5 }, 176 { "ppid", "PPID", 5, 5 }, 177 { "pgid", "PGID", 5, 5 }, 178 { "sid", "SID", 5, 5 }, 179 { "psr", "PSR", 3, 2 }, 180 { "lwp", "LWP", 6, 2 }, 181 { "lwpname", "LWPNAME", 32, 8 }, 182 { "nlwp", "NLWP", 4, 2 }, 183 { "opri", "PRI", 3, 2 }, 184 { "pri", "PRI", 3, 2 }, 185 { "f", "F", 2, 2 }, 186 { "s", "S", 1, 1 }, 187 { "c", "C", 2, 2 }, 188 { "pcpu", "%CPU", 4, 4 }, 189 { "pmem", "%MEM", 4, 4 }, 190 { "osz", "SZ", 4, 4 }, 191 { "vsz", "VSZ", 4, 4 }, 192 { "rss", "RSS", 4, 4 }, 193 { "nice", "NI", 2, 2 }, 194 { "class", "CLS", 4, 2 }, 195 { "stime", "STIME", 8, 8 }, 196 { "etime", "ELAPSED", 11, 7 }, 197 { "time", "TIME", 11, 5 }, 198 { "tty", "TT", 7, 7 }, 199 #ifdef _LP64 200 { "addr", "ADDR", 16, 8 }, 201 { "wchan", "WCHAN", 16, 8 }, 202 #else 203 { "addr", "ADDR", 8, 8 }, 204 { "wchan", "WCHAN", 8, 8 }, 205 #endif 206 { "fname", "COMMAND", 8, 8 }, 207 { "comm", "COMMAND", 80, 8 }, 208 { "args", "COMMAND", 80, 80 }, 209 { "taskid", "TASKID", 5, 5 }, 210 { "projid", "PROJID", 5, 5 }, 211 { "project", "PROJECT", 8, 8 }, 212 { "pset", "PSET", 3, 3 }, 213 { "zone", "ZONE", 8, 8 }, 214 { "zoneid", "ZONEID", 5, 5 }, 215 { "ctid", "CTID", 5, 5 }, 216 { "lgrp", "LGRP", 4, 2 }, 217 { "dmodel", "DMODEL", 6, 6 }, 218 }; 219 220 #define NFIELDS (sizeof (fname) / sizeof (fname[0])) 221 222 static int retcode = 1; 223 static int lflg; 224 static int Aflg; 225 static int uflg; 226 static int Uflg; 227 static int Gflg; 228 static int aflg; 229 static int dflg; 230 static int Lflg; 231 static int Pflg; 232 static int Wflg; 233 static int yflg; 234 static int pflg; 235 static int fflg; 236 static int cflg; 237 static int jflg; 238 static int gflg; 239 static int sflg; 240 static int tflg; 241 static int zflg; 242 static int Zflg; 243 static int hflg; 244 static int Hflg; 245 static uid_t tuid = (uid_t)-1; 246 static int errflg; 247 248 static int ndev; /* number of devices */ 249 static int maxdev; /* number of devl structures allocated */ 250 251 #define DNINCR 100 252 #define DNSIZE 14 253 static struct devl { /* device list */ 254 char dname[DNSIZE]; /* device name */ 255 dev_t ddev; /* device number */ 256 } *devl; 257 258 static struct tty { 259 char *tname; 260 dev_t tdev; 261 } *tty = NULL; /* for t option */ 262 static size_t ttysz = 0; 263 static int ntty = 0; 264 265 static pid_t *pid = NULL; /* for p option */ 266 static size_t pidsz = 0; 267 static size_t npid = 0; 268 269 static int *lgrps = NULL; /* list of lgroup IDs for for h option */ 270 static size_t lgrps_size = 0; /* size of the lgrps list */ 271 static size_t nlgrps = 0; /* number elements in the list */ 272 273 /* Maximum possible lgroup ID value */ 274 #define MAX_LGRP_ID 256 275 276 static pid_t *grpid = NULL; /* for g option */ 277 static size_t grpidsz = 0; 278 static int ngrpid = 0; 279 280 static pid_t *sessid = NULL; /* for s option */ 281 static size_t sessidsz = 0; 282 static int nsessid = 0; 283 284 static zoneid_t *zoneid = NULL; /* for z option */ 285 static size_t zoneidsz = 0; 286 static int nzoneid = 0; 287 288 static int kbytes_per_page; 289 static int pidwidth; 290 291 static char *procdir = "/proc"; /* standard /proc directory */ 292 293 static struct ughead euid_tbl; /* table to store selected euid's */ 294 static struct ughead ruid_tbl; /* table to store selected real uid's */ 295 static struct ughead egid_tbl; /* table to store selected egid's */ 296 static struct ughead rgid_tbl; /* table to store selected real gid's */ 297 static prheader_t *lpsinfobuf; /* buffer to contain lpsinfo */ 298 static size_t lpbufsize; 299 300 /* 301 * This constant defines the sentinal number of process IDs below which we 302 * only examine individual entries in /proc rather than scanning through 303 * /proc. This optimization is a huge win in the common case. 304 */ 305 #define PTHRESHOLD 40 306 307 #define UCB_OPTS "-aceglnrtuvwxSU" 308 309 static void usage(void); 310 static char *getarg(char **); 311 static char *parse_format(char *); 312 static char *gettty(psinfo_t *); 313 static int prfind(int, psinfo_t *, char **); 314 static void prcom(psinfo_t *, char *); 315 static void prtpct(ushort_t, int); 316 static void print_time(time_t, int); 317 static void print_field(psinfo_t *, struct field *, const char *); 318 static void print_zombie_field(psinfo_t *, struct field *, const char *); 319 static void pr_fields(psinfo_t *, const char *, 320 void (*print_fld)(psinfo_t *, struct field *, const char *)); 321 static int search(pid_t *, int, pid_t); 322 static void add_ugentry(struct ughead *, char *); 323 static int uconv(struct ughead *); 324 static int gconv(struct ughead *); 325 static int ugfind(id_t, struct ughead *); 326 static void prtime(timestruc_t, int, int); 327 static void przom(psinfo_t *); 328 static int namencnt(char *, int, int); 329 static char *err_string(int); 330 static int print_proc(char *pname); 331 static time_t delta_secs(const timestruc_t *); 332 static int str2id(const char *, pid_t *, long, long); 333 static int str2uid(const char *, uid_t *, unsigned long, unsigned long); 334 static void *Realloc(void *, size_t); 335 static int pidcmp(const void *p1, const void *p2); 336 337 extern int ucbmain(int, char **); 338 static int stdmain(int, char **); 339 340 int 341 main(int argc, char **argv) 342 { 343 const char *me; 344 345 /* 346 * The original two ps'es are linked in a single binary; 347 * their main()s are renamed to stdmain for /usr/bin/ps and 348 * ucbmain for /usr/ucb/ps. 349 * We try to figure out which instance of ps the user wants to run. 350 * Traditionally, the UCB variant doesn't require the flag argument 351 * start with a "-". If the first argument doesn't start with a 352 * "-", we call "ucbmain". 353 * If there's a first argument and it starts with a "-", we check 354 * whether any of the options isn't acceptable to "ucbmain"; in that 355 * case we run "stdmain". 356 * If we can't tell from the options which main to call, we check 357 * the binary we are running. We default to "stdmain" but 358 * any mention in the executable name of "ucb" causes us to call 359 * ucbmain. 360 */ 361 if (argv[1] != NULL) { 362 if (argv[1][0] != '-') 363 return (ucbmain(argc, argv)); 364 else if (argv[1][strspn(argv[1], UCB_OPTS)] != '\0') 365 return (stdmain(argc, argv)); 366 } 367 368 me = getexecname(); 369 370 if (me != NULL && strstr(me, "ucb") != NULL) 371 return (ucbmain(argc, argv)); 372 else 373 return (stdmain(argc, argv)); 374 } 375 376 static int 377 stdmain(int argc, char **argv) 378 { 379 char *p; 380 char *p1; 381 char *parg; 382 int c; 383 int i; 384 int pgerrflg = 0; /* err flg: non-numeric arg w/p & g options */ 385 size_t size, len; 386 DIR *dirp; 387 struct dirent *dentp; 388 pid_t maxpid; 389 pid_t id; 390 int ret; 391 char loc_stime_str[32]; 392 393 (void) setlocale(LC_ALL, ""); 394 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 395 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 396 #endif 397 (void) textdomain(TEXT_DOMAIN); 398 399 (void) memset(&euid_tbl, 0, sizeof (euid_tbl)); 400 (void) memset(&ruid_tbl, 0, sizeof (ruid_tbl)); 401 (void) memset(&egid_tbl, 0, sizeof (egid_tbl)); 402 (void) memset(&rgid_tbl, 0, sizeof (rgid_tbl)); 403 404 kbytes_per_page = sysconf(_SC_PAGESIZE) / 1024; 405 406 (void) gettimeofday(&now, NULL); 407 408 /* 409 * calculate width of pid fields based on configured MAXPID 410 * (must be at least 5 to retain output format compatibility) 411 */ 412 id = maxpid = (pid_t)sysconf(_SC_MAXPID); 413 pidwidth = 1; 414 while ((id /= 10) > 0) 415 ++pidwidth; 416 pidwidth = pidwidth < 5 ? 5 : pidwidth; 417 418 fname[F_PID].width = fname[F_PPID].width = pidwidth; 419 fname[F_PGID].width = fname[F_SID].width = pidwidth; 420 421 /* 422 * TRANSLATION_NOTE 423 * Specify the printf format with width and precision for 424 * the STIME field. 425 */ 426 len = snprintf(loc_stime_str, sizeof (loc_stime_str), 427 dcgettext(NULL, "%8.8s", LC_TIME), "STIME"); 428 if (len >= sizeof (loc_stime_str)) 429 len = sizeof (loc_stime_str) - 1; 430 431 fname[F_STIME].width = fname[F_STIME].minwidth = len; 432 433 while ((c = getopt(argc, argv, "jlfceAadLPWyZHh:t:p:g:u:U:G:n:s:o:z:")) 434 != EOF) 435 switch (c) { 436 case 'H': /* Show home lgroups */ 437 Hflg++; 438 break; 439 case 'h': 440 /* 441 * Show processes/threads with given home lgroups 442 */ 443 hflg++; 444 p1 = optarg; 445 do { 446 int id; 447 448 /* 449 * Get all IDs in the list, verify for 450 * correctness and place in lgrps array. 451 */ 452 parg = getarg(&p1); 453 /* Convert string to integer */ 454 ret = str2id(parg, (pid_t *)&id, 0, 455 MAX_LGRP_ID); 456 /* Complain if ID didn't parse correctly */ 457 if (ret != 0) { 458 pgerrflg++; 459 (void) fprintf(stderr, 460 gettext("ps: %s "), parg); 461 if (ret == EINVAL) 462 (void) fprintf(stderr, 463 gettext("is an invalid " 464 "non-numeric argument")); 465 else 466 (void) fprintf(stderr, 467 gettext("exceeds valid " 468 "range")); 469 (void) fprintf(stderr, 470 gettext(" for -h option\n")); 471 continue; 472 } 473 474 /* Extend lgrps array if needed */ 475 if (nlgrps == lgrps_size) { 476 /* Double the size of the lgrps array */ 477 if (lgrps_size == 0) 478 lgrps_size = SIZ; 479 lgrps_size *= 2; 480 lgrps = Realloc(lgrps, 481 lgrps_size * sizeof (int)); 482 } 483 /* place the id in the lgrps table */ 484 lgrps[nlgrps++] = id; 485 } while (*p1); 486 break; 487 case 'l': /* long listing */ 488 lflg++; 489 break; 490 case 'f': /* full listing */ 491 fflg++; 492 break; 493 case 'j': 494 jflg++; 495 break; 496 case 'c': 497 /* 498 * Format output to reflect scheduler changes: 499 * high numbers for high priorities and don't 500 * print nice or p_cpu values. 'c' option only 501 * effective when used with 'l' or 'f' options. 502 */ 503 cflg++; 504 break; 505 case 'A': /* list every process */ 506 case 'e': /* (obsolete) list every process */ 507 Aflg++; 508 tflg = Gflg = Uflg = uflg = pflg = gflg = sflg = 0; 509 zflg = hflg = 0; 510 break; 511 case 'a': 512 /* 513 * Same as 'e' except no session group leaders 514 * and no non-terminal processes. 515 */ 516 aflg++; 517 break; 518 case 'd': /* same as e except no session leaders */ 519 dflg++; 520 break; 521 case 'L': /* show lwps */ 522 Lflg++; 523 break; 524 case 'P': /* show bound processor */ 525 Pflg++; 526 break; 527 case 'W': /* truncate long names */ 528 Wflg++; 529 break; 530 case 'y': /* omit F & ADDR, report RSS & SZ in Kby */ 531 yflg++; 532 break; 533 case 'n': /* no longer needed; retain as no-op */ 534 (void) fprintf(stderr, 535 gettext("ps: warning: -n option ignored\n")); 536 break; 537 case 't': /* terminals */ 538 #define TSZ 30 539 tflg++; 540 p1 = optarg; 541 do { 542 char nambuf[TSZ+6]; /* for "/dev/" + '\0' */ 543 struct stat64 s; 544 parg = getarg(&p1); 545 p = Realloc(NULL, TSZ+1); /* for '\0' */ 546 /* zero the buffer before using it */ 547 p[0] = '\0'; 548 size = TSZ; 549 if (isdigit(*parg)) { 550 (void) strcpy(p, "tty"); 551 size -= 3; 552 } 553 (void) strncat(p, parg, size); 554 if (ntty == ttysz) { 555 if ((ttysz *= 2) == 0) 556 ttysz = NTTYS; 557 tty = Realloc(tty, 558 (ttysz + 1) * sizeof (struct tty)); 559 } 560 tty[ntty].tdev = PRNODEV; 561 (void) strcpy(nambuf, "/dev/"); 562 (void) strcat(nambuf, p); 563 if (stat64(nambuf, &s) == 0) 564 tty[ntty].tdev = s.st_rdev; 565 tty[ntty++].tname = p; 566 } while (*p1); 567 break; 568 case 'p': /* proc ids */ 569 pflg++; 570 p1 = optarg; 571 do { 572 pid_t id; 573 574 parg = getarg(&p1); 575 if ((ret = str2id(parg, &id, 0, maxpid)) != 0) { 576 pgerrflg++; 577 (void) fprintf(stderr, 578 gettext("ps: %s "), parg); 579 if (ret == EINVAL) 580 (void) fprintf(stderr, 581 gettext("is an invalid " 582 "non-numeric argument")); 583 else 584 (void) fprintf(stderr, 585 gettext("exceeds valid " 586 "range")); 587 (void) fprintf(stderr, 588 gettext(" for -p option\n")); 589 continue; 590 } 591 592 if (npid == pidsz) { 593 if ((pidsz *= 2) == 0) 594 pidsz = SIZ; 595 pid = Realloc(pid, 596 pidsz * sizeof (pid_t)); 597 } 598 pid[npid++] = id; 599 } while (*p1); 600 break; 601 case 's': /* session */ 602 sflg++; 603 p1 = optarg; 604 do { 605 pid_t id; 606 607 parg = getarg(&p1); 608 if ((ret = str2id(parg, &id, 0, maxpid)) != 0) { 609 pgerrflg++; 610 (void) fprintf(stderr, 611 gettext("ps: %s "), parg); 612 if (ret == EINVAL) 613 (void) fprintf(stderr, 614 gettext("is an invalid " 615 "non-numeric argument")); 616 else 617 (void) fprintf(stderr, 618 gettext("exceeds valid " 619 "range")); 620 (void) fprintf(stderr, 621 gettext(" for -s option\n")); 622 continue; 623 } 624 625 if (nsessid == sessidsz) { 626 if ((sessidsz *= 2) == 0) 627 sessidsz = SIZ; 628 sessid = Realloc(sessid, 629 sessidsz * sizeof (pid_t)); 630 } 631 sessid[nsessid++] = id; 632 } while (*p1); 633 break; 634 case 'g': /* proc group */ 635 gflg++; 636 p1 = optarg; 637 do { 638 pid_t id; 639 640 parg = getarg(&p1); 641 if ((ret = str2id(parg, &id, 0, maxpid)) != 0) { 642 pgerrflg++; 643 (void) fprintf(stderr, 644 gettext("ps: %s "), parg); 645 if (ret == EINVAL) 646 (void) fprintf(stderr, 647 gettext("is an invalid " 648 "non-numeric argument")); 649 else 650 (void) fprintf(stderr, 651 gettext("exceeds valid " 652 "range")); 653 (void) fprintf(stderr, 654 gettext(" for -g option\n")); 655 continue; 656 } 657 658 if (ngrpid == grpidsz) { 659 if ((grpidsz *= 2) == 0) 660 grpidsz = SIZ; 661 grpid = Realloc(grpid, 662 grpidsz * sizeof (pid_t)); 663 } 664 grpid[ngrpid++] = id; 665 } while (*p1); 666 break; 667 case 'u': /* effective user name or number */ 668 uflg++; 669 p1 = optarg; 670 do { 671 parg = getarg(&p1); 672 add_ugentry(&euid_tbl, parg); 673 } while (*p1); 674 break; 675 case 'U': /* real user name or number */ 676 Uflg++; 677 p1 = optarg; 678 do { 679 parg = getarg(&p1); 680 add_ugentry(&ruid_tbl, parg); 681 } while (*p1); 682 break; 683 case 'G': /* real group name or number */ 684 Gflg++; 685 p1 = optarg; 686 do { 687 parg = getarg(&p1); 688 add_ugentry(&rgid_tbl, parg); 689 } while (*p1); 690 break; 691 case 'o': /* output format */ 692 p = optarg; 693 while ((p = parse_format(p)) != NULL) 694 ; 695 break; 696 case 'z': /* zone name or number */ 697 zflg++; 698 p1 = optarg; 699 do { 700 zoneid_t id; 701 702 parg = getarg(&p1); 703 if (zone_get_id(parg, &id) != 0) { 704 pgerrflg++; 705 (void) fprintf(stderr, 706 gettext("ps: unknown zone %s\n"), 707 parg); 708 continue; 709 } 710 711 if (nzoneid == zoneidsz) { 712 if ((zoneidsz *= 2) == 0) 713 zoneidsz = SIZ; 714 zoneid = Realloc(zoneid, 715 zoneidsz * sizeof (zoneid_t)); 716 } 717 zoneid[nzoneid++] = id; 718 } while (*p1); 719 break; 720 case 'Z': /* show zone name */ 721 Zflg++; 722 break; 723 default: /* error on ? */ 724 errflg++; 725 break; 726 } 727 728 if (errflg || optind < argc || pgerrflg) 729 usage(); 730 731 if (tflg) 732 tty[ntty].tname = NULL; 733 /* 734 * If an appropriate option has not been specified, use the 735 * current terminal and effective uid as the default. 736 */ 737 if (!(aflg|Aflg|dflg|Gflg|hflg|Uflg|uflg|tflg|pflg|gflg|sflg|zflg)) { 738 psinfo_t info; 739 int procfd; 740 char *name; 741 char pname[100]; 742 743 /* get our own controlling tty name using /proc */ 744 (void) snprintf(pname, sizeof (pname), 745 "%s/self/psinfo", procdir); 746 if ((procfd = open(pname, O_RDONLY)) < 0 || 747 read(procfd, (char *)&info, sizeof (info)) < 0 || 748 info.pr_ttydev == PRNODEV) { 749 (void) fprintf(stderr, 750 gettext("ps: no controlling terminal\n")); 751 exit(1); 752 } 753 (void) close(procfd); 754 755 i = 0; 756 name = gettty(&info); 757 if (*name == '?') { 758 (void) fprintf(stderr, 759 gettext("ps: can't find controlling terminal\n")); 760 exit(1); 761 } 762 if (ntty == ttysz) { 763 if ((ttysz *= 2) == 0) 764 ttysz = NTTYS; 765 tty = Realloc(tty, (ttysz + 1) * sizeof (struct tty)); 766 } 767 tty[ntty].tdev = info.pr_ttydev; 768 tty[ntty++].tname = name; 769 tty[ntty].tname = NULL; 770 tflg++; 771 tuid = getuid(); 772 } 773 if (Aflg) { 774 Gflg = Uflg = uflg = pflg = sflg = gflg = aflg = dflg = 0; 775 zflg = hflg = 0; 776 } 777 if (Aflg | aflg | dflg) 778 tflg = 0; 779 780 i = 0; /* prepare to exit on name lookup errors */ 781 i += uconv(&euid_tbl); 782 i += uconv(&ruid_tbl); 783 i += gconv(&egid_tbl); 784 i += gconv(&rgid_tbl); 785 if (i) 786 exit(1); 787 788 /* allocate a buffer for lwpsinfo structures */ 789 lpbufsize = 4096; 790 if (Lflg && (lpsinfobuf = malloc(lpbufsize)) == NULL) { 791 (void) fprintf(stderr, 792 gettext("ps: no memory\n")); 793 exit(1); 794 } 795 796 if (fields) { /* print user-specified header */ 797 if (do_header) { 798 struct field *f; 799 800 for (f = fields; f != NULL; f = f->next) { 801 if (f != fields) 802 (void) printf(" "); 803 switch (f->fname) { 804 case F_TTY: 805 (void) printf("%-*s", 806 f->width, f->header); 807 break; 808 case F_LWPNAME: 809 case F_FNAME: 810 case F_COMM: 811 case F_ARGS: 812 /* 813 * Print these headers full width 814 * unless they appear at the end. 815 */ 816 if (f->next != NULL) { 817 (void) printf("%-*s", 818 f->width, f->header); 819 } else { 820 (void) printf("%s", 821 f->header); 822 } 823 break; 824 default: 825 (void) printf("%*s", 826 f->width, f->header); 827 break; 828 } 829 } 830 (void) printf("\n"); 831 } 832 } else { /* print standard header */ 833 /* 834 * All fields before 'PID' are printed with a trailing space 835 * as a separator and that is how we print the headers too. 836 */ 837 if (lflg) { 838 if (yflg) 839 (void) printf("S "); 840 else 841 (void) printf(" F S "); 842 } 843 if (Zflg) 844 (void) printf(" ZONE "); 845 if (fflg) { 846 (void) printf(" UID "); 847 } else if (lflg) 848 (void) printf(" UID "); 849 850 (void) printf("%*s", pidwidth, "PID"); 851 if (lflg || fflg) 852 (void) printf(" %*s", pidwidth, "PPID"); 853 if (jflg) 854 (void) printf(" %*s %*s", pidwidth, "PGID", 855 pidwidth, "SID"); 856 if (Lflg) 857 (void) printf(" LWP"); 858 if (Pflg) 859 (void) printf(" PSR"); 860 if (Lflg && fflg) 861 (void) printf(" NLWP"); 862 if (cflg) 863 (void) printf(" CLS PRI"); 864 else if (lflg || fflg) { 865 (void) printf(" C"); 866 if (lflg) 867 (void) printf(" PRI NI"); 868 } 869 if (lflg) { 870 if (yflg) 871 (void) printf(" RSS SZ WCHAN"); 872 else 873 (void) printf(" ADDR SZ WCHAN"); 874 } 875 if (fflg) 876 (void) printf(" %s", loc_stime_str); 877 if (Hflg) 878 (void) printf(" LGRP"); 879 if (Lflg) 880 (void) printf(" TTY LTIME CMD\n"); 881 else 882 (void) printf(" TTY TIME CMD\n"); 883 } 884 885 886 if (pflg && !(aflg|Aflg|dflg|Gflg|Uflg|uflg|hflg|tflg|gflg|sflg|zflg) && 887 npid <= PTHRESHOLD) { 888 /* 889 * If we are looking at specific processes go straight 890 * to their /proc entries and don't scan /proc. 891 */ 892 int i; 893 894 (void) qsort(pid, npid, sizeof (pid_t), pidcmp); 895 for (i = 0; i < npid; i++) { 896 char pname[12]; 897 898 if (i >= 1 && pid[i] == pid[i - 1]) 899 continue; 900 (void) sprintf(pname, "%d", (int)pid[i]); 901 if (print_proc(pname) == 0) 902 retcode = 0; 903 } 904 } else { 905 /* 906 * Determine which processes to print info about by searching 907 * the /proc directory and looking at each process. 908 */ 909 if ((dirp = opendir(procdir)) == NULL) { 910 (void) fprintf(stderr, 911 gettext("ps: cannot open PROC directory %s\n"), 912 procdir); 913 exit(1); 914 } 915 916 /* for each active process --- */ 917 while ((dentp = readdir(dirp)) != NULL) { 918 if (dentp->d_name[0] == '.') /* skip . and .. */ 919 continue; 920 if (print_proc(dentp->d_name) == 0) 921 retcode = 0; 922 } 923 924 (void) closedir(dirp); 925 } 926 return (retcode); 927 } 928 929 930 int 931 print_proc(char *pid_name) 932 { 933 char pname[PATH_MAX]; 934 int pdlen; 935 int found; 936 int procfd; /* filedescriptor for /proc/nnnnn/psinfo */ 937 char *tp; /* ptr to ttyname, if any */ 938 psinfo_t info; /* process information from /proc */ 939 lwpsinfo_t *lwpsinfo; /* array of lwpsinfo structs */ 940 941 pdlen = snprintf(pname, sizeof (pname), "%s/%s/", procdir, pid_name); 942 if (pdlen >= sizeof (pname) - 10) 943 return (1); 944 retry: 945 (void) strcpy(&pname[pdlen], "psinfo"); 946 if ((procfd = open(pname, O_RDONLY)) == -1) { 947 /* Process may have exited meanwhile. */ 948 return (1); 949 } 950 /* 951 * Get the info structure for the process and close quickly. 952 */ 953 if (read(procfd, (char *)&info, sizeof (info)) < 0) { 954 int saverr = errno; 955 956 (void) close(procfd); 957 if (saverr == EAGAIN) 958 goto retry; 959 if (saverr != ENOENT) 960 (void) fprintf(stderr, 961 gettext("ps: read() on %s: %s\n"), 962 pname, err_string(saverr)); 963 return (1); 964 } 965 (void) close(procfd); 966 967 found = 0; 968 if (info.pr_lwp.pr_state == 0) /* can't happen? */ 969 return (1); 970 971 /* 972 * Omit session group leaders for 'a' and 'd' options. 973 */ 974 if ((info.pr_pid == info.pr_sid) && (dflg || aflg)) 975 return (1); 976 if (Aflg || dflg) 977 found++; 978 else if (pflg && search(pid, npid, info.pr_pid)) 979 found++; /* ppid in p option arg list */ 980 else if (uflg && ugfind((id_t)info.pr_euid, &euid_tbl)) 981 found++; /* puid in u option arg list */ 982 else if (Uflg && ugfind((id_t)info.pr_uid, &ruid_tbl)) 983 found++; /* puid in U option arg list */ 984 #ifdef NOT_YET 985 else if (gflg && ugfind((id_t)info.pr_egid, &egid_tbl)) 986 found++; /* pgid in g option arg list */ 987 #endif /* NOT_YET */ 988 else if (Gflg && ugfind((id_t)info.pr_gid, &rgid_tbl)) 989 found++; /* pgid in G option arg list */ 990 else if (gflg && search(grpid, ngrpid, info.pr_pgid)) 991 found++; /* grpid in g option arg list */ 992 else if (sflg && search(sessid, nsessid, info.pr_sid)) 993 found++; /* sessid in s option arg list */ 994 else if (zflg && search(zoneid, nzoneid, info.pr_zoneid)) 995 found++; /* zoneid in z option arg list */ 996 else if (hflg && search((pid_t *)lgrps, nlgrps, info.pr_lwp.pr_lgrp)) 997 found++; /* home lgroup in h option arg list */ 998 if (!found && !tflg && !aflg) 999 return (1); 1000 if (!prfind(found, &info, &tp)) 1001 return (1); 1002 if (Lflg && (info.pr_nlwp + info.pr_nzomb) > 1) { 1003 ssize_t prsz; 1004 1005 (void) strcpy(&pname[pdlen], "lpsinfo"); 1006 if ((procfd = open(pname, O_RDONLY)) == -1) 1007 return (1); 1008 /* 1009 * Get the info structures for the lwps. 1010 */ 1011 prsz = read(procfd, lpsinfobuf, lpbufsize); 1012 if (prsz == -1) { 1013 int saverr = errno; 1014 1015 (void) close(procfd); 1016 if (saverr == EAGAIN) 1017 goto retry; 1018 if (saverr != ENOENT) 1019 (void) fprintf(stderr, 1020 gettext("ps: read() on %s: %s\n"), 1021 pname, err_string(saverr)); 1022 return (1); 1023 } 1024 (void) close(procfd); 1025 if (prsz == lpbufsize) { 1026 /* 1027 * buffer overflow. Realloc new buffer. 1028 * Error handling is done in Realloc(). 1029 */ 1030 lpbufsize *= 2; 1031 lpsinfobuf = Realloc(lpsinfobuf, lpbufsize); 1032 goto retry; 1033 } 1034 if (lpsinfobuf->pr_nent != (info.pr_nlwp + info.pr_nzomb)) 1035 goto retry; 1036 lwpsinfo = (lwpsinfo_t *)(lpsinfobuf + 1); 1037 } 1038 if (!Lflg || (info.pr_nlwp + info.pr_nzomb) <= 1) { 1039 prcom(&info, tp); 1040 } else { 1041 int nlwp = 0; 1042 1043 do { 1044 info.pr_lwp = *lwpsinfo; 1045 prcom(&info, tp); 1046 /* LINTED improper alignment */ 1047 lwpsinfo = (lwpsinfo_t *)((char *)lwpsinfo + 1048 lpsinfobuf->pr_entsize); 1049 } while (++nlwp < lpsinfobuf->pr_nent); 1050 } 1051 return (0); 1052 } 1053 1054 static int 1055 field_cmp(const void *l, const void *r) 1056 { 1057 struct def_field *lhs = *((struct def_field **)l); 1058 struct def_field *rhs = *((struct def_field **)r); 1059 1060 return (strcmp(lhs->fname, rhs->fname)); 1061 } 1062 1063 static void 1064 usage(void) /* print usage message and quit */ 1065 { 1066 struct def_field *df, *sorted[NFIELDS]; 1067 int pos = 80, i = 0; 1068 1069 static char usage1[] = 1070 "ps [ -aAdefHlcjLPWyZ ] [ -o format ] [ -t termlist ]"; 1071 static char usage2[] = 1072 "\t[ -u userlist ] [ -U userlist ] [ -G grouplist ]"; 1073 static char usage3[] = 1074 "\t[ -p proclist ] [ -g pgrplist ] [ -s sidlist ]"; 1075 static char usage4[] = 1076 "\t[ -z zonelist ] [-h lgrplist]"; 1077 static char usage5[] = 1078 " 'format' is one or more of:"; 1079 1080 (void) fprintf(stderr, 1081 gettext("usage: %s\n%s\n%s\n%s\n%s"), 1082 gettext(usage1), gettext(usage2), gettext(usage3), 1083 gettext(usage4), gettext(usage5)); 1084 1085 /* 1086 * Now print out the possible output formats such that they neatly fit 1087 * into eighty columns. Note that the fact that we are determining 1088 * this output programmatically means that a gettext() is impossible -- 1089 * but it would be a mistake to localize the output formats anyway as 1090 * they are tokens for input, not output themselves. 1091 */ 1092 for (df = &fname[0]; df < &fname[NFIELDS]; df++) 1093 sorted[i++] = df; 1094 1095 (void) qsort(sorted, NFIELDS, sizeof (void *), field_cmp); 1096 1097 for (i = 0; i < NFIELDS; i++) { 1098 if (pos + strlen((df = sorted[i])->fname) + 1 >= 80) { 1099 (void) fprintf(stderr, "\n\t"); 1100 pos = 8; 1101 } 1102 1103 (void) fprintf(stderr, "%s%s", pos > 8 ? " " : "", df->fname); 1104 pos += strlen(df->fname) + 1; 1105 } 1106 1107 (void) fprintf(stderr, "\n"); 1108 1109 exit(1); 1110 } 1111 1112 /* 1113 * getarg() finds the next argument in list and copies arg into argbuf. 1114 * p1 first pts to arg passed back from getopt routine. p1 is then 1115 * bumped to next character that is not a comma or blank -- p1 NULL 1116 * indicates end of list. 1117 */ 1118 static char * 1119 getarg(char **pp1) 1120 { 1121 static char argbuf[ARGSIZ]; 1122 char *p1 = *pp1; 1123 char *parga = argbuf; 1124 int c; 1125 1126 while ((c = *p1) != '\0' && (c == ',' || isspace(c))) 1127 p1++; 1128 1129 while ((c = *p1) != '\0' && c != ',' && !isspace(c)) { 1130 if (parga < argbuf + ARGSIZ - 1) 1131 *parga++ = c; 1132 p1++; 1133 } 1134 *parga = '\0'; 1135 1136 while ((c = *p1) != '\0' && (c == ',' || isspace(c))) 1137 p1++; 1138 1139 *pp1 = p1; 1140 1141 return (argbuf); 1142 } 1143 1144 /* 1145 * parse_format() takes the argument to the -o option, 1146 * sets up the next output field structure, and returns 1147 * a pointer to any further output field specifier(s). 1148 * As a side-effect, it increments errflg if encounters a format error. 1149 */ 1150 static char * 1151 parse_format(char *arg) 1152 { 1153 int c; 1154 char *name; 1155 char *header = NULL; 1156 int width = 0; 1157 struct def_field *df; 1158 struct field *f; 1159 1160 while ((c = *arg) != '\0' && (c == ',' || isspace(c))) 1161 arg++; 1162 if (c == '\0') 1163 return (NULL); 1164 name = arg; 1165 arg = strpbrk(arg, " \t\r\v\f\n,="); 1166 if (arg != NULL) { 1167 c = *arg; 1168 *arg++ = '\0'; 1169 if (c == '=') { 1170 char *s; 1171 1172 header = arg; 1173 arg = NULL; 1174 width = strlen(header); 1175 s = header + width; 1176 while (s > header && isspace(*--s)) 1177 *s = '\0'; 1178 while (isspace(*header)) 1179 header++; 1180 } 1181 } 1182 for (df = &fname[0]; df < &fname[NFIELDS]; df++) 1183 if (strcmp(name, df->fname) == 0) { 1184 if (strcmp(name, "lwp") == 0 || 1185 strcmp(name, "lwpname") == 0) 1186 Lflg++; 1187 break; 1188 } 1189 if (df >= &fname[NFIELDS]) { 1190 (void) fprintf(stderr, 1191 gettext("ps: unknown output format: -o %s\n"), 1192 name); 1193 errflg++; 1194 return (arg); 1195 } 1196 if ((f = malloc(sizeof (*f))) == NULL) { 1197 (void) fprintf(stderr, 1198 gettext("ps: malloc() for output format failed, %s\n"), 1199 err_string(errno)); 1200 exit(1); 1201 } 1202 f->next = NULL; 1203 f->fname = df - &fname[0]; 1204 f->header = header? header : df->header; 1205 if (width == 0) 1206 width = df->width; 1207 if (*f->header != '\0') 1208 do_header = 1; 1209 f->width = max(width, df->minwidth); 1210 1211 if (fields == NULL) 1212 fields = last_field = f; 1213 else { 1214 last_field->next = f; 1215 last_field = f; 1216 } 1217 1218 return (arg); 1219 } 1220 1221 static char * 1222 devlookup(dev_t ddev) 1223 { 1224 struct devl *dp; 1225 int i; 1226 1227 for (dp = devl, i = 0; i < ndev; dp++, i++) { 1228 if (dp->ddev == ddev) 1229 return (dp->dname); 1230 } 1231 return (NULL); 1232 } 1233 1234 static char * 1235 devadd(char *name, dev_t ddev) 1236 { 1237 struct devl *dp; 1238 int leng, start, i; 1239 1240 if (ndev == maxdev) { 1241 maxdev += DNINCR; 1242 devl = Realloc(devl, maxdev * sizeof (struct devl)); 1243 } 1244 dp = &devl[ndev++]; 1245 1246 dp->ddev = ddev; 1247 if (name == NULL) { 1248 (void) strcpy(dp->dname, "??"); 1249 return (dp->dname); 1250 } 1251 1252 leng = strlen(name); 1253 /* Strip off /dev/ */ 1254 if (leng < DNSIZE + 4) 1255 (void) strcpy(dp->dname, &name[5]); 1256 else { 1257 start = leng - DNSIZE - 1; 1258 1259 for (i = start; i < leng && name[i] != '/'; i++) 1260 ; 1261 if (i == leng) 1262 (void) strncpy(dp->dname, &name[start], DNSIZE); 1263 else 1264 (void) strncpy(dp->dname, &name[i+1], DNSIZE); 1265 } 1266 return (dp->dname); 1267 } 1268 1269 /* 1270 * gettty returns the user's tty number or ? if none. 1271 */ 1272 static char * 1273 gettty(psinfo_t *psinfo) 1274 { 1275 extern char *_ttyname_dev(dev_t, char *, size_t); 1276 static zoneid_t zid = -1; 1277 char devname[TTYNAME_MAX]; 1278 char *retval; 1279 1280 if (zid == -1) 1281 zid = getzoneid(); 1282 1283 if (psinfo->pr_ttydev == PRNODEV || psinfo->pr_zoneid != zid) 1284 return ("?"); 1285 1286 if ((retval = devlookup(psinfo->pr_ttydev)) != NULL) 1287 return (retval); 1288 1289 retval = _ttyname_dev(psinfo->pr_ttydev, devname, sizeof (devname)); 1290 1291 return (devadd(retval, psinfo->pr_ttydev)); 1292 } 1293 1294 /* 1295 * Find the process's tty and return 1 if process is to be printed. 1296 */ 1297 static int 1298 prfind(int found, psinfo_t *psinfo, char **tpp) 1299 { 1300 char *tp; 1301 struct tty *ttyp; 1302 1303 if (psinfo->pr_nlwp == 0) { 1304 /* process is a zombie */ 1305 *tpp = "?"; 1306 if (tflg && !found) 1307 return (0); 1308 return (1); 1309 } 1310 1311 /* 1312 * Get current terminal. If none ("?") and 'a' is set, don't print 1313 * info. If 't' is set, check if term is in list of desired terminals 1314 * and print it if it is. 1315 */ 1316 tp = gettty(psinfo); 1317 if (aflg && *tp == '?') { 1318 *tpp = tp; 1319 return (0); 1320 } 1321 if (tflg && !found) { 1322 int match = 0; 1323 char *other = NULL; 1324 for (ttyp = tty; ttyp->tname != NULL; ttyp++) { 1325 /* 1326 * Look for a name match 1327 */ 1328 if (strcmp(tp, ttyp->tname) == 0) { 1329 match = 1; 1330 break; 1331 } 1332 /* 1333 * Look for same device under different names. 1334 */ 1335 if ((other == NULL) && 1336 (ttyp->tdev != PRNODEV) && 1337 (psinfo->pr_ttydev == ttyp->tdev)) 1338 other = ttyp->tname; 1339 } 1340 if (!match && (other != NULL)) { 1341 /* 1342 * found under a different name 1343 */ 1344 match = 1; 1345 tp = other; 1346 } 1347 if (!match || (tuid != (uid_t)-1 && tuid != psinfo->pr_euid)) { 1348 /* 1349 * not found OR not matching euid 1350 */ 1351 *tpp = tp; 1352 return (0); 1353 } 1354 } 1355 *tpp = tp; 1356 return (1); 1357 } 1358 1359 /* 1360 * Print info about the process. 1361 */ 1362 static void 1363 prcom(psinfo_t *psinfo, char *ttyp) 1364 { 1365 char *cp; 1366 long tm; 1367 int bytesleft; 1368 int wcnt, length; 1369 wchar_t wchar; 1370 struct passwd *pwd; 1371 int zombie_lwp; 1372 char zonename[ZONENAME_MAX]; 1373 1374 /* 1375 * If process is zombie, call zombie print routine and return. 1376 */ 1377 if (psinfo->pr_nlwp == 0) { 1378 if (fields != NULL) 1379 pr_fields(psinfo, ttyp, print_zombie_field); 1380 else 1381 przom(psinfo); 1382 return; 1383 } 1384 1385 zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z'); 1386 1387 /* 1388 * If user specified '-o format', print requested fields and return. 1389 */ 1390 if (fields != NULL) { 1391 pr_fields(psinfo, ttyp, print_field); 1392 return; 1393 } 1394 1395 /* 1396 * All fields before 'PID' are printed with a trailing space as a 1397 * separator, rather than keeping track of which column is first. All 1398 * other fields are printed with a leading space. 1399 */ 1400 if (lflg) { 1401 if (!yflg) 1402 (void) printf("%2x ", psinfo->pr_flag & 0377); /* F */ 1403 (void) printf("%c ", psinfo->pr_lwp.pr_sname); /* S */ 1404 } 1405 1406 if (Zflg) { /* ZONE */ 1407 if (getzonenamebyid(psinfo->pr_zoneid, zonename, 1408 sizeof (zonename)) < 0) { 1409 if (snprintf(NULL, 0, "%d", 1410 ((int)psinfo->pr_zoneid)) > 7) 1411 (void) printf(" %6.6d%c ", 1412 ((int)psinfo->pr_zoneid), '*'); 1413 else 1414 (void) printf(" %7.7d ", 1415 ((int)psinfo->pr_zoneid)); 1416 } else { 1417 size_t nw; 1418 1419 nw = mbstowcs(NULL, zonename, 0); 1420 if (nw == (size_t)-1) 1421 (void) printf("%8.8s ", "ERROR"); 1422 else if (nw > 8) 1423 (void) wprintf(L"%7.7s%c ", zonename, '*'); 1424 else 1425 (void) wprintf(L"%8.8s ", zonename); 1426 } 1427 } 1428 1429 if (fflg) { /* UID */ 1430 if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) { 1431 size_t nw; 1432 1433 nw = mbstowcs(NULL, pwd->pw_name, 0); 1434 if (nw == (size_t)-1) 1435 (void) printf("%8.8s ", "ERROR"); 1436 else if (nw > 8) 1437 (void) wprintf(L"%7.7s%c ", pwd->pw_name, '*'); 1438 else 1439 (void) wprintf(L"%8.8s ", pwd->pw_name); 1440 } else { 1441 if (snprintf(NULL, 0, "%u", 1442 (psinfo->pr_euid)) > 7) 1443 (void) printf(" %6.6u%c ", psinfo->pr_euid, 1444 '*'); 1445 else 1446 (void) printf(" %7.7u ", psinfo->pr_euid); 1447 } 1448 } else if (lflg) { 1449 if (snprintf(NULL, 0, "%u", (psinfo->pr_euid)) > 6) 1450 (void) printf("%5.5u%c ", psinfo->pr_euid, '*'); 1451 else 1452 (void) printf("%6u ", psinfo->pr_euid); 1453 } 1454 (void) printf("%*d", pidwidth, (int)psinfo->pr_pid); /* PID */ 1455 if (lflg || fflg) 1456 (void) printf(" %*d", pidwidth, 1457 (int)psinfo->pr_ppid); /* PPID */ 1458 if (jflg) { 1459 (void) printf(" %*d", pidwidth, 1460 (int)psinfo->pr_pgid); /* PGID */ 1461 (void) printf(" %*d", pidwidth, 1462 (int)psinfo->pr_sid); /* SID */ 1463 } 1464 if (Lflg) 1465 (void) printf(" %5d", (int)psinfo->pr_lwp.pr_lwpid); /* LWP */ 1466 if (Pflg) { 1467 if (psinfo->pr_lwp.pr_bindpro == PBIND_NONE) /* PSR */ 1468 (void) printf(" -"); 1469 else 1470 (void) printf(" %3d", psinfo->pr_lwp.pr_bindpro); 1471 } 1472 if (Lflg && fflg) /* NLWP */ 1473 (void) printf(" %5d", psinfo->pr_nlwp + psinfo->pr_nzomb); 1474 if (cflg) { 1475 if (zombie_lwp) /* CLS */ 1476 (void) printf(" "); 1477 else 1478 (void) printf(" %4s", psinfo->pr_lwp.pr_clname); 1479 (void) printf(" %3d", psinfo->pr_lwp.pr_pri); /* PRI */ 1480 } else if (lflg || fflg) { 1481 (void) printf(" %3d", psinfo->pr_lwp.pr_cpu & 0377); /* C */ 1482 if (lflg) { /* PRI NI */ 1483 /* 1484 * Print priorities the old way (lower numbers 1485 * mean higher priority) and print nice value 1486 * for time sharing procs. 1487 */ 1488 (void) printf(" %3d", psinfo->pr_lwp.pr_oldpri); 1489 if (psinfo->pr_lwp.pr_oldpri != 0) 1490 (void) printf(" %2d", psinfo->pr_lwp.pr_nice); 1491 else 1492 (void) printf(" %2.2s", 1493 psinfo->pr_lwp.pr_clname); 1494 } 1495 } 1496 if (lflg) { 1497 if (yflg) { 1498 if (psinfo->pr_flag & SSYS) /* RSS */ 1499 (void) printf(" 0"); 1500 else if (psinfo->pr_rssize) 1501 (void) printf(" %5lu", 1502 (ulong_t)psinfo->pr_rssize); 1503 else 1504 (void) printf(" ?"); 1505 if (psinfo->pr_flag & SSYS) /* SZ */ 1506 (void) printf(" 0"); 1507 else if (psinfo->pr_size) 1508 (void) printf(" %6lu", 1509 (ulong_t)psinfo->pr_size); 1510 else 1511 (void) printf(" ?"); 1512 } else { 1513 #ifndef _LP64 1514 if (psinfo->pr_addr) /* ADDR */ 1515 (void) printf(" %8lx", 1516 (ulong_t)psinfo->pr_addr); 1517 else 1518 #endif 1519 (void) printf(" ?"); 1520 if (psinfo->pr_flag & SSYS) /* SZ */ 1521 (void) printf(" 0"); 1522 else if (psinfo->pr_size) 1523 (void) printf(" %6lu", 1524 (ulong_t)psinfo->pr_size / kbytes_per_page); 1525 else 1526 (void) printf(" ?"); 1527 } 1528 if (psinfo->pr_lwp.pr_sname != 'S') /* WCHAN */ 1529 (void) printf(" "); 1530 #ifndef _LP64 1531 else if (psinfo->pr_lwp.pr_wchan) 1532 (void) printf(" %8lx", 1533 (ulong_t)psinfo->pr_lwp.pr_wchan); 1534 #endif 1535 else 1536 (void) printf(" ?"); 1537 } 1538 if (fflg) { /* STIME */ 1539 int width = fname[F_STIME].width; 1540 if (Lflg) 1541 prtime(psinfo->pr_lwp.pr_start, width + 1, 1); 1542 else 1543 prtime(psinfo->pr_start, width + 1, 1); 1544 } 1545 1546 if (Hflg) { 1547 /* Display home lgroup */ 1548 (void) printf(" %4d", (int)psinfo->pr_lwp.pr_lgrp); 1549 } 1550 1551 (void) printf(" %-8.14s", ttyp); /* TTY */ 1552 if (Lflg) { 1553 tm = psinfo->pr_lwp.pr_time.tv_sec; 1554 if (psinfo->pr_lwp.pr_time.tv_nsec > 500000000) 1555 tm++; 1556 } else { 1557 tm = psinfo->pr_time.tv_sec; 1558 if (psinfo->pr_time.tv_nsec > 500000000) 1559 tm++; 1560 } 1561 (void) printf(" %4ld:%.2ld", tm / 60, tm % 60); /* [L]TIME */ 1562 1563 if (zombie_lwp) { 1564 (void) printf(" <defunct>\n"); 1565 return; 1566 } 1567 1568 if (!fflg) { /* CMD */ 1569 wcnt = namencnt(psinfo->pr_fname, 16, 8); 1570 (void) printf(" %.*s\n", wcnt, psinfo->pr_fname); 1571 return; 1572 } 1573 1574 1575 /* 1576 * PRARGSZ == length of cmd arg string. 1577 */ 1578 psinfo->pr_psargs[PRARGSZ-1] = '\0'; 1579 bytesleft = PRARGSZ; 1580 for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) { 1581 length = mbtowc(&wchar, cp, MB_LEN_MAX); 1582 if (length == 0) 1583 break; 1584 if (length < 0 || !iswprint(wchar)) { 1585 if (length < 0) 1586 length = 1; 1587 if (bytesleft <= length) { 1588 *cp = '\0'; 1589 break; 1590 } 1591 /* omit the unprintable character */ 1592 (void) memmove(cp, cp+length, bytesleft-length); 1593 length = 0; 1594 } 1595 bytesleft -= length; 1596 } 1597 wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, lflg ? 35 : PRARGSZ); 1598 (void) printf(" %.*s\n", wcnt, psinfo->pr_psargs); 1599 } 1600 1601 /* 1602 * Print percent from 16-bit binary fraction [0 .. 1] 1603 * Round up .01 to .1 to indicate some small percentage (the 0x7000 below). 1604 */ 1605 static void 1606 prtpct(ushort_t pct, int width) 1607 { 1608 uint_t value = pct; /* need 32 bits to compute with */ 1609 1610 value = ((value * 1000) + 0x7000) >> 15; /* [0 .. 1000] */ 1611 if (value >= 1000) 1612 value = 999; 1613 if ((width -= 2) < 2) 1614 width = 2; 1615 (void) printf("%*u.%u", width, value / 10, value % 10); 1616 } 1617 1618 static void 1619 print_time(time_t tim, int width) 1620 { 1621 char buf[30]; 1622 time_t seconds; 1623 time_t minutes; 1624 time_t hours; 1625 time_t days; 1626 1627 if (tim < 0) { 1628 (void) printf("%*s", width, "-"); 1629 return; 1630 } 1631 1632 seconds = tim % 60; 1633 tim /= 60; 1634 minutes = tim % 60; 1635 tim /= 60; 1636 hours = tim % 24; 1637 days = tim / 24; 1638 1639 if (days > 0) { 1640 (void) snprintf(buf, sizeof (buf), "%ld-%2.2ld:%2.2ld:%2.2ld", 1641 days, hours, minutes, seconds); 1642 } else if (hours > 0) { 1643 (void) snprintf(buf, sizeof (buf), "%2.2ld:%2.2ld:%2.2ld", 1644 hours, minutes, seconds); 1645 } else { 1646 (void) snprintf(buf, sizeof (buf), "%2.2ld:%2.2ld", 1647 minutes, seconds); 1648 } 1649 1650 (void) printf("%*s", width, buf); 1651 } 1652 1653 static void 1654 print_field(psinfo_t *psinfo, struct field *f, const char *ttyp) 1655 { 1656 int width = f->width; 1657 struct passwd *pwd; 1658 struct group *grp; 1659 time_t cputime; 1660 int bytesleft; 1661 int wcnt; 1662 wchar_t wchar; 1663 char *cp; 1664 int length; 1665 ulong_t mask; 1666 char c = '\0', *csave = NULL; 1667 int zombie_lwp; 1668 1669 zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z'); 1670 1671 switch (f->fname) { 1672 case F_RUSER: 1673 if ((pwd = getpwuid(psinfo->pr_uid)) != NULL) { 1674 size_t nw; 1675 1676 nw = mbstowcs(NULL, pwd->pw_name, 0); 1677 if (nw == (size_t)-1) 1678 (void) printf("%*s ", width, "ERROR"); 1679 else if (Wflg && nw > width) 1680 (void) wprintf(L"%.*s%c", width - 1, 1681 pwd->pw_name, '*'); 1682 else 1683 (void) wprintf(L"%*s", width, pwd->pw_name); 1684 } else { 1685 if (Wflg && snprintf(NULL, 0, "%u", 1686 (psinfo->pr_uid)) > width) 1687 1688 (void) printf("%*u%c", width - 1, 1689 psinfo->pr_uid, '*'); 1690 else 1691 (void) printf("%*u", width, psinfo->pr_uid); 1692 } 1693 break; 1694 case F_USER: 1695 if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) { 1696 size_t nw; 1697 1698 nw = mbstowcs(NULL, pwd->pw_name, 0); 1699 if (nw == (size_t)-1) 1700 (void) printf("%*s ", width, "ERROR"); 1701 else if (Wflg && nw > width) 1702 (void) wprintf(L"%.*s%c", width - 1, 1703 pwd->pw_name, '*'); 1704 else 1705 (void) wprintf(L"%*s", width, pwd->pw_name); 1706 } else { 1707 if (Wflg && snprintf(NULL, 0, "%u", 1708 (psinfo->pr_euid)) > width) 1709 1710 (void) printf("%*u%c", width - 1, 1711 psinfo->pr_euid, '*'); 1712 else 1713 (void) printf("%*u", width, psinfo->pr_euid); 1714 } 1715 break; 1716 case F_RGROUP: 1717 if ((grp = getgrgid(psinfo->pr_gid)) != NULL) 1718 (void) printf("%*s", width, grp->gr_name); 1719 else 1720 (void) printf("%*u", width, psinfo->pr_gid); 1721 break; 1722 case F_GROUP: 1723 if ((grp = getgrgid(psinfo->pr_egid)) != NULL) 1724 (void) printf("%*s", width, grp->gr_name); 1725 else 1726 (void) printf("%*u", width, psinfo->pr_egid); 1727 break; 1728 case F_RUID: 1729 (void) printf("%*u", width, psinfo->pr_uid); 1730 break; 1731 case F_UID: 1732 (void) printf("%*u", width, psinfo->pr_euid); 1733 break; 1734 case F_RGID: 1735 (void) printf("%*u", width, psinfo->pr_gid); 1736 break; 1737 case F_GID: 1738 (void) printf("%*u", width, psinfo->pr_egid); 1739 break; 1740 case F_PID: 1741 (void) printf("%*d", width, (int)psinfo->pr_pid); 1742 break; 1743 case F_PPID: 1744 (void) printf("%*d", width, (int)psinfo->pr_ppid); 1745 break; 1746 case F_PGID: 1747 (void) printf("%*d", width, (int)psinfo->pr_pgid); 1748 break; 1749 case F_SID: 1750 (void) printf("%*d", width, (int)psinfo->pr_sid); 1751 break; 1752 case F_PSR: 1753 if (zombie_lwp || psinfo->pr_lwp.pr_bindpro == PBIND_NONE) 1754 (void) printf("%*s", width, "-"); 1755 else 1756 (void) printf("%*d", width, psinfo->pr_lwp.pr_bindpro); 1757 break; 1758 case F_LWP: 1759 (void) printf("%*d", width, (int)psinfo->pr_lwp.pr_lwpid); 1760 break; 1761 case F_LWPNAME: { 1762 char lwpname[THREAD_NAME_MAX] = ""; 1763 char *path = NULL; 1764 int fd; 1765 1766 if (asprintf(&path, "%s/%d/lwp/%d/lwpname", procdir, 1767 (int)psinfo->pr_pid, (int)psinfo->pr_lwp.pr_lwpid) != -1 && 1768 (fd = open(path, O_RDONLY)) != -1) { 1769 (void) read(fd, lwpname, sizeof (lwpname)); 1770 lwpname[THREAD_NAME_MAX - 1] = '\0'; 1771 (void) close(fd); 1772 } 1773 1774 free(path); 1775 1776 if (f->next != NULL) 1777 (void) printf("%-*s", width, lwpname); 1778 else 1779 (void) printf("%s", lwpname); 1780 break; 1781 } 1782 case F_NLWP: 1783 (void) printf("%*d", width, psinfo->pr_nlwp + psinfo->pr_nzomb); 1784 break; 1785 case F_OPRI: 1786 if (zombie_lwp) 1787 (void) printf("%*s", width, "-"); 1788 else 1789 (void) printf("%*d", width, psinfo->pr_lwp.pr_oldpri); 1790 break; 1791 case F_PRI: 1792 if (zombie_lwp) 1793 (void) printf("%*s", width, "-"); 1794 else 1795 (void) printf("%*d", width, psinfo->pr_lwp.pr_pri); 1796 break; 1797 case F_F: 1798 mask = 0xffffffffUL; 1799 if (width < 8) 1800 mask >>= (8 - width) * 4; 1801 (void) printf("%*lx", width, psinfo->pr_flag & mask); 1802 break; 1803 case F_S: 1804 (void) printf("%*c", width, psinfo->pr_lwp.pr_sname); 1805 break; 1806 case F_C: 1807 if (zombie_lwp) 1808 (void) printf("%*s", width, "-"); 1809 else 1810 (void) printf("%*d", width, psinfo->pr_lwp.pr_cpu); 1811 break; 1812 case F_PCPU: 1813 if (zombie_lwp) 1814 (void) printf("%*s", width, "-"); 1815 else if (Lflg) 1816 prtpct(psinfo->pr_lwp.pr_pctcpu, width); 1817 else 1818 prtpct(psinfo->pr_pctcpu, width); 1819 break; 1820 case F_PMEM: 1821 prtpct(psinfo->pr_pctmem, width); 1822 break; 1823 case F_OSZ: 1824 (void) printf("%*lu", width, 1825 (ulong_t)psinfo->pr_size / kbytes_per_page); 1826 break; 1827 case F_VSZ: 1828 (void) printf("%*lu", width, (ulong_t)psinfo->pr_size); 1829 break; 1830 case F_RSS: 1831 (void) printf("%*lu", width, (ulong_t)psinfo->pr_rssize); 1832 break; 1833 case F_NICE: 1834 /* if pr_oldpri is zero, then this class has no nice */ 1835 if (zombie_lwp) 1836 (void) printf("%*s", width, "-"); 1837 else if (psinfo->pr_lwp.pr_oldpri != 0) 1838 (void) printf("%*d", width, psinfo->pr_lwp.pr_nice); 1839 else 1840 (void) printf("%*.*s", width, width, 1841 psinfo->pr_lwp.pr_clname); 1842 break; 1843 case F_CLASS: 1844 if (zombie_lwp) 1845 (void) printf("%*s", width, "-"); 1846 else 1847 (void) printf("%*.*s", width, width, 1848 psinfo->pr_lwp.pr_clname); 1849 break; 1850 case F_STIME: 1851 if (Lflg) 1852 prtime(psinfo->pr_lwp.pr_start, width, 0); 1853 else 1854 prtime(psinfo->pr_start, width, 0); 1855 break; 1856 case F_ETIME: 1857 if (Lflg) 1858 print_time(delta_secs(&psinfo->pr_lwp.pr_start), 1859 width); 1860 else 1861 print_time(delta_secs(&psinfo->pr_start), width); 1862 break; 1863 case F_TIME: 1864 if (Lflg) { 1865 cputime = psinfo->pr_lwp.pr_time.tv_sec; 1866 if (psinfo->pr_lwp.pr_time.tv_nsec > 500000000) 1867 cputime++; 1868 } else { 1869 cputime = psinfo->pr_time.tv_sec; 1870 if (psinfo->pr_time.tv_nsec > 500000000) 1871 cputime++; 1872 } 1873 print_time(cputime, width); 1874 break; 1875 case F_TTY: 1876 (void) printf("%-*s", width, ttyp); 1877 break; 1878 case F_ADDR: 1879 if (zombie_lwp) 1880 (void) printf("%*s", width, "-"); 1881 else if (Lflg) 1882 (void) printf("%*lx", width, 1883 (long)psinfo->pr_lwp.pr_addr); 1884 else 1885 (void) printf("%*lx", width, (long)psinfo->pr_addr); 1886 break; 1887 case F_WCHAN: 1888 if (!zombie_lwp && psinfo->pr_lwp.pr_wchan) 1889 (void) printf("%*lx", width, 1890 (long)psinfo->pr_lwp.pr_wchan); 1891 else 1892 (void) printf("%*.*s", width, width, "-"); 1893 break; 1894 case F_FNAME: 1895 /* 1896 * Print full width unless this is the last output format. 1897 */ 1898 if (zombie_lwp) { 1899 if (f->next != NULL) 1900 (void) printf("%-*s", width, "<defunct>"); 1901 else 1902 (void) printf("%s", "<defunct>"); 1903 break; 1904 } 1905 wcnt = namencnt(psinfo->pr_fname, 16, width); 1906 if (f->next != NULL) 1907 (void) printf("%-*.*s", width, wcnt, psinfo->pr_fname); 1908 else 1909 (void) printf("%-.*s", wcnt, psinfo->pr_fname); 1910 break; 1911 case F_COMM: 1912 if (zombie_lwp) { 1913 if (f->next != NULL) 1914 (void) printf("%-*s", width, "<defunct>"); 1915 else 1916 (void) printf("%s", "<defunct>"); 1917 break; 1918 } 1919 csave = strpbrk(psinfo->pr_psargs, " \t\r\v\f\n"); 1920 if (csave) { 1921 c = *csave; 1922 *csave = '\0'; 1923 } 1924 /* FALLTHROUGH */ 1925 case F_ARGS: 1926 /* 1927 * PRARGSZ == length of cmd arg string. 1928 */ 1929 if (zombie_lwp) { 1930 (void) printf("%-*s", width, "<defunct>"); 1931 break; 1932 } 1933 psinfo->pr_psargs[PRARGSZ-1] = '\0'; 1934 bytesleft = PRARGSZ; 1935 for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) { 1936 length = mbtowc(&wchar, cp, MB_LEN_MAX); 1937 if (length == 0) 1938 break; 1939 if (length < 0 || !iswprint(wchar)) { 1940 if (length < 0) 1941 length = 1; 1942 if (bytesleft <= length) { 1943 *cp = '\0'; 1944 break; 1945 } 1946 /* omit the unprintable character */ 1947 (void) memmove(cp, cp+length, bytesleft-length); 1948 length = 0; 1949 } 1950 bytesleft -= length; 1951 } 1952 wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, width); 1953 /* 1954 * Print full width unless this is the last format. 1955 */ 1956 if (f->next != NULL) 1957 (void) printf("%-*.*s", width, wcnt, 1958 psinfo->pr_psargs); 1959 else 1960 (void) printf("%-.*s", wcnt, 1961 psinfo->pr_psargs); 1962 if (f->fname == F_COMM && csave) 1963 *csave = c; 1964 break; 1965 case F_TASKID: 1966 (void) printf("%*d", width, (int)psinfo->pr_taskid); 1967 break; 1968 case F_PROJID: 1969 (void) printf("%*d", width, (int)psinfo->pr_projid); 1970 break; 1971 case F_PROJECT: 1972 { 1973 struct project cproj; 1974 char proj_buf[PROJECT_BUFSZ]; 1975 1976 if ((getprojbyid(psinfo->pr_projid, &cproj, 1977 (void *)&proj_buf, PROJECT_BUFSZ)) == NULL) { 1978 if (Wflg && snprintf(NULL, 0, "%d", 1979 ((int)psinfo->pr_projid)) > width) 1980 (void) printf("%.*d%c", width - 1, 1981 ((int)psinfo->pr_projid), '*'); 1982 else 1983 (void) printf("%*d", width, 1984 (int)psinfo->pr_projid); 1985 } else { 1986 size_t nw; 1987 1988 if (cproj.pj_name != NULL) 1989 nw = mbstowcs(NULL, cproj.pj_name, 0); 1990 if (cproj.pj_name == NULL) 1991 (void) printf("%*s ", width, "---"); 1992 else if (nw == (size_t)-1) 1993 (void) printf("%*s ", width, "ERROR"); 1994 else if (Wflg && nw > width) 1995 (void) wprintf(L"%.*s%c", width - 1, 1996 cproj.pj_name, '*'); 1997 else 1998 (void) wprintf(L"%*s", width, 1999 cproj.pj_name); 2000 } 2001 } 2002 break; 2003 case F_PSET: 2004 if (zombie_lwp || psinfo->pr_lwp.pr_bindpset == PS_NONE) 2005 (void) printf("%*s", width, "-"); 2006 else 2007 (void) printf("%*d", width, psinfo->pr_lwp.pr_bindpset); 2008 break; 2009 case F_ZONEID: 2010 (void) printf("%*d", width, (int)psinfo->pr_zoneid); 2011 break; 2012 case F_ZONE: 2013 { 2014 char zonename[ZONENAME_MAX]; 2015 2016 if (getzonenamebyid(psinfo->pr_zoneid, zonename, 2017 sizeof (zonename)) < 0) { 2018 if (Wflg && snprintf(NULL, 0, "%d", 2019 ((int)psinfo->pr_zoneid)) > width) 2020 (void) printf("%.*d%c", width - 1, 2021 ((int)psinfo->pr_zoneid), '*'); 2022 else 2023 (void) printf("%*d", width, 2024 (int)psinfo->pr_zoneid); 2025 } else { 2026 size_t nw; 2027 2028 nw = mbstowcs(NULL, zonename, 0); 2029 if (nw == (size_t)-1) 2030 (void) printf("%*s ", width, "ERROR"); 2031 else if (Wflg && nw > width) 2032 (void) wprintf(L"%.*s%c", width - 1, 2033 zonename, '*'); 2034 else 2035 (void) wprintf(L"%*s", width, zonename); 2036 } 2037 } 2038 break; 2039 case F_CTID: 2040 if (psinfo->pr_contract == -1) 2041 (void) printf("%*s", width, "-"); 2042 else 2043 (void) printf("%*ld", width, (long)psinfo->pr_contract); 2044 break; 2045 case F_LGRP: 2046 /* Display home lgroup */ 2047 (void) printf("%*d", width, (int)psinfo->pr_lwp.pr_lgrp); 2048 break; 2049 2050 case F_DMODEL: 2051 (void) printf("%*s", width, 2052 psinfo->pr_dmodel == PR_MODEL_LP64 ? "_LP64" : "_ILP32"); 2053 break; 2054 } 2055 } 2056 2057 static void 2058 print_zombie_field(psinfo_t *psinfo, struct field *f, const char *ttyp) 2059 { 2060 int wcnt; 2061 int width = f->width; 2062 2063 switch (f->fname) { 2064 case F_FNAME: 2065 case F_COMM: 2066 case F_ARGS: 2067 /* 2068 * Print full width unless this is the last output format. 2069 */ 2070 wcnt = min(width, sizeof ("<defunct>")); 2071 if (f->next != NULL) 2072 (void) printf("%-*.*s", width, wcnt, "<defunct>"); 2073 else 2074 (void) printf("%-.*s", wcnt, "<defunct>"); 2075 break; 2076 2077 case F_PSR: 2078 case F_PCPU: 2079 case F_PMEM: 2080 case F_NICE: 2081 case F_CLASS: 2082 case F_STIME: 2083 case F_ETIME: 2084 case F_WCHAN: 2085 case F_PSET: 2086 (void) printf("%*s", width, "-"); 2087 break; 2088 2089 case F_OPRI: 2090 case F_PRI: 2091 case F_OSZ: 2092 case F_VSZ: 2093 case F_RSS: 2094 (void) printf("%*d", width, 0); 2095 break; 2096 2097 default: 2098 print_field(psinfo, f, ttyp); 2099 break; 2100 } 2101 } 2102 2103 static void 2104 pr_fields(psinfo_t *psinfo, const char *ttyp, 2105 void (*print_fld)(psinfo_t *, struct field *, const char *)) 2106 { 2107 struct field *f; 2108 2109 for (f = fields; f != NULL; f = f->next) { 2110 print_fld(psinfo, f, ttyp); 2111 if (f->next != NULL) 2112 (void) printf(" "); 2113 } 2114 (void) printf("\n"); 2115 } 2116 2117 /* 2118 * Returns 1 if arg is found in array arr, of length num; 0 otherwise. 2119 */ 2120 static int 2121 search(pid_t *arr, int number, pid_t arg) 2122 { 2123 int i; 2124 2125 for (i = 0; i < number; i++) 2126 if (arg == arr[i]) 2127 return (1); 2128 return (0); 2129 } 2130 2131 /* 2132 * Add an entry (user, group) to the specified table. 2133 */ 2134 static void 2135 add_ugentry(struct ughead *tbl, char *name) 2136 { 2137 struct ugdata *entp; 2138 2139 if (tbl->size == tbl->nent) { /* reallocate the table entries */ 2140 if ((tbl->size *= 2) == 0) 2141 tbl->size = 32; /* first time */ 2142 tbl->ent = Realloc(tbl->ent, tbl->size*sizeof (struct ugdata)); 2143 } 2144 entp = &tbl->ent[tbl->nent++]; 2145 entp->id = 0; 2146 (void) strncpy(entp->name, name, MAXUGNAME); 2147 entp->name[MAXUGNAME] = '\0'; 2148 } 2149 2150 static int 2151 uconv(struct ughead *uhead) 2152 { 2153 struct ugdata *utbl = uhead->ent; 2154 int n = uhead->nent; 2155 struct passwd *pwd; 2156 int i; 2157 int fnd = 0; 2158 uid_t uid; 2159 2160 /* 2161 * Ask the name service for names. 2162 */ 2163 for (i = 0; i < n; i++) { 2164 /* 2165 * If name is numeric, ask for numeric id 2166 */ 2167 if (str2uid(utbl[i].name, &uid, 0, MAXEPHUID) == 0) 2168 pwd = getpwuid(uid); 2169 else 2170 pwd = getpwnam(utbl[i].name); 2171 2172 /* 2173 * If found, enter found index into tbl array. 2174 */ 2175 if (pwd == NULL) { 2176 (void) fprintf(stderr, 2177 gettext("ps: unknown user %s\n"), utbl[i].name); 2178 continue; 2179 } 2180 2181 utbl[fnd].id = pwd->pw_uid; 2182 (void) strncpy(utbl[fnd].name, pwd->pw_name, MAXUGNAME); 2183 fnd++; 2184 } 2185 2186 uhead->nent = fnd; /* in case it changed */ 2187 return (n - fnd); 2188 } 2189 2190 static int 2191 gconv(struct ughead *ghead) 2192 { 2193 struct ugdata *gtbl = ghead->ent; 2194 int n = ghead->nent; 2195 struct group *grp; 2196 gid_t gid; 2197 int i; 2198 int fnd = 0; 2199 2200 /* 2201 * Ask the name service for names. 2202 */ 2203 for (i = 0; i < n; i++) { 2204 /* 2205 * If name is numeric, ask for numeric id 2206 */ 2207 if (str2uid(gtbl[i].name, (uid_t *)&gid, 0, MAXEPHUID) == 0) 2208 grp = getgrgid(gid); 2209 else 2210 grp = getgrnam(gtbl[i].name); 2211 /* 2212 * If found, enter found index into tbl array. 2213 */ 2214 if (grp == NULL) { 2215 (void) fprintf(stderr, 2216 gettext("ps: unknown group %s\n"), gtbl[i].name); 2217 continue; 2218 } 2219 2220 gtbl[fnd].id = grp->gr_gid; 2221 (void) strncpy(gtbl[fnd].name, grp->gr_name, MAXUGNAME); 2222 fnd++; 2223 } 2224 2225 ghead->nent = fnd; /* in case it changed */ 2226 return (n - fnd); 2227 } 2228 2229 /* 2230 * Return 1 if puid is in table, otherwise 0. 2231 */ 2232 static int 2233 ugfind(id_t id, struct ughead *ughead) 2234 { 2235 struct ugdata *utbl = ughead->ent; 2236 int n = ughead->nent; 2237 int i; 2238 2239 for (i = 0; i < n; i++) 2240 if (utbl[i].id == id) 2241 return (1); 2242 return (0); 2243 } 2244 2245 /* 2246 * Print starting time of process unless process started more than 24 hours 2247 * ago, in which case the date is printed. The date is printed in the form 2248 * "MMM dd" if old format, else the blank is replaced with an '_' so 2249 * it appears as a single word (for parseability). 2250 */ 2251 static void 2252 prtime(timestruc_t st, int width, int old) 2253 { 2254 char sttim[26]; 2255 time_t starttime; 2256 2257 starttime = st.tv_sec; 2258 if (st.tv_nsec > 500000000) 2259 starttime++; 2260 if ((now.tv_sec - starttime) >= 24*60*60) { 2261 (void) strftime(sttim, sizeof (sttim), old? 2262 /* 2263 * TRANSLATION_NOTE 2264 * This time format is used by STIME field when -f option 2265 * is specified. Used for processes that begun more than 2266 * 24 hours. 2267 */ 2268 dcgettext(NULL, "%b %d", LC_TIME) : 2269 /* 2270 * TRANSLATION_NOTE 2271 * This time format is used by STIME field when -o option 2272 * is specified. Used for processes that begun more than 2273 * 24 hours. 2274 */ 2275 dcgettext(NULL, "%b_%d", LC_TIME), localtime(&starttime)); 2276 } else { 2277 /* 2278 * TRANSLATION_NOTE 2279 * This time format is used by STIME field when -f or -o option 2280 * is specified. Used for processes that begun less than 2281 * 24 hours. 2282 */ 2283 (void) strftime(sttim, sizeof (sttim), 2284 dcgettext(NULL, "%H:%M:%S", LC_TIME), 2285 localtime(&starttime)); 2286 } 2287 (void) printf("%*.*s", width, width, sttim); 2288 } 2289 2290 static void 2291 przom(psinfo_t *psinfo) 2292 { 2293 long tm; 2294 struct passwd *pwd; 2295 char zonename[ZONENAME_MAX]; 2296 2297 /* 2298 * All fields before 'PID' are printed with a trailing space as a 2299 * spearator, rather than keeping track of which column is first. All 2300 * other fields are printed with a leading space. 2301 */ 2302 if (lflg) { /* F S */ 2303 if (!yflg) 2304 (void) printf("%2x ", psinfo->pr_flag & 0377); /* F */ 2305 (void) printf("%c ", psinfo->pr_lwp.pr_sname); /* S */ 2306 } 2307 if (Zflg) { 2308 if (getzonenamebyid(psinfo->pr_zoneid, zonename, 2309 sizeof (zonename)) < 0) { 2310 if (snprintf(NULL, 0, "%d", 2311 ((int)psinfo->pr_zoneid)) > 7) 2312 (void) printf(" %6.6d%c ", 2313 ((int)psinfo->pr_zoneid), '*'); 2314 else 2315 (void) printf(" %7.7d ", 2316 ((int)psinfo->pr_zoneid)); 2317 } else { 2318 size_t nw; 2319 2320 nw = mbstowcs(NULL, zonename, 0); 2321 if (nw == (size_t)-1) 2322 (void) printf("%8.8s ", "ERROR"); 2323 else if (nw > 8) 2324 (void) wprintf(L"%7.7s%c ", zonename, '*'); 2325 else 2326 (void) wprintf(L"%8.8s ", zonename); 2327 } 2328 } 2329 if (Hflg) { 2330 /* Display home lgroup */ 2331 (void) printf(" %6d", (int)psinfo->pr_lwp.pr_lgrp); /* LGRP */ 2332 } 2333 if (fflg) { 2334 if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) { 2335 size_t nw; 2336 2337 nw = mbstowcs(NULL, pwd->pw_name, 0); 2338 if (nw == (size_t)-1) 2339 (void) printf("%8.8s ", "ERROR"); 2340 else if (nw > 8) 2341 (void) wprintf(L"%7.7s%c ", pwd->pw_name, '*'); 2342 else 2343 (void) wprintf(L"%8.8s ", pwd->pw_name); 2344 } else { 2345 if (snprintf(NULL, 0, "%u", 2346 (psinfo->pr_euid)) > 7) 2347 (void) printf(" %6.6u%c ", psinfo->pr_euid, 2348 '*'); 2349 else 2350 (void) printf(" %7.7u ", psinfo->pr_euid); 2351 } 2352 } else if (lflg) { 2353 if (snprintf(NULL, 0, "%u", (psinfo->pr_euid)) > 6) 2354 (void) printf("%5.5u%c ", psinfo->pr_euid, '*'); 2355 else 2356 (void) printf("%6u ", psinfo->pr_euid); 2357 } 2358 2359 (void) printf("%*d", pidwidth, (int)psinfo->pr_pid); /* PID */ 2360 if (lflg || fflg) 2361 (void) printf(" %*d", pidwidth, 2362 (int)psinfo->pr_ppid); /* PPID */ 2363 2364 if (jflg) { 2365 (void) printf(" %*d", pidwidth, 2366 (int)psinfo->pr_pgid); /* PGID */ 2367 (void) printf(" %*d", pidwidth, 2368 (int)psinfo->pr_sid); /* SID */ 2369 } 2370 2371 if (Lflg) 2372 (void) printf(" %5d", 0); /* LWP */ 2373 if (Pflg) 2374 (void) printf(" -"); /* PSR */ 2375 if (Lflg && fflg) 2376 (void) printf(" %5d", 0); /* NLWP */ 2377 2378 if (cflg) { 2379 (void) printf(" %4s", "-"); /* zombies have no class */ 2380 (void) printf(" %3d", psinfo->pr_lwp.pr_pri); /* PRI */ 2381 } else if (lflg || fflg) { 2382 (void) printf(" %3d", psinfo->pr_lwp.pr_cpu & 0377); /* C */ 2383 if (lflg) 2384 (void) printf(" %3d %2s", 2385 psinfo->pr_lwp.pr_oldpri, "-"); /* PRI NI */ 2386 } 2387 if (lflg) { 2388 if (yflg) /* RSS SZ WCHAN */ 2389 (void) printf(" %5d %6d %8s", 0, 0, "-"); 2390 else /* ADDR SZ WCHAN */ 2391 (void) printf(" %8s %6d %8s", "-", 0, "-"); 2392 } 2393 if (fflg) { 2394 int width = fname[F_STIME].width; 2395 (void) printf(" %*.*s", width, width, "-"); /* STIME */ 2396 } 2397 (void) printf(" %-8.14s", "?"); /* TTY */ 2398 2399 tm = psinfo->pr_time.tv_sec; 2400 if (psinfo->pr_time.tv_nsec > 500000000) 2401 tm++; 2402 (void) printf(" %4ld:%.2ld", tm / 60, tm % 60); /* TIME */ 2403 (void) printf(" <defunct>\n"); 2404 } 2405 2406 /* 2407 * Function to compute the number of printable bytes in a multibyte 2408 * command string ("internationalization"). 2409 */ 2410 static int 2411 namencnt(char *cmd, int csisize, int scrsize) 2412 { 2413 int csiwcnt = 0, scrwcnt = 0; 2414 int ncsisz, nscrsz; 2415 wchar_t wchar; 2416 int len; 2417 2418 while (*cmd != '\0') { 2419 if ((len = csisize - csiwcnt) > (int)MB_CUR_MAX) 2420 len = MB_CUR_MAX; 2421 if ((ncsisz = mbtowc(&wchar, cmd, len)) < 0) 2422 return (8); /* default to use for illegal chars */ 2423 if ((nscrsz = wcwidth(wchar)) <= 0) 2424 return (8); 2425 if (csiwcnt + ncsisz > csisize || scrwcnt + nscrsz > scrsize) 2426 break; 2427 csiwcnt += ncsisz; 2428 scrwcnt += nscrsz; 2429 cmd += ncsisz; 2430 } 2431 return (csiwcnt); 2432 } 2433 2434 static char * 2435 err_string(int err) 2436 { 2437 static char buf[32]; 2438 char *str = strerror(err); 2439 2440 if (str == NULL) 2441 (void) snprintf(str = buf, sizeof (buf), "Errno #%d", err); 2442 2443 return (str); 2444 } 2445 2446 /* If allocation fails, die */ 2447 static void * 2448 Realloc(void *ptr, size_t size) 2449 { 2450 ptr = realloc(ptr, size); 2451 if (ptr == NULL) { 2452 (void) fprintf(stderr, gettext("ps: no memory\n")); 2453 exit(1); 2454 } 2455 return (ptr); 2456 } 2457 2458 static time_t 2459 delta_secs(const timestruc_t *start) 2460 { 2461 time_t seconds = now.tv_sec - start->tv_sec; 2462 long nanosecs = now.tv_usec * 1000 - start->tv_nsec; 2463 2464 if (nanosecs >= (NANOSEC / 2)) 2465 seconds++; 2466 else if (nanosecs < -(NANOSEC / 2)) 2467 seconds--; 2468 2469 return (seconds); 2470 } 2471 2472 /* 2473 * Returns the following: 2474 * 2475 * 0 No error 2476 * EINVAL Invalid number 2477 * ERANGE Value exceeds (min, max) range 2478 */ 2479 static int 2480 str2id(const char *p, pid_t *val, long min, long max) 2481 { 2482 char *q; 2483 long number; 2484 int error; 2485 2486 errno = 0; 2487 number = strtol(p, &q, 10); 2488 2489 if (errno != 0 || q == p || *q != '\0') { 2490 if ((error = errno) == 0) { 2491 /* 2492 * strtol() can fail without setting errno, or it can 2493 * set it to EINVAL or ERANGE. In the case errno is 2494 * still zero, return EINVAL. 2495 */ 2496 error = EINVAL; 2497 } 2498 } else if (number < min || number > max) { 2499 error = ERANGE; 2500 } else { 2501 error = 0; 2502 } 2503 2504 *val = number; 2505 2506 return (error); 2507 } 2508 2509 /* 2510 * Returns the following: 2511 * 2512 * 0 No error 2513 * EINVAL Invalid number 2514 * ERANGE Value exceeds (min, max) range 2515 */ 2516 static int 2517 str2uid(const char *p, uid_t *val, unsigned long min, unsigned long max) 2518 { 2519 char *q; 2520 unsigned long number; 2521 int error; 2522 2523 errno = 0; 2524 number = strtoul(p, &q, 10); 2525 2526 if (errno != 0 || q == p || *q != '\0') { 2527 if ((error = errno) == 0) { 2528 /* 2529 * strtoul() can fail without setting errno, or it can 2530 * set it to EINVAL or ERANGE. In the case errno is 2531 * still zero, return EINVAL. 2532 */ 2533 error = EINVAL; 2534 } 2535 } else if (number < min || number > max) { 2536 error = ERANGE; 2537 } else { 2538 error = 0; 2539 } 2540 2541 *val = number; 2542 2543 return (error); 2544 } 2545 2546 static int 2547 pidcmp(const void *p1, const void *p2) 2548 { 2549 pid_t i = *((pid_t *)p1); 2550 pid_t j = *((pid_t *)p2); 2551 2552 return (i - j); 2553 }