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