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