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]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * ptree -- print family tree of processes
30 #pragma ident "%Z%%M% %I% %E% SMI"
37 #include <sys/types.h>
38 #include <sys/termios.h>
44 #include <libzonecfg.h>
46 #include <libcontract.h>
47 #include <sys/contract.h>
49 #include <libcontract_priv.h>
52 #define FAKEDPID0(p) (p->pid == 0 && p->psargs[0] == '\0')
58 pid_t pid
; /* pid == -1 indicates this is a contract */
66 struct ps
*pp
; /* parent */
67 struct ps
*sp
; /* sibling */
68 struct ps
*cp
; /* child */
71 static ps_t
**ps
; /* array of ps_t's */
72 static unsigned psize
; /* size of array */
73 static int nps
; /* number of ps_t's */
74 static ps_t
**ctps
; /* array of contract ps_t's */
75 static unsigned ctsize
; /* size of contract array */
76 static int nctps
; /* number of contract ps_t's */
77 static ps_t
*proc0
; /* process 0 */
78 static ps_t
*proc1
; /* process 1 */
85 static zoneid_t zoneid
;
86 static int columns
= 80;
88 static void markprocs(ps_t
*p
);
89 static int printone(ps_t
*p
, int level
);
90 static void insertchild(ps_t
*, ps_t
*);
91 static void prsort(ps_t
*p
);
92 static void printsubtree(ps_t
*p
, int level
);
93 static zoneid_t
getzone(char *arg
);
94 static ps_t
*fakepid0(void);
97 main(int argc
, char **argv
)
99 psinfo_t info
; /* process information structure from /proc */
102 struct winsize winsize
;
108 struct dirent
*dentp
;
114 if ((command
= strrchr(argv
[0], '/')) == NULL
)
120 while ((opt
= getopt(argc
, argv
, "acz:")) != EOF
) {
122 case 'a': /* include children of process 0 */
125 case 'c': /* display contract ownership */
128 case 'z': /* only processes in given zone */
130 zoneid
= getzone(optarg
);
142 (void) fprintf(stderr
,
143 "usage:\t%s [-ac] [-z zone] [ {pid|user} ... ]\n",
145 (void) fprintf(stderr
,
146 " (show process trees)\n");
147 (void) fprintf(stderr
,
148 " list can include process-ids and user names\n");
149 (void) fprintf(stderr
,
150 " -a : include children of process 0\n");
151 (void) fprintf(stderr
,
152 " -c : show contract ownership\n");
153 (void) fprintf(stderr
,
154 " -z : print only processes in given zone\n");
159 * Kind of a hack to determine the width of the output...
161 if ((s
= getenv("COLUMNS")) != NULL
&& (n
= atoi(s
)) > 0)
163 else if (isatty(fileno(stdout
)) &&
164 ioctl(fileno(stdout
), TIOCGWINSZ
, &winsize
) == 0 &&
166 columns
= winsize
.ws_col
;
173 * Search the /proc directory for all processes.
175 if ((dirp
= opendir("/proc")) == NULL
) {
176 (void) fprintf(stderr
, "%s: cannot open /proc directory\n",
181 (void) strcpy(pname
, "/proc");
182 pdlen
= strlen(pname
);
183 pname
[pdlen
++] = '/';
185 /* for each active process --- */
186 while (dentp
= readdir(dirp
)) {
187 int procfd
; /* filedescriptor for /proc/nnnnn/psinfo */
189 if (dentp
->d_name
[0] == '.') /* skip . and .. */
191 (void) strcpy(pname
+ pdlen
, dentp
->d_name
);
192 (void) strcpy(pname
+ strlen(pname
), "/psinfo");
194 if ((procfd
= open(pname
, O_RDONLY
)) == -1)
198 * Get the info structure for the process and close quickly.
200 if (read(procfd
, &info
, sizeof (info
)) != sizeof (info
)) {
203 (void) close(procfd
);
204 if (saverr
== EAGAIN
)
206 if (saverr
!= ENOENT
)
210 (void) close(procfd
);
213 * We make sure there's always a free slot in the table
214 * in case we need to add a fake p0.
216 if (nps
+ 1 >= psize
) {
217 if ((psize
*= 2) == 0)
219 if ((ps
= reallocarray(ps
, psize
, sizeof (ps_t
*))) == NULL
) {
224 if ((p
= malloc(sizeof (ps_t
))) == NULL
) {
230 p
->uid
= info
.pr_uid
;
231 p
->gid
= info
.pr_gid
;
232 p
->pid
= info
.pr_pid
;
233 p
->ppid
= info
.pr_ppid
;
234 p
->pgrp
= info
.pr_pgid
;
235 p
->sid
= info
.pr_sid
;
236 p
->zoneid
= info
.pr_zoneid
;
237 p
->ctid
= info
.pr_contract
;
238 p
->start
= info
.pr_start
;
239 proc_unctrl_psinfo(&info
);
240 if (info
.pr_nlwp
== 0)
241 (void) strcpy(p
->psargs
, "<defunct>");
242 else if (info
.pr_psargs
[0] == '\0')
243 (void) strncpy(p
->psargs
, info
.pr_fname
,
246 (void) strncpy(p
->psargs
, info
.pr_psargs
,
248 p
->psargs
[sizeof (p
->psargs
)-1] = '\0';
252 if (p
->pid
== p
->ppid
)
258 (void) closedir(dirp
);
264 for (n
= 0; n
< nps
; n
++) {
271 /* Parent all orphan contracts to process 0. */
272 for (n
= 0; n
< nctps
; n
++) {
275 insertchild(proc0
, p
);
279 for (p
= aflag
? proc0
->cp
: proc1
->cp
; p
!= NULL
; p
= p
->sp
) {
287 * Initially, assume we're not going to find any processes. If we do
288 * mark any, then set this to 0 to indicate no error.
299 /* in case some silly person said 'ptree /proc/[0-9]*' */
300 arg
= strrchr(*argv
, '/');
306 pid
= strtoul(arg
, &next
, 10);
307 if (errno
!= 0 || *next
!= '\0') {
308 struct passwd
*pw
= getpwnam(arg
);
310 (void) fprintf(stderr
,
311 "%s: invalid username: %s\n",
320 for (n
= 0; n
< nps
; n
++) {
324 * A match on pid causes the subtree starting at pid
325 * to be printed, regardless of the -a flag.
326 * For uid matches, we never include pid 0 and only
327 * include the children of pid 0 if -a was specified.
329 if (p
->pid
== pid
|| (p
->uid
== uid
&& p
->pid
!= 0 &&
330 (p
->ppid
!= 0 || aflag
))) {
334 for (p
= p
->pp
; p
!= NULL
&&
335 p
->done
!= 1 && p
->pid
!= 0;
337 if ((p
->ppid
!= 0 || aflag
) &&
339 p
->zoneid
== zoneid
))
341 if (uid
== (uid_t
)-1)
347 printsubtree(proc0
, 0);
349 * retc = 1 if an invalid username was supplied.
350 * errflg = 1 if no matching processes were found.
352 return (retc
|| errflg
);
358 printone(ps_t
*p
, int level
)
362 if (p
->done
&& !FAKEDPID0(p
)) {
364 if ((n
= columns
- PIDWIDTH
- indent
- 2) < 0)
367 (void) printf("%*.*s%-*d %.*s\n", indent
, indent
, " ",
368 PIDWIDTH
, (int)p
->pid
, n
, p
->psargs
);
371 (void) printf("%*.*s[process contract %d]\n",
372 indent
, indent
, " ", (int)p
->ctid
);
380 insertchild(ps_t
*pp
, ps_t
*cp
)
382 /* insert as child process of p */
386 /* sort by start time */
387 for (here
= &pp
->cp
, sp
= pp
->cp
;
389 here
= &sp
->sp
, sp
= sp
->sp
) {
390 if (cp
->start
.tv_sec
< sp
->start
.tv_sec
)
392 if (cp
->start
.tv_sec
== sp
->start
.tv_sec
&&
393 cp
->start
.tv_nsec
< sp
->start
.tv_nsec
)
402 ctsort(ctid_t ctid
, ps_t
*p
)
409 for (n
= 0; n
< nctps
; n
++)
410 if (ctps
[n
]->ctid
== ctid
) {
411 insertchild(ctps
[n
], p
);
415 if ((fd
= contract_open(ctid
, "process", "status", O_RDONLY
)) == -1)
417 if (fstat64(fd
, &st
) == -1 || ct_status_read(fd
, CTD_COMMON
, &hdl
)) {
423 if (nctps
>= ctsize
) {
424 if ((ctsize
*= 2) == 0)
426 if ((ctps
= reallocarray(ctps
, ctsize
, sizeof (ps_t
*))) == NULL
) {
431 pp
= calloc(sizeof (ps_t
), 1);
440 pp
->start
.tv_sec
= st
.st_ctime
;
443 pp
->zoneid
= ct_status_get_zoneid(hdl
);
445 * In a zlogin <zonename>, the contract belongs to the
446 * global zone and the shell opened belongs to <zonename>.
447 * If the -c and -z zonename flags are used together, then
448 * we need to adjust the zoneid in the contract's ps_t as
451 * ptree -c -z <zonename> --> zoneid == p->zoneid
452 * ptree -c -z global --> zoneid == pp->zoneid
454 * The approach assumes that no tool can create processes in
455 * different zones under the same contract. If this is
456 * possible, ptree will need to refactor how it builds
457 * its internal tree of ps_t's
459 if (zflag
&& p
->zoneid
!= pp
->zoneid
&&
460 (zoneid
== p
->zoneid
|| zoneid
== pp
->zoneid
))
461 pp
->zoneid
= p
->zoneid
;
462 if (ct_status_get_state(hdl
) == CTS_OWNED
) {
463 pp
->ppid
= ct_status_get_holder(hdl
);
465 } else if (ct_status_get_state(hdl
) == CTS_INHERITED
) {
466 ctsort(ct_status_get_holder(hdl
), pp
);
477 /* If this node already has a parent, it's sorted */
481 for (n
= 0; n
< nps
; n
++) {
484 if (pp
!= NULL
&& p
!= pp
&& p
->ppid
== pp
->pid
) {
485 if (cflag
&& p
->pid
>= 0 &&
486 p
->ctid
!= -1 && p
->ctid
!= pp
->ctid
) {
496 /* File parentless processes under their contracts */
497 if (cflag
&& p
->pid
>= 0)
502 printsubtree(ps_t
*p
, int level
)
506 printed
= printone(p
, level
);
507 if (level
!= 0 || printed
== 1)
509 for (p
= p
->cp
; p
!= NULL
; p
= p
->sp
)
510 printsubtree(p
, level
);
516 if (!zflag
|| p
->zoneid
== zoneid
)
518 for (p
= p
->cp
; p
!= NULL
; p
= p
->sp
)
523 * If there's no "top" process, we fake one; it will be the parent of
532 if ((p0
= malloc(sizeof (ps_t
))) == NULL
) {
536 (void) memset(p0
, '\0', sizeof (ps_t
));
538 /* First build all partial process trees. */
539 for (n
= 0; n
< nps
; n
++) {
545 /* Then adopt all orphans. */
546 for (n
= 0; n
< nps
; n
++) {
552 /* We've made sure earlier there's room for this. */
557 /* convert string containing zone name or id to a numeric id */
563 if (zone_get_id(arg
, &zoneid
) != 0) {
564 (void) fprintf(stderr
, "%s: unknown zone: %s\n", command
, arg
);