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