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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/ctfs.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <unistd.h> 31 #include <fcntl.h> 32 #include <string.h> 33 #include <errno.h> 34 #include <libuutil.h> 35 #include <sys/contract/process.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_fatal 249 * 250 * Display the events in the fatal event set. 251 */ 252 static void 253 verb_fatal(ct_stathdl_t hdl) 254 { 255 uint_t event; 256 int err; 257 258 if (err = ct_pr_status_get_fatal(hdl, &event)) 259 verb_error(err); 260 else 261 print_bits(event, option_events); 262 } 263 264 /* 265 * verb_members 266 * 267 * Display the list of member contracts. 268 */ 269 static void 270 verb_members(ct_stathdl_t hdl) 271 { 272 pid_t *pids; 273 uint_t npids; 274 int err; 275 276 if (err = ct_pr_status_get_members(hdl, &pids, &npids)) { 277 verb_error(err); 278 return; 279 } 280 281 print_ids(pids, npids); 282 } 283 284 /* 285 * verb_inherit 286 * 287 * Display the list of inherited contracts. 288 */ 289 static void 290 verb_inherit(ct_stathdl_t hdl) 291 { 292 ctid_t *ctids; 293 uint_t nctids; 294 int err; 295 296 if (err = ct_pr_status_get_contracts(hdl, &ctids, &nctids)) 297 verb_error(err); 298 else 299 print_ids(ctids, nctids); 300 } 301 302 /* 303 * verb_svc_fmri 304 * 305 * Display the process contract service fmri 306 */ 307 static void 308 verb_svc_fmri(ct_stathdl_t hdl) 309 { 310 char *svc_fmri; 311 int err; 312 if (err = ct_pr_status_get_svc_fmri(hdl, &svc_fmri)) 313 verb_error(err); 314 else 315 (void) printf("%s\n", svc_fmri); 316 } 317 318 /* 319 * verb_svc_aux 320 * 321 * Display the process contract service fmri auxiliar 322 */ 323 static void 324 verb_svc_aux(ct_stathdl_t hdl) 325 { 326 char *svc_aux; 327 int err; 328 if (err = ct_pr_status_get_svc_aux(hdl, &svc_aux)) 329 verb_error(err); 330 else 331 (void) printf("%s\n", svc_aux); 332 } 333 334 /* 335 * verb_svc_ctid 336 * 337 * Display the process contract service fmri ctid 338 */ 339 static void 340 verb_svc_ctid(ct_stathdl_t hdl) 341 { 342 ctid_t svc_ctid; 343 int err; 344 if (err = ct_pr_status_get_svc_ctid(hdl, &svc_ctid)) 345 verb_error(err); 346 else 347 (void) printf("%ld\n", svc_ctid); 348 } 349 350 /* 351 * verb_svc_creator 352 * 353 * Display the process contract creator's execname 354 */ 355 static void 356 verb_svc_creator(ct_stathdl_t hdl) 357 { 358 char *svc_creator; 359 int err; 360 if (err = ct_pr_status_get_svc_creator(hdl, &svc_creator)) 361 verb_error(err); 362 else 363 (void) printf("%s\n", svc_creator); 364 } 365 366 /* 367 * Common contract status fields. 368 */ 369 static verbout_t vcommon[] = { 370 "cookie", verb_cookie, 371 NULL, 372 }; 373 374 /* 375 * Process contract-specific status fields. 376 * The critical and informative event sets are here because the event 377 * names are contract-specific. They are listed first, however, so 378 * they are displayed adjacent to the "normal" common output. 379 */ 380 static verbout_t vprocess[] = { 381 "informative event set", verb_info, 382 "critical event set", verb_crit, 383 "fatal event set", verb_fatal, 384 "parameter set", verb_param, 385 "member processes", verb_members, 386 "inherited contracts", verb_inherit, 387 "service fmri", verb_svc_fmri, 388 "service fmri ctid", verb_svc_ctid, 389 "creator", verb_svc_creator, 390 "aux", verb_svc_aux, 391 NULL 392 }; 393 394 /* 395 * print_verbose 396 * 397 * Displays a contract's verbose status, common fields first. 398 */ 399 static void 400 print_verbose(ct_stathdl_t hdl, verbout_t *spec, verbout_t *common) 401 { 402 int i; 403 int tmp, maxwidth = 0; 404 405 /* 406 * Compute the width of all the fields. 407 */ 408 for (i = 0; common[i].label; i++) 409 if ((tmp = strlen(common[i].label)) > maxwidth) 410 maxwidth = tmp; 411 if (spec) 412 for (i = 0; spec[i].label; i++) 413 if ((tmp = strlen(spec[i].label)) > maxwidth) 414 maxwidth = tmp; 415 maxwidth += 2; 416 417 /* 418 * Display the data. 419 */ 420 for (i = 0; common[i].label; i++) { 421 tmp = printf("\t%s", common[i].label); 422 if (tmp < 0) 423 tmp = 0; 424 (void) printf("%-*s", maxwidth - tmp + 1, ":"); 425 common[i].func(hdl); 426 } 427 if (spec) 428 for (i = 0; spec[i].label; i++) { 429 (void) printf("\t%s%n", spec[i].label, &tmp); 430 (void) printf("%-*s", maxwidth - tmp + 1, ":"); 431 spec[i].func(hdl); 432 } 433 } 434 435 struct { 436 const char *name; 437 verbout_t *verbout; 438 } cttypes[] = { 439 { "process", vprocess }, 440 { NULL } 441 }; 442 443 /* 444 * get_type 445 * 446 * Given a type name, return an index into the above array of types. 447 */ 448 static int 449 get_type(const char *typestr) 450 { 451 int i; 452 for (i = 0; cttypes[i].name; i++) 453 if (strcmp(cttypes[i].name, typestr) == 0) 454 return (i); 455 uu_die(gettext("invalid contract type: %s\n"), typestr); 456 /* NOTREACHED */ 457 } 458 459 /* 460 * print_header 461 * 462 * Display the status header. 463 */ 464 static void 465 print_header(void) 466 { 467 (void) printf("%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s\n", "CTID", "ZONEID", 468 "TYPE", "STATE", "HOLDER", "EVENTS", "QTIME", "NTIME"); 469 } 470 471 /* 472 * print_contract 473 * 474 * Display status for contract ID 'id' from type directory 'dir'. If 475 * only contracts of a specific set of types should be displayed, 476 * 'types' will be a sorted list of type indices of length 'ntypes'. 477 */ 478 static void 479 print_contract(const char *dir, ctid_t id, verbout_t *spec, 480 int *types, int ntypes) 481 { 482 ct_stathdl_t status; 483 char hstr[100], qstr[20], nstr[20]; 484 ctstate_t state; 485 int fd = 0; 486 int t; 487 488 /* 489 * Open and obtain status. 490 */ 491 if ((fd = contract_open(id, dir, "status", O_RDONLY)) == -1) { 492 if (errno == ENOENT) 493 return; 494 uu_die(gettext("could not open contract status file")); 495 } 496 497 if (errno = ct_status_read(fd, opt_verbose ? CTD_ALL : CTD_COMMON, 498 &status)) 499 uu_die(gettext("failed to get contract status for %d"), id); 500 (void) close(fd); 501 502 /* 503 * Unless otherwise directed, don't display dead contracts. 504 */ 505 state = ct_status_get_state(status); 506 if (!opt_showall && state == CTS_DEAD) { 507 ct_status_free(status); 508 return; 509 } 510 511 /* 512 * If we are only allowed to display certain contract types, 513 * perform that filtering here. We stash a copy of spec so we 514 * don't have to recompute it later. 515 */ 516 if (types) { 517 int key = get_type(ct_status_get_type(status)); 518 spec = cttypes[key].verbout; 519 if (bsearch(&key, types, ntypes, sizeof (int), int_compar) == 520 NULL) { 521 ct_status_free(status); 522 return; 523 } 524 } 525 526 /* 527 * Precompute those fields which have both textual and 528 * numerical values. 529 */ 530 if ((state == CTS_OWNED) || (state == CTS_INHERITED)) 531 (void) snprintf(hstr, sizeof (hstr), "%ld", 532 ct_status_get_holder(status)); 533 else 534 (void) snprintf(hstr, sizeof (hstr), "%s", "-"); 535 536 if ((t = ct_status_get_qtime(status)) == -1) { 537 qstr[0] = nstr[0] = '-'; 538 qstr[1] = nstr[1] = '\0'; 539 } else { 540 (void) snprintf(qstr, sizeof (qstr), "%d", t); 541 (void) snprintf(nstr, sizeof (nstr), "%d", 542 ct_status_get_ntime(status)); 543 } 544 545 /* 546 * Emit the contract's status. 547 */ 548 (void) printf("%-7ld %-7ld %-7s %-7s %-7s %-7d %-7s %-8s\n", 549 ct_status_get_id(status), 550 ct_status_get_zoneid(status), 551 ct_status_get_type(status), 552 (state == CTS_OWNED) ? "owned" : 553 (state == CTS_INHERITED) ? "inherit" : 554 (state == CTS_ORPHAN) ? "orphan" : "dead", hstr, 555 ct_status_get_nevents(status), qstr, nstr); 556 557 /* 558 * Emit verbose status information, if requested. If we 559 * weren't provided a verbose output spec or didn't compute it 560 * earlier, do it now. 561 */ 562 if (opt_verbose) { 563 if (spec == NULL) 564 spec = cttypes[get_type(ct_status_get_type(status))]. 565 verbout; 566 print_verbose(status, spec, vcommon); 567 } 568 569 ct_status_free(status); 570 } 571 572 /* 573 * scan_type 574 * 575 * Display all contracts of the requested type. 576 */ 577 static void 578 scan_type(int typeno) 579 { 580 DIR *dir; 581 struct dirent64 *de; 582 char path[PATH_MAX]; 583 584 verbout_t *vo = cttypes[typeno].verbout; 585 const char *type = cttypes[typeno].name; 586 587 if (snprintf(path, PATH_MAX, CTFS_ROOT "/%s", type) >= PATH_MAX || 588 (dir = opendir(path)) == NULL) 589 uu_die(gettext("bad contract type: %s\n"), type); 590 while ((de = readdir64(dir)) != NULL) { 591 /* 592 * Eliminate special files (e.g. '.', '..'). 593 */ 594 if (de->d_name[0] < '0' || de->d_name[0] > '9') 595 continue; 596 print_contract(type, mystrtoul(de->d_name), vo, NULL, 0); 597 } 598 (void) closedir(dir); 599 } 600 601 /* 602 * scan_ids 603 * 604 * Display all contracts with the requested IDs. 605 */ 606 static void 607 scan_ids(ctid_t *ids, int nids) 608 { 609 int i; 610 for (i = 0; i < nids; i++) 611 print_contract("all", ids[i], NULL, NULL, 0); 612 } 613 614 /* 615 * scan_all 616 * 617 * Display the union of the requested IDs and types. So that the 618 * output is sorted by contract ID, it takes the slow road by testing 619 * each entry in /system/contract/all against its criteria. Used when 620 * the number of types is greater than 1, when we have a mixture of 621 * types and ids, or no lists were provided at all. 622 */ 623 static void 624 scan_all(int *types, int ntypes, ctid_t *ids, int nids) 625 { 626 DIR *dir; 627 struct dirent64 *de; 628 const char *path = CTFS_ROOT "/all"; 629 int key, test; 630 631 if ((dir = opendir(path)) == NULL) 632 uu_die(gettext("could not open %s"), path); 633 while ((de = readdir64(dir)) != NULL) { 634 /* 635 * Eliminate special files (e.g. '.', '..'). 636 */ 637 if (de->d_name[0] < '0' || de->d_name[0] > '9') 638 continue; 639 key = mystrtoul(de->d_name); 640 641 /* 642 * If we are given IDs to look at and this contract 643 * isn't in the ID list, or if we weren't given a list 644 * if IDs but were given a list of types, provide the 645 * list of acceptable types to print_contract. 646 */ 647 test = nids ? (bsearch(&key, ids, nids, sizeof (int), 648 int_compar) == NULL) : (ntypes != 0); 649 print_contract("all", key, NULL, (test ? types : NULL), ntypes); 650 } 651 (void) closedir(dir); 652 } 653 654 /* 655 * walk_args 656 * 657 * Apply fp to each token in the comma- or space- separated argument 658 * string str and store the results in the array starting at results. 659 */ 660 static int 661 walk_args(const char *str, int (*fp)(const char *), int *results) 662 { 663 char *copy, *token; 664 int count = 0; 665 666 if ((copy = strdup(str)) == NULL) 667 uu_die(gettext("strdup() failed")); 668 669 token = strtok(copy, ", "); 670 if (token == NULL) { 671 free(copy); 672 return (0); 673 } 674 675 do { 676 if (fp) 677 *(results++) = fp(token); 678 count++; 679 } while (token = strtok(NULL, ", ")); 680 free(copy); 681 682 return (count); 683 } 684 685 /* 686 * parse 687 * 688 * Parse the comma- or space- separated string str, using fp to covert 689 * the tokens to integers. Append the list of integers to the array 690 * pointed to by *idps, growing the array if necessary. 691 */ 692 static int 693 parse(const char *str, int **idsp, int nids, int (*fp)(const char *fp)) 694 { 695 int count; 696 int *array; 697 698 count = walk_args(str, NULL, NULL); 699 if (count == 0) 700 return (0); 701 702 if ((array = calloc(nids + count, sizeof (int))) == NULL) 703 uu_die(gettext("calloc() failed")); 704 705 if (*idsp) { 706 (void) memcpy(array, *idsp, nids * sizeof (int)); 707 free(*idsp); 708 } 709 710 (void) walk_args(str, fp, array + nids); 711 712 *idsp = array; 713 return (count + nids); 714 } 715 716 /* 717 * parse_ids 718 * 719 * Extract a list of ids from the comma- or space- separated string str 720 * and append them to the array *idsp, growing it if necessary. 721 */ 722 static int 723 parse_ids(const char *arg, int **idsp, int nids) 724 { 725 return (parse(arg, idsp, nids, mystrtoul)); 726 } 727 728 /* 729 * parse_types 730 * 731 * Extract a list of types from the comma- or space- separated string 732 * str and append them to the array *idsp, growing it if necessary. 733 */ 734 static int 735 parse_types(const char *arg, int **typesp, int ntypes) 736 { 737 return (parse(arg, typesp, ntypes, get_type)); 738 } 739 740 /* 741 * compact 742 * 743 * Sorts and removes duplicates from array. Initial size of array is 744 * in *size; final size is stored in *size. 745 */ 746 static void 747 compact(int *array, int *size) 748 { 749 int i, j, last = -1; 750 751 qsort(array, *size, sizeof (int), int_compar); 752 for (i = j = 0; i < *size; i++) { 753 if (array[i] != last) { 754 last = array[i]; 755 array[j++] = array[i]; 756 } 757 } 758 *size = j; 759 } 760 761 int 762 main(int argc, char **argv) 763 { 764 unsigned int interval = 0, count = 1; 765 ctid_t *ids = NULL; 766 int *types = NULL; 767 int nids = 0, ntypes = 0; 768 int i, s; 769 770 (void) setlocale(LC_ALL, ""); 771 (void) textdomain(TEXT_DOMAIN); 772 773 (void) uu_setpname(argv[0]); 774 775 while ((s = getopt(argc, argv, "ai:T:t:v")) != EOF) { 776 switch (s) { 777 case 'a': 778 opt_showall = 1; 779 break; 780 case 'i': 781 nids = parse_ids(optarg, (int **)&ids, nids); 782 break; 783 case 'T': 784 if (optarg) { 785 if (*optarg == 'u') 786 timestamp_fmt = UDATE; 787 else if (*optarg == 'd') 788 timestamp_fmt = DDATE; 789 else 790 usage(); 791 } else { 792 usage(); 793 } 794 break; 795 case 't': 796 ntypes = parse_types(optarg, &types, ntypes); 797 break; 798 case 'v': 799 opt_verbose = 1; 800 break; 801 default: 802 usage(); 803 } 804 } 805 806 argc -= optind; 807 argv += optind; 808 809 if (argc > 2 || argc < 0) 810 usage(); 811 812 if (argc > 0) { 813 interval = mystrtoul(argv[0]); 814 count = 0; 815 } 816 817 if (argc > 1) { 818 count = mystrtoul(argv[1]); 819 if (count == 0) 820 return (0); 821 } 822 823 if (nids) 824 compact((int *)ids, &nids); 825 if (ntypes) 826 compact(types, &ntypes); 827 828 for (i = 0; count == 0 || i < count; i++) { 829 if (i) 830 (void) sleep(interval); 831 if (timestamp_fmt != NODATE) 832 print_timestamp(timestamp_fmt); 833 print_header(); 834 if (nids && ntypes) 835 scan_all(types, ntypes, ids, nids); 836 else if (ntypes == 1) 837 scan_type(*types); 838 else if (nids) 839 scan_ids(ids, nids); 840 else 841 scan_all(types, ntypes, ids, nids); 842 } 843 844 return (0); 845 } 846