ctstat.c revision 6073:47f6aa7a8077
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 46static int opt_verbose = 0; 47static int opt_showall = 0; 48 49/* 50 * usage 51 * 52 * Educate the user. 53 */ 54static void 55usage(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 */ 68static int 69mystrtoul(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 */ 87static int 88int_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 100typedef struct optvect { 101 const char *option; 102 uint_t bit; 103} optvect_t; 104 105static 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 113static 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 */ 128static void 129print_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 */ 151static void 152print_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 170typedef 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 */ 178typedef 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 */ 189static void 190verb_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 */ 200static void 201verb_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 */ 211static void 212verb_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 */ 228static void 229verb_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 */ 239static void 240verb_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 */ 250static void 251verb_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 */ 267static void 268verb_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 */ 287static void 288verb_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 */ 305static void 306verb_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 */ 321static void 322verb_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 */ 337static void 338verb_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 */ 353static void 354verb_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 */ 367static 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 */ 378static 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 */ 397static void 398print_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 433struct { 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 */ 446static int 447get_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 */ 462static void 463print_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 */ 476static void 477print_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 */ 575static void 576scan_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 */ 604static void 605scan_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 */ 621static void 622scan_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 */ 658static int 659walk_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 */ 690static int 691parse(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 */ 720static int 721parse_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 */ 732static int 733parse_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 */ 744static void 745compact(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 759int 760main(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