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.
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.
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]
23 * Copyright (c) 2013 Gary Mills
25 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
30 * Copyright (c) 2018, Joyent, Inc.
33 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
34 /* All Rights Reserved */
37 * ps -- print things about processes.
46 #include <sys/types.h>
48 #include <sys/mkdev.h>
53 #include <sys/signal.h>
54 #include <sys/fault.h>
55 #include <sys/syscall.h>
68 #define min(a, b) ((a) > (b) ? (b) : (a))
69 #define max(a, b) ((a) < (b) ? (b) : (a))
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 */
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.
79 #define ARGSIZ ZONENAME_MAX
81 /* Max chars in a user/group name or printed u/g id */
82 #define MAXUGNAME (LOGNAME_MAX+2)
84 /* Structure for storing user or group info */
86 id_t id
; /* numeric user-id or group-id */
87 char name
[MAXUGNAME
+1]; /* user/group name, null terminated */
91 size_t size
; /* number of ugdata structs allocated */
92 size_t nent
; /* number of active entries */
93 struct ugdata
*ent
; /* pointer to array of actual entries */
96 enum fname
{ /* enumeration of field names */
97 F_USER
, /* effective user of the process */
98 F_RUSER
, /* real user of the process */
99 F_GROUP
, /* effective group of the process */
100 F_RGROUP
, /* real group of the process */
101 F_UID
, /* numeric effective uid of the process */
102 F_RUID
, /* numeric real uid of the process */
103 F_GID
, /* numeric effective gid of the process */
104 F_RGID
, /* numeric real gid of the process */
105 F_PID
, /* process id */
106 F_PPID
, /* parent process id */
107 F_PGID
, /* process group id */
108 F_SID
, /* session id */
109 F_PSR
, /* bound processor */
111 F_LWPNAME
, /* lwp name */
112 F_NLWP
, /* number of lwps */
113 F_OPRI
, /* old priority (obsolete) */
114 F_PRI
, /* new priority */
115 F_F
, /* process flags */
116 F_S
, /* letter indicating the state */
117 F_C
, /* processor utilization (obsolete) */
118 F_PCPU
, /* percent of recently used cpu time */
119 F_PMEM
, /* percent of physical memory used (rss) */
120 F_OSZ
, /* virtual size of the process in pages */
121 F_VSZ
, /* virtual size of the process in kilobytes */
122 F_RSS
, /* resident set size of the process in kilobytes */
123 F_NICE
, /* "nice" value of the process */
124 F_CLASS
, /* scheduler class */
125 F_STIME
, /* start time of the process, hh:mm:ss or Month Day */
126 F_ETIME
, /* elapsed time of the process, [[dd-]hh:]mm:ss */
127 F_TIME
, /* cpu time of the process, [[dd-]hh:]mm:ss */
128 F_TTY
, /* name of the controlling terminal */
129 F_ADDR
, /* address of the process (obsolete) */
130 F_WCHAN
, /* wait channel (sleep condition variable) */
131 F_FNAME
, /* file name of command */
132 F_COMM
, /* name of command (argv[0] value) */
133 F_ARGS
, /* name of command plus all its arguments */
134 F_TASKID
, /* task id */
135 F_PROJID
, /* project id */
136 F_PROJECT
, /* project name of the process */
137 F_PSET
, /* bound processor set */
138 F_ZONE
, /* zone name */
139 F_ZONEID
, /* zone id */
140 F_CTID
, /* process contract id */
141 F_LGRP
, /* process home lgroup */
142 F_DMODEL
/* process data model */
146 struct field
*next
; /* linked list */
147 int fname
; /* field index */
148 const char *header
; /* header to use */
149 int width
; /* width of field */
152 static struct field
*fields
= NULL
; /* fields selected via -o */
153 static struct field
*last_field
= NULL
;
154 static int do_header
= 0;
155 static struct timeval now
;
157 /* array of defined fields, in fname order */
165 static struct def_field fname
[] = {
166 /* fname header width minwidth */
167 { "user", "USER", 8, 8 },
168 { "ruser", "RUSER", 8, 8 },
169 { "group", "GROUP", 8, 8 },
170 { "rgroup", "RGROUP", 8, 8 },
171 { "uid", "UID", 5, 5 },
172 { "ruid", "RUID", 5, 5 },
173 { "gid", "GID", 5, 5 },
174 { "rgid", "RGID", 5, 5 },
175 { "pid", "PID", 5, 5 },
176 { "ppid", "PPID", 5, 5 },
177 { "pgid", "PGID", 5, 5 },
178 { "sid", "SID", 5, 5 },
179 { "psr", "PSR", 3, 2 },
180 { "lwp", "LWP", 6, 2 },
181 { "lwpname", "LWPNAME", 32, 8 },
182 { "nlwp", "NLWP", 4, 2 },
183 { "opri", "PRI", 3, 2 },
184 { "pri", "PRI", 3, 2 },
188 { "pcpu", "%CPU", 4, 4 },
189 { "pmem", "%MEM", 4, 4 },
190 { "osz", "SZ", 4, 4 },
191 { "vsz", "VSZ", 4, 4 },
192 { "rss", "RSS", 4, 4 },
193 { "nice", "NI", 2, 2 },
194 { "class", "CLS", 4, 2 },
195 { "stime", "STIME", 8, 8 },
196 { "etime", "ELAPSED", 11, 7 },
197 { "time", "TIME", 11, 5 },
198 { "tty", "TT", 7, 7 },
200 { "addr", "ADDR", 16, 8 },
201 { "wchan", "WCHAN", 16, 8 },
203 { "addr", "ADDR", 8, 8 },
204 { "wchan", "WCHAN", 8, 8 },
206 { "fname", "COMMAND", 8, 8 },
207 { "comm", "COMMAND", 80, 8 },
208 { "args", "COMMAND", 80, 80 },
209 { "taskid", "TASKID", 5, 5 },
210 { "projid", "PROJID", 5, 5 },
211 { "project", "PROJECT", 8, 8 },
212 { "pset", "PSET", 3, 3 },
213 { "zone", "ZONE", 8, 8 },
214 { "zoneid", "ZONEID", 5, 5 },
215 { "ctid", "CTID", 5, 5 },
216 { "lgrp", "LGRP", 4, 2 },
217 { "dmodel", "DMODEL", 6, 6 },
220 #define NFIELDS (sizeof (fname) / sizeof (fname[0]))
222 static int retcode
= 1;
245 static uid_t tuid
= (uid_t
)-1;
248 static int ndev
; /* number of devices */
249 static int maxdev
; /* number of devl structures allocated */
253 static struct devl
{ /* device list */
254 char dname
[DNSIZE
]; /* device name */
255 dev_t ddev
; /* device number */
261 } *tty
= NULL
; /* for t option */
262 static size_t ttysz
= 0;
265 static pid_t
*pid
= NULL
; /* for p option */
266 static size_t pidsz
= 0;
267 static size_t npid
= 0;
269 static int *lgrps
= NULL
; /* list of lgroup IDs for for h option */
270 static size_t lgrps_size
= 0; /* size of the lgrps list */
271 static size_t nlgrps
= 0; /* number elements in the list */
273 /* Maximum possible lgroup ID value */
274 #define MAX_LGRP_ID 256
276 static pid_t
*grpid
= NULL
; /* for g option */
277 static size_t grpidsz
= 0;
278 static int ngrpid
= 0;
280 static pid_t
*sessid
= NULL
; /* for s option */
281 static size_t sessidsz
= 0;
282 static int nsessid
= 0;
284 static zoneid_t
*zoneid
= NULL
; /* for z option */
285 static size_t zoneidsz
= 0;
286 static int nzoneid
= 0;
288 static int kbytes_per_page
;
291 static char *procdir
= "/proc"; /* standard /proc directory */
293 static struct ughead euid_tbl
; /* table to store selected euid's */
294 static struct ughead ruid_tbl
; /* table to store selected real uid's */
295 static struct ughead egid_tbl
; /* table to store selected egid's */
296 static struct ughead rgid_tbl
; /* table to store selected real gid's */
297 static prheader_t
*lpsinfobuf
; /* buffer to contain lpsinfo */
298 static size_t lpbufsize
;
301 * This constant defines the sentinal number of process IDs below which we
302 * only examine individual entries in /proc rather than scanning through
303 * /proc. This optimization is a huge win in the common case.
305 #define PTHRESHOLD 40
307 #define UCB_OPTS "-aceglnrtuvwxSU"
309 static void usage(void);
310 static char *getarg(char **);
311 static char *parse_format(char *);
312 static char *gettty(psinfo_t
*);
313 static int prfind(int, psinfo_t
*, char **);
314 static void prcom(psinfo_t
*, char *);
315 static void prtpct(ushort_t
, int);
316 static void print_time(time_t, int);
317 static void print_field(psinfo_t
*, struct field
*, const char *);
318 static void print_zombie_field(psinfo_t
*, struct field
*, const char *);
319 static void pr_fields(psinfo_t
*, const char *,
320 void (*print_fld
)(psinfo_t
*, struct field
*, const char *));
321 static int search(pid_t
*, int, pid_t
);
322 static void add_ugentry(struct ughead
*, char *);
323 static int uconv(struct ughead
*);
324 static int gconv(struct ughead
*);
325 static int ugfind(id_t
, struct ughead
*);
326 static void prtime(timestruc_t
, int, int);
327 static void przom(psinfo_t
*);
328 static int namencnt(char *, int, int);
329 static char *err_string(int);
330 static int print_proc(char *pname
);
331 static time_t delta_secs(const timestruc_t
*);
332 static int str2id(const char *, pid_t
*, long, long);
333 static int str2uid(const char *, uid_t
*, unsigned long, unsigned long);
334 static void *Realloc(void *, size_t);
335 static int pidcmp(const void *p1
, const void *p2
);
337 extern int ucbmain(int, char **);
338 static int stdmain(int, char **);
341 main(int argc
, char **argv
)
346 * The original two ps'es are linked in a single binary;
347 * their main()s are renamed to stdmain for /usr/bin/ps and
348 * ucbmain for /usr/ucb/ps.
349 * We try to figure out which instance of ps the user wants to run.
350 * Traditionally, the UCB variant doesn't require the flag argument
351 * start with a "-". If the first argument doesn't start with a
352 * "-", we call "ucbmain".
353 * If there's a first argument and it starts with a "-", we check
354 * whether any of the options isn't acceptable to "ucbmain"; in that
355 * case we run "stdmain".
356 * If we can't tell from the options which main to call, we check
357 * the binary we are running. We default to "stdmain" but
358 * any mention in the executable name of "ucb" causes us to call
361 if (argv
[1] != NULL
) {
362 if (argv
[1][0] != '-')
363 return (ucbmain(argc
, argv
));
364 else if (argv
[1][strspn(argv
[1], UCB_OPTS
)] != '\0')
365 return (stdmain(argc
, argv
));
370 if (me
!= NULL
&& strstr(me
, "ucb") != NULL
)
371 return (ucbmain(argc
, argv
));
373 return (stdmain(argc
, argv
));
377 stdmain(int argc
, char **argv
)
384 int pgerrflg
= 0; /* err flg: non-numeric arg w/p & g options */
387 struct dirent
*dentp
;
391 char loc_stime_str
[32];
393 (void) setlocale(LC_ALL
, "");
394 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
395 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
397 (void) textdomain(TEXT_DOMAIN
);
399 (void) memset(&euid_tbl
, 0, sizeof (euid_tbl
));
400 (void) memset(&ruid_tbl
, 0, sizeof (ruid_tbl
));
401 (void) memset(&egid_tbl
, 0, sizeof (egid_tbl
));
402 (void) memset(&rgid_tbl
, 0, sizeof (rgid_tbl
));
404 kbytes_per_page
= sysconf(_SC_PAGESIZE
) / 1024;
406 (void) gettimeofday(&now
, NULL
);
409 * calculate width of pid fields based on configured MAXPID
410 * (must be at least 5 to retain output format compatibility)
412 id
= maxpid
= (pid_t
)sysconf(_SC_MAXPID
);
414 while ((id
/= 10) > 0)
416 pidwidth
= pidwidth
< 5 ? 5 : pidwidth
;
418 fname
[F_PID
].width
= fname
[F_PPID
].width
= pidwidth
;
419 fname
[F_PGID
].width
= fname
[F_SID
].width
= pidwidth
;
423 * Specify the printf format with width and precision for
426 len
= snprintf(loc_stime_str
, sizeof (loc_stime_str
),
427 dcgettext(NULL
, "%8.8s", LC_TIME
), "STIME");
428 if (len
>= sizeof (loc_stime_str
))
429 len
= sizeof (loc_stime_str
) - 1;
431 fname
[F_STIME
].width
= fname
[F_STIME
].minwidth
= len
;
433 while ((c
= getopt(argc
, argv
, "jlfceAadLPWyZHh:t:p:g:u:U:G:n:s:o:z:"))
436 case 'H': /* Show home lgroups */
441 * Show processes/threads with given home lgroups
449 * Get all IDs in the list, verify for
450 * correctness and place in lgrps array.
453 /* Convert string to integer */
454 ret
= str2id(parg
, (pid_t
*)&id
, 0,
456 /* Complain if ID didn't parse correctly */
459 (void) fprintf(stderr
,
460 gettext("ps: %s "), parg
);
462 (void) fprintf(stderr
,
463 gettext("is an invalid "
464 "non-numeric argument"));
466 (void) fprintf(stderr
,
467 gettext("exceeds valid "
469 (void) fprintf(stderr
,
470 gettext(" for -h option\n"));
474 /* Extend lgrps array if needed */
475 if (nlgrps
== lgrps_size
) {
476 /* Double the size of the lgrps array */
480 lgrps
= Realloc(lgrps
,
481 lgrps_size
* sizeof (int));
483 /* place the id in the lgrps table */
484 lgrps
[nlgrps
++] = id
;
487 case 'l': /* long listing */
490 case 'f': /* full listing */
498 * Format output to reflect scheduler changes:
499 * high numbers for high priorities and don't
500 * print nice or p_cpu values. 'c' option only
501 * effective when used with 'l' or 'f' options.
505 case 'A': /* list every process */
506 case 'e': /* (obsolete) list every process */
508 tflg
= Gflg
= Uflg
= uflg
= pflg
= gflg
= sflg
= 0;
513 * Same as 'e' except no session group leaders
514 * and no non-terminal processes.
518 case 'd': /* same as e except no session leaders */
521 case 'L': /* show lwps */
524 case 'P': /* show bound processor */
527 case 'W': /* truncate long names */
530 case 'y': /* omit F & ADDR, report RSS & SZ in Kby */
533 case 'n': /* no longer needed; retain as no-op */
534 (void) fprintf(stderr
,
535 gettext("ps: warning: -n option ignored\n"));
537 case 't': /* terminals */
542 char nambuf
[TSZ
+6]; /* for "/dev/" + '\0' */
545 p
= Realloc(NULL
, TSZ
+1); /* for '\0' */
546 /* zero the buffer before using it */
549 if (isdigit(*parg
)) {
550 (void) strcpy(p
, "tty");
553 (void) strncat(p
, parg
, size
);
555 if ((ttysz
*= 2) == 0)
558 (ttysz
+ 1) * sizeof (struct tty
));
560 tty
[ntty
].tdev
= PRNODEV
;
561 (void) strcpy(nambuf
, "/dev/");
562 (void) strcat(nambuf
, p
);
563 if (stat64(nambuf
, &s
) == 0)
564 tty
[ntty
].tdev
= s
.st_rdev
;
565 tty
[ntty
++].tname
= p
;
568 case 'p': /* proc ids */
575 if ((ret
= str2id(parg
, &id
, 0, maxpid
)) != 0) {
577 (void) fprintf(stderr
,
578 gettext("ps: %s "), parg
);
580 (void) fprintf(stderr
,
581 gettext("is an invalid "
582 "non-numeric argument"));
584 (void) fprintf(stderr
,
585 gettext("exceeds valid "
587 (void) fprintf(stderr
,
588 gettext(" for -p option\n"));
593 if ((pidsz
*= 2) == 0)
596 pidsz
* sizeof (pid_t
));
601 case 's': /* session */
608 if ((ret
= str2id(parg
, &id
, 0, maxpid
)) != 0) {
610 (void) fprintf(stderr
,
611 gettext("ps: %s "), parg
);
613 (void) fprintf(stderr
,
614 gettext("is an invalid "
615 "non-numeric argument"));
617 (void) fprintf(stderr
,
618 gettext("exceeds valid "
620 (void) fprintf(stderr
,
621 gettext(" for -s option\n"));
625 if (nsessid
== sessidsz
) {
626 if ((sessidsz
*= 2) == 0)
628 sessid
= Realloc(sessid
,
629 sessidsz
* sizeof (pid_t
));
631 sessid
[nsessid
++] = id
;
634 case 'g': /* proc group */
641 if ((ret
= str2id(parg
, &id
, 0, maxpid
)) != 0) {
643 (void) fprintf(stderr
,
644 gettext("ps: %s "), parg
);
646 (void) fprintf(stderr
,
647 gettext("is an invalid "
648 "non-numeric argument"));
650 (void) fprintf(stderr
,
651 gettext("exceeds valid "
653 (void) fprintf(stderr
,
654 gettext(" for -g option\n"));
658 if (ngrpid
== grpidsz
) {
659 if ((grpidsz
*= 2) == 0)
661 grpid
= Realloc(grpid
,
662 grpidsz
* sizeof (pid_t
));
664 grpid
[ngrpid
++] = id
;
667 case 'u': /* effective user name or number */
672 add_ugentry(&euid_tbl
, parg
);
675 case 'U': /* real user name or number */
680 add_ugentry(&ruid_tbl
, parg
);
683 case 'G': /* real group name or number */
688 add_ugentry(&rgid_tbl
, parg
);
691 case 'o': /* output format */
693 while ((p
= parse_format(p
)) != NULL
)
696 case 'z': /* zone name or number */
703 if (zone_get_id(parg
, &id
) != 0) {
705 (void) fprintf(stderr
,
706 gettext("ps: unknown zone %s\n"),
711 if (nzoneid
== zoneidsz
) {
712 if ((zoneidsz
*= 2) == 0)
714 zoneid
= Realloc(zoneid
,
715 zoneidsz
* sizeof (zoneid_t
));
717 zoneid
[nzoneid
++] = id
;
720 case 'Z': /* show zone name */
723 default: /* error on ? */
728 if (errflg
|| optind
< argc
|| pgerrflg
)
732 tty
[ntty
].tname
= NULL
;
734 * If an appropriate option has not been specified, use the
735 * current terminal and effective uid as the default.
737 if (!(aflg
|Aflg
|dflg
|Gflg
|hflg
|Uflg
|uflg
|tflg
|pflg
|gflg
|sflg
|zflg
)) {
743 /* get our own controlling tty name using /proc */
744 (void) snprintf(pname
, sizeof (pname
),
745 "%s/self/psinfo", procdir
);
746 if ((procfd
= open(pname
, O_RDONLY
)) < 0 ||
747 read(procfd
, (char *)&info
, sizeof (info
)) < 0 ||
748 info
.pr_ttydev
== PRNODEV
) {
749 (void) fprintf(stderr
,
750 gettext("ps: no controlling terminal\n"));
753 (void) close(procfd
);
756 name
= gettty(&info
);
758 (void) fprintf(stderr
,
759 gettext("ps: can't find controlling terminal\n"));
763 if ((ttysz
*= 2) == 0)
765 tty
= Realloc(tty
, (ttysz
+ 1) * sizeof (struct tty
));
767 tty
[ntty
].tdev
= info
.pr_ttydev
;
768 tty
[ntty
++].tname
= name
;
769 tty
[ntty
].tname
= NULL
;
774 Gflg
= Uflg
= uflg
= pflg
= sflg
= gflg
= aflg
= dflg
= 0;
777 if (Aflg
| aflg
| dflg
)
780 i
= 0; /* prepare to exit on name lookup errors */
781 i
+= uconv(&euid_tbl
);
782 i
+= uconv(&ruid_tbl
);
783 i
+= gconv(&egid_tbl
);
784 i
+= gconv(&rgid_tbl
);
788 /* allocate a buffer for lwpsinfo structures */
790 if (Lflg
&& (lpsinfobuf
= malloc(lpbufsize
)) == NULL
) {
791 (void) fprintf(stderr
,
792 gettext("ps: no memory\n"));
796 if (fields
) { /* print user-specified header */
800 for (f
= fields
; f
!= NULL
; f
= f
->next
) {
805 (void) printf("%-*s",
806 f
->width
, f
->header
);
813 * Print these headers full width
814 * unless they appear at the end.
816 if (f
->next
!= NULL
) {
817 (void) printf("%-*s",
818 f
->width
, f
->header
);
826 f
->width
, f
->header
);
832 } else { /* print standard header */
834 * All fields before 'PID' are printed with a trailing space
835 * as a separator and that is how we print the headers too.
841 (void) printf(" F S ");
844 (void) printf(" ZONE ");
846 (void) printf(" UID ");
848 (void) printf(" UID ");
850 (void) printf("%*s", pidwidth
, "PID");
852 (void) printf(" %*s", pidwidth
, "PPID");
854 (void) printf(" %*s %*s", pidwidth
, "PGID",
857 (void) printf(" LWP");
859 (void) printf(" PSR");
861 (void) printf(" NLWP");
863 (void) printf(" CLS PRI");
864 else if (lflg
|| fflg
) {
867 (void) printf(" PRI NI");
871 (void) printf(" RSS SZ WCHAN");
873 (void) printf(" ADDR SZ WCHAN");
876 (void) printf(" %s", loc_stime_str
);
878 (void) printf(" LGRP");
880 (void) printf(" TTY LTIME CMD\n");
882 (void) printf(" TTY TIME CMD\n");
886 if (pflg
&& !(aflg
|Aflg
|dflg
|Gflg
|Uflg
|uflg
|hflg
|tflg
|gflg
|sflg
|zflg
) &&
887 npid
<= PTHRESHOLD
) {
889 * If we are looking at specific processes go straight
890 * to their /proc entries and don't scan /proc.
894 (void) qsort(pid
, npid
, sizeof (pid_t
), pidcmp
);
895 for (i
= 0; i
< npid
; i
++) {
898 if (i
>= 1 && pid
[i
] == pid
[i
- 1])
900 (void) sprintf(pname
, "%d", (int)pid
[i
]);
901 if (print_proc(pname
) == 0)
906 * Determine which processes to print info about by searching
907 * the /proc directory and looking at each process.
909 if ((dirp
= opendir(procdir
)) == NULL
) {
910 (void) fprintf(stderr
,
911 gettext("ps: cannot open PROC directory %s\n"),
916 /* for each active process --- */
917 while ((dentp
= readdir(dirp
)) != NULL
) {
918 if (dentp
->d_name
[0] == '.') /* skip . and .. */
920 if (print_proc(dentp
->d_name
) == 0)
924 (void) closedir(dirp
);
931 print_proc(char *pid_name
)
933 char pname
[PATH_MAX
];
936 int procfd
; /* filedescriptor for /proc/nnnnn/psinfo */
937 char *tp
; /* ptr to ttyname, if any */
938 psinfo_t info
; /* process information from /proc */
939 lwpsinfo_t
*lwpsinfo
; /* array of lwpsinfo structs */
941 pdlen
= snprintf(pname
, sizeof (pname
), "%s/%s/", procdir
, pid_name
);
942 if (pdlen
>= sizeof (pname
) - 10)
945 (void) strcpy(&pname
[pdlen
], "psinfo");
946 if ((procfd
= open(pname
, O_RDONLY
)) == -1) {
947 /* Process may have exited meanwhile. */
951 * Get the info structure for the process and close quickly.
953 if (read(procfd
, (char *)&info
, sizeof (info
)) < 0) {
956 (void) close(procfd
);
957 if (saverr
== EAGAIN
)
959 if (saverr
!= ENOENT
)
960 (void) fprintf(stderr
,
961 gettext("ps: read() on %s: %s\n"),
962 pname
, err_string(saverr
));
965 (void) close(procfd
);
968 if (info
.pr_lwp
.pr_state
== 0) /* can't happen? */
972 * Omit session group leaders for 'a' and 'd' options.
974 if ((info
.pr_pid
== info
.pr_sid
) && (dflg
|| aflg
))
978 else if (pflg
&& search(pid
, npid
, info
.pr_pid
))
979 found
++; /* ppid in p option arg list */
980 else if (uflg
&& ugfind((id_t
)info
.pr_euid
, &euid_tbl
))
981 found
++; /* puid in u option arg list */
982 else if (Uflg
&& ugfind((id_t
)info
.pr_uid
, &ruid_tbl
))
983 found
++; /* puid in U option arg list */
985 else if (gflg
&& ugfind((id_t
)info
.pr_egid
, &egid_tbl
))
986 found
++; /* pgid in g option arg list */
988 else if (Gflg
&& ugfind((id_t
)info
.pr_gid
, &rgid_tbl
))
989 found
++; /* pgid in G option arg list */
990 else if (gflg
&& search(grpid
, ngrpid
, info
.pr_pgid
))
991 found
++; /* grpid in g option arg list */
992 else if (sflg
&& search(sessid
, nsessid
, info
.pr_sid
))
993 found
++; /* sessid in s option arg list */
994 else if (zflg
&& search(zoneid
, nzoneid
, info
.pr_zoneid
))
995 found
++; /* zoneid in z option arg list */
996 else if (hflg
&& search((pid_t
*)lgrps
, nlgrps
, info
.pr_lwp
.pr_lgrp
))
997 found
++; /* home lgroup in h option arg list */
998 if (!found
&& !tflg
&& !aflg
)
1000 if (!prfind(found
, &info
, &tp
))
1002 if (Lflg
&& (info
.pr_nlwp
+ info
.pr_nzomb
) > 1) {
1005 (void) strcpy(&pname
[pdlen
], "lpsinfo");
1006 if ((procfd
= open(pname
, O_RDONLY
)) == -1)
1009 * Get the info structures for the lwps.
1011 prsz
= read(procfd
, lpsinfobuf
, lpbufsize
);
1015 (void) close(procfd
);
1016 if (saverr
== EAGAIN
)
1018 if (saverr
!= ENOENT
)
1019 (void) fprintf(stderr
,
1020 gettext("ps: read() on %s: %s\n"),
1021 pname
, err_string(saverr
));
1024 (void) close(procfd
);
1025 if (prsz
== lpbufsize
) {
1027 * buffer overflow. Realloc new buffer.
1028 * Error handling is done in Realloc().
1031 lpsinfobuf
= Realloc(lpsinfobuf
, lpbufsize
);
1034 if (lpsinfobuf
->pr_nent
!= (info
.pr_nlwp
+ info
.pr_nzomb
))
1036 lwpsinfo
= (lwpsinfo_t
*)(lpsinfobuf
+ 1);
1038 if (!Lflg
|| (info
.pr_nlwp
+ info
.pr_nzomb
) <= 1) {
1044 info
.pr_lwp
= *lwpsinfo
;
1046 /* LINTED improper alignment */
1047 lwpsinfo
= (lwpsinfo_t
*)((char *)lwpsinfo
+
1048 lpsinfobuf
->pr_entsize
);
1049 } while (++nlwp
< lpsinfobuf
->pr_nent
);
1055 field_cmp(const void *l
, const void *r
)
1057 struct def_field
*lhs
= *((struct def_field
**)l
);
1058 struct def_field
*rhs
= *((struct def_field
**)r
);
1060 return (strcmp(lhs
->fname
, rhs
->fname
));
1064 usage(void) /* print usage message and quit */
1066 struct def_field
*df
, *sorted
[NFIELDS
];
1067 int pos
= 80, i
= 0;
1069 static char usage1
[] =
1070 "ps [ -aAdefHlcjLPWyZ ] [ -o format ] [ -t termlist ]";
1071 static char usage2
[] =
1072 "\t[ -u userlist ] [ -U userlist ] [ -G grouplist ]";
1073 static char usage3
[] =
1074 "\t[ -p proclist ] [ -g pgrplist ] [ -s sidlist ]";
1075 static char usage4
[] =
1076 "\t[ -z zonelist ] [-h lgrplist]";
1077 static char usage5
[] =
1078 " 'format' is one or more of:";
1080 (void) fprintf(stderr
,
1081 gettext("usage: %s\n%s\n%s\n%s\n%s"),
1082 gettext(usage1
), gettext(usage2
), gettext(usage3
),
1083 gettext(usage4
), gettext(usage5
));
1086 * Now print out the possible output formats such that they neatly fit
1087 * into eighty columns. Note that the fact that we are determining
1088 * this output programmatically means that a gettext() is impossible --
1089 * but it would be a mistake to localize the output formats anyway as
1090 * they are tokens for input, not output themselves.
1092 for (df
= &fname
[0]; df
< &fname
[NFIELDS
]; df
++)
1095 (void) qsort(sorted
, NFIELDS
, sizeof (void *), field_cmp
);
1097 for (i
= 0; i
< NFIELDS
; i
++) {
1098 if (pos
+ strlen((df
= sorted
[i
])->fname
) + 1 >= 80) {
1099 (void) fprintf(stderr
, "\n\t");
1103 (void) fprintf(stderr
, "%s%s", pos
> 8 ? " " : "", df
->fname
);
1104 pos
+= strlen(df
->fname
) + 1;
1107 (void) fprintf(stderr
, "\n");
1113 * getarg() finds the next argument in list and copies arg into argbuf.
1114 * p1 first pts to arg passed back from getopt routine. p1 is then
1115 * bumped to next character that is not a comma or blank -- p1 NULL
1116 * indicates end of list.
1121 static char argbuf
[ARGSIZ
];
1123 char *parga
= argbuf
;
1126 while ((c
= *p1
) != '\0' && (c
== ',' || isspace(c
)))
1129 while ((c
= *p1
) != '\0' && c
!= ',' && !isspace(c
)) {
1130 if (parga
< argbuf
+ ARGSIZ
- 1)
1136 while ((c
= *p1
) != '\0' && (c
== ',' || isspace(c
)))
1145 * parse_format() takes the argument to the -o option,
1146 * sets up the next output field structure, and returns
1147 * a pointer to any further output field specifier(s).
1148 * As a side-effect, it increments errflg if encounters a format error.
1151 parse_format(char *arg
)
1155 char *header
= NULL
;
1157 struct def_field
*df
;
1160 while ((c
= *arg
) != '\0' && (c
== ',' || isspace(c
)))
1165 arg
= strpbrk(arg
, " \t\r\v\f\n,=");
1174 width
= strlen(header
);
1176 while (s
> header
&& isspace(*--s
))
1178 while (isspace(*header
))
1182 for (df
= &fname
[0]; df
< &fname
[NFIELDS
]; df
++)
1183 if (strcmp(name
, df
->fname
) == 0) {
1184 if (strcmp(name
, "lwp") == 0 ||
1185 strcmp(name
, "lwpname") == 0)
1189 if (df
>= &fname
[NFIELDS
]) {
1190 (void) fprintf(stderr
,
1191 gettext("ps: unknown output format: -o %s\n"),
1196 if ((f
= malloc(sizeof (*f
))) == NULL
) {
1197 (void) fprintf(stderr
,
1198 gettext("ps: malloc() for output format failed, %s\n"),
1203 f
->fname
= df
- &fname
[0];
1204 f
->header
= header
? header
: df
->header
;
1207 if (*f
->header
!= '\0')
1209 f
->width
= max(width
, df
->minwidth
);
1212 fields
= last_field
= f
;
1214 last_field
->next
= f
;
1222 devlookup(dev_t ddev
)
1227 for (dp
= devl
, i
= 0; i
< ndev
; dp
++, i
++) {
1228 if (dp
->ddev
== ddev
)
1235 devadd(char *name
, dev_t ddev
)
1240 if (ndev
== maxdev
) {
1242 devl
= Realloc(devl
, maxdev
* sizeof (struct devl
));
1248 (void) strcpy(dp
->dname
, "??");
1252 leng
= strlen(name
);
1253 /* Strip off /dev/ */
1254 if (leng
< DNSIZE
+ 4)
1255 (void) strcpy(dp
->dname
, &name
[5]);
1257 start
= leng
- DNSIZE
- 1;
1259 for (i
= start
; i
< leng
&& name
[i
] != '/'; i
++)
1262 (void) strncpy(dp
->dname
, &name
[start
], DNSIZE
);
1264 (void) strncpy(dp
->dname
, &name
[i
+1], DNSIZE
);
1270 * gettty returns the user's tty number or ? if none.
1273 gettty(psinfo_t
*psinfo
)
1275 extern char *_ttyname_dev(dev_t
, char *, size_t);
1276 static zoneid_t zid
= -1;
1277 char devname
[TTYNAME_MAX
];
1283 if (psinfo
->pr_ttydev
== PRNODEV
|| psinfo
->pr_zoneid
!= zid
)
1286 if ((retval
= devlookup(psinfo
->pr_ttydev
)) != NULL
)
1289 retval
= _ttyname_dev(psinfo
->pr_ttydev
, devname
, sizeof (devname
));
1291 return (devadd(retval
, psinfo
->pr_ttydev
));
1295 * Find the process's tty and return 1 if process is to be printed.
1298 prfind(int found
, psinfo_t
*psinfo
, char **tpp
)
1303 if (psinfo
->pr_nlwp
== 0) {
1304 /* process is a zombie */
1312 * Get current terminal. If none ("?") and 'a' is set, don't print
1313 * info. If 't' is set, check if term is in list of desired terminals
1314 * and print it if it is.
1316 tp
= gettty(psinfo
);
1317 if (aflg
&& *tp
== '?') {
1321 if (tflg
&& !found
) {
1324 for (ttyp
= tty
; ttyp
->tname
!= NULL
; ttyp
++) {
1326 * Look for a name match
1328 if (strcmp(tp
, ttyp
->tname
) == 0) {
1333 * Look for same device under different names.
1335 if ((other
== NULL
) &&
1336 (ttyp
->tdev
!= PRNODEV
) &&
1337 (psinfo
->pr_ttydev
== ttyp
->tdev
))
1338 other
= ttyp
->tname
;
1340 if (!match
&& (other
!= NULL
)) {
1342 * found under a different name
1347 if (!match
|| (tuid
!= (uid_t
)-1 && tuid
!= psinfo
->pr_euid
)) {
1349 * not found OR not matching euid
1360 * Print info about the process.
1363 prcom(psinfo_t
*psinfo
, char *ttyp
)
1372 char zonename
[ZONENAME_MAX
];
1375 * If process is zombie, call zombie print routine and return.
1377 if (psinfo
->pr_nlwp
== 0) {
1379 pr_fields(psinfo
, ttyp
, print_zombie_field
);
1385 zombie_lwp
= (Lflg
&& psinfo
->pr_lwp
.pr_sname
== 'Z');
1388 * If user specified '-o format', print requested fields and return.
1390 if (fields
!= NULL
) {
1391 pr_fields(psinfo
, ttyp
, print_field
);
1396 * All fields before 'PID' are printed with a trailing space as a
1397 * separator, rather than keeping track of which column is first. All
1398 * other fields are printed with a leading space.
1402 (void) printf("%2x ", psinfo
->pr_flag
& 0377); /* F */
1403 (void) printf("%c ", psinfo
->pr_lwp
.pr_sname
); /* S */
1406 if (Zflg
) { /* ZONE */
1407 if (getzonenamebyid(psinfo
->pr_zoneid
, zonename
,
1408 sizeof (zonename
)) < 0) {
1409 if (snprintf(NULL
, 0, "%d",
1410 ((int)psinfo
->pr_zoneid
)) > 7)
1411 (void) printf(" %6.6d%c ",
1412 ((int)psinfo
->pr_zoneid
), '*');
1414 (void) printf(" %7.7d ",
1415 ((int)psinfo
->pr_zoneid
));
1419 nw
= mbstowcs(NULL
, zonename
, 0);
1420 if (nw
== (size_t)-1)
1421 (void) printf("%8.8s ", "ERROR");
1423 (void) wprintf(L
"%7.7s%c ", zonename
, '*');
1425 (void) wprintf(L
"%8.8s ", zonename
);
1429 if (fflg
) { /* UID */
1430 if ((pwd
= getpwuid(psinfo
->pr_euid
)) != NULL
) {
1433 nw
= mbstowcs(NULL
, pwd
->pw_name
, 0);
1434 if (nw
== (size_t)-1)
1435 (void) printf("%8.8s ", "ERROR");
1437 (void) wprintf(L
"%7.7s%c ", pwd
->pw_name
, '*');
1439 (void) wprintf(L
"%8.8s ", pwd
->pw_name
);
1441 if (snprintf(NULL
, 0, "%u",
1442 (psinfo
->pr_euid
)) > 7)
1443 (void) printf(" %6.6u%c ", psinfo
->pr_euid
,
1446 (void) printf(" %7.7u ", psinfo
->pr_euid
);
1449 if (snprintf(NULL
, 0, "%u", (psinfo
->pr_euid
)) > 6)
1450 (void) printf("%5.5u%c ", psinfo
->pr_euid
, '*');
1452 (void) printf("%6u ", psinfo
->pr_euid
);
1454 (void) printf("%*d", pidwidth
, (int)psinfo
->pr_pid
); /* PID */
1456 (void) printf(" %*d", pidwidth
,
1457 (int)psinfo
->pr_ppid
); /* PPID */
1459 (void) printf(" %*d", pidwidth
,
1460 (int)psinfo
->pr_pgid
); /* PGID */
1461 (void) printf(" %*d", pidwidth
,
1462 (int)psinfo
->pr_sid
); /* SID */
1465 (void) printf(" %5d", (int)psinfo
->pr_lwp
.pr_lwpid
); /* LWP */
1467 if (psinfo
->pr_lwp
.pr_bindpro
== PBIND_NONE
) /* PSR */
1468 (void) printf(" -");
1470 (void) printf(" %3d", psinfo
->pr_lwp
.pr_bindpro
);
1472 if (Lflg
&& fflg
) /* NLWP */
1473 (void) printf(" %5d", psinfo
->pr_nlwp
+ psinfo
->pr_nzomb
);
1475 if (zombie_lwp
) /* CLS */
1478 (void) printf(" %4s", psinfo
->pr_lwp
.pr_clname
);
1479 (void) printf(" %3d", psinfo
->pr_lwp
.pr_pri
); /* PRI */
1480 } else if (lflg
|| fflg
) {
1481 (void) printf(" %3d", psinfo
->pr_lwp
.pr_cpu
& 0377); /* C */
1482 if (lflg
) { /* PRI NI */
1484 * Print priorities the old way (lower numbers
1485 * mean higher priority) and print nice value
1486 * for time sharing procs.
1488 (void) printf(" %3d", psinfo
->pr_lwp
.pr_oldpri
);
1489 if (psinfo
->pr_lwp
.pr_oldpri
!= 0)
1490 (void) printf(" %2d", psinfo
->pr_lwp
.pr_nice
);
1492 (void) printf(" %2.2s",
1493 psinfo
->pr_lwp
.pr_clname
);
1498 if (psinfo
->pr_flag
& SSYS
) /* RSS */
1499 (void) printf(" 0");
1500 else if (psinfo
->pr_rssize
)
1501 (void) printf(" %5lu",
1502 (ulong_t
)psinfo
->pr_rssize
);
1504 (void) printf(" ?");
1505 if (psinfo
->pr_flag
& SSYS
) /* SZ */
1506 (void) printf(" 0");
1507 else if (psinfo
->pr_size
)
1508 (void) printf(" %6lu",
1509 (ulong_t
)psinfo
->pr_size
);
1511 (void) printf(" ?");
1514 if (psinfo
->pr_addr
) /* ADDR */
1515 (void) printf(" %8lx",
1516 (ulong_t
)psinfo
->pr_addr
);
1519 (void) printf(" ?");
1520 if (psinfo
->pr_flag
& SSYS
) /* SZ */
1521 (void) printf(" 0");
1522 else if (psinfo
->pr_size
)
1523 (void) printf(" %6lu",
1524 (ulong_t
)psinfo
->pr_size
/ kbytes_per_page
);
1526 (void) printf(" ?");
1528 if (psinfo
->pr_lwp
.pr_sname
!= 'S') /* WCHAN */
1531 else if (psinfo
->pr_lwp
.pr_wchan
)
1532 (void) printf(" %8lx",
1533 (ulong_t
)psinfo
->pr_lwp
.pr_wchan
);
1536 (void) printf(" ?");
1538 if (fflg
) { /* STIME */
1539 int width
= fname
[F_STIME
].width
;
1541 prtime(psinfo
->pr_lwp
.pr_start
, width
+ 1, 1);
1543 prtime(psinfo
->pr_start
, width
+ 1, 1);
1547 /* Display home lgroup */
1548 (void) printf(" %4d", (int)psinfo
->pr_lwp
.pr_lgrp
);
1551 (void) printf(" %-8.14s", ttyp
); /* TTY */
1553 tm
= psinfo
->pr_lwp
.pr_time
.tv_sec
;
1554 if (psinfo
->pr_lwp
.pr_time
.tv_nsec
> 500000000)
1557 tm
= psinfo
->pr_time
.tv_sec
;
1558 if (psinfo
->pr_time
.tv_nsec
> 500000000)
1561 (void) printf(" %4ld:%.2ld", tm
/ 60, tm
% 60); /* [L]TIME */
1564 (void) printf(" <defunct>\n");
1568 if (!fflg
) { /* CMD */
1569 wcnt
= namencnt(psinfo
->pr_fname
, 16, 8);
1570 (void) printf(" %.*s\n", wcnt
, psinfo
->pr_fname
);
1576 * PRARGSZ == length of cmd arg string.
1578 psinfo
->pr_psargs
[PRARGSZ
-1] = '\0';
1579 bytesleft
= PRARGSZ
;
1580 for (cp
= psinfo
->pr_psargs
; *cp
!= '\0'; cp
+= length
) {
1581 length
= mbtowc(&wchar
, cp
, MB_LEN_MAX
);
1584 if (length
< 0 || !iswprint(wchar
)) {
1587 if (bytesleft
<= length
) {
1591 /* omit the unprintable character */
1592 (void) memmove(cp
, cp
+length
, bytesleft
-length
);
1595 bytesleft
-= length
;
1597 wcnt
= namencnt(psinfo
->pr_psargs
, PRARGSZ
, lflg
? 35 : PRARGSZ
);
1598 (void) printf(" %.*s\n", wcnt
, psinfo
->pr_psargs
);
1602 * Print percent from 16-bit binary fraction [0 .. 1]
1603 * Round up .01 to .1 to indicate some small percentage (the 0x7000 below).
1606 prtpct(ushort_t pct
, int width
)
1608 uint_t value
= pct
; /* need 32 bits to compute with */
1610 value
= ((value
* 1000) + 0x7000) >> 15; /* [0 .. 1000] */
1613 if ((width
-= 2) < 2)
1615 (void) printf("%*u.%u", width
, value
/ 10, value
% 10);
1619 print_time(time_t tim
, int width
)
1628 (void) printf("%*s", width
, "-");
1640 (void) snprintf(buf
, sizeof (buf
), "%ld-%2.2ld:%2.2ld:%2.2ld",
1641 days
, hours
, minutes
, seconds
);
1642 } else if (hours
> 0) {
1643 (void) snprintf(buf
, sizeof (buf
), "%2.2ld:%2.2ld:%2.2ld",
1644 hours
, minutes
, seconds
);
1646 (void) snprintf(buf
, sizeof (buf
), "%2.2ld:%2.2ld",
1650 (void) printf("%*s", width
, buf
);
1654 print_field(psinfo_t
*psinfo
, struct field
*f
, const char *ttyp
)
1656 int width
= f
->width
;
1666 char c
= '\0', *csave
= NULL
;
1669 zombie_lwp
= (Lflg
&& psinfo
->pr_lwp
.pr_sname
== 'Z');
1673 if ((pwd
= getpwuid(psinfo
->pr_uid
)) != NULL
) {
1676 nw
= mbstowcs(NULL
, pwd
->pw_name
, 0);
1677 if (nw
== (size_t)-1)
1678 (void) printf("%*s ", width
, "ERROR");
1679 else if (Wflg
&& nw
> width
)
1680 (void) wprintf(L
"%.*s%c", width
- 1,
1683 (void) wprintf(L
"%*s", width
, pwd
->pw_name
);
1685 if (Wflg
&& snprintf(NULL
, 0, "%u",
1686 (psinfo
->pr_uid
)) > width
)
1688 (void) printf("%*u%c", width
- 1,
1689 psinfo
->pr_uid
, '*');
1691 (void) printf("%*u", width
, psinfo
->pr_uid
);
1695 if ((pwd
= getpwuid(psinfo
->pr_euid
)) != NULL
) {
1698 nw
= mbstowcs(NULL
, pwd
->pw_name
, 0);
1699 if (nw
== (size_t)-1)
1700 (void) printf("%*s ", width
, "ERROR");
1701 else if (Wflg
&& nw
> width
)
1702 (void) wprintf(L
"%.*s%c", width
- 1,
1705 (void) wprintf(L
"%*s", width
, pwd
->pw_name
);
1707 if (Wflg
&& snprintf(NULL
, 0, "%u",
1708 (psinfo
->pr_euid
)) > width
)
1710 (void) printf("%*u%c", width
- 1,
1711 psinfo
->pr_euid
, '*');
1713 (void) printf("%*u", width
, psinfo
->pr_euid
);
1717 if ((grp
= getgrgid(psinfo
->pr_gid
)) != NULL
)
1718 (void) printf("%*s", width
, grp
->gr_name
);
1720 (void) printf("%*u", width
, psinfo
->pr_gid
);
1723 if ((grp
= getgrgid(psinfo
->pr_egid
)) != NULL
)
1724 (void) printf("%*s", width
, grp
->gr_name
);
1726 (void) printf("%*u", width
, psinfo
->pr_egid
);
1729 (void) printf("%*u", width
, psinfo
->pr_uid
);
1732 (void) printf("%*u", width
, psinfo
->pr_euid
);
1735 (void) printf("%*u", width
, psinfo
->pr_gid
);
1738 (void) printf("%*u", width
, psinfo
->pr_egid
);
1741 (void) printf("%*d", width
, (int)psinfo
->pr_pid
);
1744 (void) printf("%*d", width
, (int)psinfo
->pr_ppid
);
1747 (void) printf("%*d", width
, (int)psinfo
->pr_pgid
);
1750 (void) printf("%*d", width
, (int)psinfo
->pr_sid
);
1753 if (zombie_lwp
|| psinfo
->pr_lwp
.pr_bindpro
== PBIND_NONE
)
1754 (void) printf("%*s", width
, "-");
1756 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_bindpro
);
1759 (void) printf("%*d", width
, (int)psinfo
->pr_lwp
.pr_lwpid
);
1762 char lwpname
[THREAD_NAME_MAX
] = "";
1766 if (asprintf(&path
, "%s/%d/lwp/%d/lwpname", procdir
,
1767 (int)psinfo
->pr_pid
, (int)psinfo
->pr_lwp
.pr_lwpid
) != -1 &&
1768 (fd
= open(path
, O_RDONLY
)) != -1) {
1769 (void) read(fd
, lwpname
, sizeof (lwpname
));
1770 lwpname
[THREAD_NAME_MAX
- 1] = '\0';
1776 if (f
->next
!= NULL
)
1777 (void) printf("%-*s", width
, lwpname
);
1779 (void) printf("%s", lwpname
);
1783 (void) printf("%*d", width
, psinfo
->pr_nlwp
+ psinfo
->pr_nzomb
);
1787 (void) printf("%*s", width
, "-");
1789 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_oldpri
);
1793 (void) printf("%*s", width
, "-");
1795 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_pri
);
1798 mask
= 0xffffffffUL
;
1800 mask
>>= (8 - width
) * 4;
1801 (void) printf("%*lx", width
, psinfo
->pr_flag
& mask
);
1804 (void) printf("%*c", width
, psinfo
->pr_lwp
.pr_sname
);
1808 (void) printf("%*s", width
, "-");
1810 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_cpu
);
1814 (void) printf("%*s", width
, "-");
1816 prtpct(psinfo
->pr_lwp
.pr_pctcpu
, width
);
1818 prtpct(psinfo
->pr_pctcpu
, width
);
1821 prtpct(psinfo
->pr_pctmem
, width
);
1824 (void) printf("%*lu", width
,
1825 (ulong_t
)psinfo
->pr_size
/ kbytes_per_page
);
1828 (void) printf("%*lu", width
, (ulong_t
)psinfo
->pr_size
);
1831 (void) printf("%*lu", width
, (ulong_t
)psinfo
->pr_rssize
);
1834 /* if pr_oldpri is zero, then this class has no nice */
1836 (void) printf("%*s", width
, "-");
1837 else if (psinfo
->pr_lwp
.pr_oldpri
!= 0)
1838 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_nice
);
1840 (void) printf("%*.*s", width
, width
,
1841 psinfo
->pr_lwp
.pr_clname
);
1845 (void) printf("%*s", width
, "-");
1847 (void) printf("%*.*s", width
, width
,
1848 psinfo
->pr_lwp
.pr_clname
);
1852 prtime(psinfo
->pr_lwp
.pr_start
, width
, 0);
1854 prtime(psinfo
->pr_start
, width
, 0);
1858 print_time(delta_secs(&psinfo
->pr_lwp
.pr_start
),
1861 print_time(delta_secs(&psinfo
->pr_start
), width
);
1865 cputime
= psinfo
->pr_lwp
.pr_time
.tv_sec
;
1866 if (psinfo
->pr_lwp
.pr_time
.tv_nsec
> 500000000)
1869 cputime
= psinfo
->pr_time
.tv_sec
;
1870 if (psinfo
->pr_time
.tv_nsec
> 500000000)
1873 print_time(cputime
, width
);
1876 (void) printf("%-*s", width
, ttyp
);
1880 (void) printf("%*s", width
, "-");
1882 (void) printf("%*lx", width
,
1883 (long)psinfo
->pr_lwp
.pr_addr
);
1885 (void) printf("%*lx", width
, (long)psinfo
->pr_addr
);
1888 if (!zombie_lwp
&& psinfo
->pr_lwp
.pr_wchan
)
1889 (void) printf("%*lx", width
,
1890 (long)psinfo
->pr_lwp
.pr_wchan
);
1892 (void) printf("%*.*s", width
, width
, "-");
1896 * Print full width unless this is the last output format.
1899 if (f
->next
!= NULL
)
1900 (void) printf("%-*s", width
, "<defunct>");
1902 (void) printf("%s", "<defunct>");
1905 wcnt
= namencnt(psinfo
->pr_fname
, 16, width
);
1906 if (f
->next
!= NULL
)
1907 (void) printf("%-*.*s", width
, wcnt
, psinfo
->pr_fname
);
1909 (void) printf("%-.*s", wcnt
, psinfo
->pr_fname
);
1913 if (f
->next
!= NULL
)
1914 (void) printf("%-*s", width
, "<defunct>");
1916 (void) printf("%s", "<defunct>");
1919 csave
= strpbrk(psinfo
->pr_psargs
, " \t\r\v\f\n");
1927 * PRARGSZ == length of cmd arg string.
1930 (void) printf("%-*s", width
, "<defunct>");
1933 psinfo
->pr_psargs
[PRARGSZ
-1] = '\0';
1934 bytesleft
= PRARGSZ
;
1935 for (cp
= psinfo
->pr_psargs
; *cp
!= '\0'; cp
+= length
) {
1936 length
= mbtowc(&wchar
, cp
, MB_LEN_MAX
);
1939 if (length
< 0 || !iswprint(wchar
)) {
1942 if (bytesleft
<= length
) {
1946 /* omit the unprintable character */
1947 (void) memmove(cp
, cp
+length
, bytesleft
-length
);
1950 bytesleft
-= length
;
1952 wcnt
= namencnt(psinfo
->pr_psargs
, PRARGSZ
, width
);
1954 * Print full width unless this is the last format.
1956 if (f
->next
!= NULL
)
1957 (void) printf("%-*.*s", width
, wcnt
,
1960 (void) printf("%-.*s", wcnt
,
1962 if (f
->fname
== F_COMM
&& csave
)
1966 (void) printf("%*d", width
, (int)psinfo
->pr_taskid
);
1969 (void) printf("%*d", width
, (int)psinfo
->pr_projid
);
1973 struct project cproj
;
1974 char proj_buf
[PROJECT_BUFSZ
];
1976 if ((getprojbyid(psinfo
->pr_projid
, &cproj
,
1977 (void *)&proj_buf
, PROJECT_BUFSZ
)) == NULL
) {
1978 if (Wflg
&& snprintf(NULL
, 0, "%d",
1979 ((int)psinfo
->pr_projid
)) > width
)
1980 (void) printf("%.*d%c", width
- 1,
1981 ((int)psinfo
->pr_projid
), '*');
1983 (void) printf("%*d", width
,
1984 (int)psinfo
->pr_projid
);
1988 if (cproj
.pj_name
!= NULL
)
1989 nw
= mbstowcs(NULL
, cproj
.pj_name
, 0);
1990 if (cproj
.pj_name
== NULL
)
1991 (void) printf("%*s ", width
, "---");
1992 else if (nw
== (size_t)-1)
1993 (void) printf("%*s ", width
, "ERROR");
1994 else if (Wflg
&& nw
> width
)
1995 (void) wprintf(L
"%.*s%c", width
- 1,
1996 cproj
.pj_name
, '*');
1998 (void) wprintf(L
"%*s", width
,
2004 if (zombie_lwp
|| psinfo
->pr_lwp
.pr_bindpset
== PS_NONE
)
2005 (void) printf("%*s", width
, "-");
2007 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_bindpset
);
2010 (void) printf("%*d", width
, (int)psinfo
->pr_zoneid
);
2014 char zonename
[ZONENAME_MAX
];
2016 if (getzonenamebyid(psinfo
->pr_zoneid
, zonename
,
2017 sizeof (zonename
)) < 0) {
2018 if (Wflg
&& snprintf(NULL
, 0, "%d",
2019 ((int)psinfo
->pr_zoneid
)) > width
)
2020 (void) printf("%.*d%c", width
- 1,
2021 ((int)psinfo
->pr_zoneid
), '*');
2023 (void) printf("%*d", width
,
2024 (int)psinfo
->pr_zoneid
);
2028 nw
= mbstowcs(NULL
, zonename
, 0);
2029 if (nw
== (size_t)-1)
2030 (void) printf("%*s ", width
, "ERROR");
2031 else if (Wflg
&& nw
> width
)
2032 (void) wprintf(L
"%.*s%c", width
- 1,
2035 (void) wprintf(L
"%*s", width
, zonename
);
2040 if (psinfo
->pr_contract
== -1)
2041 (void) printf("%*s", width
, "-");
2043 (void) printf("%*ld", width
, (long)psinfo
->pr_contract
);
2046 /* Display home lgroup */
2047 (void) printf("%*d", width
, (int)psinfo
->pr_lwp
.pr_lgrp
);
2051 (void) printf("%*s", width
,
2052 psinfo
->pr_dmodel
== PR_MODEL_LP64
? "_LP64" : "_ILP32");
2058 print_zombie_field(psinfo_t
*psinfo
, struct field
*f
, const char *ttyp
)
2061 int width
= f
->width
;
2068 * Print full width unless this is the last output format.
2070 wcnt
= min(width
, sizeof ("<defunct>"));
2071 if (f
->next
!= NULL
)
2072 (void) printf("%-*.*s", width
, wcnt
, "<defunct>");
2074 (void) printf("%-.*s", wcnt
, "<defunct>");
2086 (void) printf("%*s", width
, "-");
2094 (void) printf("%*d", width
, 0);
2098 print_field(psinfo
, f
, ttyp
);
2104 pr_fields(psinfo_t
*psinfo
, const char *ttyp
,
2105 void (*print_fld
)(psinfo_t
*, struct field
*, const char *))
2109 for (f
= fields
; f
!= NULL
; f
= f
->next
) {
2110 print_fld(psinfo
, f
, ttyp
);
2111 if (f
->next
!= NULL
)
2114 (void) printf("\n");
2118 * Returns 1 if arg is found in array arr, of length num; 0 otherwise.
2121 search(pid_t
*arr
, int number
, pid_t arg
)
2125 for (i
= 0; i
< number
; i
++)
2132 * Add an entry (user, group) to the specified table.
2135 add_ugentry(struct ughead
*tbl
, char *name
)
2137 struct ugdata
*entp
;
2139 if (tbl
->size
== tbl
->nent
) { /* reallocate the table entries */
2140 if ((tbl
->size
*= 2) == 0)
2141 tbl
->size
= 32; /* first time */
2142 tbl
->ent
= Realloc(tbl
->ent
, tbl
->size
*sizeof (struct ugdata
));
2144 entp
= &tbl
->ent
[tbl
->nent
++];
2146 (void) strncpy(entp
->name
, name
, MAXUGNAME
);
2147 entp
->name
[MAXUGNAME
] = '\0';
2151 uconv(struct ughead
*uhead
)
2153 struct ugdata
*utbl
= uhead
->ent
;
2154 int n
= uhead
->nent
;
2161 * Ask the name service for names.
2163 for (i
= 0; i
< n
; i
++) {
2165 * If name is numeric, ask for numeric id
2167 if (str2uid(utbl
[i
].name
, &uid
, 0, MAXEPHUID
) == 0)
2168 pwd
= getpwuid(uid
);
2170 pwd
= getpwnam(utbl
[i
].name
);
2173 * If found, enter found index into tbl array.
2176 (void) fprintf(stderr
,
2177 gettext("ps: unknown user %s\n"), utbl
[i
].name
);
2181 utbl
[fnd
].id
= pwd
->pw_uid
;
2182 (void) strncpy(utbl
[fnd
].name
, pwd
->pw_name
, MAXUGNAME
);
2186 uhead
->nent
= fnd
; /* in case it changed */
2191 gconv(struct ughead
*ghead
)
2193 struct ugdata
*gtbl
= ghead
->ent
;
2194 int n
= ghead
->nent
;
2201 * Ask the name service for names.
2203 for (i
= 0; i
< n
; i
++) {
2205 * If name is numeric, ask for numeric id
2207 if (str2uid(gtbl
[i
].name
, (uid_t
*)&gid
, 0, MAXEPHUID
) == 0)
2208 grp
= getgrgid(gid
);
2210 grp
= getgrnam(gtbl
[i
].name
);
2212 * If found, enter found index into tbl array.
2215 (void) fprintf(stderr
,
2216 gettext("ps: unknown group %s\n"), gtbl
[i
].name
);
2220 gtbl
[fnd
].id
= grp
->gr_gid
;
2221 (void) strncpy(gtbl
[fnd
].name
, grp
->gr_name
, MAXUGNAME
);
2225 ghead
->nent
= fnd
; /* in case it changed */
2230 * Return 1 if puid is in table, otherwise 0.
2233 ugfind(id_t id
, struct ughead
*ughead
)
2235 struct ugdata
*utbl
= ughead
->ent
;
2236 int n
= ughead
->nent
;
2239 for (i
= 0; i
< n
; i
++)
2240 if (utbl
[i
].id
== id
)
2246 * Print starting time of process unless process started more than 24 hours
2247 * ago, in which case the date is printed. The date is printed in the form
2248 * "MMM dd" if old format, else the blank is replaced with an '_' so
2249 * it appears as a single word (for parseability).
2252 prtime(timestruc_t st
, int width
, int old
)
2257 starttime
= st
.tv_sec
;
2258 if (st
.tv_nsec
> 500000000)
2260 if ((now
.tv_sec
- starttime
) >= 24*60*60) {
2261 (void) strftime(sttim
, sizeof (sttim
), old
?
2264 * This time format is used by STIME field when -f option
2265 * is specified. Used for processes that begun more than
2268 dcgettext(NULL
, "%b %d", LC_TIME
) :
2271 * This time format is used by STIME field when -o option
2272 * is specified. Used for processes that begun more than
2275 dcgettext(NULL
, "%b_%d", LC_TIME
), localtime(&starttime
));
2279 * This time format is used by STIME field when -f or -o option
2280 * is specified. Used for processes that begun less than
2283 (void) strftime(sttim
, sizeof (sttim
),
2284 dcgettext(NULL
, "%H:%M:%S", LC_TIME
),
2285 localtime(&starttime
));
2287 (void) printf("%*.*s", width
, width
, sttim
);
2291 przom(psinfo_t
*psinfo
)
2295 char zonename
[ZONENAME_MAX
];
2298 * All fields before 'PID' are printed with a trailing space as a
2299 * spearator, rather than keeping track of which column is first. All
2300 * other fields are printed with a leading space.
2302 if (lflg
) { /* F S */
2304 (void) printf("%2x ", psinfo
->pr_flag
& 0377); /* F */
2305 (void) printf("%c ", psinfo
->pr_lwp
.pr_sname
); /* S */
2308 if (getzonenamebyid(psinfo
->pr_zoneid
, zonename
,
2309 sizeof (zonename
)) < 0) {
2310 if (snprintf(NULL
, 0, "%d",
2311 ((int)psinfo
->pr_zoneid
)) > 7)
2312 (void) printf(" %6.6d%c ",
2313 ((int)psinfo
->pr_zoneid
), '*');
2315 (void) printf(" %7.7d ",
2316 ((int)psinfo
->pr_zoneid
));
2320 nw
= mbstowcs(NULL
, zonename
, 0);
2321 if (nw
== (size_t)-1)
2322 (void) printf("%8.8s ", "ERROR");
2324 (void) wprintf(L
"%7.7s%c ", zonename
, '*');
2326 (void) wprintf(L
"%8.8s ", zonename
);
2330 /* Display home lgroup */
2331 (void) printf(" %6d", (int)psinfo
->pr_lwp
.pr_lgrp
); /* LGRP */
2334 if ((pwd
= getpwuid(psinfo
->pr_euid
)) != NULL
) {
2337 nw
= mbstowcs(NULL
, pwd
->pw_name
, 0);
2338 if (nw
== (size_t)-1)
2339 (void) printf("%8.8s ", "ERROR");
2341 (void) wprintf(L
"%7.7s%c ", pwd
->pw_name
, '*');
2343 (void) wprintf(L
"%8.8s ", pwd
->pw_name
);
2345 if (snprintf(NULL
, 0, "%u",
2346 (psinfo
->pr_euid
)) > 7)
2347 (void) printf(" %6.6u%c ", psinfo
->pr_euid
,
2350 (void) printf(" %7.7u ", psinfo
->pr_euid
);
2353 if (snprintf(NULL
, 0, "%u", (psinfo
->pr_euid
)) > 6)
2354 (void) printf("%5.5u%c ", psinfo
->pr_euid
, '*');
2356 (void) printf("%6u ", psinfo
->pr_euid
);
2359 (void) printf("%*d", pidwidth
, (int)psinfo
->pr_pid
); /* PID */
2361 (void) printf(" %*d", pidwidth
,
2362 (int)psinfo
->pr_ppid
); /* PPID */
2365 (void) printf(" %*d", pidwidth
,
2366 (int)psinfo
->pr_pgid
); /* PGID */
2367 (void) printf(" %*d", pidwidth
,
2368 (int)psinfo
->pr_sid
); /* SID */
2372 (void) printf(" %5d", 0); /* LWP */
2374 (void) printf(" -"); /* PSR */
2376 (void) printf(" %5d", 0); /* NLWP */
2379 (void) printf(" %4s", "-"); /* zombies have no class */
2380 (void) printf(" %3d", psinfo
->pr_lwp
.pr_pri
); /* PRI */
2381 } else if (lflg
|| fflg
) {
2382 (void) printf(" %3d", psinfo
->pr_lwp
.pr_cpu
& 0377); /* C */
2384 (void) printf(" %3d %2s",
2385 psinfo
->pr_lwp
.pr_oldpri
, "-"); /* PRI NI */
2388 if (yflg
) /* RSS SZ WCHAN */
2389 (void) printf(" %5d %6d %8s", 0, 0, "-");
2390 else /* ADDR SZ WCHAN */
2391 (void) printf(" %8s %6d %8s", "-", 0, "-");
2394 int width
= fname
[F_STIME
].width
;
2395 (void) printf(" %*.*s", width
, width
, "-"); /* STIME */
2397 (void) printf(" %-8.14s", "?"); /* TTY */
2399 tm
= psinfo
->pr_time
.tv_sec
;
2400 if (psinfo
->pr_time
.tv_nsec
> 500000000)
2402 (void) printf(" %4ld:%.2ld", tm
/ 60, tm
% 60); /* TIME */
2403 (void) printf(" <defunct>\n");
2407 * Function to compute the number of printable bytes in a multibyte
2408 * command string ("internationalization").
2411 namencnt(char *cmd
, int csisize
, int scrsize
)
2413 int csiwcnt
= 0, scrwcnt
= 0;
2418 while (*cmd
!= '\0') {
2419 if ((len
= csisize
- csiwcnt
) > (int)MB_CUR_MAX
)
2421 if ((ncsisz
= mbtowc(&wchar
, cmd
, len
)) < 0)
2422 return (8); /* default to use for illegal chars */
2423 if ((nscrsz
= wcwidth(wchar
)) <= 0)
2425 if (csiwcnt
+ ncsisz
> csisize
|| scrwcnt
+ nscrsz
> scrsize
)
2437 static char buf
[32];
2438 char *str
= strerror(err
);
2441 (void) snprintf(str
= buf
, sizeof (buf
), "Errno #%d", err
);
2446 /* If allocation fails, die */
2448 Realloc(void *ptr
, size_t size
)
2450 ptr
= realloc(ptr
, size
);
2452 (void) fprintf(stderr
, gettext("ps: no memory\n"));
2459 delta_secs(const timestruc_t
*start
)
2461 time_t seconds
= now
.tv_sec
- start
->tv_sec
;
2462 long nanosecs
= now
.tv_usec
* 1000 - start
->tv_nsec
;
2464 if (nanosecs
>= (NANOSEC
/ 2))
2466 else if (nanosecs
< -(NANOSEC
/ 2))
2473 * Returns the following:
2476 * EINVAL Invalid number
2477 * ERANGE Value exceeds (min, max) range
2480 str2id(const char *p
, pid_t
*val
, long min
, long max
)
2487 number
= strtol(p
, &q
, 10);
2489 if (errno
!= 0 || q
== p
|| *q
!= '\0') {
2490 if ((error
= errno
) == 0) {
2492 * strtol() can fail without setting errno, or it can
2493 * set it to EINVAL or ERANGE. In the case errno is
2494 * still zero, return EINVAL.
2498 } else if (number
< min
|| number
> max
) {
2510 * Returns the following:
2513 * EINVAL Invalid number
2514 * ERANGE Value exceeds (min, max) range
2517 str2uid(const char *p
, uid_t
*val
, unsigned long min
, unsigned long max
)
2520 unsigned long number
;
2524 number
= strtoul(p
, &q
, 10);
2526 if (errno
!= 0 || q
== p
|| *q
!= '\0') {
2527 if ((error
= errno
) == 0) {
2529 * strtoul() can fail without setting errno, or it can
2530 * set it to EINVAL or ERANGE. In the case errno is
2531 * still zero, return EINVAL.
2535 } else if (number
< min
|| number
> max
) {
2547 pidcmp(const void *p1
, const void *p2
)
2549 pid_t i
= *((pid_t
*)p1
);
2550 pid_t j
= *((pid_t
*)p2
);