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 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/ctfs.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 #include <fcntl.h> 35 #include <string.h> 36 #include <errno.h> 37 #include <libuutil.h> 38 #include <sys/contract/process.h> 39 #include <limits.h> 40 #include <libcontract.h> 41 #include <libcontract_priv.h> 42 #include <dirent.h> 43 44 #include <locale.h> 45 #include <langinfo.h> 46 47 static int opt_verbose = 0; 48 static int opt_showall = 0; 49 50 /* 51 * usage 52 * 53 * Educate the user. 54 */ 55 static void 56 usage(void) 57 { 58 (void) fprintf(stderr, gettext("Usage: %s [-a] [-i ctidlist] " 59 "[-t typelist] [-v] [interval [count]]\n"), uu_getpname()); 60 exit(UU_EXIT_USAGE); 61 } 62 63 /* 64 * mystrtoul 65 * 66 * Convert a string into an int in [0, INT_MAX]. Exit if the argument 67 * doen't fit this description. 68 */ 69 static int 70 mystrtoul(const char *arg) 71 { 72 unsigned int result; 73 74 if (uu_strtoint(arg, &result, sizeof (result), 10, 0, INT_MAX) == -1) { 75 uu_warn(gettext("invalid numerical argument \"%s\"\n"), arg); 76 usage(); 77 } 78 79 return (result); 80 } 81 82 /* 83 * int_compar 84 * 85 * A simple integer comparator. Also used for id_ts, since they're the 86 * same thing. 87 */ 88 static int 89 int_compar(const void *a1, const void *a2) 90 { 91 int id1 = *(int *)a1; 92 int id2 = *(int *)a2; 93 94 if (id1 > id2) 95 return (1); 96 if (id2 > id1) 97 return (-1); 98 return (0); 99 } 100 101 typedef struct optvect { 102 const char *option; 103 uint_t bit; 104 } optvect_t; 105 106 static optvect_t option_params[] = { 107 { "inherit", CT_PR_INHERIT }, 108 { "noorphan", CT_PR_NOORPHAN }, 109 { "pgrponly", CT_PR_PGRPONLY }, 110 { "regent", CT_PR_REGENT }, 111 { NULL } 112 }; 113 114 static optvect_t option_events[] = { 115 { "core", CT_PR_EV_CORE }, 116 { "signal", CT_PR_EV_SIGNAL }, 117 { "hwerr", CT_PR_EV_HWERR }, 118 { "empty", CT_PR_EV_EMPTY }, 119 { "fork", CT_PR_EV_FORK }, 120 { "exit", CT_PR_EV_EXIT }, 121 { NULL } 122 }; 123 124 /* 125 * print_bits 126 * 127 * Display a set whose membership is identified by a bitfield. 128 */ 129 static void 130 print_bits(uint_t bits, optvect_t *desc) 131 { 132 int i, printed = 0; 133 134 for (i = 0; desc[i].option; i++) 135 if (desc[i].bit & bits) { 136 if (printed) 137 (void) putchar(' '); 138 printed = 1; 139 (void) fputs(desc[i].option, stdout); 140 } 141 if (printed) 142 (void) putchar('\n'); 143 else 144 (void) puts("none"); 145 } 146 147 /* 148 * print_ids 149 * 150 * Display a list of ids, sorted. 151 */ 152 static void 153 print_ids(id_t *ids, uint_t nids) 154 { 155 int i; 156 int first = 1; 157 158 qsort(ids, nids, sizeof (int), int_compar); 159 160 for (i = 0; i < nids; i++) { 161 /*LINTED*/ 162 (void) printf(" %d" + first, ids[i]); 163 first = 0; 164 } 165 if (first) 166 (void) puts("none"); 167 else 168 (void) putchar('\n'); 169 } 170 171 typedef void printfunc_t(ct_stathdl_t); 172 173 /* 174 * A structure defining a displayed field. Includes a label to be 175 * printed along side the field value, and a function which extracts 176 * the data from a status structure, formats it, and displays it on 177 * stdout. 178 */ 179 typedef struct verbout { 180 const char *label; /* field label */ 181 printfunc_t *func; /* field display function */ 182 } verbout_t; 183 184 /* 185 * verb_cookie 186 * 187 * Used to display an error encountered when reading a contract status 188 * field. 189 */ 190 static void 191 verb_error(int err) 192 { 193 (void) printf("(error: %s)\n", strerror(err)); 194 } 195 196 /* 197 * verb_cookie 198 * 199 * Display the contract's cookie. 200 */ 201 static void 202 verb_cookie(ct_stathdl_t hdl) 203 { 204 (void) printf("%#llx\n", ct_status_get_cookie(hdl)); 205 } 206 207 /* 208 * verb_info 209 * 210 * Display the parameters in the parameter set. 211 */ 212 static void 213 verb_param(ct_stathdl_t hdl) 214 { 215 uint_t param; 216 int err; 217 218 if (err = ct_pr_status_get_param(hdl, ¶m)) 219 verb_error(err); 220 else 221 print_bits(param, option_params); 222 } 223 224 /* 225 * verb_info 226 * 227 * Display the events in the informative event set. 228 */ 229 static void 230 verb_info(ct_stathdl_t hdl) 231 { 232 print_bits(ct_status_get_informative(hdl), option_events); 233 } 234 235 /* 236 * verb_crit 237 * 238 * Display the events in the critical event set. 239 */ 240 static void 241 verb_crit(ct_stathdl_t hdl) 242 { 243 print_bits(ct_status_get_critical(hdl), option_events); 244 } 245 246 /* 247 * verb_fatal 248 * 249 * Display the events in the fatal event set. 250 */ 251 static void 252 verb_fatal(ct_stathdl_t hdl) 253 { 254 uint_t event; 255 int err; 256 257 if (err = ct_pr_status_get_fatal(hdl, &event)) 258 verb_error(err); 259 else 260 print_bits(event, option_events); 261 } 262 263 /* 264 * verb_members 265 * 266 * Display the list of member contracts. 267 */ 268 static void 269 verb_members(ct_stathdl_t hdl) 270 { 271 pid_t *pids; 272 uint_t npids; 273 int err; 274 275 if (err = ct_pr_status_get_members(hdl, &pids, &npids)) { 276 verb_error(err); 277 return; 278 } 279 280 print_ids(pids, npids); 281 } 282 283 /* 284 * verb_inherit 285 * 286 * Display the list of inherited contracts. 287 */ 288 static void 289 verb_inherit(ct_stathdl_t hdl) 290 { 291 ctid_t *ctids; 292 uint_t nctids; 293 int err; 294 295 if (err = ct_pr_status_get_contracts(hdl, &ctids, &nctids)) 296 verb_error(err); 297 else 298 print_ids(ctids, nctids); 299 } 300 301 /* 302 * Common contract status fields. 303 */ 304 static verbout_t vcommon[] = { 305 "cookie", verb_cookie, 306 NULL, 307 }; 308 309 /* 310 * Process contract-specific status fields. 311 * The critical and informative event sets are here because the event 312 * names are contract-specific. They are listed first, however, so 313 * they are displayed adjacent to the "normal" common output. 314 */ 315 static verbout_t vprocess[] = { 316 "informative event set", verb_info, 317 "critical event set", verb_crit, 318 "fatal event set", verb_fatal, 319 "parameter set", verb_param, 320 "member processes", verb_members, 321 "inherited contracts", verb_inherit, 322 NULL 323 }; 324 325 /* 326 * print_verbose 327 * 328 * Displays a contract's verbose status, common fields first. 329 */ 330 static void 331 print_verbose(ct_stathdl_t hdl, verbout_t *spec, verbout_t *common) 332 { 333 int i; 334 int tmp, maxwidth = 0; 335 336 /* 337 * Compute the width of all the fields. 338 */ 339 for (i = 0; common[i].label; i++) 340 if ((tmp = strlen(common[i].label)) > maxwidth) 341 maxwidth = tmp; 342 if (spec) 343 for (i = 0; spec[i].label; i++) 344 if ((tmp = strlen(spec[i].label)) > maxwidth) 345 maxwidth = tmp; 346 maxwidth += 2; 347 348 /* 349 * Display the data. 350 */ 351 for (i = 0; common[i].label; i++) { 352 tmp = printf("\t%s", common[i].label); 353 if (tmp < 0) 354 tmp = 0; 355 (void) printf("%-*s", maxwidth - tmp + 1, ":"); 356 common[i].func(hdl); 357 } 358 if (spec) 359 for (i = 0; spec[i].label; i++) { 360 (void) printf("\t%s%n", spec[i].label, &tmp); 361 (void) printf("%-*s", maxwidth - tmp + 1, ":"); 362 spec[i].func(hdl); 363 } 364 } 365 366 struct { 367 const char *name; 368 verbout_t *verbout; 369 } cttypes[] = { 370 { "process", vprocess }, 371 { NULL } 372 }; 373 374 /* 375 * get_type 376 * 377 * Given a type name, return an index into the above array of types. 378 */ 379 static int 380 get_type(const char *typestr) 381 { 382 int i; 383 for (i = 0; cttypes[i].name; i++) 384 if (strcmp(cttypes[i].name, typestr) == 0) 385 return (i); 386 uu_die(gettext("invalid contract type: %s\n"), typestr); 387 /* NOTREACHED */ 388 } 389 390 /* 391 * print_header 392 * 393 * Display the status header. 394 */ 395 static void 396 print_header(void) 397 { 398 (void) printf("%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s\n", "CTID", "ZONEID", 399 "TYPE", "STATE", "HOLDER", "EVENTS", "QTIME", "NTIME"); 400 } 401 402 /* 403 * print_contract 404 * 405 * Display status for contract ID 'id' from type directory 'dir'. If 406 * only contracts of a specific set of types should be displayed, 407 * 'types' will be a sorted list of type indices of length 'ntypes'. 408 */ 409 static void 410 print_contract(const char *dir, ctid_t id, verbout_t *spec, 411 int *types, int ntypes) 412 { 413 ct_stathdl_t status; 414 char hstr[100], qstr[20], nstr[20]; 415 ctstate_t state; 416 int fd = 0; 417 int t; 418 419 /* 420 * Open and obtain status. 421 */ 422 if ((fd = contract_open(id, dir, "status", O_RDONLY)) == -1) { 423 if (errno == ENOENT) 424 return; 425 uu_die(gettext("could not open contract status file")); 426 } 427 428 if (errno = ct_status_read(fd, opt_verbose ? CTD_ALL : CTD_COMMON, 429 &status)) 430 uu_die(gettext("failed to get contract status for %d"), id); 431 (void) close(fd); 432 433 /* 434 * Unless otherwise directed, don't display dead contracts. 435 */ 436 state = ct_status_get_state(status); 437 if (!opt_showall && state == CTS_DEAD) { 438 ct_status_free(status); 439 return; 440 } 441 442 /* 443 * If we are only allowed to display certain contract types, 444 * perform that filtering here. We stash a copy of spec so we 445 * don't have to recompute it later. 446 */ 447 if (types) { 448 int key = get_type(ct_status_get_type(status)); 449 spec = cttypes[key].verbout; 450 if (bsearch(&key, types, ntypes, sizeof (int), int_compar) == 451 NULL) { 452 ct_status_free(status); 453 return; 454 } 455 } 456 457 /* 458 * Precompute those fields which have both textual and 459 * numerical values. 460 */ 461 if ((state == CTS_OWNED) || (state == CTS_INHERITED)) 462 (void) snprintf(hstr, sizeof (hstr), "%ld", 463 ct_status_get_holder(status)); 464 else 465 (void) snprintf(hstr, sizeof (hstr), "%s", "-"); 466 467 if ((t = ct_status_get_qtime(status)) == -1) { 468 qstr[0] = nstr[0] = '-'; 469 qstr[1] = nstr[1] = '\0'; 470 } else { 471 (void) snprintf(qstr, sizeof (qstr), "%d", t); 472 (void) snprintf(nstr, sizeof (nstr), "%d", 473 ct_status_get_ntime(status)); 474 } 475 476 /* 477 * Emit the contract's status. 478 */ 479 (void) printf("%-7ld %-7ld %-7s %-7s %-7s %-7d %-7s %-8s\n", 480 ct_status_get_id(status), 481 ct_status_get_zoneid(status), 482 ct_status_get_type(status), 483 (state == CTS_OWNED) ? "owned" : 484 (state == CTS_INHERITED) ? "inherit" : 485 (state == CTS_ORPHAN) ? "orphan" : "dead", hstr, 486 ct_status_get_nevents(status), qstr, nstr); 487 488 /* 489 * Emit verbose status information, if requested. If we 490 * weren't provided a verbose output spec or didn't compute it 491 * earlier, do it now. 492 */ 493 if (opt_verbose) { 494 if (spec == NULL) 495 spec = cttypes[get_type(ct_status_get_type(status))]. 496 verbout; 497 print_verbose(status, spec, vcommon); 498 } 499 500 ct_status_free(status); 501 } 502 503 /* 504 * scan_type 505 * 506 * Display all contracts of the requested type. 507 */ 508 static void 509 scan_type(int typeno) 510 { 511 DIR *dir; 512 struct dirent64 *de; 513 char path[PATH_MAX]; 514 515 verbout_t *vo = cttypes[typeno].verbout; 516 const char *type = cttypes[typeno].name; 517 518 if (snprintf(path, PATH_MAX, CTFS_ROOT "/%s", type) >= PATH_MAX || 519 (dir = opendir(path)) == NULL) 520 uu_die(gettext("bad contract type: %s\n"), type); 521 while ((de = readdir64(dir)) != NULL) { 522 /* 523 * Eliminate special files (e.g. '.', '..'). 524 */ 525 if (de->d_name[0] < '0' || de->d_name[0] > '9') 526 continue; 527 print_contract(type, mystrtoul(de->d_name), vo, NULL, 0); 528 } 529 (void) closedir(dir); 530 } 531 532 /* 533 * scan_ids 534 * 535 * Display all contracts with the requested IDs. 536 */ 537 static void 538 scan_ids(ctid_t *ids, int nids) 539 { 540 int i; 541 for (i = 0; i < nids; i++) 542 print_contract("all", ids[i], NULL, NULL, 0); 543 } 544 545 /* 546 * scan_all 547 * 548 * Display the union of the requested IDs and types. So that the 549 * output is sorted by contract ID, it takes the slow road by testing 550 * each entry in /system/contract/all against its criteria. Used when 551 * the number of types is greater than 1, when we have a mixture of 552 * types and ids, or no lists were provided at all. 553 */ 554 static void 555 scan_all(int *types, int ntypes, ctid_t *ids, int nids) 556 { 557 DIR *dir; 558 struct dirent64 *de; 559 const char *path = CTFS_ROOT "/all"; 560 int key, test; 561 562 if ((dir = opendir(path)) == NULL) 563 uu_die(gettext("could not open %s"), path); 564 while ((de = readdir64(dir)) != NULL) { 565 /* 566 * Eliminate special files (e.g. '.', '..'). 567 */ 568 if (de->d_name[0] < '0' || de->d_name[0] > '9') 569 continue; 570 key = mystrtoul(de->d_name); 571 572 /* 573 * If we are given IDs to look at and this contract 574 * isn't in the ID list, or if we weren't given a list 575 * if IDs but were given a list of types, provide the 576 * list of acceptable types to print_contract. 577 */ 578 test = nids ? (bsearch(&key, ids, nids, sizeof (int), 579 int_compar) == NULL) : (ntypes != 0); 580 print_contract("all", key, NULL, (test ? types : NULL), ntypes); 581 } 582 (void) closedir(dir); 583 } 584 585 /* 586 * walk_args 587 * 588 * Apply fp to each token in the comma- or space- separated argument 589 * string str and store the results in the array starting at results. 590 */ 591 static int 592 walk_args(const char *str, int (*fp)(const char *), int *results) 593 { 594 char *copy, *token; 595 int count = 0; 596 597 if ((copy = strdup(str)) == NULL) 598 uu_die(gettext("strdup() failed")); 599 600 token = strtok(copy, ", "); 601 if (token == NULL) { 602 free(copy); 603 return (0); 604 } 605 606 do { 607 if (fp) 608 *(results++) = fp(token); 609 count++; 610 } while (token = strtok(NULL, ", ")); 611 free(copy); 612 613 return (count); 614 } 615 616 /* 617 * parse 618 * 619 * Parse the comma- or space- separated string str, using fp to covert 620 * the tokens to integers. Append the list of integers to the array 621 * pointed to by *idps, growing the array if necessary. 622 */ 623 static int 624 parse(const char *str, int **idsp, int nids, int (*fp)(const char *fp)) 625 { 626 int count; 627 int *array; 628 629 count = walk_args(str, NULL, NULL); 630 if (count == 0) 631 return (0); 632 633 if ((array = calloc(nids + count, sizeof (int))) == NULL) 634 uu_die(gettext("calloc() failed")); 635 636 if (*idsp) { 637 (void) memcpy(array, *idsp, nids * sizeof (int)); 638 free(*idsp); 639 } 640 641 (void) walk_args(str, fp, array + nids); 642 643 *idsp = array; 644 return (count + nids); 645 } 646 647 /* 648 * parse_ids 649 * 650 * Extract a list of ids from the comma- or space- separated string str 651 * and append them to the array *idsp, growing it if necessary. 652 */ 653 static int 654 parse_ids(const char *arg, int **idsp, int nids) 655 { 656 return (parse(arg, idsp, nids, mystrtoul)); 657 } 658 659 /* 660 * parse_types 661 * 662 * Extract a list of types from the comma- or space- separated string 663 * str and append them to the array *idsp, growing it if necessary. 664 */ 665 static int 666 parse_types(const char *arg, int **typesp, int ntypes) 667 { 668 return (parse(arg, typesp, ntypes, get_type)); 669 } 670 671 /* 672 * compact 673 * 674 * Sorts and removes duplicates from array. Initial size of array is 675 * in *size; final size is stored in *size. 676 */ 677 static void 678 compact(int *array, int *size) 679 { 680 int i, j, last = -1; 681 682 qsort(array, *size, sizeof (int), int_compar); 683 for (i = j = 0; i < *size; i++) { 684 if (array[i] != last) { 685 last = array[i]; 686 array[j++] = array[i]; 687 } 688 } 689 *size = j; 690 } 691 692 int 693 main(int argc, char **argv) 694 { 695 unsigned int interval = 0, count = 1; 696 ctid_t *ids = NULL; 697 int *types = NULL; 698 int nids = 0, ntypes = 0; 699 int i, s; 700 701 (void) setlocale(LC_ALL, ""); 702 (void) textdomain(TEXT_DOMAIN); 703 704 (void) uu_setpname(argv[0]); 705 706 while ((s = getopt(argc, argv, "ai:t:v")) != EOF) { 707 switch (s) { 708 case 'a': 709 opt_showall = 1; 710 break; 711 case 'i': 712 nids = parse_ids(optarg, (int **)&ids, nids); 713 break; 714 case 't': 715 ntypes = parse_types(optarg, &types, ntypes); 716 break; 717 case 'v': 718 opt_verbose = 1; 719 break; 720 default: 721 usage(); 722 } 723 } 724 725 argc -= optind; 726 argv += optind; 727 728 if (argc > 2 || argc < 0) 729 usage(); 730 731 if (argc > 0) { 732 interval = mystrtoul(argv[0]); 733 count = 0; 734 } 735 736 if (argc > 1) { 737 count = mystrtoul(argv[1]); 738 if (count == 0) 739 return (0); 740 } 741 742 if (nids) 743 compact((int *)ids, &nids); 744 if (ntypes) 745 compact(types, &ntypes); 746 747 for (i = 0; count == 0 || i < count; i++) { 748 if (i) 749 (void) sleep(interval); 750 print_header(); 751 if (nids && ntypes) 752 scan_all(types, ntypes, ids, nids); 753 else if (ntypes == 1) 754 scan_type(*types); 755 else if (nids) 756 scan_ids(ids, nids); 757 else 758 scan_all(types, ntypes, ids, nids); 759 } 760 761 return (0); 762 } 763