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