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