Print this page
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/cmd/ptools/ptree/ptree.c
+++ new/usr/src/cmd/ptools/ptree/ptree.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 * Copyright (c) 2014, Joyent, Inc. All rights reserved.
25 25 */
26 26
27 27 /*
28 28 * ptree -- print family tree of processes
29 29 */
30 30
31 31 #include <assert.h>
32 32 #include <stdio.h>
33 33 #include <string.h>
34 34 #include <errno.h>
35 35 #include <fcntl.h>
36 36 #include <sys/types.h>
37 37 #include <sys/termios.h>
38 38 #include <unistd.h>
39 39 #include <stdlib.h>
40 40 #include <dirent.h>
41 41 #include <pwd.h>
42 42 #include <libproc.h>
43 43 #include <libzonecfg.h>
44 44 #include <limits.h>
45 45 #include <libcontract.h>
46 46 #include <sys/contract.h>
47 47 #include <sys/ctfs.h>
48 48 #include <libcontract_priv.h>
49 49 #include <sys/stat.h>
50 50 #include "ptools_common.h"
51 51
52 52 #define FAKEDPID0(p) (p->pid == 0 && p->psargs[0] == '\0')
53 53
54 54 typedef struct ps {
55 55 int done;
56 56 uid_t uid;
57 57 uid_t gid;
58 58 pid_t pid; /* pid == -1 indicates this is a contract */
59 59 pid_t ppid;
60 60 pid_t pgrp;
61 61 pid_t sid;
62 62 zoneid_t zoneid;
63 63 ctid_t ctid;
64 64 timestruc_t start;
65 65 char psargs[PRARGSZ];
66 66 struct ps *pp; /* parent */
67 67 struct ps *sp; /* sibling */
68 68 struct ps *cp; /* child */
69 69 } ps_t;
70 70
71 71 static ps_t **ps; /* array of ps_t's */
72 72 static unsigned psize; /* size of array */
73 73 static int nps; /* number of ps_t's */
74 74 static ps_t **ctps; /* array of contract ps_t's */
75 75 static unsigned ctsize; /* size of contract array */
76 76 static int nctps; /* number of contract ps_t's */
77 77 static ps_t *proc0; /* process 0 */
78 78 static ps_t *proc1; /* process 1 */
79 79
80 80 static char *command;
81 81
82 82 static int aflag = 0;
83 83 static int cflag = 0;
84 84 static int zflag = 0;
85 85 static zoneid_t zoneid;
86 86 static int columns = 80;
87 87
88 88 static void markprocs(ps_t *p);
89 89 static int printone(ps_t *p, int level);
90 90 static void insertchild(ps_t *, ps_t *);
91 91 static void prsort(ps_t *p);
92 92 static void printsubtree(ps_t *p, int level);
93 93 static zoneid_t getzone(char *arg);
94 94 static ps_t *fakepid0(void);
95 95
96 96 int
97 97 main(int argc, char **argv)
98 98 {
99 99 psinfo_t info; /* process information structure from /proc */
100 100 int opt;
101 101 int errflg = 0;
102 102 struct winsize winsize;
103 103 char *s;
104 104 int n;
105 105 int retc = 0;
106 106 char ppath[PATH_MAX];
107 107
108 108 DIR *dirp;
109 109 struct dirent *dentp;
110 110 char pname[PATH_MAX];
111 111 int pdlen;
112 112
113 113 ps_t *p;
114 114
115 115 if ((command = strrchr(argv[0], '/')) == NULL)
116 116 command = argv[0];
117 117 else
118 118 command++;
119 119
120 120 /* options */
121 121 while ((opt = getopt(argc, argv, "acz:")) != EOF) {
122 122 switch (opt) {
123 123 case 'a': /* include children of process 0 */
124 124 aflag = 1;
125 125 break;
126 126 case 'c': /* display contract ownership */
127 127 aflag = cflag = 1;
128 128 break;
129 129 case 'z': /* only processes in given zone */
130 130 zflag = 1;
131 131 zoneid = getzone(optarg);
132 132 break;
133 133 default:
134 134 errflg = 1;
135 135 break;
136 136 }
137 137 }
138 138
139 139 argc -= optind;
140 140 argv += optind;
141 141
142 142 if (errflg) {
143 143 (void) fprintf(stderr,
144 144 "usage:\t%s [-ac] [-z zone] [ {pid|user} ... ]\n",
145 145 command);
146 146 (void) fprintf(stderr,
147 147 " (show process trees)\n");
148 148 (void) fprintf(stderr,
149 149 " list can include process-ids and user names\n");
150 150 (void) fprintf(stderr,
151 151 " -a : include children of process 0\n");
152 152 (void) fprintf(stderr,
153 153 " -c : show contract ownership\n");
154 154 (void) fprintf(stderr,
155 155 " -z : print only processes in given zone\n");
156 156 return (2);
157 157 }
158 158
159 159 /*
160 160 * Kind of a hack to determine the width of the output...
161 161 */
162 162 if ((s = getenv("COLUMNS")) != NULL && (n = atoi(s)) > 0)
163 163 columns = n;
164 164 else if (isatty(fileno(stdout)) &&
165 165 ioctl(fileno(stdout), TIOCGWINSZ, &winsize) == 0 &&
166 166 winsize.ws_col != 0)
167 167 columns = winsize.ws_col;
168 168
169 169 nps = 0;
170 170 psize = 0;
171 171 ps = NULL;
172 172
173 173 (void) proc_snprintf(ppath, sizeof (ppath), "/proc");
174 174
175 175 /*
176 176 * Search the /proc directory for all processes.
177 177 */
178 178 if ((dirp = opendir(ppath)) == NULL) {
179 179 (void) fprintf(stderr, "%s: cannot open %s directory\n",
180 180 command, ppath);
181 181 return (1);
182 182 }
183 183
184 184 (void) strcpy(pname, ppath);
185 185 pdlen = strlen(pname);
186 186 pname[pdlen++] = '/';
187 187
188 188 /* for each active process --- */
189 189 while (dentp = readdir(dirp)) {
190 190 int procfd; /* filedescriptor for /proc/nnnnn/psinfo */
191 191
192 192 if (dentp->d_name[0] == '.') /* skip . and .. */
193 193 continue;
194 194 (void) strcpy(pname + pdlen, dentp->d_name);
195 195 (void) strcpy(pname + strlen(pname), "/psinfo");
196 196 retry:
197 197 if ((procfd = open(pname, O_RDONLY)) == -1)
198 198 continue;
199 199
200 200 /*
201 201 * Get the info structure for the process and close quickly.
202 202 */
203 203 if (read(procfd, &info, sizeof (info)) != sizeof (info)) {
204 204 int saverr = errno;
205 205
206 206 (void) close(procfd);
207 207 if (saverr == EAGAIN)
208 208 goto retry;
209 209 if (saverr != ENOENT)
210 210 perror(pname);
211 211 continue;
212 212 }
213 213 (void) close(procfd);
214 214
215 215 /*
216 216 * We make sure there's always a free slot in the table
217 217 * in case we need to add a fake p0.
218 218 */
219 219 if (nps + 1 >= psize) {
220 220 if ((psize *= 2) == 0)
221 221 psize = 20;
222 222 if ((ps = realloc(ps, psize*sizeof (ps_t *))) == NULL) {
223 223 perror("realloc()");
224 224 return (1);
225 225 }
226 226 }
227 227 if ((p = malloc(sizeof (ps_t))) == NULL) {
228 228 perror("malloc()");
229 229 return (1);
230 230 }
231 231 ps[nps++] = p;
232 232 p->done = 0;
233 233 p->uid = info.pr_uid;
234 234 p->gid = info.pr_gid;
235 235 p->pid = info.pr_pid;
236 236 p->ppid = info.pr_ppid;
237 237 p->pgrp = info.pr_pgid;
238 238 p->sid = info.pr_sid;
239 239 p->zoneid = info.pr_zoneid;
240 240 p->ctid = info.pr_contract;
241 241 p->start = info.pr_start;
242 242 proc_unctrl_psinfo(&info);
243 243 if (info.pr_nlwp == 0)
244 244 (void) strcpy(p->psargs, "<defunct>");
245 245 else if (info.pr_psargs[0] == '\0')
246 246 (void) strncpy(p->psargs, info.pr_fname,
247 247 sizeof (p->psargs));
248 248 else
249 249 (void) strncpy(p->psargs, info.pr_psargs,
250 250 sizeof (p->psargs));
251 251 p->psargs[sizeof (p->psargs)-1] = '\0';
252 252 p->pp = NULL;
253 253 p->sp = NULL;
254 254 p->cp = NULL;
255 255 if (p->pid == p->ppid)
256 256 proc0 = p;
257 257 if (p->pid == 1)
258 258 proc1 = p;
259 259 }
260 260
261 261 (void) closedir(dirp);
262 262 if (proc0 == NULL)
263 263 proc0 = fakepid0();
264 264 if (proc1 == NULL)
265 265 proc1 = proc0;
266 266
267 267 for (n = 0; n < nps; n++) {
268 268 p = ps[n];
269 269 if (p->pp == NULL)
270 270 prsort(p);
271 271 }
272 272
273 273 if (cflag)
274 274 /* Parent all orphan contracts to process 0. */
275 275 for (n = 0; n < nctps; n++) {
276 276 p = ctps[n];
277 277 if (p->pp == NULL)
278 278 insertchild(proc0, p);
279 279 }
280 280
281 281 if (argc == 0) {
282 282 for (p = aflag ? proc0->cp : proc1->cp; p != NULL; p = p->sp) {
283 283 markprocs(p);
284 284 printsubtree(p, 0);
285 285 }
286 286 return (0);
287 287 }
288 288
289 289 /*
290 290 * Initially, assume we're not going to find any processes. If we do
291 291 * mark any, then set this to 0 to indicate no error.
292 292 */
293 293 errflg = 1;
294 294
295 295 while (argc-- > 0) {
296 296 char *arg;
297 297 char *next;
298 298 pid_t pid;
299 299 uid_t uid;
300 300 int n;
301 301
302 302 /* in case some silly person said 'ptree /proc/[0-9]*' */
303 303 arg = strrchr(*argv, '/');
304 304 if (arg++ == NULL)
305 305 arg = *argv;
306 306 argv++;
307 307 uid = (uid_t)-1;
308 308 errno = 0;
309 309 pid = strtoul(arg, &next, 10);
310 310 if (errno != 0 || *next != '\0') {
311 311 struct passwd *pw = getpwnam(arg);
312 312 if (pw == NULL) {
313 313 (void) fprintf(stderr,
314 314 "%s: invalid username: %s\n",
315 315 command, arg);
316 316 retc = 1;
317 317 continue;
318 318 }
319 319 uid = pw->pw_uid;
320 320 pid = -1;
321 321 }
322 322
323 323 for (n = 0; n < nps; n++) {
324 324 ps_t *p = ps[n];
325 325
326 326 /*
327 327 * A match on pid causes the subtree starting at pid
328 328 * to be printed, regardless of the -a flag.
329 329 * For uid matches, we never include pid 0 and only
330 330 * include the children of pid 0 if -a was specified.
331 331 */
332 332 if (p->pid == pid || (p->uid == uid && p->pid != 0 &&
333 333 (p->ppid != 0 || aflag))) {
334 334 errflg = 0;
335 335 markprocs(p);
336 336 if (p->pid != 0)
337 337 for (p = p->pp; p != NULL &&
338 338 p->done != 1 && p->pid != 0;
339 339 p = p->pp)
340 340 if ((p->ppid != 0 || aflag) &&
341 341 (!zflag ||
342 342 p->zoneid == zoneid))
343 343 p->done = 1;
344 344 if (uid == (uid_t)-1)
345 345 break;
346 346 }
347 347 }
348 348 }
349 349
350 350 printsubtree(proc0, 0);
351 351 /*
352 352 * retc = 1 if an invalid username was supplied.
353 353 * errflg = 1 if no matching processes were found.
354 354 */
355 355 return (retc || errflg);
356 356 }
357 357
358 358 #define PIDWIDTH 5
359 359
360 360 static int
361 361 printone(ps_t *p, int level)
362 362 {
363 363 int n, indent;
364 364
365 365 if (p->done && !FAKEDPID0(p)) {
366 366 indent = level * 2;
367 367 if ((n = columns - PIDWIDTH - indent - 2) < 0)
368 368 n = 0;
369 369 if (p->pid >= 0) {
370 370 (void) printf("%*.*s%-*d %.*s\n", indent, indent, " ",
371 371 PIDWIDTH, (int)p->pid, n, p->psargs);
372 372 } else {
373 373 assert(cflag != 0);
374 374 (void) printf("%*.*s[process contract %d]\n",
375 375 indent, indent, " ", (int)p->ctid);
376 376 }
377 377 return (1);
378 378 }
379 379 return (0);
380 380 }
381 381
382 382 static void
383 383 insertchild(ps_t *pp, ps_t *cp)
384 384 {
385 385 /* insert as child process of p */
386 386 ps_t **here;
387 387 ps_t *sp;
388 388
389 389 /* sort by start time */
390 390 for (here = &pp->cp, sp = pp->cp;
391 391 sp != NULL;
392 392 here = &sp->sp, sp = sp->sp) {
393 393 if (cp->start.tv_sec < sp->start.tv_sec)
394 394 break;
395 395 if (cp->start.tv_sec == sp->start.tv_sec &&
396 396 cp->start.tv_nsec < sp->start.tv_nsec)
397 397 break;
398 398 }
399 399 cp->pp = pp;
400 400 cp->sp = sp;
401 401 *here = cp;
402 402 }
403 403
404 404 static void
405 405 ctsort(ctid_t ctid, ps_t *p)
406 406 {
407 407 ps_t *pp;
408 408 int fd, n;
409 409 ct_stathdl_t hdl;
410 410 struct stat64 st;
411 411
412 412 for (n = 0; n < nctps; n++)
413 413 if (ctps[n]->ctid == ctid) {
414 414 insertchild(ctps[n], p);
415 415 return;
416 416 }
417 417
418 418 if ((fd = contract_open(ctid, "process", "status", O_RDONLY)) == -1)
419 419 return;
420 420 if (fstat64(fd, &st) == -1 || ct_status_read(fd, CTD_COMMON, &hdl)) {
421 421 (void) close(fd);
422 422 return;
423 423 }
424 424 (void) close(fd);
425 425
426 426 if (nctps >= ctsize) {
427 427 if ((ctsize *= 2) == 0)
428 428 ctsize = 20;
429 429 if ((ctps = realloc(ctps, ctsize * sizeof (ps_t *))) == NULL) {
430 430 perror("realloc()");
431 431 exit(1);
432 432 }
433 433 }
434 434 pp = calloc(sizeof (ps_t), 1);
435 435 if (pp == NULL) {
436 436 perror("calloc()");
437 437 exit(1);
438 438 }
439 439 ctps[nctps++] = pp;
440 440
441 441 pp->pid = -1;
442 442 pp->ctid = ctid;
443 443 pp->start.tv_sec = st.st_ctime;
444 444 insertchild(pp, p);
445 445
446 446 pp->zoneid = ct_status_get_zoneid(hdl);
447 447 /*
448 448 * In a zlogin <zonename>, the contract belongs to the
449 449 * global zone and the shell opened belongs to <zonename>.
450 450 * If the -c and -z zonename flags are used together, then
451 451 * we need to adjust the zoneid in the contract's ps_t as
452 452 * follows:
453 453 *
454 454 * ptree -c -z <zonename> --> zoneid == p->zoneid
455 455 * ptree -c -z global --> zoneid == pp->zoneid
456 456 *
457 457 * The approach assumes that no tool can create processes in
458 458 * different zones under the same contract. If this is
459 459 * possible, ptree will need to refactor how it builds
460 460 * its internal tree of ps_t's
461 461 */
462 462 if (zflag && p->zoneid != pp->zoneid &&
463 463 (zoneid == p->zoneid || zoneid == pp->zoneid))
464 464 pp->zoneid = p->zoneid;
465 465 if (ct_status_get_state(hdl) == CTS_OWNED) {
466 466 pp->ppid = ct_status_get_holder(hdl);
467 467 prsort(pp);
468 468 } else if (ct_status_get_state(hdl) == CTS_INHERITED) {
469 469 ctsort(ct_status_get_holder(hdl), pp);
470 470 }
471 471 ct_status_free(hdl);
472 472 }
473 473
474 474 static void
475 475 prsort(ps_t *p)
476 476 {
477 477 int n;
478 478 ps_t *pp;
479 479
480 480 /* If this node already has a parent, it's sorted */
481 481 if (p->pp != NULL)
482 482 return;
483 483
484 484 for (n = 0; n < nps; n++) {
485 485 pp = ps[n];
486 486
487 487 if (pp != NULL && p != pp && p->ppid == pp->pid) {
488 488 if (cflag && p->pid >= 0 &&
489 489 p->ctid != -1 && p->ctid != pp->ctid) {
490 490 ctsort(p->ctid, p);
491 491 } else {
492 492 insertchild(pp, p);
493 493 prsort(pp);
494 494 }
495 495 return;
496 496 }
497 497 }
498 498
499 499 /* File parentless processes under their contracts */
500 500 if (cflag && p->pid >= 0)
501 501 ctsort(p->ctid, p);
502 502 }
503 503
504 504 static void
505 505 printsubtree(ps_t *p, int level)
506 506 {
507 507 int printed;
508 508
509 509 printed = printone(p, level);
510 510 if (level != 0 || printed == 1)
511 511 level++;
512 512 for (p = p->cp; p != NULL; p = p->sp)
513 513 printsubtree(p, level);
514 514 }
515 515
516 516 static void
517 517 markprocs(ps_t *p)
518 518 {
519 519 if (!zflag || p->zoneid == zoneid)
520 520 p->done = 1;
521 521 for (p = p->cp; p != NULL; p = p->sp)
522 522 markprocs(p);
523 523 }
524 524
525 525 /*
526 526 * If there's no "top" process, we fake one; it will be the parent of
527 527 * all orphans.
528 528 */
529 529 static ps_t *
530 530 fakepid0(void)
531 531 {
532 532 ps_t *p0, *p;
533 533 int n;
534 534
535 535 if ((p0 = malloc(sizeof (ps_t))) == NULL) {
536 536 perror("malloc()");
537 537 exit(1);
538 538 }
539 539 (void) memset(p0, '\0', sizeof (ps_t));
540 540
541 541 /* First build all partial process trees. */
542 542 for (n = 0; n < nps; n++) {
543 543 p = ps[n];
544 544 if (p->pp == NULL)
545 545 prsort(p);
546 546 }
547 547
548 548 /* Then adopt all orphans. */
549 549 for (n = 0; n < nps; n++) {
550 550 p = ps[n];
551 551 if (p->pp == NULL)
552 552 insertchild(p0, p);
553 553 }
554 554
555 555 /* We've made sure earlier there's room for this. */
556 556 ps[nps++] = p0;
557 557 return (p0);
558 558 }
559 559
560 560 /* convert string containing zone name or id to a numeric id */
561 561 static zoneid_t
562 562 getzone(char *arg)
563 563 {
564 564 zoneid_t zoneid;
565 565
566 566 if (zone_get_id(arg, &zoneid) != 0) {
567 567 (void) fprintf(stderr, "%s: unknown zone: %s\n", command, arg);
568 568 exit(1);
569 569 }
570 570 return (zoneid);
571 571 }
|
↓ open down ↓ |
571 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX