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