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