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) 2012, Joyent, Inc. All rights reserved.
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_NLWP
, /* number of lwps */
112 F_OPRI
, /* old priority (obsolete) */
113 F_PRI
, /* new priority */
114 F_F
, /* process flags */
115 F_S
, /* letter indicating the state */
116 F_C
, /* processor utilization (obsolete) */
117 F_PCPU
, /* percent of recently used cpu time */
118 F_PMEM
, /* percent of physical memory used (rss) */
119 F_OSZ
, /* virtual size of the process in pages */
120 F_VSZ
, /* virtual size of the process in kilobytes */
121 F_RSS
, /* resident set size of the process in kilobytes */
122 F_NICE
, /* "nice" value of the process */
123 F_CLASS
, /* scheduler class */
124 F_STIME
, /* start time of the process, hh:mm:ss or Month Day */
125 F_ETIME
, /* elapsed time of the process, [[dd-]hh:]mm:ss */
126 F_TIME
, /* cpu time of the process, [[dd-]hh:]mm:ss */
127 F_TTY
, /* name of the controlling terminal */
128 F_ADDR
, /* address of the process (obsolete) */
129 F_WCHAN
, /* wait channel (sleep condition variable) */
130 F_FNAME
, /* file name of command */
131 F_COMM
, /* name of command (argv[0] value) */
132 F_ARGS
, /* name of command plus all its arguments */
133 F_TASKID
, /* task id */
134 F_PROJID
, /* project id */
135 F_PROJECT
, /* project name of the process */
136 F_PSET
, /* bound processor set */
137 F_ZONE
, /* zone name */
138 F_ZONEID
, /* zone id */
139 F_CTID
, /* process contract id */
140 F_LGRP
, /* process home lgroup */
141 F_DMODEL
/* process data model */
145 struct field
*next
; /* linked list */
146 int fname
; /* field index */
147 const char *header
; /* header to use */
148 int width
; /* width of field */
151 static struct field
*fields
= NULL
; /* fields selected via -o */
152 static struct field
*last_field
= NULL
;
153 static int do_header
= 0;
154 static struct timeval now
;
156 /* array of defined fields, in fname order */
164 static struct def_field fname
[] = {
165 /* fname header width minwidth */
166 { "user", "USER", 8, 8 },
167 { "ruser", "RUSER", 8, 8 },
168 { "group", "GROUP", 8, 8 },
169 { "rgroup", "RGROUP", 8, 8 },
170 { "uid", "UID", 5, 5 },
171 { "ruid", "RUID", 5, 5 },
172 { "gid", "GID", 5, 5 },
173 { "rgid", "RGID", 5, 5 },
174 { "pid", "PID", 5, 5 },
175 { "ppid", "PPID", 5, 5 },
176 { "pgid", "PGID", 5, 5 },
177 { "sid", "SID", 5, 5 },
178 { "psr", "PSR", 3, 2 },
179 { "lwp", "LWP", 6, 2 },
180 { "nlwp", "NLWP", 4, 2 },
181 { "opri", "PRI", 3, 2 },
182 { "pri", "PRI", 3, 2 },
186 { "pcpu", "%CPU", 4, 4 },
187 { "pmem", "%MEM", 4, 4 },
188 { "osz", "SZ", 4, 4 },
189 { "vsz", "VSZ", 4, 4 },
190 { "rss", "RSS", 4, 4 },
191 { "nice", "NI", 2, 2 },
192 { "class", "CLS", 4, 2 },
193 { "stime", "STIME", 8, 8 },
194 { "etime", "ELAPSED", 11, 7 },
195 { "time", "TIME", 11, 5 },
196 { "tty", "TT", 7, 7 },
198 { "addr", "ADDR", 16, 8 },
199 { "wchan", "WCHAN", 16, 8 },
201 { "addr", "ADDR", 8, 8 },
202 { "wchan", "WCHAN", 8, 8 },
204 { "fname", "COMMAND", 8, 8 },
205 { "comm", "COMMAND", 80, 8 },
206 { "args", "COMMAND", 80, 80 },
207 { "taskid", "TASKID", 5, 5 },
208 { "projid", "PROJID", 5, 5 },
209 { "project", "PROJECT", 8, 8 },
210 { "pset", "PSET", 3, 3 },
211 { "zone", "ZONE", 8, 8 },
212 { "zoneid", "ZONEID", 5, 5 },
213 { "ctid", "CTID", 5, 5 },
214 { "lgrp", "LGRP", 4, 2 },
215 { "dmodel", "DMODEL", 6, 6 },
218 #define NFIELDS (sizeof (fname) / sizeof (fname[0]))
220 static int retcode
= 1;
243 static uid_t tuid
= (uid_t
)-1;
246 static int ndev
; /* number of devices */
247 static int maxdev
; /* number of devl structures allocated */
251 static struct devl
{ /* device list */
252 char dname
[DNSIZE
]; /* device name */
253 dev_t ddev
; /* device number */
259 } *tty
= NULL
; /* for t option */
260 static size_t ttysz
= 0;
263 static pid_t
*pid
= NULL
; /* for p option */
264 static size_t pidsz
= 0;
265 static size_t npid
= 0;
267 static int *lgrps
= NULL
; /* list of lgroup IDs for for h option */
268 static size_t lgrps_size
= 0; /* size of the lgrps list */
269 static size_t nlgrps
= 0; /* number elements in the list */
271 /* Maximum possible lgroup ID value */
272 #define MAX_LGRP_ID 256
274 static pid_t
*grpid
= NULL
; /* for g option */
275 static size_t grpidsz
= 0;
276 static int ngrpid
= 0;
278 static pid_t
*sessid
= NULL
; /* for s option */
279 static size_t sessidsz
= 0;
280 static int nsessid
= 0;
282 static zoneid_t
*zoneid
= NULL
; /* for z option */
283 static size_t zoneidsz
= 0;
284 static int nzoneid
= 0;
286 static int kbytes_per_page
;
289 static char *procdir
= "/proc"; /* standard /proc directory */
291 static struct ughead euid_tbl
; /* table to store selected euid's */
292 static struct ughead ruid_tbl
; /* table to store selected real uid's */
293 static struct ughead egid_tbl
; /* table to store selected egid's */
294 static struct ughead rgid_tbl
; /* table to store selected real gid's */
295 static prheader_t
*lpsinfobuf
; /* buffer to contain lpsinfo */
296 static size_t lpbufsize
;
299 * This constant defines the sentinal number of process IDs below which we
300 * only examine individual entries in /proc rather than scanning through
301 * /proc. This optimization is a huge win in the common case.
303 #define PTHRESHOLD 40
305 static void usage(void);
306 static char *getarg(char **);
307 static char *parse_format(char *);
308 static char *gettty(psinfo_t
*);
309 static int prfind(int, psinfo_t
*, char **);
310 static void prcom(psinfo_t
*, char *);
311 static void prtpct(ushort_t
, int);
312 static void print_time(time_t, int);
313 static void print_field(psinfo_t
*, struct field
*, const char *);
314 static void print_zombie_field(psinfo_t
*, struct field
*, const char *);
315 static void pr_fields(psinfo_t
*, const char *,
316 void (*print_fld
)(psinfo_t
*, struct field
*, const char *));
317 static int search(pid_t
*, int, pid_t
);
318 static void add_ugentry(struct ughead
*, char *);
319 static int uconv(struct ughead
*);
320 static int gconv(struct ughead
*);
321 static int ugfind(id_t
, struct ughead
*);
322 static void prtime(timestruc_t
, int, int);
323 static void przom(psinfo_t
*);
324 static int namencnt(char *, int, int);
325 static char *err_string(int);
326 static int print_proc(char *pname
);
327 static time_t delta_secs(const timestruc_t
*);
328 static int str2id(const char *, pid_t
*, long, long);
329 static int str2uid(const char *, uid_t
*, unsigned long, unsigned long);
330 static void *Realloc(void *, size_t);
331 static int pidcmp(const void *p1
, const void *p2
);
334 main(int argc
, char **argv
)
341 int pgerrflg
= 0; /* err flg: non-numeric arg w/p & g options */
344 struct dirent
*dentp
;
348 char loc_stime_str
[32];
350 (void) setlocale(LC_ALL
, "");
351 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
352 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
354 (void) textdomain(TEXT_DOMAIN
);
356 (void) memset(&euid_tbl
, 0, sizeof (euid_tbl
));
357 (void) memset(&ruid_tbl
, 0, sizeof (ruid_tbl
));
358 (void) memset(&egid_tbl
, 0, sizeof (egid_tbl
));
359 (void) memset(&rgid_tbl
, 0, sizeof (rgid_tbl
));
361 kbytes_per_page
= sysconf(_SC_PAGESIZE
) / 1024;
363 (void) gettimeofday(&now
, NULL
);
366 * calculate width of pid fields based on configured MAXPID
367 * (must be at least 5 to retain output format compatibility)
369 id
= maxpid
= (pid_t
)sysconf(_SC_MAXPID
);
371 while ((id
/= 10) > 0)
373 pidwidth
= pidwidth
< 5 ? 5 : pidwidth
;
375 fname
[F_PID
].width
= fname
[F_PPID
].width
= pidwidth
;
376 fname
[F_PGID
].width
= fname
[F_SID
].width
= pidwidth
;
380 * Specify the printf format with width and precision for
383 len
= snprintf(loc_stime_str
, sizeof (loc_stime_str
),
384 dcgettext(NULL
, "%8.8s", LC_TIME
), "STIME");
385 if (len
>= sizeof (loc_stime_str
))
386 len
= sizeof (loc_stime_str
) - 1;
388 fname
[F_STIME
].width
= fname
[F_STIME
].minwidth
= len
;
390 while ((c
= getopt(argc
, argv
, "jlfceAadLPWyZHh:t:p:g:u:U:G:n:s:o:z:"))
393 case 'H': /* Show home lgroups */
398 * Show processes/threads with given home lgroups
406 * Get all IDs in the list, verify for
407 * correctness and place in lgrps array.
410 /* Convert string to integer */
411 ret
= str2id(parg
, (pid_t
*)&id
, 0,
413 /* Complain if ID didn't parse correctly */
416 (void) fprintf(stderr
,
417 gettext("ps: %s "), parg
);
419 (void) fprintf(stderr
,
420 gettext("is an invalid "
421 "non-numeric argument"));
423 (void) fprintf(stderr
,
424 gettext("exceeds valid "
426 (void) fprintf(stderr
,
427 gettext(" for -h option\n"));
431 /* Extend lgrps array if needed */
432 if (nlgrps
== lgrps_size
) {
433 /* Double the size of the lgrps array */
437 lgrps
= Realloc(lgrps
,
438 lgrps_size
* sizeof (int));
440 /* place the id in the lgrps table */
441 lgrps
[nlgrps
++] = id
;
444 case 'l': /* long listing */
447 case 'f': /* full listing */
455 * Format output to reflect scheduler changes:
456 * high numbers for high priorities and don't
457 * print nice or p_cpu values. 'c' option only
458 * effective when used with 'l' or 'f' options.
462 case 'A': /* list every process */
463 case 'e': /* (obsolete) list every process */
465 tflg
= Gflg
= Uflg
= uflg
= pflg
= gflg
= sflg
= 0;
470 * Same as 'e' except no session group leaders
471 * and no non-terminal processes.
475 case 'd': /* same as e except no session leaders */
478 case 'L': /* show lwps */
481 case 'P': /* show bound processor */
484 case 'W': /* truncate long names */
487 case 'y': /* omit F & ADDR, report RSS & SZ in Kby */
490 case 'n': /* no longer needed; retain as no-op */
491 (void) fprintf(stderr
,
492 gettext("ps: warning: -n option ignored\n"));
494 case 't': /* terminals */
499 char nambuf
[TSZ
+6]; /* for "/dev/" + '\0' */
502 p
= Realloc(NULL
, TSZ
+1); /* for '\0' */
503 /* zero the buffer before using it */
506 if (isdigit(*parg
)) {
507 (void) strcpy(p
, "tty");
510 (void) strncat(p
, parg
, size
);
512 if ((ttysz
*= 2) == 0)
515 (ttysz
+ 1) * sizeof (struct tty
));
517 tty
[ntty
].tdev
= PRNODEV
;
518 (void) strcpy(nambuf
, "/dev/");
519 (void) strcat(nambuf
, p
);
520 if (stat64(nambuf
, &s
) == 0)
521 tty
[ntty
].tdev
= s
.st_rdev
;
522 tty
[ntty
++].tname
= p
;
525 case 'p': /* proc ids */
532 if ((ret
= str2id(parg
, &id
, 0, maxpid
)) != 0) {
534 (void) fprintf(stderr
,
535 gettext("ps: %s "), parg
);
537 (void) fprintf(stderr
,
538 gettext("is an invalid "
539 "non-numeric argument"));
541 (void) fprintf(stderr
,
542 gettext("exceeds valid "
544 (void) fprintf(stderr
,
545 gettext(" for -p option\n"));
550 if ((pidsz
*= 2) == 0)
553 pidsz
* sizeof (pid_t
));
558 case 's': /* session */
565 if ((ret
= str2id(parg
, &id
, 0, maxpid
)) != 0) {
567 (void) fprintf(stderr
,
568 gettext("ps: %s "), parg
);
570 (void) fprintf(stderr
,
571 gettext("is an invalid "
572 "non-numeric argument"));
574 (void) fprintf(stderr
,
575 gettext("exceeds valid "
577 (void) fprintf(stderr
,
578 gettext(" for -s option\n"));
582 if (nsessid
== sessidsz
) {
583 if ((sessidsz
*= 2) == 0)
585 sessid
= Realloc(sessid
,
586 sessidsz
* sizeof (pid_t
));
588 sessid
[nsessid
++] = id
;
591 case 'g': /* proc group */
598 if ((ret
= str2id(parg
, &id
, 0, maxpid
)) != 0) {
600 (void) fprintf(stderr
,
601 gettext("ps: %s "), parg
);
603 (void) fprintf(stderr
,
604 gettext("is an invalid "
605 "non-numeric argument"));
607 (void) fprintf(stderr
,
608 gettext("exceeds valid "
610 (void) fprintf(stderr
,
611 gettext(" for -g option\n"));
615 if (ngrpid
== grpidsz
) {
616 if ((grpidsz
*= 2) == 0)
618 grpid
= Realloc(grpid
,
619 grpidsz
* sizeof (pid_t
));
621 grpid
[ngrpid
++] = id
;
624 case 'u': /* effective user name or number */
629 add_ugentry(&euid_tbl
, parg
);
632 case 'U': /* real user name or number */
637 add_ugentry(&ruid_tbl
, parg
);
640 case 'G': /* real group name or number */
645 add_ugentry(&rgid_tbl
, parg
);
648 case 'o': /* output format */
650 while ((p
= parse_format(p
)) != NULL
)
653 case 'z': /* zone name or number */
660 if (zone_get_id(parg
, &id
) != 0) {
662 (void) fprintf(stderr
,
663 gettext("ps: unknown zone %s\n"),
668 if (nzoneid
== zoneidsz
) {
669 if ((zoneidsz
*= 2) == 0)
671 zoneid
= Realloc(zoneid
,
672 zoneidsz
* sizeof (zoneid_t
));
674 zoneid
[nzoneid
++] = id
;
677 case 'Z': /* show zone name */
680 default: /* error on ? */
685 if (errflg
|| optind
< argc
|| pgerrflg
)
689 tty
[ntty
].tname
= NULL
;
691 * If an appropriate option has not been specified, use the
692 * current terminal and effective uid as the default.
694 if (!(aflg
|Aflg
|dflg
|Gflg
|hflg
|Uflg
|uflg
|tflg
|pflg
|gflg
|sflg
|zflg
)) {
700 /* get our own controlling tty name using /proc */
701 (void) snprintf(pname
, sizeof (pname
),
702 "%s/self/psinfo", procdir
);
703 if ((procfd
= open(pname
, O_RDONLY
)) < 0 ||
704 read(procfd
, (char *)&info
, sizeof (info
)) < 0 ||
705 info
.pr_ttydev
== PRNODEV
) {
706 (void) fprintf(stderr
,
707 gettext("ps: no controlling terminal\n"));
710 (void) close(procfd
);
713 name
= gettty(&info
);
715 (void) fprintf(stderr
,
716 gettext("ps: can't find controlling terminal\n"));
720 if ((ttysz
*= 2) == 0)
722 tty
= Realloc(tty
, (ttysz
+ 1) * sizeof (struct tty
));
724 tty
[ntty
].tdev
= info
.pr_ttydev
;
725 tty
[ntty
++].tname
= name
;
726 tty
[ntty
].tname
= NULL
;
731 Gflg
= Uflg
= uflg
= pflg
= sflg
= gflg
= aflg
= dflg
= 0;
734 if (Aflg
| aflg
| dflg
)
737 i
= 0; /* prepare to exit on name lookup errors */
738 i
+= uconv(&euid_tbl
);
739 i
+= uconv(&ruid_tbl
);
740 i
+= gconv(&egid_tbl
);
741 i
+= gconv(&rgid_tbl
);
745 /* allocate a buffer for lwpsinfo structures */
747 if (Lflg
&& (lpsinfobuf
= malloc(lpbufsize
)) == NULL
) {
748 (void) fprintf(stderr
,
749 gettext("ps: no memory\n"));
753 if (fields
) { /* print user-specified header */
757 for (f
= fields
; f
!= NULL
; f
= f
->next
) {
762 (void) printf("%-*s",
763 f
->width
, f
->header
);
769 * Print these headers full width
770 * unless they appear at the end.
772 if (f
->next
!= NULL
) {
773 (void) printf("%-*s",
774 f
->width
, f
->header
);
782 f
->width
, f
->header
);
788 } else { /* print standard header */
790 * All fields before 'PID' are printed with a trailing space
791 * as a separator and that is how we print the headers too.
797 (void) printf(" F S ");
800 (void) printf(" ZONE ");
802 (void) printf(" UID ");
804 (void) printf(" UID ");
806 (void) printf("%*s", pidwidth
, "PID");
808 (void) printf(" %*s", pidwidth
, "PPID");
810 (void) printf(" %*s %*s", pidwidth
, "PGID",
813 (void) printf(" LWP");
815 (void) printf(" PSR");
817 (void) printf(" NLWP");
819 (void) printf(" CLS PRI");
820 else if (lflg
|| fflg
) {
823 (void) printf(" PRI NI");
827 (void) printf(" RSS SZ WCHAN");
829 (void) printf(" ADDR SZ WCHAN");
832 (void) printf(" %s", loc_stime_str
);
834 (void) printf(" LGRP");
836 (void) printf(" TTY LTIME CMD\n");
838 (void) printf(" TTY TIME CMD\n");
842 if (pflg
&& !(aflg
|Aflg
|dflg
|Gflg
|Uflg
|uflg
|hflg
|tflg
|gflg
|sflg
|zflg
) &&
843 npid
<= PTHRESHOLD
) {
845 * If we are looking at specific processes go straight
846 * to their /proc entries and don't scan /proc.
850 (void) qsort(pid
, npid
, sizeof (pid_t
), pidcmp
);
851 for (i
= 0; i
< npid
; i
++) {
854 if (i
>= 1 && pid
[i
] == pid
[i
- 1])
856 (void) sprintf(pname
, "%d", (int)pid
[i
]);
857 if (print_proc(pname
) == 0)
862 * Determine which processes to print info about by searching
863 * the /proc directory and looking at each process.
865 if ((dirp
= opendir(procdir
)) == NULL
) {
866 (void) fprintf(stderr
,
867 gettext("ps: cannot open PROC directory %s\n"),
872 /* for each active process --- */
873 while ((dentp
= readdir(dirp
)) != NULL
) {
874 if (dentp
->d_name
[0] == '.') /* skip . and .. */
876 if (print_proc(dentp
->d_name
) == 0)
880 (void) closedir(dirp
);
887 print_proc(char *pid_name
)
889 char pname
[PATH_MAX
];
892 int procfd
; /* filedescriptor for /proc/nnnnn/psinfo */
893 char *tp
; /* ptr to ttyname, if any */
894 psinfo_t info
; /* process information from /proc */
895 lwpsinfo_t
*lwpsinfo
; /* array of lwpsinfo structs */
897 pdlen
= snprintf(pname
, sizeof (pname
), "%s/%s/", procdir
, pid_name
);
898 if (pdlen
>= sizeof (pname
) - 10)
901 (void) strcpy(&pname
[pdlen
], "psinfo");
902 if ((procfd
= open(pname
, O_RDONLY
)) == -1) {
903 /* Process may have exited meanwhile. */
907 * Get the info structure for the process and close quickly.
909 if (read(procfd
, (char *)&info
, sizeof (info
)) < 0) {
912 (void) close(procfd
);
913 if (saverr
== EAGAIN
)
915 if (saverr
!= ENOENT
)
916 (void) fprintf(stderr
,
917 gettext("ps: read() on %s: %s\n"),
918 pname
, err_string(saverr
));
921 (void) close(procfd
);
924 if (info
.pr_lwp
.pr_state
== 0) /* can't happen? */
928 * Omit session group leaders for 'a' and 'd' options.
930 if ((info
.pr_pid
== info
.pr_sid
) && (dflg
|| aflg
))
934 else if (pflg
&& search(pid
, npid
, info
.pr_pid
))
935 found
++; /* ppid in p option arg list */
936 else if (uflg
&& ugfind((id_t
)info
.pr_euid
, &euid_tbl
))
937 found
++; /* puid in u option arg list */
938 else if (Uflg
&& ugfind((id_t
)info
.pr_uid
, &ruid_tbl
))
939 found
++; /* puid in U option arg list */
941 else if (gflg
&& ugfind((id_t
)info
.pr_egid
, &egid_tbl
))
942 found
++; /* pgid in g option arg list */
944 else if (Gflg
&& ugfind((id_t
)info
.pr_gid
, &rgid_tbl
))
945 found
++; /* pgid in G option arg list */
946 else if (gflg
&& search(grpid
, ngrpid
, info
.pr_pgid
))
947 found
++; /* grpid in g option arg list */
948 else if (sflg
&& search(sessid
, nsessid
, info
.pr_sid
))
949 found
++; /* sessid in s option arg list */
950 else if (zflg
&& search(zoneid
, nzoneid
, info
.pr_zoneid
))
951 found
++; /* zoneid in z option arg list */
952 else if (hflg
&& search((pid_t
*)lgrps
, nlgrps
, info
.pr_lwp
.pr_lgrp
))
953 found
++; /* home lgroup in h option arg list */
954 if (!found
&& !tflg
&& !aflg
)
956 if (!prfind(found
, &info
, &tp
))
958 if (Lflg
&& (info
.pr_nlwp
+ info
.pr_nzomb
) > 1) {
961 (void) strcpy(&pname
[pdlen
], "lpsinfo");
962 if ((procfd
= open(pname
, O_RDONLY
)) == -1)
965 * Get the info structures for the lwps.
967 prsz
= read(procfd
, lpsinfobuf
, lpbufsize
);
971 (void) close(procfd
);
972 if (saverr
== EAGAIN
)
974 if (saverr
!= ENOENT
)
975 (void) fprintf(stderr
,
976 gettext("ps: read() on %s: %s\n"),
977 pname
, err_string(saverr
));
980 (void) close(procfd
);
981 if (prsz
== lpbufsize
) {
983 * buffer overflow. Realloc new buffer.
984 * Error handling is done in Realloc().
987 lpsinfobuf
= Realloc(lpsinfobuf
, lpbufsize
);
990 if (lpsinfobuf
->pr_nent
!= (info
.pr_nlwp
+ info
.pr_nzomb
))
992 lwpsinfo
= (lwpsinfo_t
*)(lpsinfobuf
+ 1);
994 if (!Lflg
|| (info
.pr_nlwp
+ info
.pr_nzomb
) <= 1) {
1000 info
.pr_lwp
= *lwpsinfo
;
1002 /* LINTED improper alignment */
1003 lwpsinfo
= (lwpsinfo_t
*)((char *)lwpsinfo
+
1004 lpsinfobuf
->pr_entsize
);
1005 } while (++nlwp
< lpsinfobuf
->pr_nent
);
1011 field_cmp(const void *l
, const void *r
)
1013 struct def_field
*lhs
= *((struct def_field
**)l
);
1014 struct def_field
*rhs
= *((struct def_field
**)r
);
1016 return (strcmp(lhs
->fname
, rhs
->fname
));
1020 usage(void) /* print usage message and quit */
1022 struct def_field
*df
, *sorted
[NFIELDS
];
1023 int pos
= 80, i
= 0;
1025 static char usage1
[] =
1026 "ps [ -aAdefHlcjLPWyZ ] [ -o format ] [ -t termlist ]";
1027 static char usage2
[] =
1028 "\t[ -u userlist ] [ -U userlist ] [ -G grouplist ]";
1029 static char usage3
[] =
1030 "\t[ -p proclist ] [ -g pgrplist ] [ -s sidlist ]";
1031 static char usage4
[] =
1032 "\t[ -z zonelist ] [-h lgrplist]";
1033 static char usage5
[] =
1034 " 'format' is one or more of:";
1036 (void) fprintf(stderr
,
1037 gettext("usage: %s\n%s\n%s\n%s\n%s"),
1038 gettext(usage1
), gettext(usage2
), gettext(usage3
),
1039 gettext(usage4
), gettext(usage5
));
1042 * Now print out the possible output formats such that they neatly fit
1043 * into eighty columns. Note that the fact that we are determining
1044 * this output programmatically means that a gettext() is impossible --
1045 * but it would be a mistake to localize the output formats anyway as
1046 * they are tokens for input, not output themselves.
1048 for (df
= &fname
[0]; df
< &fname
[NFIELDS
]; df
++)
1051 (void) qsort(sorted
, NFIELDS
, sizeof (void *), field_cmp
);
1053 for (i
= 0; i
< NFIELDS
; i
++) {
1054 if (pos
+ strlen((df
= sorted
[i
])->fname
) + 1 >= 80) {
1055 (void) fprintf(stderr
, "\n\t");
1059 (void) fprintf(stderr
, "%s%s", pos
> 8 ? " " : "", df
->fname
);
1060 pos
+= strlen(df
->fname
) + 1;
1063 (void) fprintf(stderr
, "\n");
1069 * getarg() finds the next argument in list and copies arg into argbuf.
1070 * p1 first pts to arg passed back from getopt routine. p1 is then
1071 * bumped to next character that is not a comma or blank -- p1 NULL
1072 * indicates end of list.
1077 static char argbuf
[ARGSIZ
];
1079 char *parga
= argbuf
;
1082 while ((c
= *p1
) != '\0' && (c
== ',' || isspace(c
)))
1085 while ((c
= *p1
) != '\0' && c
!= ',' && !isspace(c
)) {
1086 if (parga
< argbuf
+ ARGSIZ
- 1)
1092 while ((c
= *p1
) != '\0' && (c
== ',' || isspace(c
)))
1101 * parse_format() takes the argument to the -o option,
1102 * sets up the next output field structure, and returns
1103 * a pointer to any further output field specifier(s).
1104 * As a side-effect, it increments errflg if encounters a format error.
1107 parse_format(char *arg
)
1111 char *header
= NULL
;
1113 struct def_field
*df
;
1116 while ((c
= *arg
) != '\0' && (c
== ',' || isspace(c
)))
1121 arg
= strpbrk(arg
, " \t\r\v\f\n,=");
1130 width
= strlen(header
);
1132 while (s
> header
&& isspace(*--s
))
1134 while (isspace(*header
))
1138 for (df
= &fname
[0]; df
< &fname
[NFIELDS
]; df
++)
1139 if (strcmp(name
, df
->fname
) == 0) {
1140 if (strcmp(name
, "lwp") == 0)
1144 if (df
>= &fname
[NFIELDS
]) {
1145 (void) fprintf(stderr
,
1146 gettext("ps: unknown output format: -o %s\n"),
1151 if ((f
= malloc(sizeof (*f
))) == NULL
) {
1152 (void) fprintf(stderr
,
1153 gettext("ps: malloc() for output format failed, %s\n"),
1158 f
->fname
= df
- &fname
[0];
1159 f
->header
= header
? header
: df
->header
;
1162 if (*f
->header
!= '\0')
1164 f
->width
= max(width
, df
->minwidth
);
1167 fields
= last_field
= f
;
1169 last_field
->next
= f
;
1177 devlookup(dev_t ddev
)
1182 for (dp
= devl
, i
= 0; i
< ndev
; dp
++, i
++) {
1183 if (dp
->ddev
== ddev
)
1190 devadd(char *name
, dev_t ddev
)
1195 if (ndev
== maxdev
) {
1197 devl
= Realloc(devl
, maxdev
* sizeof (struct devl
));
1203 (void) strcpy(dp
->dname
, "??");
1207 leng
= strlen(name
);
1208 /* Strip off /dev/ */
1209 if (leng
< DNSIZE
+ 4)
1210 (void) strcpy(dp
->dname
, &name
[5]);
1212 start
= leng
- DNSIZE
- 1;
1214 for (i
= start
; i
< leng
&& name
[i
] != '/'; i
++)
1217 (void) strncpy(dp
->dname
, &name
[start
], DNSIZE
);
1219 (void) strncpy(dp
->dname
, &name
[i
+1], DNSIZE
);
1225 * gettty returns the user's tty number or ? if none.
1228 gettty(psinfo_t
*psinfo
)
1230 extern char *_ttyname_dev(dev_t
, char *, size_t);
1231 static zoneid_t zid
= -1;
1232 char devname
[TTYNAME_MAX
];
1238 if (psinfo
->pr_ttydev
== PRNODEV
|| psinfo
->pr_zoneid
!= zid
)
1241 if ((retval
= devlookup(psinfo
->pr_ttydev
)) != NULL
)
1244 retval
= _ttyname_dev(psinfo
->pr_ttydev
, devname
, sizeof (devname
));
1246 return (devadd(retval
, psinfo
->pr_ttydev
));
1250 * Find the process's tty and return 1 if process is to be printed.
1253 prfind(int found
, psinfo_t
*psinfo
, char **tpp
)
1258 if (psinfo
->pr_nlwp
== 0) {
1259 /* process is a zombie */
1267 * Get current terminal. If none ("?") and 'a' is set, don't print
1268 * info. If 't' is set, check if term is in list of desired terminals
1269 * and print it if it is.
1271 tp
= gettty(psinfo
);
1272 if (aflg
&& *tp
== '?') {
1276 if (tflg
&& !found
) {
1279 for (ttyp
= tty
; ttyp
->tname
!= NULL
; ttyp
++) {
1281 * Look for a name match
1283 if (strcmp(tp
, ttyp
->tname
) == 0) {
1288 * Look for same device under different names.
1290 if ((other
== NULL
) &&
1291 (ttyp
->tdev
!= PRNODEV
) &&
1292 (psinfo
->pr_ttydev
== ttyp
->tdev
))
1293 other
= ttyp
->tname
;
1295 if (!match
&& (other
!= NULL
)) {
1297 * found under a different name
1302 if (!match
|| (tuid
!= (uid_t
)-1 && tuid
!= psinfo
->pr_euid
)) {
1304 * not found OR not matching euid
1315 * Print info about the process.
1318 prcom(psinfo_t
*psinfo
, char *ttyp
)
1327 char zonename
[ZONENAME_MAX
];
1330 * If process is zombie, call zombie print routine and return.
1332 if (psinfo
->pr_nlwp
== 0) {
1334 pr_fields(psinfo
, ttyp
, print_zombie_field
);
1340 zombie_lwp
= (Lflg
&& psinfo
->pr_lwp
.pr_sname
== 'Z');
1343 * If user specified '-o format', print requested fields and return.
1345 if (fields
!= NULL
) {
1346 pr_fields(psinfo
, ttyp
, print_field
);
1351 * All fields before 'PID' are printed with a trailing space as a
1352 * separator, rather than keeping track of which column is first. All
1353 * other fields are printed with a leading space.
1357 (void) printf("%2x ", psinfo
->pr_flag
& 0377); /* F */
1358 (void) printf("%c ", psinfo
->pr_lwp
.pr_sname
); /* S */
1361 if (Zflg
) { /* ZONE */
1362 if (getzonenamebyid(psinfo
->pr_zoneid
, zonename
,
1363 sizeof (zonename
)) < 0) {
1364 if (snprintf(NULL
, 0, "%d",
1365 ((int)psinfo
->pr_zoneid
)) > 7)
1366 (void) printf(" %6.6d%c ",
1367 ((int)psinfo
->pr_zoneid
), '*');
1369 (void) printf(" %7.7d ",
1370 ((int)psinfo
->pr_zoneid
));
1374 nw
= mbstowcs(NULL
, zonename
, 0);
1375 if (nw
== (size_t)-1)
1376 (void) printf("%8.8s ", "ERROR");
1378 (void) wprintf(L
"%7.7s%c ", zonename
, '*');
1380 (void) wprintf(L
"%8.8s ", zonename
);
1384 if (fflg
) { /* UID */
1385 if ((pwd
= getpwuid(psinfo
->pr_euid
)) != NULL
) {
1388 nw
= mbstowcs(NULL
, pwd
->pw_name
, 0);
1389 if (nw
== (size_t)-1)
1390 (void) printf("%8.8s ", "ERROR");
1392 (void) wprintf(L
"%7.7s%c ", pwd
->pw_name
, '*');
1394 (void) wprintf(L
"%8.8s ", pwd
->pw_name
);
1396 if (snprintf(NULL
, 0, "%u",
1397 (psinfo
->pr_euid
)) > 7)
1398 (void) printf(" %6.6u%c ", psinfo
->pr_euid
,
1401 (void) printf(" %7.7u ", psinfo
->pr_euid
);
1404 if (snprintf(NULL
, 0, "%u", (psinfo
->pr_euid
)) > 6)
1405 (void) printf("%5.5u%c ", psinfo
->pr_euid
, '*');
1407 (void) printf("%6u ", psinfo
->pr_euid
);
1409 (void) printf("%*d", pidwidth
, (int)psinfo
->pr_pid
); /* PID */
1411 (void) printf(" %*d", pidwidth
,
1412 (int)psinfo
->pr_ppid
); /* PPID */
1414 (void) printf(" %*d", pidwidth
,
1415 (int)psinfo
->pr_pgid
); /* PGID */
1416 (void) printf(" %*d", pidwidth
,
1417 (int)psinfo
->pr_sid
); /* SID */
1420 (void) printf(" %5d", (int)psinfo
->pr_lwp
.pr_lwpid
); /* LWP */
1422 if (psinfo
->pr_lwp
.pr_bindpro
== PBIND_NONE
) /* PSR */
1423 (void) printf(" -");
1425 (void) printf(" %3d", psinfo
->pr_lwp
.pr_bindpro
);
1427 if (Lflg
&& fflg
) /* NLWP */
1428 (void) printf(" %5d", psinfo
->pr_nlwp
+ psinfo
->pr_nzomb
);
1430 if (zombie_lwp
) /* CLS */
1433 (void) printf(" %4s", psinfo
->pr_lwp
.pr_clname
);
1434 (void) printf(" %3d", psinfo
->pr_lwp
.pr_pri
); /* PRI */
1435 } else if (lflg
|| fflg
) {
1436 (void) printf(" %3d", psinfo
->pr_lwp
.pr_cpu
& 0377); /* C */
1437 if (lflg
) { /* PRI NI */
1439 * Print priorities the old way (lower numbers
1440 * mean higher priority) and print nice value
1441 * for time sharing procs.
1443 (void) printf(" %3d", psinfo
->pr_lwp
.pr_oldpri
);
1444 if (psinfo
->pr_lwp
.pr_oldpri
!= 0)
1445 (void) printf(" %2d", psinfo
->pr_lwp
.pr_nice
);
1447 (void) printf(" %2.2s",
1448 psinfo
->pr_lwp
.pr_clname
);
1453 if (psinfo
->pr_flag
& SSYS
) /* RSS */
1454 (void) printf(" 0");
1455 else if (psinfo
->pr_rssize
)
1456 (void) printf(" %5lu",
1457 (ulong_t
)psinfo
->pr_rssize
);
1459 (void) printf(" ?");
1460 if (psinfo
->pr_flag
& SSYS
) /* SZ */
1461 (void) printf(" 0");
1462 else if (psinfo
->pr_size
)
1463 (void) printf(" %6lu",
1464 (ulong_t
)psinfo
->pr_size
);
1466 (void) printf(" ?");
1469 if (psinfo
->pr_addr
) /* ADDR */
1470 (void) printf(" %8lx",
1471 (ulong_t
)psinfo
->pr_addr
);
1474 (void) printf(" ?");
1475 if (psinfo
->pr_flag
& SSYS
) /* SZ */
1476 (void) printf(" 0");
1477 else if (psinfo
->pr_size
)
1478 (void) printf(" %6lu",
1479 (ulong_t
)psinfo
->pr_size
/ kbytes_per_page
);
1481 (void) printf(" ?");
1483 if (psinfo
->pr_lwp
.pr_sname
!= 'S') /* WCHAN */
1486 else if (psinfo
->pr_lwp
.pr_wchan
)
1487 (void) printf(" %8lx",
1488 (ulong_t
)psinfo
->pr_lwp
.pr_wchan
);
1491 (void) printf(" ?");
1493 if (fflg
) { /* STIME */
1494 int width
= fname
[F_STIME
].width
;
1496 prtime(psinfo
->pr_lwp
.pr_start
, width
+ 1, 1);
1498 prtime(psinfo
->pr_start
, width
+ 1, 1);
1502 /* Display home lgroup */
1503 (void) printf(" %4d", (int)psinfo
->pr_lwp
.pr_lgrp
);
1506 (void) printf(" %-8.14s", ttyp
); /* TTY */
1508 tm
= psinfo
->pr_lwp
.pr_time
.tv_sec
;
1509 if (psinfo
->pr_lwp
.pr_time
.tv_nsec
> 500000000)
1512 tm
= psinfo
->pr_time
.tv_sec
;
1513 if (psinfo
->pr_time
.tv_nsec
> 500000000)
1516 (void) printf(" %4ld:%.2ld", tm
/ 60, tm
% 60); /* [L]TIME */
1519 (void) printf(" <defunct>\n");
1523 if (!fflg
) { /* CMD */
1524 wcnt
= namencnt(psinfo
->pr_fname
, 16, 8);
1525 (void) printf(" %.*s\n", wcnt
, psinfo
->pr_fname
);
1531 * PRARGSZ == length of cmd arg string.
1533 psinfo
->pr_psargs
[PRARGSZ
-1] = '\0';
1534 bytesleft
= PRARGSZ
;
1535 for (cp
= psinfo
->pr_psargs
; *cp
!= '\0'; cp
+= length
) {
1536 length
= mbtowc(&wchar
, cp
, MB_LEN_MAX
);
1539 if (length
< 0 || !iswprint(wchar
)) {
1542 if (bytesleft
<= length
) {
1546 /* omit the unprintable character */
1547 (void) memmove(cp
, cp
+length
, bytesleft
-length
);
1550 bytesleft
-= length
;
1552 wcnt
= namencnt(psinfo
->pr_psargs
, PRARGSZ
, lflg
? 35 : PRARGSZ
);
1553 (void) printf(" %.*s\n", wcnt
, psinfo
->pr_psargs
);
1557 * Print percent from 16-bit binary fraction [0 .. 1]
1558 * Round up .01 to .1 to indicate some small percentage (the 0x7000 below).
1561 prtpct(ushort_t pct
, int width
)
1563 uint_t value
= pct
; /* need 32 bits to compute with */
1565 value
= ((value
* 1000) + 0x7000) >> 15; /* [0 .. 1000] */
1568 if ((width
-= 2) < 2)
1570 (void) printf("%*u.%u", width
, value
/ 10, value
% 10);
1574 print_time(time_t tim
, int width
)
1583 (void) printf("%*s", width
, "-");
1595 (void) snprintf(buf
, sizeof (buf
), "%ld-%2.2ld:%2.2ld:%2.2ld",
1596 days
, hours
, minutes
, seconds
);
1597 } else if (hours
> 0) {
1598 (void) snprintf(buf
, sizeof (buf
), "%2.2ld:%2.2ld:%2.2ld",
1599 hours
, minutes
, seconds
);
1601 (void) snprintf(buf
, sizeof (buf
), "%2.2ld:%2.2ld",
1605 (void) printf("%*s", width
, buf
);
1609 print_field(psinfo_t
*psinfo
, struct field
*f
, const char *ttyp
)
1611 int width
= f
->width
;
1621 char c
= '\0', *csave
= NULL
;
1624 zombie_lwp
= (Lflg
&& psinfo
->pr_lwp
.pr_sname
== 'Z');
1628 if ((pwd
= getpwuid(psinfo
->pr_uid
)) != NULL
) {
1631 nw
= mbstowcs(NULL
, pwd
->pw_name
, 0);
1632 if (nw
== (size_t)-1)
1633 (void) printf("%*s ", width
, "ERROR");
1634 else if (Wflg
&& nw
> width
)
1635 (void) wprintf(L
"%.*s%c", width
- 1,
1638 (void) wprintf(L
"%*s", width
, pwd
->pw_name
);
1640 if (Wflg
&& snprintf(NULL
, 0, "%u",
1641 (psinfo
->pr_uid
)) > width
)
1643 (void) printf("%*u%c", width
- 1,
1644 psinfo
->pr_uid
, '*');
1646 (void) printf("%*u", width
, psinfo
->pr_uid
);
1650 if ((pwd
= getpwuid(psinfo
->pr_euid
)) != NULL
) {
1653 nw
= mbstowcs(NULL
, pwd
->pw_name
, 0);
1654 if (nw
== (size_t)-1)
1655 (void) printf("%*s ", width
, "ERROR");
1656 else if (Wflg
&& nw
> width
)
1657 (void) wprintf(L
"%.*s%c", width
- 1,
1660 (void) wprintf(L
"%*s", width
, pwd
->pw_name
);
1662 if (Wflg
&& snprintf(NULL
, 0, "%u",
1663 (psinfo
->pr_euid
)) > width
)
1665 (void) printf("%*u%c", width
- 1,
1666 psinfo
->pr_euid
, '*');
1668 (void) printf("%*u", width
, psinfo
->pr_euid
);
1672 if ((grp
= getgrgid(psinfo
->pr_gid
)) != NULL
)
1673 (void) printf("%*s", width
, grp
->gr_name
);
1675 (void) printf("%*u", width
, psinfo
->pr_gid
);
1678 if ((grp
= getgrgid(psinfo
->pr_egid
)) != NULL
)
1679 (void) printf("%*s", width
, grp
->gr_name
);
1681 (void) printf("%*u", width
, psinfo
->pr_egid
);
1684 (void) printf("%*u", width
, psinfo
->pr_uid
);
1687 (void) printf("%*u", width
, psinfo
->pr_euid
);
1690 (void) printf("%*u", width
, psinfo
->pr_gid
);
1693 (void) printf("%*u", width
, psinfo
->pr_egid
);
1696 (void) printf("%*d", width
, (int)psinfo
->pr_pid
);
1699 (void) printf("%*d", width
, (int)psinfo
->pr_ppid
);
1702 (void) printf("%*d", width
, (int)psinfo
->pr_pgid
);
1705 (void) printf("%*d", width
, (int)psinfo
->pr_sid
);
1708 if (zombie_lwp
|| psinfo
->pr_lwp
.pr_bindpro
== PBIND_NONE
)
1709 (void) printf("%*s", width
, "-");
1711 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_bindpro
);
1714 (void) printf("%*d", width
, (int)psinfo
->pr_lwp
.pr_lwpid
);
1717 (void) printf("%*d", width
, psinfo
->pr_nlwp
+ psinfo
->pr_nzomb
);
1721 (void) printf("%*s", width
, "-");
1723 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_oldpri
);
1727 (void) printf("%*s", width
, "-");
1729 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_pri
);
1732 mask
= 0xffffffffUL
;
1734 mask
>>= (8 - width
) * 4;
1735 (void) printf("%*lx", width
, psinfo
->pr_flag
& mask
);
1738 (void) printf("%*c", width
, psinfo
->pr_lwp
.pr_sname
);
1742 (void) printf("%*s", width
, "-");
1744 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_cpu
);
1748 (void) printf("%*s", width
, "-");
1750 prtpct(psinfo
->pr_lwp
.pr_pctcpu
, width
);
1752 prtpct(psinfo
->pr_pctcpu
, width
);
1755 prtpct(psinfo
->pr_pctmem
, width
);
1758 (void) printf("%*lu", width
,
1759 (ulong_t
)psinfo
->pr_size
/ kbytes_per_page
);
1762 (void) printf("%*lu", width
, (ulong_t
)psinfo
->pr_size
);
1765 (void) printf("%*lu", width
, (ulong_t
)psinfo
->pr_rssize
);
1768 /* if pr_oldpri is zero, then this class has no nice */
1770 (void) printf("%*s", width
, "-");
1771 else if (psinfo
->pr_lwp
.pr_oldpri
!= 0)
1772 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_nice
);
1774 (void) printf("%*.*s", width
, width
,
1775 psinfo
->pr_lwp
.pr_clname
);
1779 (void) printf("%*s", width
, "-");
1781 (void) printf("%*.*s", width
, width
,
1782 psinfo
->pr_lwp
.pr_clname
);
1786 prtime(psinfo
->pr_lwp
.pr_start
, width
, 0);
1788 prtime(psinfo
->pr_start
, width
, 0);
1792 print_time(delta_secs(&psinfo
->pr_lwp
.pr_start
),
1795 print_time(delta_secs(&psinfo
->pr_start
), width
);
1799 cputime
= psinfo
->pr_lwp
.pr_time
.tv_sec
;
1800 if (psinfo
->pr_lwp
.pr_time
.tv_nsec
> 500000000)
1803 cputime
= psinfo
->pr_time
.tv_sec
;
1804 if (psinfo
->pr_time
.tv_nsec
> 500000000)
1807 print_time(cputime
, width
);
1810 (void) printf("%-*s", width
, ttyp
);
1814 (void) printf("%*s", width
, "-");
1816 (void) printf("%*lx", width
,
1817 (long)psinfo
->pr_lwp
.pr_addr
);
1819 (void) printf("%*lx", width
, (long)psinfo
->pr_addr
);
1822 if (!zombie_lwp
&& psinfo
->pr_lwp
.pr_wchan
)
1823 (void) printf("%*lx", width
,
1824 (long)psinfo
->pr_lwp
.pr_wchan
);
1826 (void) printf("%*.*s", width
, width
, "-");
1830 * Print full width unless this is the last output format.
1833 if (f
->next
!= NULL
)
1834 (void) printf("%-*s", width
, "<defunct>");
1836 (void) printf("%s", "<defunct>");
1839 wcnt
= namencnt(psinfo
->pr_fname
, 16, width
);
1840 if (f
->next
!= NULL
)
1841 (void) printf("%-*.*s", width
, wcnt
, psinfo
->pr_fname
);
1843 (void) printf("%-.*s", wcnt
, psinfo
->pr_fname
);
1847 if (f
->next
!= NULL
)
1848 (void) printf("%-*s", width
, "<defunct>");
1850 (void) printf("%s", "<defunct>");
1853 csave
= strpbrk(psinfo
->pr_psargs
, " \t\r\v\f\n");
1861 * PRARGSZ == length of cmd arg string.
1864 (void) printf("%-*s", width
, "<defunct>");
1867 psinfo
->pr_psargs
[PRARGSZ
-1] = '\0';
1868 bytesleft
= PRARGSZ
;
1869 for (cp
= psinfo
->pr_psargs
; *cp
!= '\0'; cp
+= length
) {
1870 length
= mbtowc(&wchar
, cp
, MB_LEN_MAX
);
1873 if (length
< 0 || !iswprint(wchar
)) {
1876 if (bytesleft
<= length
) {
1880 /* omit the unprintable character */
1881 (void) memmove(cp
, cp
+length
, bytesleft
-length
);
1884 bytesleft
-= length
;
1886 wcnt
= namencnt(psinfo
->pr_psargs
, PRARGSZ
, width
);
1888 * Print full width unless this is the last format.
1890 if (f
->next
!= NULL
)
1891 (void) printf("%-*.*s", width
, wcnt
,
1894 (void) printf("%-.*s", wcnt
,
1896 if (f
->fname
== F_COMM
&& csave
)
1900 (void) printf("%*d", width
, (int)psinfo
->pr_taskid
);
1903 (void) printf("%*d", width
, (int)psinfo
->pr_projid
);
1907 struct project cproj
;
1908 char proj_buf
[PROJECT_BUFSZ
];
1910 if ((getprojbyid(psinfo
->pr_projid
, &cproj
,
1911 (void *)&proj_buf
, PROJECT_BUFSZ
)) == NULL
) {
1912 if (Wflg
&& snprintf(NULL
, 0, "%d",
1913 ((int)psinfo
->pr_projid
)) > width
)
1914 (void) printf("%.*d%c", width
- 1,
1915 ((int)psinfo
->pr_projid
), '*');
1917 (void) printf("%*d", width
,
1918 (int)psinfo
->pr_projid
);
1922 if (cproj
.pj_name
!= NULL
)
1923 nw
= mbstowcs(NULL
, cproj
.pj_name
, 0);
1924 if (cproj
.pj_name
== NULL
)
1925 (void) printf("%*s ", width
, "---");
1926 else if (nw
== (size_t)-1)
1927 (void) printf("%*s ", width
, "ERROR");
1928 else if (Wflg
&& nw
> width
)
1929 (void) wprintf(L
"%.*s%c", width
- 1,
1930 cproj
.pj_name
, '*');
1932 (void) wprintf(L
"%*s", width
,
1938 if (zombie_lwp
|| psinfo
->pr_lwp
.pr_bindpset
== PS_NONE
)
1939 (void) printf("%*s", width
, "-");
1941 (void) printf("%*d", width
, psinfo
->pr_lwp
.pr_bindpset
);
1944 (void) printf("%*d", width
, (int)psinfo
->pr_zoneid
);
1948 char zonename
[ZONENAME_MAX
];
1950 if (getzonenamebyid(psinfo
->pr_zoneid
, zonename
,
1951 sizeof (zonename
)) < 0) {
1952 if (Wflg
&& snprintf(NULL
, 0, "%d",
1953 ((int)psinfo
->pr_zoneid
)) > width
)
1954 (void) printf("%.*d%c", width
- 1,
1955 ((int)psinfo
->pr_zoneid
), '*');
1957 (void) printf("%*d", width
,
1958 (int)psinfo
->pr_zoneid
);
1962 nw
= mbstowcs(NULL
, zonename
, 0);
1963 if (nw
== (size_t)-1)
1964 (void) printf("%*s ", width
, "ERROR");
1965 else if (Wflg
&& nw
> width
)
1966 (void) wprintf(L
"%.*s%c", width
- 1,
1969 (void) wprintf(L
"%*s", width
, zonename
);
1974 if (psinfo
->pr_contract
== -1)
1975 (void) printf("%*s", width
, "-");
1977 (void) printf("%*ld", width
, (long)psinfo
->pr_contract
);
1980 /* Display home lgroup */
1981 (void) printf("%*d", width
, (int)psinfo
->pr_lwp
.pr_lgrp
);
1985 (void) printf("%*s", width
,
1986 psinfo
->pr_dmodel
== PR_MODEL_LP64
? "_LP64" : "_ILP32");
1992 print_zombie_field(psinfo_t
*psinfo
, struct field
*f
, const char *ttyp
)
1995 int width
= f
->width
;
2002 * Print full width unless this is the last output format.
2004 wcnt
= min(width
, sizeof ("<defunct>"));
2005 if (f
->next
!= NULL
)
2006 (void) printf("%-*.*s", width
, wcnt
, "<defunct>");
2008 (void) printf("%-.*s", wcnt
, "<defunct>");
2020 (void) printf("%*s", width
, "-");
2028 (void) printf("%*d", width
, 0);
2032 print_field(psinfo
, f
, ttyp
);
2038 pr_fields(psinfo_t
*psinfo
, const char *ttyp
,
2039 void (*print_fld
)(psinfo_t
*, struct field
*, const char *))
2043 for (f
= fields
; f
!= NULL
; f
= f
->next
) {
2044 print_fld(psinfo
, f
, ttyp
);
2045 if (f
->next
!= NULL
)
2048 (void) printf("\n");
2052 * Returns 1 if arg is found in array arr, of length num; 0 otherwise.
2055 search(pid_t
*arr
, int number
, pid_t arg
)
2059 for (i
= 0; i
< number
; i
++)
2066 * Add an entry (user, group) to the specified table.
2069 add_ugentry(struct ughead
*tbl
, char *name
)
2071 struct ugdata
*entp
;
2073 if (tbl
->size
== tbl
->nent
) { /* reallocate the table entries */
2074 if ((tbl
->size
*= 2) == 0)
2075 tbl
->size
= 32; /* first time */
2076 tbl
->ent
= Realloc(tbl
->ent
, tbl
->size
*sizeof (struct ugdata
));
2078 entp
= &tbl
->ent
[tbl
->nent
++];
2080 (void) strncpy(entp
->name
, name
, MAXUGNAME
);
2081 entp
->name
[MAXUGNAME
] = '\0';
2085 uconv(struct ughead
*uhead
)
2087 struct ugdata
*utbl
= uhead
->ent
;
2088 int n
= uhead
->nent
;
2095 * Ask the name service for names.
2097 for (i
= 0; i
< n
; i
++) {
2099 * If name is numeric, ask for numeric id
2101 if (str2uid(utbl
[i
].name
, &uid
, 0, MAXEPHUID
) == 0)
2102 pwd
= getpwuid(uid
);
2104 pwd
= getpwnam(utbl
[i
].name
);
2107 * If found, enter found index into tbl array.
2110 (void) fprintf(stderr
,
2111 gettext("ps: unknown user %s\n"), utbl
[i
].name
);
2115 utbl
[fnd
].id
= pwd
->pw_uid
;
2116 (void) strncpy(utbl
[fnd
].name
, pwd
->pw_name
, MAXUGNAME
);
2120 uhead
->nent
= fnd
; /* in case it changed */
2125 gconv(struct ughead
*ghead
)
2127 struct ugdata
*gtbl
= ghead
->ent
;
2128 int n
= ghead
->nent
;
2135 * Ask the name service for names.
2137 for (i
= 0; i
< n
; i
++) {
2139 * If name is numeric, ask for numeric id
2141 if (str2uid(gtbl
[i
].name
, (uid_t
*)&gid
, 0, MAXEPHUID
) == 0)
2142 grp
= getgrgid(gid
);
2144 grp
= getgrnam(gtbl
[i
].name
);
2146 * If found, enter found index into tbl array.
2149 (void) fprintf(stderr
,
2150 gettext("ps: unknown group %s\n"), gtbl
[i
].name
);
2154 gtbl
[fnd
].id
= grp
->gr_gid
;
2155 (void) strncpy(gtbl
[fnd
].name
, grp
->gr_name
, MAXUGNAME
);
2159 ghead
->nent
= fnd
; /* in case it changed */
2164 * Return 1 if puid is in table, otherwise 0.
2167 ugfind(id_t id
, struct ughead
*ughead
)
2169 struct ugdata
*utbl
= ughead
->ent
;
2170 int n
= ughead
->nent
;
2173 for (i
= 0; i
< n
; i
++)
2174 if (utbl
[i
].id
== id
)
2180 * Print starting time of process unless process started more than 24 hours
2181 * ago, in which case the date is printed. The date is printed in the form
2182 * "MMM dd" if old format, else the blank is replaced with an '_' so
2183 * it appears as a single word (for parseability).
2186 prtime(timestruc_t st
, int width
, int old
)
2191 starttime
= st
.tv_sec
;
2192 if (st
.tv_nsec
> 500000000)
2194 if ((now
.tv_sec
- starttime
) >= 24*60*60) {
2195 (void) strftime(sttim
, sizeof (sttim
), old
?
2198 * This time format is used by STIME field when -f option
2199 * is specified. Used for processes that begun more than
2202 dcgettext(NULL
, "%b %d", LC_TIME
) :
2205 * This time format is used by STIME field when -o option
2206 * is specified. Used for processes that begun more than
2209 dcgettext(NULL
, "%b_%d", LC_TIME
), localtime(&starttime
));
2213 * This time format is used by STIME field when -f or -o option
2214 * is specified. Used for processes that begun less than
2217 (void) strftime(sttim
, sizeof (sttim
),
2218 dcgettext(NULL
, "%H:%M:%S", LC_TIME
),
2219 localtime(&starttime
));
2221 (void) printf("%*.*s", width
, width
, sttim
);
2225 przom(psinfo_t
*psinfo
)
2229 char zonename
[ZONENAME_MAX
];
2232 * All fields before 'PID' are printed with a trailing space as a
2233 * spearator, rather than keeping track of which column is first. All
2234 * other fields are printed with a leading space.
2236 if (lflg
) { /* F S */
2238 (void) printf("%2x ", psinfo
->pr_flag
& 0377); /* F */
2239 (void) printf("%c ", psinfo
->pr_lwp
.pr_sname
); /* S */
2242 if (getzonenamebyid(psinfo
->pr_zoneid
, zonename
,
2243 sizeof (zonename
)) < 0) {
2244 if (snprintf(NULL
, 0, "%d",
2245 ((int)psinfo
->pr_zoneid
)) > 7)
2246 (void) printf(" %6.6d%c ",
2247 ((int)psinfo
->pr_zoneid
), '*');
2249 (void) printf(" %7.7d ",
2250 ((int)psinfo
->pr_zoneid
));
2254 nw
= mbstowcs(NULL
, zonename
, 0);
2255 if (nw
== (size_t)-1)
2256 (void) printf("%8.8s ", "ERROR");
2258 (void) wprintf(L
"%7.7s%c ", zonename
, '*');
2260 (void) wprintf(L
"%8.8s ", zonename
);
2264 /* Display home lgroup */
2265 (void) printf(" %6d", (int)psinfo
->pr_lwp
.pr_lgrp
); /* LGRP */
2268 if ((pwd
= getpwuid(psinfo
->pr_euid
)) != NULL
) {
2271 nw
= mbstowcs(NULL
, pwd
->pw_name
, 0);
2272 if (nw
== (size_t)-1)
2273 (void) printf("%8.8s ", "ERROR");
2275 (void) wprintf(L
"%7.7s%c ", pwd
->pw_name
, '*');
2277 (void) wprintf(L
"%8.8s ", pwd
->pw_name
);
2279 if (snprintf(NULL
, 0, "%u",
2280 (psinfo
->pr_euid
)) > 7)
2281 (void) printf(" %6.6u%c ", psinfo
->pr_euid
,
2284 (void) printf(" %7.7u ", psinfo
->pr_euid
);
2287 if (snprintf(NULL
, 0, "%u", (psinfo
->pr_euid
)) > 6)
2288 (void) printf("%5.5u%c ", psinfo
->pr_euid
, '*');
2290 (void) printf("%6u ", psinfo
->pr_euid
);
2293 (void) printf("%*d", pidwidth
, (int)psinfo
->pr_pid
); /* PID */
2295 (void) printf(" %*d", pidwidth
,
2296 (int)psinfo
->pr_ppid
); /* PPID */
2299 (void) printf(" %*d", pidwidth
,
2300 (int)psinfo
->pr_pgid
); /* PGID */
2301 (void) printf(" %*d", pidwidth
,
2302 (int)psinfo
->pr_sid
); /* SID */
2306 (void) printf(" %5d", 0); /* LWP */
2308 (void) printf(" -"); /* PSR */
2310 (void) printf(" %5d", 0); /* NLWP */
2313 (void) printf(" %4s", "-"); /* zombies have no class */
2314 (void) printf(" %3d", psinfo
->pr_lwp
.pr_pri
); /* PRI */
2315 } else if (lflg
|| fflg
) {
2316 (void) printf(" %3d", psinfo
->pr_lwp
.pr_cpu
& 0377); /* C */
2318 (void) printf(" %3d %2s",
2319 psinfo
->pr_lwp
.pr_oldpri
, "-"); /* PRI NI */
2322 if (yflg
) /* RSS SZ WCHAN */
2323 (void) printf(" %5d %6d %8s", 0, 0, "-");
2324 else /* ADDR SZ WCHAN */
2325 (void) printf(" %8s %6d %8s", "-", 0, "-");
2328 int width
= fname
[F_STIME
].width
;
2329 (void) printf(" %*.*s", width
, width
, "-"); /* STIME */
2331 (void) printf(" %-8.14s", "?"); /* TTY */
2333 tm
= psinfo
->pr_time
.tv_sec
;
2334 if (psinfo
->pr_time
.tv_nsec
> 500000000)
2336 (void) printf(" %4ld:%.2ld", tm
/ 60, tm
% 60); /* TIME */
2337 (void) printf(" <defunct>\n");
2341 * Function to compute the number of printable bytes in a multibyte
2342 * command string ("internationalization").
2345 namencnt(char *cmd
, int csisize
, int scrsize
)
2347 int csiwcnt
= 0, scrwcnt
= 0;
2352 while (*cmd
!= '\0') {
2353 if ((len
= csisize
- csiwcnt
) > (int)MB_CUR_MAX
)
2355 if ((ncsisz
= mbtowc(&wchar
, cmd
, len
)) < 0)
2356 return (8); /* default to use for illegal chars */
2357 if ((nscrsz
= wcwidth(wchar
)) <= 0)
2359 if (csiwcnt
+ ncsisz
> csisize
|| scrwcnt
+ nscrsz
> scrsize
)
2371 static char buf
[32];
2372 char *str
= strerror(err
);
2375 (void) snprintf(str
= buf
, sizeof (buf
), "Errno #%d", err
);
2380 /* If allocation fails, die */
2382 Realloc(void *ptr
, size_t size
)
2384 ptr
= realloc(ptr
, size
);
2386 (void) fprintf(stderr
, gettext("ps: no memory\n"));
2393 delta_secs(const timestruc_t
*start
)
2395 time_t seconds
= now
.tv_sec
- start
->tv_sec
;
2396 long nanosecs
= now
.tv_usec
* 1000 - start
->tv_nsec
;
2398 if (nanosecs
>= (NANOSEC
/ 2))
2400 else if (nanosecs
< -(NANOSEC
/ 2))
2407 * Returns the following:
2410 * EINVAL Invalid number
2411 * ERANGE Value exceeds (min, max) range
2414 str2id(const char *p
, pid_t
*val
, long min
, long max
)
2421 number
= strtol(p
, &q
, 10);
2423 if (errno
!= 0 || q
== p
|| *q
!= '\0') {
2424 if ((error
= errno
) == 0) {
2426 * strtol() can fail without setting errno, or it can
2427 * set it to EINVAL or ERANGE. In the case errno is
2428 * still zero, return EINVAL.
2432 } else if (number
< min
|| number
> max
) {
2444 * Returns the following:
2447 * EINVAL Invalid number
2448 * ERANGE Value exceeds (min, max) range
2451 str2uid(const char *p
, uid_t
*val
, unsigned long min
, unsigned long max
)
2454 unsigned long number
;
2458 number
= strtoul(p
, &q
, 10);
2460 if (errno
!= 0 || q
== p
|| *q
!= '\0') {
2461 if ((error
= errno
) == 0) {
2463 * strtoul() can fail without setting errno, or it can
2464 * set it to EINVAL or ERANGE. In the case errno is
2465 * still zero, return EINVAL.
2469 } else if (number
< min
|| number
> max
) {
2481 pidcmp(const void *p1
, const void *p2
)
2483 pid_t i
= *((pid_t
*)p1
);
2484 pid_t j
= *((pid_t
*)p2
);