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