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