1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * ptree -- print family tree of processes
28 */
29
30 #pragma ident "%Z%%M% %I% %E% SMI"
31
32 #include <assert.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <sys/types.h>
38 #include <sys/termios.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <dirent.h>
42 #include <pwd.h>
43 #include <libproc.h>
44 #include <libzonecfg.h>
45 #include <limits.h>
46 #include <libcontract.h>
47 #include <sys/contract.h>
48 #include <sys/ctfs.h>
49 #include <libcontract_priv.h>
50 #include <sys/stat.h>
51
52 #define FAKEDPID0(p) (p->pid == 0 && p->psargs[0] == '\0')
53
54 typedef struct ps {
55 int done;
56 uid_t uid;
57 uid_t gid;
58 pid_t pid; /* pid == -1 indicates this is a contract */
59 pid_t ppid;
60 pid_t pgrp;
61 pid_t sid;
62 zoneid_t zoneid;
63 ctid_t ctid;
64 timestruc_t start;
65 char psargs[PRARGSZ];
66 struct ps *pp; /* parent */
67 struct ps *sp; /* sibling */
68 struct ps *cp; /* child */
69 } ps_t;
70
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 */
79
80 static char *command;
81
82 static int aflag = 0;
83 static int cflag = 0;
84 static int zflag = 0;
85 static zoneid_t zoneid;
86 static int columns = 80;
87
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);
95
96 int
97 main(int argc, char **argv)
98 {
99 psinfo_t info; /* process information structure from /proc */
100 int opt;
101 int errflg = 0;
102 struct winsize winsize;
103 char *s;
104 int n;
105 int retc = 0;
106
107 DIR *dirp;
108 struct dirent *dentp;
109 char pname[100];
110 int pdlen;
111
112 ps_t *p;
113
114 if ((command = strrchr(argv[0], '/')) == NULL)
115 command = argv[0];
116 else
117 command++;
118
119 /* options */
120 while ((opt = getopt(argc, argv, "acz:")) != EOF) {
121 switch (opt) {
122 case 'a': /* include children of process 0 */
123 aflag = 1;
124 break;
125 case 'c': /* display contract ownership */
126 aflag = cflag = 1;
127 break;
128 case 'z': /* only processes in given zone */
129 zflag = 1;
130 zoneid = getzone(optarg);
131 break;
132 default:
133 errflg = 1;
134 break;
135 }
136 }
137
138 argc -= optind;
139 argv += optind;
140
141 if (errflg) {
142 (void) fprintf(stderr,
143 "usage:\t%s [-ac] [-z zone] [ {pid|user} ... ]\n",
144 command);
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");
155 return (2);
156 }
157
158 /*
159 * Kind of a hack to determine the width of the output...
160 */
161 if ((s = getenv("COLUMNS")) != NULL && (n = atoi(s)) > 0)
162 columns = n;
163 else if (isatty(fileno(stdout)) &&
164 ioctl(fileno(stdout), TIOCGWINSZ, &winsize) == 0 &&
165 winsize.ws_col != 0)
166 columns = winsize.ws_col;
167
168 nps = 0;
169 psize = 0;
170 ps = NULL;
171
172 /*
173 * Search the /proc directory for all processes.
204 if (saverr == EAGAIN)
205 goto retry;
206 if (saverr != ENOENT)
207 perror(pname);
208 continue;
209 }
210 (void) close(procfd);
211
212 /*
213 * We make sure there's always a free slot in the table
214 * in case we need to add a fake p0.
215 */
216 if (nps + 1 >= psize) {
217 if ((psize *= 2) == 0)
218 psize = 20;
219 if ((ps = realloc(ps, psize*sizeof (ps_t *))) == NULL) {
220 perror("realloc()");
221 return (1);
222 }
223 }
224 if ((p = malloc(sizeof (ps_t))) == NULL) {
225 perror("malloc()");
226 return (1);
227 }
228 ps[nps++] = p;
229 p->done = 0;
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,
244 sizeof (p->psargs));
245 else
246 (void) strncpy(p->psargs, info.pr_psargs,
247 sizeof (p->psargs));
248 p->psargs[sizeof (p->psargs)-1] = '\0';
249 p->pp = NULL;
250 p->sp = NULL;
251 p->cp = NULL;
252 if (p->pid == p->ppid)
253 proc0 = p;
254 if (p->pid == 1)
255 proc1 = p;
256 }
257
258 (void) closedir(dirp);
259 if (proc0 == NULL)
260 proc0 = fakepid0();
261 if (proc1 == NULL)
262 proc1 = proc0;
263
264 for (n = 0; n < nps; n++) {
265 p = ps[n];
266 if (p->pp == NULL)
267 prsort(p);
268 }
269
270 if (cflag)
271 /* Parent all orphan contracts to process 0. */
318 }
319
320 for (n = 0; n < nps; n++) {
321 ps_t *p = ps[n];
322
323 /*
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.
328 */
329 if (p->pid == pid || (p->uid == uid && p->pid != 0 &&
330 (p->ppid != 0 || aflag))) {
331 errflg = 0;
332 markprocs(p);
333 if (p->pid != 0)
334 for (p = p->pp; p != NULL &&
335 p->done != 1 && p->pid != 0;
336 p = p->pp)
337 if ((p->ppid != 0 || aflag) &&
338 (!zflag ||
339 p->zoneid == zoneid))
340 p->done = 1;
341 if (uid == (uid_t)-1)
342 break;
343 }
344 }
345 }
346
347 printsubtree(proc0, 0);
348 /*
349 * retc = 1 if an invalid username was supplied.
350 * errflg = 1 if no matching processes were found.
351 */
352 return (retc || errflg);
353 }
354
355 #define PIDWIDTH 5
356
357 static int
358 printone(ps_t *p, int level)
359 {
360 int n, indent;
361
362 if (p->done && !FAKEDPID0(p)) {
363 indent = level * 2;
364 if ((n = columns - PIDWIDTH - indent - 2) < 0)
365 n = 0;
366 if (p->pid >= 0) {
367 (void) printf("%*.*s%-*d %.*s\n", indent, indent, " ",
368 PIDWIDTH, (int)p->pid, n, p->psargs);
369 } else {
370 assert(cflag != 0);
371 (void) printf("%*.*s[process contract %d]\n",
372 indent, indent, " ", (int)p->ctid);
373 }
374 return (1);
375 }
376 return (0);
377 }
378
379 static void
380 insertchild(ps_t *pp, ps_t *cp)
381 {
382 /* insert as child process of p */
383 ps_t **here;
384 ps_t *sp;
385
386 /* sort by start time */
387 for (here = &pp->cp, sp = pp->cp;
388 sp != NULL;
389 here = &sp->sp, sp = sp->sp) {
390 if (cp->start.tv_sec < sp->start.tv_sec)
391 break;
392 if (cp->start.tv_sec == sp->start.tv_sec &&
393 cp->start.tv_nsec < sp->start.tv_nsec)
394 break;
395 }
396 cp->pp = pp;
397 cp->sp = sp;
398 *here = cp;
399 }
400
401 static void
402 ctsort(ctid_t ctid, ps_t *p)
403 {
404 ps_t *pp;
405 int fd, n;
406 ct_stathdl_t hdl;
407 struct stat64 st;
408
409 for (n = 0; n < nctps; n++)
410 if (ctps[n]->ctid == ctid) {
411 insertchild(ctps[n], p);
412 return;
413 }
414
415 if ((fd = contract_open(ctid, "process", "status", O_RDONLY)) == -1)
416 return;
417 if (fstat64(fd, &st) == -1 || ct_status_read(fd, CTD_COMMON, &hdl)) {
418 (void) close(fd);
419 return;
420 }
421 (void) close(fd);
422
423 if (nctps >= ctsize) {
424 if ((ctsize *= 2) == 0)
425 ctsize = 20;
426 if ((ctps = realloc(ctps, ctsize * sizeof (ps_t *))) == NULL) {
427 perror("realloc()");
428 exit(1);
429 }
430 }
431 pp = calloc(sizeof (ps_t), 1);
432 if (pp == NULL) {
433 perror("calloc()");
434 exit(1);
435 }
436 ctps[nctps++] = pp;
437
438 pp->pid = -1;
439 pp->ctid = ctid;
440 pp->start.tv_sec = st.st_ctime;
441 insertchild(pp, p);
442
443 pp->zoneid = ct_status_get_zoneid(hdl);
444 /*
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
449 * follows:
450 *
451 * ptree -c -z <zonename> --> zoneid == p->zoneid
452 * ptree -c -z global --> zoneid == pp->zoneid
453 *
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
458 */
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);
493 }
494 }
495
496 /* File parentless processes under their contracts */
497 if (cflag && p->pid >= 0)
498 ctsort(p->ctid, p);
499 }
500
501 static void
502 printsubtree(ps_t *p, int level)
503 {
504 int printed;
505
506 printed = printone(p, level);
507 if (level != 0 || printed == 1)
508 level++;
509 for (p = p->cp; p != NULL; p = p->sp)
510 printsubtree(p, level);
511 }
512
513 static void
514 markprocs(ps_t *p)
515 {
516 if (!zflag || p->zoneid == zoneid)
517 p->done = 1;
518 for (p = p->cp; p != NULL; p = p->sp)
519 markprocs(p);
520 }
521
522 /*
523 * If there's no "top" process, we fake one; it will be the parent of
524 * all orphans.
525 */
526 static ps_t *
527 fakepid0(void)
528 {
529 ps_t *p0, *p;
530 int n;
531
532 if ((p0 = malloc(sizeof (ps_t))) == NULL) {
533 perror("malloc()");
534 exit(1);
535 }
536 (void) memset(p0, '\0', sizeof (ps_t));
537
538 /* First build all partial process trees. */
539 for (n = 0; n < nps; n++) {
540 p = ps[n];
541 if (p->pp == NULL)
542 prsort(p);
543 }
544
545 /* Then adopt all orphans. */
546 for (n = 0; n < nps; n++) {
547 p = ps[n];
548 if (p->pp == NULL)
549 insertchild(p0, p);
550 }
551
552 /* We've made sure earlier there's room for this. */
553 ps[nps++] = p0;
554 return (p0);
555 }
556
557 /* convert string containing zone name or id to a numeric id */
558 static zoneid_t
559 getzone(char *arg)
560 {
561 zoneid_t zoneid;
562
563 if (zone_get_id(arg, &zoneid) != 0) {
564 (void) fprintf(stderr, "%s: unknown zone: %s\n", command, arg);
565 exit(1);
566 }
567 return (zoneid);
568 }
|
1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2019 Joyent, Inc.
25 */
26
27 /*
28 * ptree -- print family tree of processes
29 */
30
31 #include <assert.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <sys/types.h>
37 #include <sys/termios.h>
38 #include <unistd.h>
39 #include <stdlib.h>
40 #include <dirent.h>
41 #include <pwd.h>
42 #include <libproc.h>
43 #include <libzonecfg.h>
44 #include <limits.h>
45 #include <libcontract.h>
46 #include <sys/contract.h>
47 #include <sys/ctfs.h>
48 #include <libcontract_priv.h>
49 #include <sys/stat.h>
50 #include <stdbool.h>
51
52 #define FAKEDPID0(p) (p->pid == 0 && p->psargs[0] == '\0')
53
54 typedef struct ps {
55 int done;
56 uid_t uid;
57 uid_t gid;
58 pid_t pid; /* pid == -1 indicates this is a contract */
59 pid_t ppid;
60 pid_t pgrp;
61 pid_t sid;
62 zoneid_t zoneid;
63 ctid_t ctid;
64 char *svc_fmri;
65 timestruc_t start;
66 char psargs[PRARGSZ];
67 struct ps *pp; /* parent */
68 struct ps *sp; /* sibling */
69 struct ps *cp; /* child */
70 } ps_t;
71
72 static ps_t **ps; /* array of ps_t's */
73 static unsigned psize; /* size of array */
74 static int nps; /* number of ps_t's */
75 static ps_t **ctps; /* array of contract ps_t's */
76 static unsigned ctsize; /* size of contract array */
77 static int nctps; /* number of contract ps_t's */
78 static ps_t *proc0; /* process 0 */
79 static ps_t *proc1; /* process 1 */
80
81 static char *command;
82
83 static int aflag = 0;
84 static int cflag = 0;
85 static int sflag = 0;
86 static int zflag = 0;
87 static zoneid_t zoneid;
88 static char *match_svc;
89 static char *match_inst;
90 static int columns = 80;
91
92 static bool match_proc(ps_t *);
93 static void markprocs(ps_t *);
94 static int printone(ps_t *, int);
95 static void insertchild(ps_t *, ps_t *);
96 static void prsort(ps_t *);
97 static void printsubtree(ps_t *, int);
98 static void p_get_svc_fmri(ps_t *, ct_stathdl_t);
99 static char *parse_svc(const char *, char **);
100 static zoneid_t getzone(const char *);
101 static ps_t *fakepid0(void);
102
103 int
104 main(int argc, char **argv)
105 {
106 psinfo_t info; /* process information structure from /proc */
107 int opt;
108 int errflg = 0;
109 struct winsize winsize;
110 char *s;
111 int n;
112 int retc = 0;
113
114 DIR *dirp;
115 struct dirent *dentp;
116 char pname[100];
117 int pdlen;
118
119 ps_t *p;
120
121 if ((command = strrchr(argv[0], '/')) == NULL)
122 command = argv[0];
123 else
124 command++;
125
126 /* options */
127 while ((opt = getopt(argc, argv, "acs:z:")) != EOF) {
128 switch (opt) {
129 case 'a': /* include children of process 0 */
130 aflag = 1;
131 break;
132 case 'c': /* display contract ownership */
133 aflag = cflag = 1;
134 break;
135 case 's':
136 sflag = 1;
137 match_svc = parse_svc(optarg, &match_inst);
138 break;
139 case 'z': /* only processes in given zone */
140 zflag = 1;
141 zoneid = getzone(optarg);
142 break;
143 default:
144 errflg = 1;
145 break;
146 }
147 }
148
149 argc -= optind;
150 argv += optind;
151
152 if (errflg) {
153 (void) fprintf(stderr,
154 "usage:\t%s [-ac] [-s svc] [-z zone] [ {pid|user} ... ]\n",
155 command);
156 (void) fprintf(stderr,
157 " (show process trees)\n");
158 (void) fprintf(stderr,
159 " list can include process-ids and user names\n");
160 (void) fprintf(stderr,
161 " -a : include children of process 0\n");
162 (void) fprintf(stderr,
163 " -c : show contracts\n");
164 (void) fprintf(stderr,
165 " -s : print only processes with given service FMRI\n");
166 (void) fprintf(stderr,
167 " -z : print only processes in given zone\n");
168 return (2);
169 }
170
171 /*
172 * Kind of a hack to determine the width of the output...
173 */
174 if ((s = getenv("COLUMNS")) != NULL && (n = atoi(s)) > 0)
175 columns = n;
176 else if (isatty(fileno(stdout)) &&
177 ioctl(fileno(stdout), TIOCGWINSZ, &winsize) == 0 &&
178 winsize.ws_col != 0)
179 columns = winsize.ws_col;
180
181 nps = 0;
182 psize = 0;
183 ps = NULL;
184
185 /*
186 * Search the /proc directory for all processes.
217 if (saverr == EAGAIN)
218 goto retry;
219 if (saverr != ENOENT)
220 perror(pname);
221 continue;
222 }
223 (void) close(procfd);
224
225 /*
226 * We make sure there's always a free slot in the table
227 * in case we need to add a fake p0.
228 */
229 if (nps + 1 >= psize) {
230 if ((psize *= 2) == 0)
231 psize = 20;
232 if ((ps = realloc(ps, psize*sizeof (ps_t *))) == NULL) {
233 perror("realloc()");
234 return (1);
235 }
236 }
237 if ((p = calloc(1, sizeof (ps_t))) == NULL) {
238 perror("calloc()");
239 return (1);
240 }
241 ps[nps++] = p;
242 p->done = 0;
243 p->uid = info.pr_uid;
244 p->gid = info.pr_gid;
245 p->pid = info.pr_pid;
246 p->ppid = info.pr_ppid;
247 p->pgrp = info.pr_pgid;
248 p->sid = info.pr_sid;
249 p->zoneid = info.pr_zoneid;
250 p->ctid = info.pr_contract;
251 p->start = info.pr_start;
252 proc_unctrl_psinfo(&info);
253 if (info.pr_nlwp == 0)
254 (void) strcpy(p->psargs, "<defunct>");
255 else if (info.pr_psargs[0] == '\0')
256 (void) strncpy(p->psargs, info.pr_fname,
257 sizeof (p->psargs));
258 else
259 (void) strncpy(p->psargs, info.pr_psargs,
260 sizeof (p->psargs));
261 p->psargs[sizeof (p->psargs)-1] = '\0';
262 p->pp = NULL;
263 p->sp = NULL;
264 p->cp = NULL;
265
266 if (sflag)
267 p_get_svc_fmri(p, NULL);
268
269 if (p->pid == p->ppid)
270 proc0 = p;
271 if (p->pid == 1)
272 proc1 = p;
273 }
274
275 (void) closedir(dirp);
276 if (proc0 == NULL)
277 proc0 = fakepid0();
278 if (proc1 == NULL)
279 proc1 = proc0;
280
281 for (n = 0; n < nps; n++) {
282 p = ps[n];
283 if (p->pp == NULL)
284 prsort(p);
285 }
286
287 if (cflag)
288 /* Parent all orphan contracts to process 0. */
335 }
336
337 for (n = 0; n < nps; n++) {
338 ps_t *p = ps[n];
339
340 /*
341 * A match on pid causes the subtree starting at pid
342 * to be printed, regardless of the -a flag.
343 * For uid matches, we never include pid 0 and only
344 * include the children of pid 0 if -a was specified.
345 */
346 if (p->pid == pid || (p->uid == uid && p->pid != 0 &&
347 (p->ppid != 0 || aflag))) {
348 errflg = 0;
349 markprocs(p);
350 if (p->pid != 0)
351 for (p = p->pp; p != NULL &&
352 p->done != 1 && p->pid != 0;
353 p = p->pp)
354 if ((p->ppid != 0 || aflag) &&
355 match_proc(p))
356 p->done = 1;
357 if (uid == (uid_t)-1)
358 break;
359 }
360 }
361 }
362
363 printsubtree(proc0, 0);
364 /*
365 * retc = 1 if an invalid username was supplied.
366 * errflg = 1 if no matching processes were found.
367 */
368 return (retc || errflg);
369 }
370
371 #define PIDWIDTH 5
372
373 static int
374 printone(ps_t *p, int level)
375 {
376 int n, indent;
377
378 if (p->done && !FAKEDPID0(p)) {
379 indent = level * 2;
380 if ((n = columns - PIDWIDTH - indent - 2) < 0)
381 n = 0;
382 if (p->pid >= 0) {
383 (void) printf("%*.*s%-*d %.*s\n", indent, indent, " ",
384 PIDWIDTH, (int)p->pid, n, p->psargs);
385 } else {
386 assert(cflag != 0);
387 (void) printf("%*.*s[process contract %d: %s]\n",
388 indent, indent, " ", (int)p->ctid,
389 p->svc_fmri == NULL ? "?" : p->svc_fmri);
390 }
391 return (1);
392 }
393 return (0);
394 }
395
396 static void
397 insertchild(ps_t *pp, ps_t *cp)
398 {
399 /* insert as child process of p */
400 ps_t **here;
401 ps_t *sp;
402
403 /* sort by start time */
404 for (here = &pp->cp, sp = pp->cp;
405 sp != NULL;
406 here = &sp->sp, sp = sp->sp) {
407 if (cp->start.tv_sec < sp->start.tv_sec)
408 break;
409 if (cp->start.tv_sec == sp->start.tv_sec &&
410 cp->start.tv_nsec < sp->start.tv_nsec)
411 break;
412 }
413 cp->pp = pp;
414 cp->sp = sp;
415 *here = cp;
416 }
417
418 static ct_stathdl_t
419 ct_status_open(ctid_t ctid, struct stat64 *stp)
420 {
421 ct_stathdl_t hdl;
422 int fd;
423
424 if ((fd = contract_open(ctid, "process", "status", O_RDONLY)) == -1)
425 return (NULL);
426
427 if (fstat64(fd, stp) == -1 || ct_status_read(fd, CTD_FIXED, &hdl)) {
428 (void) close(fd);
429 return (NULL);
430 }
431
432 (void) close(fd);
433
434 return (hdl);
435 }
436
437 /*
438 * strdup() failure is OK - better to report something than fail totally.
439 */
440 static void
441 p_get_svc_fmri(ps_t *p, ct_stathdl_t inhdl)
442 {
443 ct_stathdl_t hdl = inhdl;
444 struct stat64 st;
445 char *fmri;
446
447 if (hdl == NULL && (hdl = ct_status_open(p->ctid, &st)) == NULL)
448 return;
449
450 if (ct_pr_status_get_svc_fmri(hdl, &fmri) == 0)
451 p->svc_fmri = strdup(fmri);
452
453 if (inhdl == NULL)
454 ct_status_free(hdl);
455 }
456
457 static void
458 ctsort(ctid_t ctid, ps_t *p)
459 {
460 ps_t *pp;
461 int n;
462 ct_stathdl_t hdl;
463 struct stat64 st;
464
465 for (n = 0; n < nctps; n++)
466 if (ctps[n]->ctid == ctid) {
467 insertchild(ctps[n], p);
468 return;
469 }
470
471 if ((hdl = ct_status_open(ctid, &st)) == NULL)
472 return;
473
474 if (nctps >= ctsize) {
475 if ((ctsize *= 2) == 0)
476 ctsize = 20;
477 if ((ctps = realloc(ctps, ctsize * sizeof (ps_t *))) == NULL) {
478 perror("realloc()");
479 exit(1);
480 }
481 }
482 pp = calloc(sizeof (ps_t), 1);
483 if (pp == NULL) {
484 perror("calloc()");
485 exit(1);
486 }
487 ctps[nctps++] = pp;
488
489 pp->pid = -1;
490 pp->ctid = ctid;
491
492 p_get_svc_fmri(pp, hdl);
493
494 pp->start.tv_sec = st.st_ctime;
495 insertchild(pp, p);
496
497 pp->zoneid = ct_status_get_zoneid(hdl);
498
499 /*
500 * In a zlogin <zonename>, the contract belongs to the
501 * global zone and the shell opened belongs to <zonename>.
502 * If the -c and -z zonename flags are used together, then
503 * we need to adjust the zoneid in the contract's ps_t as
504 * follows:
505 *
506 * ptree -c -z <zonename> --> zoneid == p->zoneid
507 * ptree -c -z global --> zoneid == pp->zoneid
508 *
509 * The approach assumes that no tool can create processes in
510 * different zones under the same contract. If this is
511 * possible, ptree will need to refactor how it builds
512 * its internal tree of ps_t's
513 */
514 if (zflag && p->zoneid != pp->zoneid &&
515 (zoneid == p->zoneid || zoneid == pp->zoneid))
516 pp->zoneid = p->zoneid;
517 if (ct_status_get_state(hdl) == CTS_OWNED) {
518 pp->ppid = ct_status_get_holder(hdl);
548 }
549 }
550
551 /* File parentless processes under their contracts */
552 if (cflag && p->pid >= 0)
553 ctsort(p->ctid, p);
554 }
555
556 static void
557 printsubtree(ps_t *p, int level)
558 {
559 int printed;
560
561 printed = printone(p, level);
562 if (level != 0 || printed == 1)
563 level++;
564 for (p = p->cp; p != NULL; p = p->sp)
565 printsubtree(p, level);
566 }
567
568 /*
569 * Match against the service name (and just the final component), and any
570 * specified instance name.
571 */
572 static bool
573 match_proc(ps_t *p)
574 {
575 bool matched = false;
576 const char *cp;
577 char *p_inst;
578 char *p_svc;
579
580 if (zflag && p->zoneid != zoneid)
581 return (false);
582
583 if (!sflag)
584 return (true);
585
586 if (p->svc_fmri == NULL)
587 return (false);
588
589 p_svc = parse_svc(p->svc_fmri, &p_inst);
590
591 if (strcmp(p_svc, match_svc) != 0 &&
592 ((cp = strrchr(p_svc, '/')) == NULL ||
593 strcmp(cp + 1, match_svc) != 0)) {
594 goto out;
595 }
596
597 if (strlen(match_inst) == 0 ||
598 strcmp(p_inst, match_inst) == 0)
599 matched = true;
600
601 out:
602 free(p_svc);
603 free(p_inst);
604 return (matched);
605 }
606
607 static void
608 markprocs(ps_t *p)
609 {
610 if (match_proc(p))
611 p->done = 1;
612
613 for (p = p->cp; p != NULL; p = p->sp)
614 markprocs(p);
615 }
616
617 /*
618 * If there's no "top" process, we fake one; it will be the parent of
619 * all orphans.
620 */
621 static ps_t *
622 fakepid0(void)
623 {
624 ps_t *p0, *p;
625 int n;
626
627 if ((p0 = calloc(1, sizeof (ps_t))) == NULL) {
628 perror("calloc()");
629 exit(1);
630 }
631 (void) memset(p0, '\0', sizeof (ps_t));
632
633 /* First build all partial process trees. */
634 for (n = 0; n < nps; n++) {
635 p = ps[n];
636 if (p->pp == NULL)
637 prsort(p);
638 }
639
640 /* Then adopt all orphans. */
641 for (n = 0; n < nps; n++) {
642 p = ps[n];
643 if (p->pp == NULL)
644 insertchild(p0, p);
645 }
646
647 /* We've made sure earlier there's room for this. */
648 ps[nps++] = p0;
649 return (p0);
650 }
651
652 /* convert string containing zone name or id to a numeric id */
653 static zoneid_t
654 getzone(const char *arg)
655 {
656 zoneid_t zoneid;
657
658 if (zone_get_id(arg, &zoneid) != 0) {
659 (void) fprintf(stderr, "%s: unknown zone: %s\n", command, arg);
660 exit(1);
661 }
662 return (zoneid);
663 }
664
665 /* svc:/mysvc:default -> mysvc, default */
666 static char *
667 parse_svc(const char *arg, char **instp)
668 {
669 const char *p = arg;
670 char *ret;
671 char *cp;
672
673 if (strncmp(p, "svc:/", strlen("svc:/")) == 0)
674 p += strlen("svc:/");
675
676 if ((ret = strdup(p)) == NULL) {
677 perror("strdup()");
678 exit(1);
679 }
680
681 if ((cp = strrchr(ret, ':')) != NULL) {
682 *cp = '\0';
683 cp++;
684 } else {
685 cp = "";
686 }
687
688 if ((*instp = strdup(cp)) == NULL) {
689 perror("strdup()");
690 exit(1);
691 }
692
693 return (ret);
694 }
|