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 /* 23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * svcs - display attributes of service instances 28 * 29 * We have two output formats and six instance selection mechanisms. The 30 * primary output format is a line of attributes (selected by -o), possibly 31 * followed by process description lines (if -p is specified), for each 32 * instance selected. The columns available to display are described by the 33 * struct column columns array. The columns to actually display are kept in 34 * the opt_columns array as indicies into the columns array. The selection 35 * mechanisms available for this format are service FMRIs (selects all child 36 * instances), instance FMRIs, instance FMRI glob patterns, instances with 37 * a certain restarter (-R), dependencies of instances (-d), and dependents of 38 * instances (-D). Since the lines must be sorted (per -sS), we'll just stick 39 * each into a data structure and print them in order when we're done. To 40 * avoid listing the same instance twice (when -d and -D aren't given), we'll 41 * use a hash table of FMRIs to record that we've listed (added to the tree) 42 * an instance. 43 * 44 * The secondary output format (-l "long") is a paragraph of text for the 45 * services or instances selected. Not needing to be sorted, it's implemented 46 * by just calling print_detailed() for each FMRI given. 47 */ 48 49 #include "svcs.h" 50 #include "notify_params.h" 51 52 /* Get the byteorder macros to ease sorting. */ 53 #include <sys/types.h> 54 #include <netinet/in.h> 55 #include <inttypes.h> 56 57 #include <sys/contract.h> 58 #include <sys/ctfs.h> 59 #include <sys/stat.h> 60 61 #include <assert.h> 62 #include <errno.h> 63 #include <fcntl.h> 64 #include <fnmatch.h> 65 #include <libcontract.h> 66 #include <libcontract_priv.h> 67 #include <libintl.h> 68 #include <libscf.h> 69 #include <libscf_priv.h> 70 #include <libuutil.h> 71 #include <libnvpair.h> 72 #include <locale.h> 73 #include <procfs.h> 74 #include <stdarg.h> 75 #include <stdio.h> 76 #include <stdlib.h> 77 #include <strings.h> 78 #include <time.h> 79 80 81 #ifndef TEXT_DOMAIN 82 #define TEXT_DOMAIN "SUNW_OST_OSCMD" 83 #endif /* TEXT_DOMAIN */ 84 85 #define LEGACY_UNKNOWN "unknown" 86 87 /* Flags for pg_get_single_val() */ 88 #define EMPTY_OK 0x01 89 #define MULTI_OK 0x02 90 91 92 /* 93 * An AVL-storable node for output lines and the keys to sort them by. 94 */ 95 struct avl_string { 96 uu_avl_node_t node; 97 char *key; 98 char *str; 99 }; 100 101 /* 102 * For lists of parsed restarter FMRIs. 103 */ 104 struct pfmri_list { 105 const char *scope; 106 const char *service; 107 const char *instance; 108 struct pfmri_list *next; 109 }; 110 111 112 /* 113 * Globals 114 */ 115 scf_handle_t *h; 116 static scf_propertygroup_t *g_pg; 117 static scf_property_t *g_prop; 118 static scf_value_t *g_val; 119 120 static size_t line_sz; /* Bytes in the header line. */ 121 static size_t sortkey_sz; /* Bytes in sort keys. */ 122 static uu_avl_pool_t *lines_pool; 123 static uu_avl_t *lines; /* Output lines. */ 124 int exit_status; 125 ssize_t max_scf_name_length; 126 ssize_t max_scf_value_length; 127 ssize_t max_scf_fmri_length; 128 static ssize_t max_scf_type_length; 129 static time_t now; 130 static struct pfmri_list *restarters = NULL; 131 static int first_paragraph = 1; /* For -l mode. */ 132 static char *common_name_buf; /* Sized for maximal length value. */ 133 char *locale; /* Current locale. */ 134 135 /* 136 * Pathname storage for path generated from the fmri. 137 * Used for reading the ctid and (start) pid files for an inetd service. 138 */ 139 static char genfmri_filename[MAXPATHLEN] = ""; 140 141 /* Options */ 142 static int *opt_columns = NULL; /* Indices into columns to display. */ 143 static int opt_cnum = 0; 144 static int opt_processes = 0; /* Print processes? */ 145 static int *opt_sort = NULL; /* Indices into columns to sort. */ 146 static int opt_snum = 0; 147 static int opt_nstate_shown = 0; /* Will nstate be shown? */ 148 static int opt_verbose = 0; 149 150 /* Minimize string constants. */ 151 static const char * const scf_property_state = SCF_PROPERTY_STATE; 152 static const char * const scf_property_next_state = SCF_PROPERTY_NEXT_STATE; 153 static const char * const scf_property_contract = SCF_PROPERTY_CONTRACT; 154 155 156 /* 157 * Utility functions 158 */ 159 160 /* 161 * For unexpected libscf errors. The ending newline is necessary to keep 162 * uu_die() from appending the errno error. 163 */ 164 #ifndef NDEBUG 165 void 166 do_scfdie(const char *file, int line) 167 { 168 uu_die(gettext("%s:%d: Unexpected libscf error: %s. Exiting.\n"), 169 file, line, scf_strerror(scf_error())); 170 } 171 #else 172 void 173 scfdie(void) 174 { 175 uu_die(gettext("Unexpected libscf error: %s. Exiting.\n"), 176 scf_strerror(scf_error())); 177 } 178 #endif 179 180 void * 181 safe_malloc(size_t sz) 182 { 183 void *ptr; 184 185 ptr = malloc(sz); 186 if (ptr == NULL) 187 uu_die(gettext("Out of memory")); 188 189 return (ptr); 190 } 191 192 char * 193 safe_strdup(const char *str) 194 { 195 char *cp; 196 197 cp = strdup(str); 198 if (cp == NULL) 199 uu_die(gettext("Out of memory.\n")); 200 201 return (cp); 202 } 203 204 /* 205 * FMRI hashtable. For uniquifing listings. 206 */ 207 208 struct ht_elem { 209 const char *fmri; 210 struct ht_elem *next; 211 }; 212 213 static struct ht_elem **ht_buckets; 214 static uint_t ht_buckets_num; 215 static uint_t ht_num; 216 217 static void 218 ht_init() 219 { 220 ht_buckets_num = 8; 221 ht_buckets = safe_malloc(sizeof (*ht_buckets) * ht_buckets_num); 222 bzero(ht_buckets, sizeof (*ht_buckets) * ht_buckets_num); 223 ht_num = 0; 224 } 225 226 static uint_t 227 ht_hash_fmri(const char *fmri) 228 { 229 uint_t h = 0, g; 230 const char *p, *k; 231 232 /* All FMRIs begin with svc:/, so skip that part. */ 233 assert(strncmp(fmri, "svc:/", sizeof ("svc:/") - 1) == 0); 234 k = fmri + sizeof ("svc:/") - 1; 235 236 /* 237 * Generic hash function from uts/common/os/modhash.c. 238 */ 239 for (p = k; *p != '\0'; ++p) { 240 h = (h << 4) + *p; 241 if ((g = (h & 0xf0000000)) != 0) { 242 h ^= (g >> 24); 243 h ^= g; 244 } 245 } 246 247 return (h); 248 } 249 250 static void 251 ht_grow() 252 { 253 uint_t new_ht_buckets_num; 254 struct ht_elem **new_ht_buckets; 255 int i; 256 257 new_ht_buckets_num = ht_buckets_num * 2; 258 assert(new_ht_buckets_num > ht_buckets_num); 259 new_ht_buckets = 260 safe_malloc(sizeof (*new_ht_buckets) * new_ht_buckets_num); 261 bzero(new_ht_buckets, sizeof (*new_ht_buckets) * new_ht_buckets_num); 262 263 for (i = 0; i < ht_buckets_num; ++i) { 264 struct ht_elem *elem, *next; 265 266 for (elem = ht_buckets[i]; elem != NULL; elem = next) { 267 uint_t h; 268 269 next = elem->next; 270 271 h = ht_hash_fmri(elem->fmri); 272 273 elem->next = 274 new_ht_buckets[h & (new_ht_buckets_num - 1)]; 275 new_ht_buckets[h & (new_ht_buckets_num - 1)] = elem; 276 } 277 } 278 279 free(ht_buckets); 280 281 ht_buckets = new_ht_buckets; 282 ht_buckets_num = new_ht_buckets_num; 283 } 284 285 /* 286 * Add an FMRI to the hash table. Returns 1 if it was already there, 287 * 0 otherwise. 288 */ 289 static int 290 ht_add(const char *fmri) 291 { 292 uint_t h; 293 struct ht_elem *elem; 294 295 h = ht_hash_fmri(fmri); 296 297 elem = ht_buckets[h & (ht_buckets_num - 1)]; 298 299 for (; elem != NULL; elem = elem->next) { 300 if (strcmp(elem->fmri, fmri) == 0) 301 return (1); 302 } 303 304 /* Grow when average chain length is over 3. */ 305 if (ht_num > 3 * ht_buckets_num) 306 ht_grow(); 307 308 ++ht_num; 309 310 elem = safe_malloc(sizeof (*elem)); 311 elem->fmri = strdup(fmri); 312 elem->next = ht_buckets[h & (ht_buckets_num - 1)]; 313 ht_buckets[h & (ht_buckets_num - 1)] = elem; 314 315 return (0); 316 } 317 318 319 320 /* 321 * Convenience libscf wrapper functions. 322 */ 323 324 /* 325 * Get the single value of the named property in the given property group, 326 * which must have type ty, and put it in *vp. If ty is SCF_TYPE_ASTRING, vp 327 * is taken to be a char **, and sz is the size of the buffer. sz is unused 328 * otherwise. Return 0 on success, -1 if the property doesn't exist, has the 329 * wrong type, or doesn't have a single value. If flags has EMPTY_OK, don't 330 * complain if the property has no values (but return nonzero). If flags has 331 * MULTI_OK and the property has multiple values, succeed with E2BIG. 332 */ 333 int 334 pg_get_single_val(scf_propertygroup_t *pg, const char *propname, scf_type_t ty, 335 void *vp, size_t sz, uint_t flags) 336 { 337 char *buf; 338 size_t buf_sz; 339 int ret = -1, r; 340 boolean_t multi = B_FALSE; 341 342 assert((flags & ~(EMPTY_OK | MULTI_OK)) == 0); 343 344 if (scf_pg_get_property(pg, propname, g_prop) == -1) { 345 if (scf_error() != SCF_ERROR_NOT_FOUND) 346 scfdie(); 347 348 goto out; 349 } 350 351 if (scf_property_is_type(g_prop, ty) != SCF_SUCCESS) { 352 if (scf_error() == SCF_ERROR_TYPE_MISMATCH) 353 goto misconfigured; 354 scfdie(); 355 } 356 357 if (scf_property_get_value(g_prop, g_val) != SCF_SUCCESS) { 358 switch (scf_error()) { 359 case SCF_ERROR_NOT_FOUND: 360 if (flags & EMPTY_OK) 361 goto out; 362 goto misconfigured; 363 364 case SCF_ERROR_CONSTRAINT_VIOLATED: 365 if (flags & MULTI_OK) { 366 multi = B_TRUE; 367 break; 368 } 369 goto misconfigured; 370 371 case SCF_ERROR_PERMISSION_DENIED: 372 default: 373 scfdie(); 374 } 375 } 376 377 switch (ty) { 378 case SCF_TYPE_ASTRING: 379 r = scf_value_get_astring(g_val, vp, sz) > 0 ? SCF_SUCCESS : -1; 380 break; 381 382 case SCF_TYPE_BOOLEAN: 383 r = scf_value_get_boolean(g_val, (uint8_t *)vp); 384 break; 385 386 case SCF_TYPE_COUNT: 387 r = scf_value_get_count(g_val, (uint64_t *)vp); 388 break; 389 390 case SCF_TYPE_INTEGER: 391 r = scf_value_get_integer(g_val, (int64_t *)vp); 392 break; 393 394 case SCF_TYPE_TIME: { 395 int64_t sec; 396 int32_t ns; 397 r = scf_value_get_time(g_val, &sec, &ns); 398 ((struct timeval *)vp)->tv_sec = sec; 399 ((struct timeval *)vp)->tv_usec = ns / 1000; 400 break; 401 } 402 403 case SCF_TYPE_USTRING: 404 r = scf_value_get_ustring(g_val, vp, sz) > 0 ? SCF_SUCCESS : -1; 405 break; 406 407 default: 408 #ifndef NDEBUG 409 uu_warn("%s:%d: Unknown type %d.\n", __FILE__, __LINE__, ty); 410 #endif 411 abort(); 412 } 413 if (r != SCF_SUCCESS) 414 scfdie(); 415 416 ret = multi ? E2BIG : 0; 417 goto out; 418 419 misconfigured: 420 buf_sz = max_scf_fmri_length + 1; 421 buf = safe_malloc(buf_sz); 422 if (scf_property_to_fmri(g_prop, buf, buf_sz) == -1) 423 scfdie(); 424 425 uu_warn(gettext("Property \"%s\" is misconfigured.\n"), buf); 426 427 free(buf); 428 429 out: 430 return (ret); 431 } 432 433 static scf_snapshot_t * 434 get_running_snapshot(scf_instance_t *inst) 435 { 436 scf_snapshot_t *snap; 437 438 snap = scf_snapshot_create(h); 439 if (snap == NULL) 440 scfdie(); 441 442 if (scf_instance_get_snapshot(inst, "running", snap) == 0) 443 return (snap); 444 445 if (scf_error() != SCF_ERROR_NOT_FOUND) 446 scfdie(); 447 448 scf_snapshot_destroy(snap); 449 return (NULL); 450 } 451 452 /* 453 * As pg_get_single_val(), except look the property group up in an 454 * instance. If "use_running" is set, and the running snapshot exists, 455 * do a composed lookup there. Otherwise, do an (optionally composed) 456 * lookup on the current values. Note that lookups using snapshots are 457 * always composed. 458 */ 459 int 460 inst_get_single_val(scf_instance_t *inst, const char *pgname, 461 const char *propname, scf_type_t ty, void *vp, size_t sz, uint_t flags, 462 int use_running, int composed) 463 { 464 scf_snapshot_t *snap = NULL; 465 int r; 466 467 if (use_running) 468 snap = get_running_snapshot(inst); 469 if (composed || use_running) 470 r = scf_instance_get_pg_composed(inst, snap, pgname, g_pg); 471 else 472 r = scf_instance_get_pg(inst, pgname, g_pg); 473 if (snap) 474 scf_snapshot_destroy(snap); 475 if (r == -1) 476 return (-1); 477 478 r = pg_get_single_val(g_pg, propname, ty, vp, sz, flags); 479 480 return (r); 481 } 482 483 static int 484 instance_enabled(scf_instance_t *inst, boolean_t temp) 485 { 486 uint8_t b; 487 488 if (inst_get_single_val(inst, 489 temp ? SCF_PG_GENERAL_OVR : SCF_PG_GENERAL, SCF_PROPERTY_ENABLED, 490 SCF_TYPE_BOOLEAN, &b, 0, 0, 0, 0) != 0) 491 return (-1); 492 493 return (b ? 1 : 0); 494 } 495 496 /* 497 * Get a string property from the restarter property group of the given 498 * instance. Return an empty string on normal problems. 499 */ 500 static void 501 get_restarter_string_prop(scf_instance_t *inst, const char *pname, 502 char *buf, size_t buf_sz) 503 { 504 if (inst_get_single_val(inst, SCF_PG_RESTARTER, pname, 505 SCF_TYPE_ASTRING, buf, buf_sz, 0, 0, 1) != 0) 506 *buf = '\0'; 507 } 508 509 static int 510 get_restarter_time_prop(scf_instance_t *inst, const char *pname, 511 struct timeval *tvp, int ok_if_empty) 512 { 513 int r; 514 515 r = inst_get_single_val(inst, SCF_PG_RESTARTER, pname, SCF_TYPE_TIME, 516 tvp, NULL, ok_if_empty ? EMPTY_OK : 0, 0, 1); 517 518 return (r == 0 ? 0 : -1); 519 } 520 521 static int 522 get_restarter_count_prop(scf_instance_t *inst, const char *pname, uint64_t *cp, 523 uint_t flags) 524 { 525 return (inst_get_single_val(inst, SCF_PG_RESTARTER, pname, 526 SCF_TYPE_COUNT, cp, 0, flags, 0, 1)); 527 } 528 529 530 /* 531 * Generic functions 532 */ 533 534 /* 535 * Return an array of pids associated with the given contract id. 536 * Returned pids are added to the end of the pidsp array. 537 */ 538 static void 539 ctid_to_pids(uint64_t c, pid_t **pidsp, uint_t *np) 540 { 541 ct_stathdl_t ctst; 542 uint_t m; 543 int fd; 544 int r, err; 545 pid_t *pids; 546 547 fd = contract_open(c, NULL, "status", O_RDONLY); 548 if (fd < 0) 549 return; 550 551 err = ct_status_read(fd, CTD_ALL, &ctst); 552 if (err != 0) { 553 uu_warn(gettext("Could not read status of contract " 554 "%ld: %s.\n"), c, strerror(err)); 555 (void) close(fd); 556 return; 557 } 558 559 (void) close(fd); 560 561 r = ct_pr_status_get_members(ctst, &pids, &m); 562 assert(r == 0); 563 564 if (m == 0) { 565 ct_status_free(ctst); 566 return; 567 } 568 569 *pidsp = realloc(*pidsp, (*np + m) * sizeof (*pidsp)); 570 if (*pidsp == NULL) 571 uu_die(gettext("Out of memory")); 572 573 bcopy(pids, *pidsp + *np, m * sizeof (*pids)); 574 *np += m; 575 576 ct_status_free(ctst); 577 } 578 579 static int 580 propvals_to_pids(scf_propertygroup_t *pg, const char *pname, pid_t **pidsp, 581 uint_t *np, scf_property_t *prop, scf_value_t *val, scf_iter_t *iter) 582 { 583 scf_type_t ty; 584 uint64_t c; 585 int r; 586 587 if (scf_pg_get_property(pg, pname, prop) != 0) { 588 if (scf_error() != SCF_ERROR_NOT_FOUND) 589 scfdie(); 590 591 return (ENOENT); 592 } 593 594 if (scf_property_type(prop, &ty) != 0) 595 scfdie(); 596 597 if (ty != SCF_TYPE_COUNT) 598 return (EINVAL); 599 600 if (scf_iter_property_values(iter, prop) != 0) 601 scfdie(); 602 603 for (;;) { 604 r = scf_iter_next_value(iter, val); 605 if (r == -1) 606 scfdie(); 607 if (r == 0) 608 break; 609 610 if (scf_value_get_count(val, &c) != 0) 611 scfdie(); 612 613 ctid_to_pids(c, pidsp, np); 614 } 615 616 return (0); 617 } 618 619 /* 620 * Check if instance has general/restarter property that matches 621 * given string. Restarter string must be in canonified form. 622 * Returns 0 for success; -1 otherwise. 623 */ 624 static int 625 check_for_restarter(scf_instance_t *inst, const char *restarter) 626 { 627 char *fmri_buf; 628 char *fmri_buf_canonified = NULL; 629 int ret = -1; 630 631 if (inst == NULL) 632 return (-1); 633 634 /* Get restarter */ 635 fmri_buf = safe_malloc(max_scf_fmri_length + 1); 636 if (inst_get_single_val(inst, SCF_PG_GENERAL, 637 SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, fmri_buf, 638 max_scf_fmri_length + 1, 0, 0, 1) != 0) 639 goto out; 640 641 fmri_buf_canonified = safe_malloc(max_scf_fmri_length + 1); 642 if (scf_canonify_fmri(fmri_buf, fmri_buf_canonified, 643 (max_scf_fmri_length + 1)) < 0) 644 goto out; 645 646 if (strcmp(fmri_buf, restarter) == 0) 647 ret = 0; 648 649 out: 650 free(fmri_buf); 651 if (fmri_buf_canonified) 652 free(fmri_buf_canonified); 653 return (ret); 654 } 655 656 /* 657 * Common code that is used by ctids_by_restarter and pids_by_restarter. 658 * Checks for a common restarter and if one is available, it generates 659 * the appropriate filename using wip->fmri and stores that in the 660 * global genfmri_filename. 661 * 662 * Restarters currently supported are: svc:/network/inetd:default 663 * If a restarter specific action is available, then restarter_spec 664 * is set to 1. If a restarter specific action is not available, then 665 * restarter_spec is set to 0 and a -1 is returned. 666 * 667 * Returns: 668 * 0 if success: restarter specific action found and filename generated 669 * -1 if restarter specific action not found, 670 * if restarter specific action found but an error was encountered 671 * during the generation of the wip->fmri based filename 672 */ 673 static int 674 common_by_restarter(scf_instance_t *inst, const char *fmri, 675 int *restarter_specp) 676 { 677 int ret = -1; 678 int r; 679 680 /* Check for inetd specific restarter */ 681 if (check_for_restarter(inst, "svc:/network/inetd:default") != 0) { 682 *restarter_specp = 0; 683 return (ret); 684 } 685 686 *restarter_specp = 1; 687 688 /* Get the ctid filename associated with this instance */ 689 r = gen_filenms_from_fmri(fmri, "ctid", genfmri_filename, NULL); 690 691 switch (r) { 692 case 0: 693 break; 694 695 case -1: 696 /* 697 * Unable to get filename from fmri. Print warning 698 * and return failure with no ctids. 699 */ 700 uu_warn(gettext("Unable to read contract ids for %s -- " 701 "FMRI is too long\n"), fmri); 702 return (ret); 703 704 case -2: 705 /* 706 * The directory didn't exist, so no contracts. 707 * Return failure with no ctids. 708 */ 709 return (ret); 710 711 default: 712 uu_warn(gettext("%s:%d: gen_filenms_from_fmri() failed with " 713 "unknown error %d\n"), __FILE__, __LINE__, r); 714 abort(); 715 } 716 717 return (0); 718 719 } 720 721 /* 722 * Get or print a contract id using a restarter specific action. 723 * 724 * If the print_flag is not set, this routine gets the single contract 725 * id associated with this instance. 726 * If the print flag is set, then print each contract id found. 727 * 728 * Returns: 729 * 0 if success: restarter specific action found and used with no error 730 * -1 if restarter specific action not found 731 * -1 if restarter specific action found, but there was a failure 732 * -1 if print flag is not set and no contract id is found or multiple 733 * contract ids were found 734 * E2BIG if print flag is not set, MULTI_OK bit in flag is set and multiple 735 * contract ids were found 736 */ 737 static int 738 ctids_by_restarter(scf_walkinfo_t *wip, uint64_t *cp, int print_flag, 739 uint_t flags, int *restarter_specp, void (*callback_header)(), 740 void (*callback_ctid)(uint64_t)) 741 { 742 FILE *fp; 743 int ret = -1; 744 int fscanf_ret; 745 uint64_t cp2; 746 int rest_ret; 747 748 /* Check if callbacks are needed and were passed in */ 749 if (print_flag) { 750 if ((callback_header == NULL) || (callback_ctid == NULL)) 751 return (ret); 752 } 753 754 /* Check for restarter specific action and generation of filename */ 755 rest_ret = common_by_restarter(wip->inst, wip->fmri, restarter_specp); 756 if (rest_ret != 0) 757 return (rest_ret); 758 759 /* 760 * If fopen fails, then ctid file hasn't been created yet. 761 * If print_flag is set, this is ok; otherwise fail. 762 */ 763 if ((fp = fopen(genfmri_filename, "r")) == NULL) { 764 if (print_flag) 765 return (0); 766 goto out; 767 } 768 769 if (print_flag) { 770 /* 771 * Print all contract ids that are found. 772 * First callback to print ctid header. 773 */ 774 callback_header(); 775 776 /* fscanf may not set errno, so be sure to clear it first */ 777 errno = 0; 778 while ((fscanf_ret = fscanf(fp, "%llu", cp)) == 1) { 779 /* Callback to print contract id */ 780 callback_ctid(*cp); 781 errno = 0; 782 } 783 /* EOF is not a failure when no errno. */ 784 if ((fscanf_ret != EOF) || (errno != 0)) { 785 uu_die(gettext("Unable to read ctid file for %s"), 786 wip->fmri); 787 } 788 (void) putchar('\n'); 789 ret = 0; 790 } else { 791 /* Must find 1 ctid or fail */ 792 if (fscanf(fp, "%llu", cp) == 1) { 793 /* If 2nd ctid found - fail */ 794 if (fscanf(fp, "%llu", &cp2) == 1) { 795 if (flags & MULTI_OK) 796 ret = E2BIG; 797 } else { 798 /* Success - found only 1 ctid */ 799 ret = 0; 800 } 801 } 802 } 803 (void) fclose(fp); 804 805 out: 806 return (ret); 807 } 808 809 /* 810 * Get the process ids associated with an instance using a restarter 811 * specific action. 812 * 813 * Returns: 814 * 0 if success: restarter specific action found and used with no error 815 * -1 restarter specific action not found or if failure 816 */ 817 static int 818 pids_by_restarter(scf_instance_t *inst, const char *fmri, 819 pid_t **pids, uint_t *np, int *restarter_specp) 820 { 821 uint64_t c; 822 FILE *fp; 823 int fscanf_ret; 824 int rest_ret; 825 826 /* Check for restarter specific action and generation of filename */ 827 rest_ret = common_by_restarter(inst, fmri, restarter_specp); 828 if (rest_ret != 0) 829 return (rest_ret); 830 831 /* 832 * If fopen fails with ENOENT then the ctid file hasn't been 833 * created yet so return success. 834 * For all other errors - fail with uu_die. 835 */ 836 if ((fp = fopen(genfmri_filename, "r")) == NULL) { 837 if (errno == ENOENT) 838 return (0); 839 uu_die(gettext("Unable to open ctid file for %s"), fmri); 840 } 841 842 /* fscanf may not set errno, so be sure to clear it first */ 843 errno = 0; 844 while ((fscanf_ret = fscanf(fp, "%llu", &c)) == 1) { 845 if (c == 0) { 846 (void) fclose(fp); 847 uu_die(gettext("ctid file for %s has corrupt data"), 848 fmri); 849 } 850 ctid_to_pids(c, pids, np); 851 errno = 0; 852 } 853 /* EOF is not a failure when no errno. */ 854 if ((fscanf_ret != EOF) || (errno != 0)) { 855 uu_die(gettext("Unable to read ctid file for %s"), fmri); 856 } 857 858 (void) fclose(fp); 859 return (0); 860 } 861 862 static int 863 instance_processes(scf_instance_t *inst, const char *fmri, 864 pid_t **pids, uint_t *np) 865 { 866 scf_iter_t *iter; 867 int ret; 868 int restarter_spec; 869 870 /* Use the restarter specific get pids routine, if available. */ 871 ret = pids_by_restarter(inst, fmri, pids, np, &restarter_spec); 872 if (restarter_spec == 1) 873 return (ret); 874 875 if ((iter = scf_iter_create(h)) == NULL) 876 scfdie(); 877 878 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, g_pg) == 0) { 879 *pids = NULL; 880 *np = 0; 881 882 (void) propvals_to_pids(g_pg, scf_property_contract, pids, np, 883 g_prop, g_val, iter); 884 885 (void) propvals_to_pids(g_pg, SCF_PROPERTY_TRANSIENT_CONTRACT, 886 pids, np, g_prop, g_val, iter); 887 888 ret = 0; 889 } else { 890 if (scf_error() != SCF_ERROR_NOT_FOUND) 891 scfdie(); 892 893 ret = -1; 894 } 895 896 scf_iter_destroy(iter); 897 898 return (ret); 899 } 900 901 static int 902 get_psinfo(pid_t pid, psinfo_t *psip) 903 { 904 char path[100]; 905 int fd; 906 907 (void) snprintf(path, sizeof (path), "/proc/%lu/psinfo", pid); 908 909 fd = open64(path, O_RDONLY); 910 if (fd < 0) 911 return (-1); 912 913 if (read(fd, psip, sizeof (*psip)) < 0) 914 uu_die(gettext("Could not read info for process %lu"), pid); 915 916 (void) close(fd); 917 918 return (0); 919 } 920 921 922 923 /* 924 * Column sprint and sortkey functions 925 */ 926 927 struct column { 928 const char *name; 929 int width; 930 931 /* 932 * This function should write the value for the column into buf, and 933 * grow or allocate buf accordingly. It should always write at least 934 * width bytes, blanking unused bytes with spaces. If the field is 935 * greater than the column width we allow it to overlap other columns. 936 * In particular, it shouldn't write any null bytes. (Though an extra 937 * null byte past the end is currently tolerated.) If the property 938 * group is non-NULL, then we are dealing with a legacy service. 939 */ 940 void (*sprint)(char **, scf_walkinfo_t *); 941 942 int sortkey_width; 943 944 /* 945 * This function should write sortkey_width bytes into buf which will 946 * cause memcmp() to sort it properly. (Unlike sprint() above, 947 * however, an extra null byte may overrun the buffer.) The second 948 * argument controls whether the results are sorted in forward or 949 * reverse order. 950 */ 951 void (*get_sortkey)(char *, int, scf_walkinfo_t *); 952 }; 953 954 static void 955 reverse_bytes(char *buf, size_t len) 956 { 957 int i; 958 959 for (i = 0; i < len; ++i) 960 buf[i] = ~buf[i]; 961 } 962 963 /* CTID */ 964 #define CTID_COLUMN_WIDTH 6 965 966 static void 967 sprint_ctid(char **buf, scf_walkinfo_t *wip) 968 { 969 int r; 970 uint64_t c; 971 size_t newsize = (*buf ? strlen(*buf) : 0) + CTID_COLUMN_WIDTH + 2; 972 char *newbuf = safe_malloc(newsize); 973 int restarter_spec; 974 975 /* 976 * Use the restarter specific get pids routine, if available. 977 * Only check for non-legacy services (wip->pg == 0). 978 */ 979 if (wip->pg != NULL) { 980 r = pg_get_single_val(wip->pg, scf_property_contract, 981 SCF_TYPE_COUNT, &c, 0, EMPTY_OK | MULTI_OK); 982 } else { 983 r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec, 984 NULL, NULL); 985 if (restarter_spec == 0) { 986 /* No restarter specific routine */ 987 r = get_restarter_count_prop(wip->inst, 988 scf_property_contract, &c, EMPTY_OK | MULTI_OK); 989 } 990 } 991 992 if (r == 0) 993 (void) snprintf(newbuf, newsize, "%s%*lu ", 994 *buf ? *buf : "", CTID_COLUMN_WIDTH, (ctid_t)c); 995 else if (r == E2BIG) 996 (void) snprintf(newbuf, newsize, "%s%*lu* ", 997 *buf ? *buf : "", CTID_COLUMN_WIDTH - 1, (ctid_t)c); 998 else 999 (void) snprintf(newbuf, newsize, "%s%*s ", 1000 *buf ? *buf : "", CTID_COLUMN_WIDTH, "-"); 1001 if (*buf) 1002 free(*buf); 1003 *buf = newbuf; 1004 } 1005 1006 #define CTID_SORTKEY_WIDTH (sizeof (uint64_t)) 1007 1008 static void 1009 sortkey_ctid(char *buf, int reverse, scf_walkinfo_t *wip) 1010 { 1011 int r; 1012 uint64_t c; 1013 int restarter_spec; 1014 1015 /* 1016 * Use the restarter specific get pids routine, if available. 1017 * Only check for non-legacy services (wip->pg == 0). 1018 */ 1019 if (wip->pg != NULL) { 1020 r = pg_get_single_val(wip->pg, scf_property_contract, 1021 SCF_TYPE_COUNT, &c, 0, EMPTY_OK); 1022 } else { 1023 r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec, 1024 NULL, NULL); 1025 if (restarter_spec == 0) { 1026 /* No restarter specific routine */ 1027 r = get_restarter_count_prop(wip->inst, 1028 scf_property_contract, &c, EMPTY_OK); 1029 } 1030 } 1031 1032 if (r == 0) { 1033 /* 1034 * Use the id itself, but it must be big-endian for this to 1035 * work. 1036 */ 1037 c = BE_64(c); 1038 1039 bcopy(&c, buf, CTID_SORTKEY_WIDTH); 1040 } else { 1041 bzero(buf, CTID_SORTKEY_WIDTH); 1042 } 1043 1044 if (reverse) 1045 reverse_bytes(buf, CTID_SORTKEY_WIDTH); 1046 } 1047 1048 /* DESC */ 1049 #define DESC_COLUMN_WIDTH 100 1050 1051 static void 1052 sprint_desc(char **buf, scf_walkinfo_t *wip) 1053 { 1054 char *x; 1055 size_t newsize; 1056 char *newbuf; 1057 1058 if (common_name_buf == NULL) 1059 common_name_buf = safe_malloc(max_scf_value_length + 1); 1060 1061 bzero(common_name_buf, max_scf_value_length + 1); 1062 1063 if (wip->pg != NULL) { 1064 common_name_buf[0] = '-'; 1065 } else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale, 1066 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1067 1, 1) == -1 && 1068 inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C", 1069 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1070 1, 1) == -1) { 1071 common_name_buf[0] = '-'; 1072 } 1073 1074 /* 1075 * Collapse multi-line tm_common_name values into a single line. 1076 */ 1077 for (x = common_name_buf; *x != '\0'; x++) 1078 if (*x == '\n') 1079 *x = ' '; 1080 1081 if (strlen(common_name_buf) > DESC_COLUMN_WIDTH) 1082 newsize = (*buf ? strlen(*buf) : 0) + 1083 strlen(common_name_buf) + 1; 1084 else 1085 newsize = (*buf ? strlen(*buf) : 0) + DESC_COLUMN_WIDTH + 1; 1086 newbuf = safe_malloc(newsize); 1087 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1088 DESC_COLUMN_WIDTH, common_name_buf); 1089 if (*buf) 1090 free(*buf); 1091 *buf = newbuf; 1092 } 1093 1094 /* ARGSUSED */ 1095 static void 1096 sortkey_desc(char *buf, int reverse, scf_walkinfo_t *wip) 1097 { 1098 bzero(buf, DESC_COLUMN_WIDTH); 1099 } 1100 1101 /* State columns (STATE, NSTATE, S, N, SN, STA, NSTA) */ 1102 1103 static char 1104 state_to_char(const char *state) 1105 { 1106 if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0) 1107 return ('u'); 1108 1109 if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0) 1110 return ('0'); 1111 1112 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) 1113 return ('1'); 1114 1115 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) 1116 return ('m'); 1117 1118 if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0) 1119 return ('d'); 1120 1121 if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) 1122 return ('D'); 1123 1124 if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0) 1125 return ('L'); 1126 1127 return ('?'); 1128 } 1129 1130 /* Return true if inst is transitioning. */ 1131 static int 1132 transitioning(scf_instance_t *inst) 1133 { 1134 char nstate_name[MAX_SCF_STATE_STRING_SZ]; 1135 1136 get_restarter_string_prop(inst, scf_property_next_state, nstate_name, 1137 sizeof (nstate_name)); 1138 1139 return (state_to_char(nstate_name) != '?'); 1140 } 1141 1142 /* ARGSUSED */ 1143 static void 1144 sortkey_states(const char *pname, char *buf, int reverse, scf_walkinfo_t *wip) 1145 { 1146 char state_name[MAX_SCF_STATE_STRING_SZ]; 1147 1148 /* 1149 * Lower numbers are printed first, so these are arranged from least 1150 * interesting ("legacy run") to most interesting (unknown). 1151 */ 1152 if (wip->pg == NULL) { 1153 get_restarter_string_prop(wip->inst, pname, state_name, 1154 sizeof (state_name)); 1155 1156 if (strcmp(state_name, SCF_STATE_STRING_ONLINE) == 0) 1157 *buf = 2; 1158 else if (strcmp(state_name, SCF_STATE_STRING_DEGRADED) == 0) 1159 *buf = 3; 1160 else if (strcmp(state_name, SCF_STATE_STRING_OFFLINE) == 0) 1161 *buf = 4; 1162 else if (strcmp(state_name, SCF_STATE_STRING_MAINT) == 0) 1163 *buf = 5; 1164 else if (strcmp(state_name, SCF_STATE_STRING_DISABLED) == 0) 1165 *buf = 1; 1166 else if (strcmp(state_name, SCF_STATE_STRING_UNINIT) == 0) 1167 *buf = 6; 1168 else 1169 *buf = 7; 1170 } else 1171 *buf = 0; 1172 1173 if (reverse) 1174 *buf = 255 - *buf; 1175 } 1176 1177 static void 1178 sprint_state(char **buf, scf_walkinfo_t *wip) 1179 { 1180 char state_name[MAX_SCF_STATE_STRING_SZ + 1]; 1181 size_t newsize; 1182 char *newbuf; 1183 1184 if (wip->pg == NULL) { 1185 get_restarter_string_prop(wip->inst, scf_property_state, 1186 state_name, sizeof (state_name)); 1187 1188 /* Don't print blank fields, to ease parsing. */ 1189 if (state_name[0] == '\0') { 1190 state_name[0] = '-'; 1191 state_name[1] = '\0'; 1192 } 1193 1194 if (!opt_nstate_shown && transitioning(wip->inst)) { 1195 /* Append an asterisk if nstate is valid. */ 1196 (void) strcat(state_name, "*"); 1197 } 1198 } else 1199 (void) strcpy(state_name, SCF_STATE_STRING_LEGACY); 1200 1201 newsize = (*buf ? strlen(*buf) : 0) + MAX_SCF_STATE_STRING_SZ + 2; 1202 newbuf = safe_malloc(newsize); 1203 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1204 MAX_SCF_STATE_STRING_SZ + 1, state_name); 1205 1206 if (*buf) 1207 free(*buf); 1208 *buf = newbuf; 1209 } 1210 1211 static void 1212 sortkey_state(char *buf, int reverse, scf_walkinfo_t *wip) 1213 { 1214 sortkey_states(scf_property_state, buf, reverse, wip); 1215 } 1216 1217 static void 1218 sprint_nstate(char **buf, scf_walkinfo_t *wip) 1219 { 1220 char next_state_name[MAX_SCF_STATE_STRING_SZ]; 1221 boolean_t blank = 0; 1222 size_t newsize; 1223 char *newbuf; 1224 1225 if (wip->pg == NULL) { 1226 get_restarter_string_prop(wip->inst, scf_property_next_state, 1227 next_state_name, sizeof (next_state_name)); 1228 1229 /* Don't print blank fields, to ease parsing. */ 1230 if (next_state_name[0] == '\0' || 1231 strcmp(next_state_name, SCF_STATE_STRING_NONE) == 0) 1232 blank = 1; 1233 } else 1234 blank = 1; 1235 1236 if (blank) { 1237 next_state_name[0] = '-'; 1238 next_state_name[1] = '\0'; 1239 } 1240 1241 newsize = (*buf ? strlen(*buf) : 0) + MAX_SCF_STATE_STRING_SZ + 1; 1242 newbuf = safe_malloc(newsize); 1243 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1244 MAX_SCF_STATE_STRING_SZ - 1, next_state_name); 1245 if (*buf) 1246 free(*buf); 1247 *buf = newbuf; 1248 } 1249 1250 static void 1251 sortkey_nstate(char *buf, int reverse, scf_walkinfo_t *wip) 1252 { 1253 sortkey_states(scf_property_next_state, buf, reverse, wip); 1254 } 1255 1256 static void 1257 sprint_s(char **buf, scf_walkinfo_t *wip) 1258 { 1259 char tmp[3]; 1260 char state_name[MAX_SCF_STATE_STRING_SZ]; 1261 size_t newsize = (*buf ? strlen(*buf) : 0) + 4; 1262 char *newbuf = safe_malloc(newsize); 1263 1264 if (wip->pg == NULL) { 1265 get_restarter_string_prop(wip->inst, scf_property_state, 1266 state_name, sizeof (state_name)); 1267 tmp[0] = state_to_char(state_name); 1268 1269 if (!opt_nstate_shown && transitioning(wip->inst)) 1270 tmp[1] = '*'; 1271 else 1272 tmp[1] = ' '; 1273 } else { 1274 tmp[0] = 'L'; 1275 tmp[1] = ' '; 1276 } 1277 tmp[2] = ' '; 1278 (void) snprintf(newbuf, newsize, "%s%-*s", *buf ? *buf : "", 1279 3, tmp); 1280 if (*buf) 1281 free(*buf); 1282 *buf = newbuf; 1283 } 1284 1285 static void 1286 sprint_n(char **buf, scf_walkinfo_t *wip) 1287 { 1288 char tmp[2]; 1289 size_t newsize = (*buf ? strlen(*buf) : 0) + 3; 1290 char *newbuf = safe_malloc(newsize); 1291 char nstate_name[MAX_SCF_STATE_STRING_SZ]; 1292 1293 if (wip->pg == NULL) { 1294 get_restarter_string_prop(wip->inst, scf_property_next_state, 1295 nstate_name, sizeof (nstate_name)); 1296 1297 if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0) 1298 tmp[0] = '-'; 1299 else 1300 tmp[0] = state_to_char(nstate_name); 1301 } else 1302 tmp[0] = '-'; 1303 1304 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1305 2, tmp); 1306 if (*buf) 1307 free(*buf); 1308 *buf = newbuf; 1309 } 1310 1311 static void 1312 sprint_sn(char **buf, scf_walkinfo_t *wip) 1313 { 1314 char tmp[3]; 1315 size_t newsize = (*buf ? strlen(*buf) : 0) + 4; 1316 char *newbuf = safe_malloc(newsize); 1317 char nstate_name[MAX_SCF_STATE_STRING_SZ]; 1318 char state_name[MAX_SCF_STATE_STRING_SZ]; 1319 1320 if (wip->pg == NULL) { 1321 get_restarter_string_prop(wip->inst, scf_property_state, 1322 state_name, sizeof (state_name)); 1323 get_restarter_string_prop(wip->inst, scf_property_next_state, 1324 nstate_name, sizeof (nstate_name)); 1325 tmp[0] = state_to_char(state_name); 1326 1327 if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0) 1328 tmp[1] = '-'; 1329 else 1330 tmp[1] = state_to_char(nstate_name); 1331 } else { 1332 tmp[0] = 'L'; 1333 tmp[1] = '-'; 1334 } 1335 1336 tmp[2] = ' '; 1337 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1338 3, tmp); 1339 if (*buf) 1340 free(*buf); 1341 *buf = newbuf; 1342 } 1343 1344 /* ARGSUSED */ 1345 static void 1346 sortkey_sn(char *buf, int reverse, scf_walkinfo_t *wip) 1347 { 1348 sortkey_state(buf, reverse, wip); 1349 sortkey_nstate(buf + 1, reverse, wip); 1350 } 1351 1352 static const char * 1353 state_abbrev(const char *state) 1354 { 1355 if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0) 1356 return ("UN"); 1357 if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0) 1358 return ("OFF"); 1359 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) 1360 return ("ON"); 1361 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) 1362 return ("MNT"); 1363 if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0) 1364 return ("DIS"); 1365 if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) 1366 return ("DGD"); 1367 if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0) 1368 return ("LRC"); 1369 1370 return ("?"); 1371 } 1372 1373 static void 1374 sprint_sta(char **buf, scf_walkinfo_t *wip) 1375 { 1376 char state_name[MAX_SCF_STATE_STRING_SZ]; 1377 char sta[5]; 1378 size_t newsize = (*buf ? strlen(*buf) : 0) + 6; 1379 char *newbuf = safe_malloc(newsize); 1380 1381 if (wip->pg == NULL) 1382 get_restarter_string_prop(wip->inst, scf_property_state, 1383 state_name, sizeof (state_name)); 1384 else 1385 (void) strcpy(state_name, SCF_STATE_STRING_LEGACY); 1386 1387 (void) strcpy(sta, state_abbrev(state_name)); 1388 1389 if (wip->pg == NULL && !opt_nstate_shown && transitioning(wip->inst)) 1390 (void) strcat(sta, "*"); 1391 1392 (void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "", sta); 1393 if (*buf) 1394 free(*buf); 1395 *buf = newbuf; 1396 } 1397 1398 static void 1399 sprint_nsta(char **buf, scf_walkinfo_t *wip) 1400 { 1401 char state_name[MAX_SCF_STATE_STRING_SZ]; 1402 size_t newsize = (*buf ? strlen(*buf) : 0) + 6; 1403 char *newbuf = safe_malloc(newsize); 1404 1405 if (wip->pg == NULL) 1406 get_restarter_string_prop(wip->inst, scf_property_next_state, 1407 state_name, sizeof (state_name)); 1408 else 1409 (void) strcpy(state_name, SCF_STATE_STRING_NONE); 1410 1411 if (strcmp(state_name, SCF_STATE_STRING_NONE) == 0) 1412 (void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "", 1413 "-"); 1414 else 1415 (void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "", 1416 state_abbrev(state_name)); 1417 if (*buf) 1418 free(*buf); 1419 *buf = newbuf; 1420 } 1421 1422 /* FMRI */ 1423 #define FMRI_COLUMN_WIDTH 50 1424 static void 1425 sprint_fmri(char **buf, scf_walkinfo_t *wip) 1426 { 1427 char *fmri_buf = safe_malloc(max_scf_fmri_length + 1); 1428 size_t newsize; 1429 char *newbuf; 1430 1431 if (wip->pg == NULL) { 1432 if (scf_instance_to_fmri(wip->inst, fmri_buf, 1433 max_scf_fmri_length + 1) == -1) 1434 scfdie(); 1435 } else { 1436 (void) strcpy(fmri_buf, SCF_FMRI_LEGACY_PREFIX); 1437 if (pg_get_single_val(wip->pg, SCF_LEGACY_PROPERTY_NAME, 1438 SCF_TYPE_ASTRING, fmri_buf + 1439 sizeof (SCF_FMRI_LEGACY_PREFIX) - 1, 1440 max_scf_fmri_length + 1 - 1441 (sizeof (SCF_FMRI_LEGACY_PREFIX) - 1), 0) != 0) 1442 (void) strcat(fmri_buf, LEGACY_UNKNOWN); 1443 } 1444 1445 if (strlen(fmri_buf) > FMRI_COLUMN_WIDTH) 1446 newsize = (*buf ? strlen(*buf) : 0) + strlen(fmri_buf) + 2; 1447 else 1448 newsize = (*buf ? strlen(*buf) : 0) + FMRI_COLUMN_WIDTH + 2; 1449 newbuf = safe_malloc(newsize); 1450 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1451 FMRI_COLUMN_WIDTH, fmri_buf); 1452 free(fmri_buf); 1453 if (*buf) 1454 free(*buf); 1455 *buf = newbuf; 1456 } 1457 1458 static void 1459 sortkey_fmri(char *buf, int reverse, scf_walkinfo_t *wip) 1460 { 1461 char *tmp = NULL; 1462 1463 sprint_fmri(&tmp, wip); 1464 bcopy(tmp, buf, FMRI_COLUMN_WIDTH); 1465 free(tmp); 1466 if (reverse) 1467 reverse_bytes(buf, FMRI_COLUMN_WIDTH); 1468 } 1469 1470 /* Component columns */ 1471 #define COMPONENT_COLUMN_WIDTH 20 1472 static void 1473 sprint_scope(char **buf, scf_walkinfo_t *wip) 1474 { 1475 char *scope_buf = safe_malloc(max_scf_name_length + 1); 1476 size_t newsize = (*buf ? strlen(*buf) : 0) + COMPONENT_COLUMN_WIDTH + 2; 1477 char *newbuf = safe_malloc(newsize); 1478 1479 assert(wip->scope != NULL); 1480 1481 if (scf_scope_get_name(wip->scope, scope_buf, max_scf_name_length) < 0) 1482 scfdie(); 1483 1484 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1485 COMPONENT_COLUMN_WIDTH, scope_buf); 1486 if (*buf) 1487 free(*buf); 1488 *buf = newbuf; 1489 free(scope_buf); 1490 } 1491 1492 static void 1493 sortkey_scope(char *buf, int reverse, scf_walkinfo_t *wip) 1494 { 1495 char *tmp = NULL; 1496 1497 sprint_scope(&tmp, wip); 1498 bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH); 1499 free(tmp); 1500 if (reverse) 1501 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH); 1502 } 1503 1504 static void 1505 sprint_service(char **buf, scf_walkinfo_t *wip) 1506 { 1507 char *svc_buf = safe_malloc(max_scf_name_length + 1); 1508 char *newbuf; 1509 size_t newsize; 1510 1511 if (wip->pg == NULL) { 1512 if (scf_service_get_name(wip->svc, svc_buf, 1513 max_scf_name_length + 1) < 0) 1514 scfdie(); 1515 } else { 1516 if (pg_get_single_val(wip->pg, "name", SCF_TYPE_ASTRING, 1517 svc_buf, max_scf_name_length + 1, EMPTY_OK) != 0) 1518 (void) strcpy(svc_buf, LEGACY_UNKNOWN); 1519 } 1520 1521 1522 if (strlen(svc_buf) > COMPONENT_COLUMN_WIDTH) 1523 newsize = (*buf ? strlen(*buf) : 0) + strlen(svc_buf) + 2; 1524 else 1525 newsize = (*buf ? strlen(*buf) : 0) + 1526 COMPONENT_COLUMN_WIDTH + 2; 1527 newbuf = safe_malloc(newsize); 1528 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1529 COMPONENT_COLUMN_WIDTH, svc_buf); 1530 free(svc_buf); 1531 if (*buf) 1532 free(*buf); 1533 *buf = newbuf; 1534 } 1535 1536 static void 1537 sortkey_service(char *buf, int reverse, scf_walkinfo_t *wip) 1538 { 1539 char *tmp = NULL; 1540 1541 sprint_service(&tmp, wip); 1542 bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH); 1543 free(tmp); 1544 if (reverse) 1545 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH); 1546 } 1547 1548 /* INST */ 1549 static void 1550 sprint_instance(char **buf, scf_walkinfo_t *wip) 1551 { 1552 char *tmp = safe_malloc(max_scf_name_length + 1); 1553 size_t newsize = (*buf ? strlen(*buf) : 0) + COMPONENT_COLUMN_WIDTH + 2; 1554 char *newbuf = safe_malloc(newsize); 1555 1556 if (wip->pg == NULL) { 1557 if (scf_instance_get_name(wip->inst, tmp, 1558 max_scf_name_length + 1) < 0) 1559 scfdie(); 1560 } else { 1561 tmp[0] = '-'; 1562 tmp[1] = '\0'; 1563 } 1564 1565 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1566 COMPONENT_COLUMN_WIDTH, tmp); 1567 if (*buf) 1568 free(*buf); 1569 *buf = newbuf; 1570 free(tmp); 1571 } 1572 1573 static void 1574 sortkey_instance(char *buf, int reverse, scf_walkinfo_t *wip) 1575 { 1576 char *tmp = NULL; 1577 1578 sprint_instance(&tmp, wip); 1579 bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH); 1580 free(tmp); 1581 if (reverse) 1582 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH); 1583 } 1584 1585 /* STIME */ 1586 #define STIME_COLUMN_WIDTH 8 1587 #define FORMAT_TIME "%k:%M:%S" 1588 #define FORMAT_DATE "%b_%d " 1589 #define FORMAT_YEAR "%Y " 1590 1591 /* 1592 * sprint_stime() will allocate a new buffer and snprintf the services's 1593 * state timestamp. If the timestamp is unavailable for some reason 1594 * a '-' is given instead. 1595 */ 1596 static void 1597 sprint_stime(char **buf, scf_walkinfo_t *wip) 1598 { 1599 int r; 1600 struct timeval tv; 1601 time_t then; 1602 struct tm *tm; 1603 char st_buf[STIME_COLUMN_WIDTH + 1]; 1604 size_t newsize = (*buf ? strlen(*buf) : 0) + STIME_COLUMN_WIDTH + 2; 1605 char *newbuf = safe_malloc(newsize); 1606 1607 if (wip->pg == NULL) { 1608 r = get_restarter_time_prop(wip->inst, 1609 SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0); 1610 } else { 1611 r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP, 1612 SCF_TYPE_TIME, &tv, NULL, 0); 1613 } 1614 1615 if (r != 0) { 1616 /* 1617 * There's something amiss with our service 1618 * so we'll print a '-' for STIME. 1619 */ 1620 (void) snprintf(newbuf, newsize, "%s%-*s", *buf ? *buf : "", 1621 STIME_COLUMN_WIDTH + 1, "-"); 1622 } else { 1623 /* tv should be valid so we'll format it */ 1624 then = (time_t)tv.tv_sec; 1625 1626 tm = localtime(&then); 1627 /* 1628 * Print time if started within the past 24 hours, print date 1629 * if within the past 12 months or, finally, print year if 1630 * started greater than 12 months ago. 1631 */ 1632 if (now - then < 24 * 60 * 60) { 1633 (void) strftime(st_buf, sizeof (st_buf), 1634 gettext(FORMAT_TIME), tm); 1635 } else if (now - then < 12 * 30 * 24 * 60 * 60) { 1636 (void) strftime(st_buf, sizeof (st_buf), 1637 gettext(FORMAT_DATE), tm); 1638 } else { 1639 (void) strftime(st_buf, sizeof (st_buf), 1640 gettext(FORMAT_YEAR), tm); 1641 } 1642 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1643 STIME_COLUMN_WIDTH + 1, st_buf); 1644 } 1645 if (*buf) 1646 free(*buf); 1647 *buf = newbuf; 1648 } 1649 1650 #define STIME_SORTKEY_WIDTH (sizeof (uint64_t) + sizeof (uint32_t)) 1651 1652 /* ARGSUSED */ 1653 static void 1654 sortkey_stime(char *buf, int reverse, scf_walkinfo_t *wip) 1655 { 1656 struct timeval tv; 1657 int r; 1658 1659 if (wip->pg == NULL) 1660 r = get_restarter_time_prop(wip->inst, 1661 SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0); 1662 else 1663 r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP, 1664 SCF_TYPE_TIME, &tv, NULL, 0); 1665 1666 if (r == 0) { 1667 int64_t sec; 1668 int32_t us; 1669 1670 /* Stick it straight into the buffer. */ 1671 sec = tv.tv_sec; 1672 us = tv.tv_usec; 1673 1674 sec = BE_64(sec); 1675 us = BE_32(us); 1676 bcopy(&sec, buf, sizeof (sec)); 1677 bcopy(&us, buf + sizeof (sec), sizeof (us)); 1678 } else { 1679 bzero(buf, STIME_SORTKEY_WIDTH); 1680 } 1681 1682 if (reverse) 1683 reverse_bytes(buf, STIME_SORTKEY_WIDTH); 1684 } 1685 1686 1687 /* 1688 * Information about columns which can be displayed. If you add something, 1689 * check MAX_COLUMN_NAME_LENGTH_STR & update description_of_column() below. 1690 */ 1691 static const struct column columns[] = { 1692 { "CTID", CTID_COLUMN_WIDTH, sprint_ctid, 1693 CTID_SORTKEY_WIDTH, sortkey_ctid }, 1694 { "DESC", DESC_COLUMN_WIDTH, sprint_desc, 1695 DESC_COLUMN_WIDTH, sortkey_desc }, 1696 { "FMRI", FMRI_COLUMN_WIDTH, sprint_fmri, 1697 FMRI_COLUMN_WIDTH, sortkey_fmri }, 1698 { "INST", COMPONENT_COLUMN_WIDTH, sprint_instance, 1699 COMPONENT_COLUMN_WIDTH, sortkey_instance }, 1700 { "N", 1, sprint_n, 1, sortkey_nstate }, 1701 { "NSTA", 4, sprint_nsta, 1, sortkey_nstate }, 1702 { "NSTATE", MAX_SCF_STATE_STRING_SZ - 1, sprint_nstate, 1703 1, sortkey_nstate }, 1704 { "S", 2, sprint_s, 1, sortkey_state }, 1705 { "SCOPE", COMPONENT_COLUMN_WIDTH, sprint_scope, 1706 COMPONENT_COLUMN_WIDTH, sortkey_scope }, 1707 { "SN", 2, sprint_sn, 2, sortkey_sn }, 1708 { "SVC", COMPONENT_COLUMN_WIDTH, sprint_service, 1709 COMPONENT_COLUMN_WIDTH, sortkey_service }, 1710 { "STA", 4, sprint_sta, 1, sortkey_state }, 1711 { "STATE", MAX_SCF_STATE_STRING_SZ - 1 + 1, sprint_state, 1712 1, sortkey_state }, 1713 { "STIME", STIME_COLUMN_WIDTH, sprint_stime, 1714 STIME_SORTKEY_WIDTH, sortkey_stime }, 1715 }; 1716 1717 #define MAX_COLUMN_NAME_LENGTH_STR "6" 1718 1719 static const int ncolumns = sizeof (columns) / sizeof (columns[0]); 1720 1721 /* 1722 * Necessary thanks to gettext() & xgettext. 1723 */ 1724 static const char * 1725 description_of_column(int c) 1726 { 1727 const char *s = NULL; 1728 1729 switch (c) { 1730 case 0: 1731 s = gettext("contract ID for service (see contract(4))"); 1732 break; 1733 case 1: 1734 s = gettext("human-readable description of the service"); 1735 break; 1736 case 2: 1737 s = gettext("Fault Managed Resource Identifier for service"); 1738 break; 1739 case 3: 1740 s = gettext("portion of the FMRI indicating service instance"); 1741 break; 1742 case 4: 1743 s = gettext("abbreviation for next state (if in transition)"); 1744 break; 1745 case 5: 1746 s = gettext("abbreviation for next state (if in transition)"); 1747 break; 1748 case 6: 1749 s = gettext("name for next state (if in transition)"); 1750 break; 1751 case 7: 1752 s = gettext("abbreviation for current state"); 1753 break; 1754 case 8: 1755 s = gettext("name for scope associated with service"); 1756 break; 1757 case 9: 1758 s = gettext("abbreviation for current state and next state"); 1759 break; 1760 case 10: 1761 s = gettext("portion of the FMRI representing service name"); 1762 break; 1763 case 11: 1764 s = gettext("abbreviation for current state"); 1765 break; 1766 case 12: 1767 s = gettext("name for current state"); 1768 break; 1769 case 13: 1770 s = gettext("time of last state change"); 1771 break; 1772 } 1773 1774 assert(s != NULL); 1775 return (s); 1776 } 1777 1778 1779 static void 1780 print_usage(const char *progname, FILE *f, boolean_t do_exit) 1781 { 1782 (void) fprintf(f, gettext( 1783 "Usage: %1$s [-aHpv] [-o col[,col ... ]] [-R restarter] " 1784 "[-sS col] [<service> ...]\n" 1785 " %1$s -d | -D [-Hpv] [-o col[,col ... ]] [-sS col] " 1786 "[<service> ...]\n" 1787 " %1$s -l <service> ...\n" 1788 " %1$s -x [-v] [<service> ...]\n" 1789 " %1$s -?\n"), progname); 1790 1791 if (do_exit) 1792 exit(UU_EXIT_USAGE); 1793 } 1794 1795 #define argserr(progname) print_usage(progname, stderr, B_TRUE) 1796 1797 static void 1798 print_help(const char *progname) 1799 { 1800 int i; 1801 1802 print_usage(progname, stdout, B_FALSE); 1803 1804 (void) printf(gettext("\n" 1805 "\t-a list all service instances rather than " 1806 "only those that are enabled\n" 1807 "\t-d list dependencies of the specified service(s)\n" 1808 "\t-D list dependents of the specified service(s)\n" 1809 "\t-H omit header line from output\n" 1810 "\t-l list detailed information about the specified service(s)\n" 1811 "\t-o list only the specified columns in the output\n" 1812 "\t-p list process IDs and names associated with each service\n" 1813 "\t-R list only those services with the specified restarter\n" 1814 "\t-s sort output in ascending order by the specified column(s)\n" 1815 "\t-S sort output in descending order by the specified column(s)\n" 1816 "\t-v list verbose information appropriate to the type of output\n" 1817 "\t-x explain the status of services that might require maintenance,\n" 1818 "\t or explain the status of the specified service(s)\n" 1819 "\n\t" 1820 "Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n" 1821 "\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n" 1822 "\n" 1823 "\t%1$s [opts] svc:/network/smtp:sendmail\n" 1824 "\t%1$s [opts] network/smtp:sendmail\n" 1825 "\t%1$s [opts] network/*mail\n" 1826 "\t%1$s [opts] network/smtp\n" 1827 "\t%1$s [opts] smtp:sendmail\n" 1828 "\t%1$s [opts] smtp\n" 1829 "\t%1$s [opts] sendmail\n" 1830 "\n\t" 1831 "Columns for output or sorting can be specified using these names:\n" 1832 "\n"), progname); 1833 1834 for (i = 0; i < ncolumns; i++) { 1835 (void) printf("\t%-" MAX_COLUMN_NAME_LENGTH_STR "s %s\n", 1836 columns[i].name, description_of_column(i)); 1837 } 1838 } 1839 1840 1841 /* 1842 * A getsubopt()-like function which returns an index into the columns table. 1843 * On success, *optionp is set to point to the next sub-option, or the 1844 * terminating null if there are none. 1845 */ 1846 static int 1847 getcolumnopt(char **optionp) 1848 { 1849 char *str = *optionp, *cp; 1850 int i; 1851 1852 assert(optionp != NULL); 1853 assert(*optionp != NULL); 1854 1855 cp = strchr(*optionp, ','); 1856 if (cp != NULL) 1857 *cp = '\0'; 1858 1859 for (i = 0; i < ncolumns; ++i) { 1860 if (strcasecmp(str, columns[i].name) == 0) { 1861 if (cp != NULL) 1862 *optionp = cp + 1; 1863 else 1864 *optionp = strchr(*optionp, '\0'); 1865 1866 return (i); 1867 } 1868 } 1869 1870 return (-1); 1871 } 1872 1873 static void 1874 print_header() 1875 { 1876 int i; 1877 char *line_buf, *cp; 1878 1879 line_buf = safe_malloc(line_sz); 1880 cp = line_buf; 1881 for (i = 0; i < opt_cnum; ++i) { 1882 const struct column * const colp = &columns[opt_columns[i]]; 1883 1884 (void) snprintf(cp, colp->width + 1, "%-*s", colp->width, 1885 colp->name); 1886 cp += colp->width; 1887 *cp++ = ' '; 1888 } 1889 1890 /* Trim the trailing whitespace */ 1891 --cp; 1892 while (*cp == ' ') 1893 --cp; 1894 *(cp+1) = '\0'; 1895 (void) puts(line_buf); 1896 1897 free(line_buf); 1898 } 1899 1900 1901 1902 /* 1903 * Long listing (-l) functions. 1904 */ 1905 1906 static int 1907 pidcmp(const void *l, const void *r) 1908 { 1909 pid_t lp = *(pid_t *)l, rp = *(pid_t *)r; 1910 1911 if (lp < rp) 1912 return (-1); 1913 if (lp > rp) 1914 return (1); 1915 return (0); 1916 } 1917 1918 /* 1919 * This is the strlen() of the longest label ("description"), plus intercolumn 1920 * space. 1921 */ 1922 #define DETAILED_WIDTH (11 + 2) 1923 1924 /* 1925 * Callback routine to print header for contract id. 1926 * Called by ctids_by_restarter and print_detailed. 1927 */ 1928 static void 1929 print_ctid_header() 1930 { 1931 (void) printf("%-*s", DETAILED_WIDTH, "contract_id"); 1932 } 1933 1934 /* 1935 * Callback routine to print a contract id. 1936 * Called by ctids_by_restarter and print_detailed. 1937 */ 1938 static void 1939 print_ctid_detailed(uint64_t c) 1940 { 1941 (void) printf("%lu ", (ctid_t)c); 1942 } 1943 1944 static void 1945 detailed_list_processes(scf_walkinfo_t *wip) 1946 { 1947 uint64_t c; 1948 pid_t *pids; 1949 uint_t i, n; 1950 psinfo_t psi; 1951 1952 if (get_restarter_count_prop(wip->inst, scf_property_contract, &c, 1953 EMPTY_OK) != 0) 1954 return; 1955 1956 if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0) 1957 return; 1958 1959 qsort(pids, n, sizeof (*pids), pidcmp); 1960 1961 for (i = 0; i < n; ++i) { 1962 (void) printf("%-*s%lu", DETAILED_WIDTH, gettext("process"), 1963 pids[i]); 1964 1965 if (get_psinfo(pids[i], &psi) == 0) 1966 (void) printf(" %.*s", PRARGSZ, psi.pr_psargs); 1967 1968 (void) putchar('\n'); 1969 } 1970 1971 free(pids); 1972 } 1973 1974 /* 1975 * Determines the state of a dependency. If the FMRI specifies a file, then we 1976 * fake up a state based on whether we can access the file. 1977 */ 1978 static void 1979 get_fmri_state(char *fmri, char *state, size_t state_sz) 1980 { 1981 char *lfmri; 1982 const char *svc_name, *inst_name, *pg_name, *path; 1983 scf_service_t *svc; 1984 scf_instance_t *inst; 1985 scf_iter_t *iter; 1986 1987 lfmri = safe_strdup(fmri); 1988 1989 /* 1990 * Check for file:// dependencies 1991 */ 1992 if (scf_parse_file_fmri(lfmri, NULL, &path) == SCF_SUCCESS) { 1993 struct stat64 statbuf; 1994 const char *msg; 1995 1996 if (stat64(path, &statbuf) == 0) 1997 msg = "online"; 1998 else if (errno == ENOENT) 1999 msg = "absent"; 2000 else 2001 msg = "unknown"; 2002 2003 (void) strlcpy(state, msg, state_sz); 2004 return; 2005 } 2006 2007 /* 2008 * scf_parse_file_fmri() may have overwritten part of the string, so 2009 * copy it back. 2010 */ 2011 (void) strcpy(lfmri, fmri); 2012 2013 if (scf_parse_svc_fmri(lfmri, NULL, &svc_name, &inst_name, 2014 &pg_name, NULL) != SCF_SUCCESS) { 2015 free(lfmri); 2016 (void) strlcpy(state, "invalid", state_sz); 2017 return; 2018 } 2019 2020 free(lfmri); 2021 2022 if (svc_name == NULL || pg_name != NULL) { 2023 (void) strlcpy(state, "invalid", state_sz); 2024 return; 2025 } 2026 2027 if (inst_name != NULL) { 2028 /* instance: get state */ 2029 inst = scf_instance_create(h); 2030 if (inst == NULL) 2031 scfdie(); 2032 2033 if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL, 2034 NULL, SCF_DECODE_FMRI_EXACT) == SCF_SUCCESS) 2035 get_restarter_string_prop(inst, scf_property_state, 2036 state, state_sz); 2037 else { 2038 switch (scf_error()) { 2039 case SCF_ERROR_INVALID_ARGUMENT: 2040 (void) strlcpy(state, "invalid", state_sz); 2041 break; 2042 case SCF_ERROR_NOT_FOUND: 2043 (void) strlcpy(state, "absent", state_sz); 2044 break; 2045 2046 default: 2047 scfdie(); 2048 } 2049 } 2050 2051 scf_instance_destroy(inst); 2052 return; 2053 } 2054 2055 /* 2056 * service: If only one instance, use that state. Otherwise, say 2057 * "multiple". 2058 */ 2059 if ((svc = scf_service_create(h)) == NULL || 2060 (inst = scf_instance_create(h)) == NULL || 2061 (iter = scf_iter_create(h)) == NULL) 2062 scfdie(); 2063 2064 if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL, 2065 SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) { 2066 switch (scf_error()) { 2067 case SCF_ERROR_INVALID_ARGUMENT: 2068 (void) strlcpy(state, "invalid", state_sz); 2069 goto out; 2070 case SCF_ERROR_NOT_FOUND: 2071 (void) strlcpy(state, "absent", state_sz); 2072 goto out; 2073 2074 default: 2075 scfdie(); 2076 } 2077 } 2078 2079 if (scf_iter_service_instances(iter, svc) != SCF_SUCCESS) 2080 scfdie(); 2081 2082 switch (scf_iter_next_instance(iter, inst)) { 2083 case 0: 2084 (void) strlcpy(state, "absent", state_sz); 2085 goto out; 2086 2087 case 1: 2088 break; 2089 2090 default: 2091 scfdie(); 2092 } 2093 2094 /* Get the state in case this is the only instance. */ 2095 get_restarter_string_prop(inst, scf_property_state, state, state_sz); 2096 2097 switch (scf_iter_next_instance(iter, inst)) { 2098 case 0: 2099 break; 2100 2101 case 1: 2102 /* Nope, multiple instances. */ 2103 (void) strlcpy(state, "multiple", state_sz); 2104 goto out; 2105 2106 default: 2107 scfdie(); 2108 } 2109 2110 out: 2111 scf_iter_destroy(iter); 2112 scf_instance_destroy(inst); 2113 scf_service_destroy(svc); 2114 } 2115 2116 static void 2117 print_application_properties(scf_walkinfo_t *wip, scf_snapshot_t *snap) 2118 { 2119 scf_iter_t *pg_iter, *prop_iter, *val_iter; 2120 scf_propertygroup_t *pg; 2121 scf_property_t *prop; 2122 scf_value_t *val; 2123 scf_pg_tmpl_t *pt; 2124 scf_prop_tmpl_t *prt; 2125 char *pg_name_buf = safe_malloc(max_scf_name_length + 1); 2126 char *prop_name_buf = safe_malloc(max_scf_name_length + 1); 2127 char *snap_name = safe_malloc(max_scf_name_length + 1); 2128 char *val_buf = safe_malloc(max_scf_value_length + 1); 2129 char *desc, *cp; 2130 scf_type_t type; 2131 int i, j, k; 2132 uint8_t vis; 2133 2134 if ((pg_iter = scf_iter_create(h)) == NULL || 2135 (prop_iter = scf_iter_create(h)) == NULL || 2136 (val_iter = scf_iter_create(h)) == NULL || 2137 (val = scf_value_create(h)) == NULL || 2138 (prop = scf_property_create(h)) == NULL || 2139 (pt = scf_tmpl_pg_create(h)) == NULL || 2140 (prt = scf_tmpl_prop_create(h)) == NULL || 2141 (pg = scf_pg_create(h)) == NULL) 2142 scfdie(); 2143 2144 if (scf_iter_instance_pgs_typed_composed(pg_iter, wip->inst, snap, 2145 SCF_PG_APP_DEFAULT) == -1) 2146 scfdie(); 2147 2148 /* 2149 * Format for output: 2150 * pg (pgtype) 2151 * description 2152 * pg/prop (proptype) = <value> <value> 2153 * description 2154 */ 2155 while ((i = scf_iter_next_pg(pg_iter, pg)) == 1) { 2156 int tmpl = 0; 2157 2158 if (scf_pg_get_name(pg, pg_name_buf, max_scf_name_length) < 0) 2159 scfdie(); 2160 if (scf_snapshot_get_name(snap, snap_name, 2161 max_scf_name_length) < 0) 2162 scfdie(); 2163 2164 if (scf_tmpl_get_by_pg_name(wip->fmri, snap_name, pg_name_buf, 2165 SCF_PG_APP_DEFAULT, pt, 0) == 0) 2166 tmpl = 1; 2167 else 2168 tmpl = 0; 2169 2170 (void) printf("%s (%s)\n", pg_name_buf, SCF_PG_APP_DEFAULT); 2171 2172 if (tmpl == 1 && scf_tmpl_pg_description(pt, NULL, &desc) > 0) { 2173 (void) printf(" %s\n", desc); 2174 free(desc); 2175 } 2176 2177 if (scf_iter_pg_properties(prop_iter, pg) == -1) 2178 scfdie(); 2179 while ((j = scf_iter_next_property(prop_iter, prop)) == 1) { 2180 if (scf_property_get_name(prop, prop_name_buf, 2181 max_scf_name_length) < 0) 2182 scfdie(); 2183 if (scf_property_type(prop, &type) == -1) 2184 scfdie(); 2185 2186 if ((tmpl == 1) && 2187 (scf_tmpl_get_by_prop(pt, prop_name_buf, prt, 2188 0) != 0)) 2189 tmpl = 0; 2190 2191 if (tmpl == 1 && 2192 scf_tmpl_prop_visibility(prt, &vis) != -1 && 2193 vis == SCF_TMPL_VISIBILITY_HIDDEN) 2194 continue; 2195 2196 (void) printf("%s/%s (%s) = ", pg_name_buf, 2197 prop_name_buf, scf_type_to_string(type)); 2198 2199 if (scf_iter_property_values(val_iter, prop) == -1) 2200 scfdie(); 2201 2202 while ((k = scf_iter_next_value(val_iter, val)) == 1) { 2203 if (scf_value_get_as_string(val, val_buf, 2204 max_scf_value_length + 1) < 0) 2205 scfdie(); 2206 if (strpbrk(val_buf, " \t\n\"()") != NULL) { 2207 (void) printf("\""); 2208 for (cp = val_buf; *cp != '\0'; ++cp) { 2209 if (*cp == '"' || *cp == '\\') 2210 (void) putc('\\', 2211 stdout); 2212 2213 (void) putc(*cp, stdout); 2214 } 2215 (void) printf("\""); 2216 } else { 2217 (void) printf("%s ", val_buf); 2218 } 2219 } 2220 2221 (void) printf("\n"); 2222 2223 if (k == -1) 2224 scfdie(); 2225 2226 if (tmpl == 1 && scf_tmpl_prop_description(prt, NULL, 2227 &desc) > 0) { 2228 (void) printf(" %s\n", desc); 2229 free(desc); 2230 } 2231 } 2232 if (j == -1) 2233 scfdie(); 2234 } 2235 if (i == -1) 2236 scfdie(); 2237 2238 2239 scf_iter_destroy(pg_iter); 2240 scf_iter_destroy(prop_iter); 2241 scf_iter_destroy(val_iter); 2242 scf_value_destroy(val); 2243 scf_property_destroy(prop); 2244 scf_tmpl_pg_destroy(pt); 2245 scf_tmpl_prop_destroy(prt); 2246 scf_pg_destroy(pg); 2247 free(pg_name_buf); 2248 free(prop_name_buf); 2249 free(snap_name); 2250 free(val_buf); 2251 } 2252 2253 static void 2254 print_detailed_dependency(scf_propertygroup_t *pg) 2255 { 2256 scf_property_t *eprop; 2257 scf_iter_t *iter; 2258 scf_type_t ty; 2259 char *val_buf; 2260 int i; 2261 2262 if ((eprop = scf_property_create(h)) == NULL || 2263 (iter = scf_iter_create(h)) == NULL) 2264 scfdie(); 2265 2266 val_buf = safe_malloc(max_scf_value_length + 1); 2267 2268 if (scf_pg_get_property(pg, SCF_PROPERTY_ENTITIES, eprop) != 2269 SCF_SUCCESS || 2270 scf_property_type(eprop, &ty) != SCF_SUCCESS || 2271 ty != SCF_TYPE_FMRI) 2272 return; 2273 2274 (void) printf("%-*s", DETAILED_WIDTH, gettext("dependency")); 2275 2276 /* Print the grouping */ 2277 if (pg_get_single_val(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING, 2278 val_buf, max_scf_value_length + 1, 0) == 0) 2279 (void) fputs(val_buf, stdout); 2280 else 2281 (void) putchar('?'); 2282 2283 (void) putchar('/'); 2284 2285 if (pg_get_single_val(pg, SCF_PROPERTY_RESTART_ON, SCF_TYPE_ASTRING, 2286 val_buf, max_scf_value_length + 1, 0) == 0) 2287 (void) fputs(val_buf, stdout); 2288 else 2289 (void) putchar('?'); 2290 2291 /* Print the dependency entities. */ 2292 if (scf_iter_property_values(iter, eprop) == -1) 2293 scfdie(); 2294 2295 while ((i = scf_iter_next_value(iter, g_val)) == 1) { 2296 char state[MAX_SCF_STATE_STRING_SZ]; 2297 2298 if (scf_value_get_astring(g_val, val_buf, 2299 max_scf_value_length + 1) < 0) 2300 scfdie(); 2301 2302 (void) putchar(' '); 2303 (void) fputs(val_buf, stdout); 2304 2305 /* Print the state. */ 2306 state[0] = '-'; 2307 state[1] = '\0'; 2308 2309 get_fmri_state(val_buf, state, sizeof (state)); 2310 2311 (void) printf(" (%s)", state); 2312 } 2313 if (i == -1) 2314 scfdie(); 2315 2316 (void) putchar('\n'); 2317 2318 free(val_buf); 2319 scf_iter_destroy(iter); 2320 scf_property_destroy(eprop); 2321 } 2322 2323 /* ARGSUSED */ 2324 static int 2325 print_detailed(void *unused, scf_walkinfo_t *wip) 2326 { 2327 scf_snapshot_t *snap; 2328 scf_propertygroup_t *rpg; 2329 scf_iter_t *pg_iter; 2330 2331 char *buf; 2332 char *timebuf; 2333 size_t tbsz; 2334 int ret; 2335 uint64_t c; 2336 int temp, perm; 2337 struct timeval tv; 2338 time_t stime; 2339 struct tm *tmp; 2340 int restarter_spec; 2341 int restarter_ret; 2342 2343 const char * const fmt = "%-*s%s\n"; 2344 2345 assert(wip->pg == NULL); 2346 2347 rpg = scf_pg_create(h); 2348 if (rpg == NULL) 2349 scfdie(); 2350 2351 if (first_paragraph) 2352 first_paragraph = 0; 2353 else 2354 (void) putchar('\n'); 2355 2356 buf = safe_malloc(max_scf_fmri_length + 1); 2357 2358 if (scf_instance_to_fmri(wip->inst, buf, max_scf_fmri_length + 1) != -1) 2359 (void) printf(fmt, DETAILED_WIDTH, "fmri", buf); 2360 2361 if (common_name_buf == NULL) 2362 common_name_buf = safe_malloc(max_scf_value_length + 1); 2363 2364 if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale, 2365 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1) 2366 == 0) 2367 (void) printf(fmt, DETAILED_WIDTH, gettext("name"), 2368 common_name_buf); 2369 else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C", 2370 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1) 2371 == 0) 2372 (void) printf(fmt, DETAILED_WIDTH, gettext("name"), 2373 common_name_buf); 2374 2375 /* 2376 * Synthesize an 'enabled' property that hides the enabled_ovr 2377 * implementation from the user. If the service has been temporarily 2378 * set to a state other than its permanent value, alert the user with 2379 * a '(temporary)' message. 2380 */ 2381 perm = instance_enabled(wip->inst, B_FALSE); 2382 temp = instance_enabled(wip->inst, B_TRUE); 2383 if (temp != -1) { 2384 if (temp != perm) 2385 (void) printf(gettext("%-*s%s (temporary)\n"), 2386 DETAILED_WIDTH, gettext("enabled"), 2387 temp ? gettext("true") : gettext("false")); 2388 else 2389 (void) printf(fmt, DETAILED_WIDTH, 2390 gettext("enabled"), temp ? gettext("true") : 2391 gettext("false")); 2392 } else if (perm != -1) { 2393 (void) printf(fmt, DETAILED_WIDTH, gettext("enabled"), 2394 perm ? gettext("true") : gettext("false")); 2395 } 2396 2397 /* 2398 * Property values may be longer than max_scf_fmri_length, but these 2399 * shouldn't be, so we'll just reuse buf. The user can use svcprop if 2400 * he suspects something fishy. 2401 */ 2402 if (scf_instance_get_pg(wip->inst, SCF_PG_RESTARTER, rpg) != 0) { 2403 if (scf_error() != SCF_ERROR_NOT_FOUND) 2404 scfdie(); 2405 2406 scf_pg_destroy(rpg); 2407 rpg = NULL; 2408 } 2409 2410 if (rpg) { 2411 if (pg_get_single_val(rpg, scf_property_state, SCF_TYPE_ASTRING, 2412 buf, max_scf_fmri_length + 1, 0) == 0) 2413 (void) printf(fmt, DETAILED_WIDTH, gettext("state"), 2414 buf); 2415 2416 if (pg_get_single_val(rpg, scf_property_next_state, 2417 SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0) 2418 (void) printf(fmt, DETAILED_WIDTH, 2419 gettext("next_state"), buf); 2420 2421 if (pg_get_single_val(rpg, SCF_PROPERTY_STATE_TIMESTAMP, 2422 SCF_TYPE_TIME, &tv, NULL, 0) == 0) { 2423 stime = tv.tv_sec; 2424 tmp = localtime(&stime); 2425 for (tbsz = 50; ; tbsz *= 2) { 2426 timebuf = safe_malloc(tbsz); 2427 if (strftime(timebuf, tbsz, NULL, tmp) != 0) 2428 break; 2429 free(timebuf); 2430 } 2431 (void) printf(fmt, DETAILED_WIDTH, 2432 gettext("state_time"), 2433 timebuf); 2434 free(timebuf); 2435 } 2436 2437 if (pg_get_single_val(rpg, SCF_PROPERTY_ALT_LOGFILE, 2438 SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0) 2439 (void) printf(fmt, DETAILED_WIDTH, 2440 gettext("alt_logfile"), buf); 2441 2442 if (pg_get_single_val(rpg, SCF_PROPERTY_LOGFILE, 2443 SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0) 2444 (void) printf(fmt, DETAILED_WIDTH, gettext("logfile"), 2445 buf); 2446 } 2447 2448 if (inst_get_single_val(wip->inst, SCF_PG_GENERAL, 2449 SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, buf, 2450 max_scf_fmri_length + 1, 0, 0, 1) == 0) 2451 (void) printf(fmt, DETAILED_WIDTH, gettext("restarter"), buf); 2452 else 2453 (void) printf(fmt, DETAILED_WIDTH, gettext("restarter"), 2454 SCF_SERVICE_STARTD); 2455 2456 free(buf); 2457 2458 /* 2459 * Use the restarter specific routine to print the ctids, if available. 2460 * If restarter specific action is available and it fails, then die. 2461 */ 2462 restarter_ret = ctids_by_restarter(wip, &c, 1, 0, 2463 &restarter_spec, print_ctid_header, print_ctid_detailed); 2464 if (restarter_spec == 1) { 2465 if (restarter_ret != 0) 2466 uu_die(gettext("Unable to get restarter for %s"), 2467 wip->fmri); 2468 goto restarter_common; 2469 } 2470 2471 if (rpg) { 2472 scf_iter_t *iter; 2473 2474 if ((iter = scf_iter_create(h)) == NULL) 2475 scfdie(); 2476 2477 if (scf_pg_get_property(rpg, scf_property_contract, g_prop) == 2478 0) { 2479 if (scf_property_is_type(g_prop, SCF_TYPE_COUNT) == 0) { 2480 2481 /* Callback to print ctid header */ 2482 print_ctid_header(); 2483 2484 if (scf_iter_property_values(iter, g_prop) != 0) 2485 scfdie(); 2486 2487 for (;;) { 2488 ret = scf_iter_next_value(iter, g_val); 2489 if (ret == -1) 2490 scfdie(); 2491 if (ret == 0) 2492 break; 2493 2494 if (scf_value_get_count(g_val, &c) != 0) 2495 scfdie(); 2496 2497 /* Callback to print contract id. */ 2498 print_ctid_detailed(c); 2499 } 2500 2501 (void) putchar('\n'); 2502 } else { 2503 if (scf_error() != SCF_ERROR_TYPE_MISMATCH) 2504 scfdie(); 2505 } 2506 } else { 2507 if (scf_error() != SCF_ERROR_NOT_FOUND) 2508 scfdie(); 2509 } 2510 2511 scf_iter_destroy(iter); 2512 } else { 2513 if (scf_error() != SCF_ERROR_NOT_FOUND) 2514 scfdie(); 2515 } 2516 2517 restarter_common: 2518 scf_pg_destroy(rpg); 2519 2520 /* Dependencies. */ 2521 if ((pg_iter = scf_iter_create(h)) == NULL) 2522 scfdie(); 2523 2524 snap = get_running_snapshot(wip->inst); 2525 2526 if (scf_iter_instance_pgs_typed_composed(pg_iter, wip->inst, snap, 2527 SCF_GROUP_DEPENDENCY) != SCF_SUCCESS) 2528 scfdie(); 2529 2530 while ((ret = scf_iter_next_pg(pg_iter, g_pg)) == 1) 2531 print_detailed_dependency(g_pg); 2532 if (ret == -1) 2533 scfdie(); 2534 2535 scf_iter_destroy(pg_iter); 2536 2537 if (opt_processes) 2538 detailed_list_processes(wip); 2539 2540 /* "application" type property groups */ 2541 if (opt_verbose == 1) 2542 print_application_properties(wip, snap); 2543 2544 scf_snapshot_destroy(snap); 2545 2546 return (0); 2547 } 2548 2549 int 2550 qsort_str_compare(const void *p1, const void *p2) 2551 { 2552 return (strcmp((const char *)p1, (const char *)p2)); 2553 } 2554 2555 /* 2556 * get_notify_param_classes() 2557 * return the fma classes that don't have a tag in fma_tags[], otherwise NULL 2558 */ 2559 static char ** 2560 get_notify_param_classes() 2561 { 2562 scf_handle_t *h = _scf_handle_create_and_bind(SCF_VERSION); 2563 scf_instance_t *inst = scf_instance_create(h); 2564 scf_snapshot_t *snap = scf_snapshot_create(h); 2565 scf_snaplevel_t *slvl = scf_snaplevel_create(h); 2566 scf_propertygroup_t *pg = scf_pg_create(h); 2567 scf_iter_t *iter = scf_iter_create(h); 2568 int size = 4; 2569 int n = 0; 2570 size_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1; 2571 int err; 2572 char *pgname = safe_malloc(sz); 2573 char **buf = safe_malloc(size * sizeof (char *)); 2574 2575 if (h == NULL || inst == NULL || snap == NULL || slvl == NULL || 2576 pg == NULL || iter == NULL) { 2577 uu_die(gettext("Failed object creation: %s\n"), 2578 scf_strerror(scf_error())); 2579 } 2580 2581 if (scf_handle_decode_fmri(h, SCF_NOTIFY_PARAMS_INST, NULL, NULL, inst, 2582 NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0) 2583 uu_die(gettext("Failed to decode %s: %s\n"), 2584 SCF_NOTIFY_PARAMS_INST, scf_strerror(scf_error())); 2585 2586 if (scf_instance_get_snapshot(inst, "running", snap) != 0) 2587 uu_die(gettext("Failed to get snapshot: %s\n"), 2588 scf_strerror(scf_error())); 2589 2590 if (scf_snapshot_get_base_snaplevel(snap, slvl) != 0) 2591 uu_die(gettext("Failed to get base snaplevel: %s\n"), 2592 scf_strerror(scf_error())); 2593 2594 if (scf_iter_snaplevel_pgs_typed(iter, slvl, 2595 SCF_NOTIFY_PARAMS_PG_TYPE) != 0) 2596 uu_die(gettext("Failed to get iterator: %s\n"), 2597 scf_strerror(scf_error())); 2598 2599 while ((err = scf_iter_next_pg(iter, pg)) == 1) { 2600 char *c; 2601 2602 if (scf_pg_get_name(pg, pgname, sz) == -1) 2603 uu_die(gettext("Failed to get pg name: %s\n"), 2604 scf_strerror(scf_error())); 2605 if ((c = strrchr(pgname, ',')) != NULL) 2606 *c = '\0'; 2607 if (has_fma_tag(pgname)) 2608 continue; 2609 if (!is_fma_token(pgname)) 2610 /* 2611 * We don't emmit a warning here so that we don't 2612 * pollute the output 2613 */ 2614 continue; 2615 2616 if (n + 1 >= size) { 2617 size *= 2; 2618 buf = realloc(buf, size * sizeof (char *)); 2619 if (buf == NULL) 2620 uu_die(gettext("Out of memory.\n")); 2621 } 2622 buf[n] = safe_strdup(pgname); 2623 ++n; 2624 } 2625 /* 2626 * NULL terminate buf 2627 */ 2628 buf[n] = NULL; 2629 if (err == -1) 2630 uu_die(gettext("Failed to iterate pgs: %s\n"), 2631 scf_strerror(scf_error())); 2632 2633 /* sort the classes */ 2634 qsort((void *)buf, n, sizeof (char *), qsort_str_compare); 2635 2636 free(pgname); 2637 scf_iter_destroy(iter); 2638 scf_pg_destroy(pg); 2639 scf_snaplevel_destroy(slvl); 2640 scf_snapshot_destroy(snap); 2641 scf_instance_destroy(inst); 2642 scf_handle_destroy(h); 2643 2644 return (buf); 2645 } 2646 2647 /* 2648 * get_fma_notify_params() 2649 * populates an nvlist_t with notifycation parameters for a given FMA class 2650 * returns 0 if the nvlist is populated, 1 otherwise; 2651 */ 2652 int 2653 get_fma_notify_params(nvlist_t *nvl, const char *class) 2654 { 2655 if (_scf_get_fma_notify_params(class, nvl, 0) != 0) { 2656 /* 2657 * if the preferences have just been deleted 2658 * or does not exist, just skip. 2659 */ 2660 if (scf_error() != SCF_ERROR_NOT_FOUND && 2661 scf_error() != SCF_ERROR_DELETED) 2662 uu_warn(gettext( 2663 "Failed get_fma_notify_params %s\n"), 2664 scf_strerror(scf_error())); 2665 2666 return (1); 2667 } 2668 2669 return (0); 2670 } 2671 2672 /* 2673 * print_notify_fma() 2674 * outputs the notification paramets of FMA events. 2675 * It first outputs classes in fma_tags[], then outputs the other classes 2676 * sorted alphabetically 2677 */ 2678 static void 2679 print_notify_fma(void) 2680 { 2681 nvlist_t *nvl; 2682 char **tmp = NULL; 2683 char **classes, *p; 2684 const char *class; 2685 uint32_t i; 2686 2687 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 2688 uu_die(gettext("Out of memory.\n")); 2689 2690 for (i = 0; (class = get_fma_class(i)) != NULL; ++i) { 2691 if (get_fma_notify_params(nvl, class) == 0) 2692 listnotify_print(nvl, get_fma_tag(i)); 2693 } 2694 2695 if ((classes = get_notify_param_classes()) == NULL) 2696 goto cleanup; 2697 2698 tmp = classes; 2699 for (p = *tmp; p; ++tmp, p = *tmp) { 2700 if (get_fma_notify_params(nvl, p) == 0) 2701 listnotify_print(nvl, re_tag(p)); 2702 2703 free(p); 2704 } 2705 2706 free(classes); 2707 2708 cleanup: 2709 nvlist_free(nvl); 2710 } 2711 2712 /* 2713 * print_notify_fmri() 2714 * prints notifycation parameters for an SMF instance. 2715 */ 2716 static void 2717 print_notify_fmri(const char *fmri) 2718 { 2719 nvlist_t *nvl; 2720 2721 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 2722 uu_die(gettext("Out of memory.\n")); 2723 2724 if (_scf_get_svc_notify_params(fmri, nvl, SCF_TRANSITION_ALL, 0, 0) != 2725 SCF_SUCCESS) { 2726 if (scf_error() != SCF_ERROR_NOT_FOUND && 2727 scf_error() != SCF_ERROR_DELETED) 2728 uu_warn(gettext( 2729 "Failed _scf_get_svc_notify_params: %s\n"), 2730 scf_strerror(scf_error())); 2731 } else { 2732 if (strcmp(SCF_INSTANCE_GLOBAL, fmri) == 0) 2733 safe_printf( 2734 gettext("System wide notification parameters:\n")); 2735 safe_printf("%s:\n", fmri); 2736 listnotify_print(nvl, NULL); 2737 } 2738 nvlist_free(nvl); 2739 } 2740 2741 /* 2742 * print_notify_special() 2743 * prints notification parameters for FMA events and system wide SMF state 2744 * transitions parameters 2745 */ 2746 static void 2747 print_notify_special() 2748 { 2749 safe_printf("Notification parameters for FMA Events\n"); 2750 print_notify_fma(); 2751 print_notify_fmri(SCF_INSTANCE_GLOBAL); 2752 } 2753 2754 /* 2755 * print_notify() 2756 * callback function to print notification parameters for SMF state transition 2757 * instances. It skips global and notify-params instances as they should be 2758 * printed by print_notify_special() 2759 */ 2760 /* ARGSUSED */ 2761 static int 2762 print_notify(void *unused, scf_walkinfo_t *wip) 2763 { 2764 if (strcmp(SCF_INSTANCE_GLOBAL, wip->fmri) == 0 || 2765 strcmp(SCF_NOTIFY_PARAMS_INST, wip->fmri) == 0) 2766 return (0); 2767 2768 print_notify_fmri(wip->fmri); 2769 2770 return (0); 2771 } 2772 2773 /* 2774 * Append a one-lined description of each process in inst's contract(s) and 2775 * return the augmented string. 2776 */ 2777 static char * 2778 add_processes(scf_walkinfo_t *wip, char *line, scf_propertygroup_t *lpg) 2779 { 2780 pid_t *pids = NULL; 2781 uint_t i, n = 0; 2782 2783 if (lpg == NULL) { 2784 if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0) 2785 return (line); 2786 } else { 2787 /* Legacy services */ 2788 scf_iter_t *iter; 2789 2790 if ((iter = scf_iter_create(h)) == NULL) 2791 scfdie(); 2792 2793 (void) propvals_to_pids(lpg, scf_property_contract, &pids, &n, 2794 g_prop, g_val, iter); 2795 2796 scf_iter_destroy(iter); 2797 } 2798 2799 if (n == 0) 2800 return (line); 2801 2802 qsort(pids, n, sizeof (*pids), pidcmp); 2803 2804 for (i = 0; i < n; ++i) { 2805 char *cp, stime[9]; 2806 psinfo_t psi; 2807 struct tm *tm; 2808 int len = 1 + 15 + 8 + 3 + 6 + 1 + PRFNSZ; 2809 2810 if (get_psinfo(pids[i], &psi) != 0) 2811 continue; 2812 2813 line = realloc(line, strlen(line) + len); 2814 if (line == NULL) 2815 uu_die(gettext("Out of memory.\n")); 2816 2817 cp = strchr(line, '\0'); 2818 2819 tm = localtime(&psi.pr_start.tv_sec); 2820 2821 /* 2822 * Print time if started within the past 24 hours, print date 2823 * if within the past 12 months, print year if started greater 2824 * than 12 months ago. 2825 */ 2826 if (now - psi.pr_start.tv_sec < 24 * 60 * 60) 2827 (void) strftime(stime, sizeof (stime), 2828 gettext(FORMAT_TIME), tm); 2829 else if (now - psi.pr_start.tv_sec < 12 * 30 * 24 * 60 * 60) 2830 (void) strftime(stime, sizeof (stime), 2831 gettext(FORMAT_DATE), tm); 2832 else 2833 (void) strftime(stime, sizeof (stime), 2834 gettext(FORMAT_YEAR), tm); 2835 2836 (void) snprintf(cp, len, "\n %-8s %6ld %.*s", 2837 stime, pids[i], PRFNSZ, psi.pr_fname); 2838 } 2839 2840 free(pids); 2841 2842 return (line); 2843 } 2844 2845 /*ARGSUSED*/ 2846 static int 2847 list_instance(void *unused, scf_walkinfo_t *wip) 2848 { 2849 struct avl_string *lp; 2850 char *cp; 2851 int i; 2852 uu_avl_index_t idx; 2853 2854 /* 2855 * If the user has specified a restarter, check for a match first 2856 */ 2857 if (restarters != NULL) { 2858 struct pfmri_list *rest; 2859 int match; 2860 char *restarter_fmri; 2861 const char *scope_name, *svc_name, *inst_name, *pg_name; 2862 2863 /* legacy services don't have restarters */ 2864 if (wip->pg != NULL) 2865 return (0); 2866 2867 restarter_fmri = safe_malloc(max_scf_fmri_length + 1); 2868 2869 if (inst_get_single_val(wip->inst, SCF_PG_GENERAL, 2870 SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, restarter_fmri, 2871 max_scf_fmri_length + 1, 0, 0, 1) != 0) 2872 (void) strcpy(restarter_fmri, SCF_SERVICE_STARTD); 2873 2874 if (scf_parse_svc_fmri(restarter_fmri, &scope_name, &svc_name, 2875 &inst_name, &pg_name, NULL) != SCF_SUCCESS) { 2876 free(restarter_fmri); 2877 return (0); 2878 } 2879 2880 match = 0; 2881 for (rest = restarters; rest != NULL; rest = rest->next) { 2882 if (strcmp(rest->scope, scope_name) == 0 && 2883 strcmp(rest->service, svc_name) == 0 && 2884 strcmp(rest->instance, inst_name) == 0) 2885 match = 1; 2886 } 2887 2888 free(restarter_fmri); 2889 2890 if (!match) 2891 return (0); 2892 } 2893 2894 if (wip->pg == NULL && ht_buckets != NULL && ht_add(wip->fmri)) { 2895 /* It was already there. */ 2896 return (0); 2897 } 2898 2899 lp = safe_malloc(sizeof (*lp)); 2900 2901 lp->str = NULL; 2902 for (i = 0; i < opt_cnum; ++i) { 2903 columns[opt_columns[i]].sprint(&lp->str, wip); 2904 } 2905 cp = lp->str + strlen(lp->str); 2906 cp--; 2907 while (*cp == ' ') 2908 cp--; 2909 *(cp+1) = '\0'; 2910 2911 /* If we're supposed to list the processes, too, do that now. */ 2912 if (opt_processes) 2913 lp->str = add_processes(wip, lp->str, wip->pg); 2914 2915 /* Create the sort key. */ 2916 cp = lp->key = safe_malloc(sortkey_sz); 2917 for (i = 0; i < opt_snum; ++i) { 2918 int j = opt_sort[i] & 0xff; 2919 2920 assert(columns[j].get_sortkey != NULL); 2921 columns[j].get_sortkey(cp, opt_sort[i] & ~0xff, wip); 2922 cp += columns[j].sortkey_width; 2923 } 2924 2925 /* Insert into AVL tree. */ 2926 uu_avl_node_init(lp, &lp->node, lines_pool); 2927 (void) uu_avl_find(lines, lp, NULL, &idx); 2928 uu_avl_insert(lines, lp, idx); 2929 2930 return (0); 2931 } 2932 2933 static int 2934 list_if_enabled(void *unused, scf_walkinfo_t *wip) 2935 { 2936 if (wip->pg != NULL || 2937 instance_enabled(wip->inst, B_FALSE) == 1 || 2938 instance_enabled(wip->inst, B_TRUE) == 1) 2939 return (list_instance(unused, wip)); 2940 2941 return (0); 2942 } 2943 2944 /* 2945 * Service FMRI selection: Lookup and call list_instance() for the instances. 2946 * Instance FMRI selection: Lookup and call list_instance(). 2947 * 2948 * Note: This is shoehorned into a walk_dependencies() callback prototype so 2949 * it can be used in list_dependencies. 2950 */ 2951 static int 2952 list_svc_or_inst_fmri(void *complain, scf_walkinfo_t *wip) 2953 { 2954 char *fmri; 2955 const char *svc_name, *inst_name, *pg_name, *save; 2956 scf_iter_t *iter; 2957 int ret; 2958 2959 fmri = safe_strdup(wip->fmri); 2960 2961 if (scf_parse_svc_fmri(fmri, NULL, &svc_name, &inst_name, &pg_name, 2962 NULL) != SCF_SUCCESS) { 2963 if (complain) 2964 uu_warn(gettext("FMRI \"%s\" is invalid.\n"), 2965 wip->fmri); 2966 exit_status = UU_EXIT_FATAL; 2967 free(fmri); 2968 return (0); 2969 } 2970 2971 /* 2972 * Yes, this invalidates *_name, but we only care whether they're NULL 2973 * or not. 2974 */ 2975 free(fmri); 2976 2977 if (svc_name == NULL || pg_name != NULL) { 2978 if (complain) 2979 uu_warn(gettext("FMRI \"%s\" does not designate a " 2980 "service or instance.\n"), wip->fmri); 2981 return (0); 2982 } 2983 2984 if (inst_name != NULL) { 2985 /* instance */ 2986 if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc, 2987 wip->inst, NULL, NULL, 0) != SCF_SUCCESS) { 2988 if (scf_error() != SCF_ERROR_NOT_FOUND) 2989 scfdie(); 2990 2991 if (complain) 2992 uu_warn(gettext( 2993 "Instance \"%s\" does not exist.\n"), 2994 wip->fmri); 2995 return (0); 2996 } 2997 2998 return (list_instance(NULL, wip)); 2999 } 3000 3001 /* service: Walk the instances. */ 3002 if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc, NULL, 3003 NULL, NULL, 0) != SCF_SUCCESS) { 3004 if (scf_error() != SCF_ERROR_NOT_FOUND) 3005 scfdie(); 3006 3007 if (complain) 3008 uu_warn(gettext("Service \"%s\" does not exist.\n"), 3009 wip->fmri); 3010 3011 exit_status = UU_EXIT_FATAL; 3012 3013 return (0); 3014 } 3015 3016 iter = scf_iter_create(h); 3017 if (iter == NULL) 3018 scfdie(); 3019 3020 if (scf_iter_service_instances(iter, wip->svc) != SCF_SUCCESS) 3021 scfdie(); 3022 3023 if ((fmri = malloc(max_scf_fmri_length + 1)) == NULL) { 3024 scf_iter_destroy(iter); 3025 exit_status = UU_EXIT_FATAL; 3026 return (0); 3027 } 3028 3029 save = wip->fmri; 3030 wip->fmri = fmri; 3031 while ((ret = scf_iter_next_instance(iter, wip->inst)) == 1) { 3032 if (scf_instance_to_fmri(wip->inst, fmri, 3033 max_scf_fmri_length + 1) <= 0) 3034 scfdie(); 3035 (void) list_instance(NULL, wip); 3036 } 3037 free(fmri); 3038 wip->fmri = save; 3039 if (ret == -1) 3040 scfdie(); 3041 3042 exit_status = UU_EXIT_OK; 3043 3044 scf_iter_destroy(iter); 3045 3046 return (0); 3047 } 3048 3049 /* 3050 * Dependency selection: Straightforward since each instance lists the 3051 * services it depends on. 3052 */ 3053 3054 static void 3055 walk_dependencies(scf_walkinfo_t *wip, scf_walk_callback callback, void *data) 3056 { 3057 scf_snapshot_t *snap; 3058 scf_iter_t *iter, *viter; 3059 int ret, vret; 3060 char *dep; 3061 3062 assert(wip->inst != NULL); 3063 3064 if ((iter = scf_iter_create(h)) == NULL || 3065 (viter = scf_iter_create(h)) == NULL) 3066 scfdie(); 3067 3068 snap = get_running_snapshot(wip->inst); 3069 3070 if (scf_iter_instance_pgs_typed_composed(iter, wip->inst, snap, 3071 SCF_GROUP_DEPENDENCY) != SCF_SUCCESS) 3072 scfdie(); 3073 3074 dep = safe_malloc(max_scf_value_length + 1); 3075 3076 while ((ret = scf_iter_next_pg(iter, g_pg)) == 1) { 3077 scf_type_t ty; 3078 3079 /* Ignore exclude_any dependencies. */ 3080 if (scf_pg_get_property(g_pg, SCF_PROPERTY_GROUPING, g_prop) != 3081 SCF_SUCCESS) { 3082 if (scf_error() != SCF_ERROR_NOT_FOUND) 3083 scfdie(); 3084 3085 continue; 3086 } 3087 3088 if (scf_property_type(g_prop, &ty) != SCF_SUCCESS) 3089 scfdie(); 3090 3091 if (ty != SCF_TYPE_ASTRING) 3092 continue; 3093 3094 if (scf_property_get_value(g_prop, g_val) != SCF_SUCCESS) { 3095 if (scf_error() != SCF_ERROR_CONSTRAINT_VIOLATED) 3096 scfdie(); 3097 3098 continue; 3099 } 3100 3101 if (scf_value_get_astring(g_val, dep, 3102 max_scf_value_length + 1) < 0) 3103 scfdie(); 3104 3105 if (strcmp(dep, SCF_DEP_EXCLUDE_ALL) == 0) 3106 continue; 3107 3108 if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENTITIES, g_prop) != 3109 SCF_SUCCESS) { 3110 if (scf_error() != SCF_ERROR_NOT_FOUND) 3111 scfdie(); 3112 3113 continue; 3114 } 3115 3116 if (scf_iter_property_values(viter, g_prop) != SCF_SUCCESS) 3117 scfdie(); 3118 3119 while ((vret = scf_iter_next_value(viter, g_val)) == 1) { 3120 if (scf_value_get_astring(g_val, dep, 3121 max_scf_value_length + 1) < 0) 3122 scfdie(); 3123 3124 wip->fmri = dep; 3125 if (callback(data, wip) != 0) 3126 goto out; 3127 } 3128 if (vret == -1) 3129 scfdie(); 3130 } 3131 if (ret == -1) 3132 scfdie(); 3133 3134 out: 3135 scf_iter_destroy(viter); 3136 scf_iter_destroy(iter); 3137 scf_snapshot_destroy(snap); 3138 } 3139 3140 static int 3141 list_dependencies(void *data, scf_walkinfo_t *wip) 3142 { 3143 walk_dependencies(wip, list_svc_or_inst_fmri, data); 3144 return (0); 3145 } 3146 3147 3148 /* 3149 * Dependent selection: The "providing" service's or instance's FMRI is parsed 3150 * into the provider_* variables, the instances are walked, and any instance 3151 * which lists an FMRI which parses to these components is selected. This is 3152 * inefficient in the face of multiple operands, but that should be uncommon. 3153 */ 3154 3155 static char *provider_scope; 3156 static char *provider_svc; 3157 static char *provider_inst; /* NULL for services */ 3158 3159 /*ARGSUSED*/ 3160 static int 3161 check_against_provider(void *arg, scf_walkinfo_t *wip) 3162 { 3163 char *cfmri; 3164 const char *scope_name, *svc_name, *inst_name, *pg_name; 3165 int *matchp = arg; 3166 3167 cfmri = safe_strdup(wip->fmri); 3168 3169 if (scf_parse_svc_fmri(cfmri, &scope_name, &svc_name, &inst_name, 3170 &pg_name, NULL) != SCF_SUCCESS) { 3171 free(cfmri); 3172 return (0); 3173 } 3174 3175 if (svc_name == NULL || pg_name != NULL) { 3176 free(cfmri); 3177 return (0); 3178 } 3179 3180 /* 3181 * If the user has specified an instance, then also match dependencies 3182 * on the service itself. 3183 */ 3184 *matchp = (strcmp(provider_scope, scope_name) == 0 && 3185 strcmp(provider_svc, svc_name) == 0 && 3186 (provider_inst == NULL ? (inst_name == NULL) : 3187 (inst_name == NULL || strcmp(provider_inst, inst_name) == 0))); 3188 3189 free(cfmri); 3190 3191 /* Stop on matches. */ 3192 return (*matchp); 3193 } 3194 3195 static int 3196 list_if_dependent(void *unused, scf_walkinfo_t *wip) 3197 { 3198 /* Only proceed if this instance depends on provider_*. */ 3199 int match = 0; 3200 3201 (void) walk_dependencies(wip, check_against_provider, &match); 3202 3203 if (match) 3204 return (list_instance(unused, wip)); 3205 3206 return (0); 3207 } 3208 3209 /*ARGSUSED*/ 3210 static int 3211 list_dependents(void *unused, scf_walkinfo_t *wip) 3212 { 3213 char *save; 3214 int ret; 3215 3216 if (scf_scope_get_name(wip->scope, provider_scope, 3217 max_scf_fmri_length) <= 0 || 3218 scf_service_get_name(wip->svc, provider_svc, 3219 max_scf_fmri_length) <= 0) 3220 scfdie(); 3221 3222 save = provider_inst; 3223 if (wip->inst == NULL) 3224 provider_inst = NULL; 3225 else if (scf_instance_get_name(wip->inst, provider_inst, 3226 max_scf_fmri_length) <= 0) 3227 scfdie(); 3228 3229 ret = scf_walk_fmri(h, 0, NULL, 0, list_if_dependent, NULL, NULL, 3230 uu_warn); 3231 3232 provider_inst = save; 3233 3234 return (ret); 3235 } 3236 3237 /* 3238 * main() & helpers 3239 */ 3240 3241 static void 3242 add_sort_column(const char *col, int reverse) 3243 { 3244 int i; 3245 3246 ++opt_snum; 3247 3248 opt_sort = realloc(opt_sort, opt_snum * sizeof (*opt_sort)); 3249 if (opt_sort == NULL) 3250 uu_die(gettext("Too many sort criteria: out of memory.\n")); 3251 3252 for (i = 0; i < ncolumns; ++i) { 3253 if (strcasecmp(col, columns[i].name) == 0) 3254 break; 3255 } 3256 3257 if (i < ncolumns) 3258 opt_sort[opt_snum - 1] = (reverse ? i | 0x100 : i); 3259 else 3260 uu_die(gettext("Unrecognized sort column \"%s\".\n"), col); 3261 3262 sortkey_sz += columns[i].sortkey_width; 3263 } 3264 3265 static void 3266 add_restarter(const char *fmri) 3267 { 3268 char *cfmri; 3269 const char *pg_name; 3270 struct pfmri_list *rest; 3271 3272 cfmri = safe_strdup(fmri); 3273 rest = safe_malloc(sizeof (*rest)); 3274 3275 if (scf_parse_svc_fmri(cfmri, &rest->scope, &rest->service, 3276 &rest->instance, &pg_name, NULL) != SCF_SUCCESS) 3277 uu_die(gettext("Restarter FMRI \"%s\" is invalid.\n"), fmri); 3278 3279 if (rest->instance == NULL || pg_name != NULL) 3280 uu_die(gettext("Restarter FMRI \"%s\" does not designate an " 3281 "instance.\n"), fmri); 3282 3283 rest->next = restarters; 3284 restarters = rest; 3285 return; 3286 3287 err: 3288 free(cfmri); 3289 free(rest); 3290 } 3291 3292 /* ARGSUSED */ 3293 static int 3294 line_cmp(const void *l_arg, const void *r_arg, void *private) 3295 { 3296 const struct avl_string *l = l_arg; 3297 const struct avl_string *r = r_arg; 3298 3299 return (memcmp(l->key, r->key, sortkey_sz)); 3300 } 3301 3302 /* ARGSUSED */ 3303 static int 3304 print_line(void *e, void *private) 3305 { 3306 struct avl_string *lp = e; 3307 3308 (void) puts(lp->str); 3309 3310 return (UU_WALK_NEXT); 3311 } 3312 3313 int 3314 main(int argc, char **argv) 3315 { 3316 char opt, opt_mode; 3317 int i, n; 3318 char *columns_str = NULL; 3319 char *cp; 3320 const char *progname; 3321 int err; 3322 3323 int show_all = 0; 3324 int show_header = 1; 3325 3326 const char * const options = "aHpvno:R:s:S:dDl?x"; 3327 3328 (void) setlocale(LC_ALL, ""); 3329 3330 locale = setlocale(LC_MESSAGES, ""); 3331 if (locale) { 3332 locale = safe_strdup(locale); 3333 _scf_sanitize_locale(locale); 3334 } 3335 3336 (void) textdomain(TEXT_DOMAIN); 3337 progname = uu_setpname(argv[0]); 3338 3339 exit_status = UU_EXIT_OK; 3340 3341 max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH); 3342 max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 3343 max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH); 3344 max_scf_type_length = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH); 3345 3346 if (max_scf_name_length == -1 || max_scf_value_length == -1 || 3347 max_scf_fmri_length == -1 || max_scf_type_length == -1) 3348 scfdie(); 3349 3350 now = time(NULL); 3351 assert(now != -1); 3352 3353 /* 3354 * opt_mode is the mode of operation. 0 for plain, 'd' for 3355 * dependencies, 'D' for dependents, and 'l' for detailed (long). We 3356 * need to know now so we know which options are valid. 3357 */ 3358 opt_mode = 0; 3359 while ((opt = getopt(argc, argv, options)) != -1) { 3360 switch (opt) { 3361 case '?': 3362 if (optopt == '?') { 3363 print_help(progname); 3364 return (UU_EXIT_OK); 3365 } else { 3366 argserr(progname); 3367 /* NOTREACHED */ 3368 } 3369 3370 case 'd': 3371 case 'D': 3372 case 'l': 3373 if (opt_mode != 0) 3374 argserr(progname); 3375 3376 opt_mode = opt; 3377 break; 3378 3379 case 'n': 3380 if (opt_mode != 0) 3381 argserr(progname); 3382 3383 opt_mode = opt; 3384 break; 3385 3386 case 'x': 3387 if (opt_mode != 0) 3388 argserr(progname); 3389 3390 opt_mode = opt; 3391 break; 3392 3393 default: 3394 break; 3395 } 3396 } 3397 3398 sortkey_sz = 0; 3399 3400 optind = 1; /* Reset getopt() */ 3401 while ((opt = getopt(argc, argv, options)) != -1) { 3402 switch (opt) { 3403 case 'a': 3404 if (opt_mode != 0) 3405 argserr(progname); 3406 show_all = 1; 3407 break; 3408 3409 case 'H': 3410 if (opt_mode == 'l' || opt_mode == 'x') 3411 argserr(progname); 3412 show_header = 0; 3413 break; 3414 3415 case 'p': 3416 if (opt_mode == 'x') 3417 argserr(progname); 3418 opt_processes = 1; 3419 break; 3420 3421 case 'v': 3422 opt_verbose = 1; 3423 break; 3424 3425 case 'o': 3426 if (opt_mode == 'l' || opt_mode == 'x') 3427 argserr(progname); 3428 columns_str = optarg; 3429 break; 3430 3431 case 'R': 3432 if (opt_mode != 0 || opt_mode == 'x') 3433 argserr(progname); 3434 3435 add_restarter(optarg); 3436 break; 3437 3438 case 's': 3439 case 'S': 3440 if (opt_mode != 0) 3441 argserr(progname); 3442 3443 add_sort_column(optarg, optopt == 'S'); 3444 break; 3445 3446 case 'd': 3447 case 'D': 3448 case 'l': 3449 case 'n': 3450 case 'x': 3451 assert(opt_mode == optopt); 3452 break; 3453 3454 case '?': 3455 argserr(progname); 3456 /* NOTREACHED */ 3457 3458 default: 3459 assert(0); 3460 abort(); 3461 } 3462 } 3463 3464 /* 3465 * -a is only meaningful when given no arguments 3466 */ 3467 if (show_all && optind != argc) 3468 uu_warn(gettext("-a ignored when used with arguments.\n")); 3469 3470 h = scf_handle_create(SCF_VERSION); 3471 if (h == NULL) 3472 scfdie(); 3473 3474 if (scf_handle_bind(h) == -1) 3475 uu_die(gettext("Could not bind to repository server: %s. " 3476 "Exiting.\n"), scf_strerror(scf_error())); 3477 3478 if ((g_pg = scf_pg_create(h)) == NULL || 3479 (g_prop = scf_property_create(h)) == NULL || 3480 (g_val = scf_value_create(h)) == NULL) 3481 scfdie(); 3482 3483 argc -= optind; 3484 argv += optind; 3485 3486 /* 3487 * If we're in long mode, take care of it now before we deal with the 3488 * sorting and the columns, since we won't use them anyway. 3489 */ 3490 if (opt_mode == 'l') { 3491 if (argc == 0) 3492 argserr(progname); 3493 3494 if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE, 3495 print_detailed, NULL, &exit_status, uu_warn)) != 0) { 3496 uu_warn(gettext("failed to iterate over " 3497 "instances: %s\n"), scf_strerror(err)); 3498 exit_status = UU_EXIT_FATAL; 3499 } 3500 3501 return (exit_status); 3502 } 3503 3504 if (opt_mode == 'n') { 3505 print_notify_special(); 3506 if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE, 3507 print_notify, NULL, &exit_status, uu_warn)) != 0) { 3508 uu_warn(gettext("failed to iterate over " 3509 "instances: %s\n"), scf_strerror(err)); 3510 exit_status = UU_EXIT_FATAL; 3511 } 3512 3513 return (exit_status); 3514 } 3515 3516 if (opt_mode == 'x') { 3517 explain(opt_verbose, argc, argv); 3518 3519 return (exit_status); 3520 } 3521 3522 3523 if (opt_snum == 0) { 3524 /* Default sort. */ 3525 add_sort_column("state", 0); 3526 add_sort_column("stime", 0); 3527 add_sort_column("fmri", 0); 3528 } 3529 3530 if (columns_str == NULL) { 3531 if (!opt_verbose) 3532 columns_str = safe_strdup("state,stime,fmri"); 3533 else 3534 columns_str = 3535 safe_strdup("state,nstate,stime,ctid,fmri"); 3536 } 3537 3538 /* Decode columns_str into opt_columns. */ 3539 line_sz = 0; 3540 3541 opt_cnum = 1; 3542 for (cp = columns_str; *cp != '\0'; ++cp) 3543 if (*cp == ',') 3544 ++opt_cnum; 3545 3546 opt_columns = malloc(opt_cnum * sizeof (*opt_columns)); 3547 if (opt_columns == NULL) 3548 uu_die(gettext("Too many columns.\n")); 3549 3550 for (n = 0; *columns_str != '\0'; ++n) { 3551 i = getcolumnopt(&columns_str); 3552 if (i == -1) 3553 uu_die(gettext("Unknown column \"%s\".\n"), 3554 columns_str); 3555 3556 if (strcmp(columns[i].name, "N") == 0 || 3557 strcmp(columns[i].name, "SN") == 0 || 3558 strcmp(columns[i].name, "NSTA") == 0 || 3559 strcmp(columns[i].name, "NSTATE") == 0) 3560 opt_nstate_shown = 1; 3561 3562 opt_columns[n] = i; 3563 line_sz += columns[i].width + 1; 3564 } 3565 3566 3567 if ((lines_pool = uu_avl_pool_create("lines_pool", 3568 sizeof (struct avl_string), offsetof(struct avl_string, node), 3569 line_cmp, UU_AVL_DEBUG)) == NULL || 3570 (lines = uu_avl_create(lines_pool, NULL, 0)) == NULL) 3571 uu_die(gettext("Unexpected libuutil error: %s. Exiting.\n"), 3572 uu_strerror(uu_error())); 3573 3574 switch (opt_mode) { 3575 case 0: 3576 ht_init(); 3577 3578 /* Always show all FMRIs when given arguments or restarters */ 3579 if (argc != 0 || restarters != NULL) 3580 show_all = 1; 3581 3582 if ((err = scf_walk_fmri(h, argc, argv, 3583 SCF_WALK_MULTIPLE | SCF_WALK_LEGACY, 3584 show_all ? list_instance : list_if_enabled, NULL, 3585 &exit_status, uu_warn)) != 0) { 3586 uu_warn(gettext("failed to iterate over " 3587 "instances: %s\n"), scf_strerror(err)); 3588 exit_status = UU_EXIT_FATAL; 3589 } 3590 break; 3591 3592 case 'd': 3593 if (argc == 0) 3594 argserr(progname); 3595 3596 if ((err = scf_walk_fmri(h, argc, argv, 3597 SCF_WALK_MULTIPLE, list_dependencies, NULL, 3598 &exit_status, uu_warn)) != 0) { 3599 uu_warn(gettext("failed to iterate over " 3600 "instances: %s\n"), scf_strerror(err)); 3601 exit_status = UU_EXIT_FATAL; 3602 } 3603 break; 3604 3605 case 'D': 3606 if (argc == 0) 3607 argserr(progname); 3608 3609 provider_scope = safe_malloc(max_scf_fmri_length); 3610 provider_svc = safe_malloc(max_scf_fmri_length); 3611 provider_inst = safe_malloc(max_scf_fmri_length); 3612 3613 if ((err = scf_walk_fmri(h, argc, argv, 3614 SCF_WALK_MULTIPLE | SCF_WALK_SERVICE, 3615 list_dependents, NULL, &exit_status, uu_warn)) != 0) { 3616 uu_warn(gettext("failed to iterate over " 3617 "instances: %s\n"), scf_strerror(err)); 3618 exit_status = UU_EXIT_FATAL; 3619 } 3620 3621 free(provider_scope); 3622 free(provider_svc); 3623 free(provider_inst); 3624 break; 3625 3626 case 'n': 3627 break; 3628 3629 default: 3630 assert(0); 3631 abort(); 3632 } 3633 3634 if (show_header) 3635 print_header(); 3636 3637 (void) uu_avl_walk(lines, print_line, NULL, 0); 3638 3639 return (exit_status); 3640 } 3641