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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 * 22 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * svcs - display attributes of service instances 30 * 31 * We have two output formats and six instance selection mechanisms. The 32 * primary output format is a line of attributes (selected by -o), possibly 33 * followed by process description lines (if -p is specified), for each 34 * instance selected. The columns available to display are described by the 35 * struct column columns array. The columns to actually display are kept in 36 * the opt_columns array as indicies into the columns array. The selection 37 * mechanisms available for this format are service FMRIs (selects all child 38 * instances), instance FMRIs, instance FMRI glob patterns, instances with 39 * a certain restarter (-R), dependencies of instances (-d), and dependents of 40 * instances (-D). Since the lines must be sorted (per -sS), we'll just stick 41 * each into a data structure and print them in order when we're done. To 42 * avoid listing the same instance twice (when -d and -D aren't given), we'll 43 * use a hash table of FMRIs to record that we've listed (added to the tree) 44 * an instance. 45 * 46 * The secondary output format (-l "long") is a paragraph of text for the 47 * services or instances selected. Not needing to be sorted, it's implemented 48 * by just calling print_detailed() for each FMRI given. 49 */ 50 51 #include "svcs.h" 52 53 /* Get the byteorder macros to ease sorting. */ 54 #include <sys/types.h> 55 #include <netinet/in.h> 56 #include <inttypes.h> 57 58 #include <sys/contract.h> 59 #include <sys/ctfs.h> 60 #include <sys/stat.h> 61 62 #include <assert.h> 63 #include <ctype.h> 64 #include <errno.h> 65 #include <fcntl.h> 66 #include <fnmatch.h> 67 #include <libcontract.h> 68 #include <libcontract_priv.h> 69 #include <libintl.h> 70 #include <libscf.h> 71 #include <libscf_priv.h> 72 #include <libuutil.h> 73 #include <locale.h> 74 #include <procfs.h> 75 #include <stdarg.h> 76 #include <stdio.h> 77 #include <stdlib.h> 78 #include <strings.h> 79 #include <time.h> 80 81 82 #ifndef TEXT_DOMAIN 83 #define TEXT_DOMAIN "SUNW_OST_OSCMD" 84 #endif /* TEXT_DOMAIN */ 85 86 #define LEGACY_SCHEME "lrc:" 87 #define LEGACY_UNKNOWN "unknown" 88 89 /* Flags for pg_get_single_val() */ 90 #define EMPTY_OK 0x01 91 #define MULTI_OK 0x02 92 93 94 /* 95 * An AVL-storable node for output lines and the keys to sort them by. 96 */ 97 struct avl_string { 98 uu_avl_node_t node; 99 char *key; 100 char *str; 101 }; 102 103 /* 104 * For lists of parsed restarter FMRIs. 105 */ 106 struct pfmri_list { 107 const char *scope; 108 const char *service; 109 const char *instance; 110 struct pfmri_list *next; 111 }; 112 113 114 /* 115 * Globals 116 */ 117 scf_handle_t *h; 118 static scf_propertygroup_t *g_pg; 119 static scf_property_t *g_prop; 120 static scf_value_t *g_val; 121 122 static size_t line_sz; /* Bytes in the header line. */ 123 static size_t sortkey_sz; /* Bytes in sort keys. */ 124 static uu_avl_pool_t *lines_pool; 125 static uu_avl_t *lines; /* Output lines. */ 126 int exit_status; 127 ssize_t max_scf_name_length; 128 ssize_t max_scf_value_length; 129 ssize_t max_scf_fmri_length; 130 static time_t now; 131 static struct pfmri_list *restarters = NULL; 132 static int first_paragraph = 1; /* For -l mode. */ 133 static char *common_name_buf; /* Sized for maximal length value. */ 134 char *locale; /* Current locale. */ 135 136 /* Options */ 137 static int *opt_columns = NULL; /* Indices into columns to display. */ 138 static int opt_cnum = 0; 139 static int opt_processes = 0; /* Print processes? */ 140 static int *opt_sort = NULL; /* Indices into columns to sort. */ 141 static int opt_snum = 0; 142 static int opt_nstate_shown = 0; /* Will nstate be shown? */ 143 static int opt_verbose = 0; 144 145 /* Minimize string constants. */ 146 static const char * const scf_property_state = SCF_PROPERTY_STATE; 147 static const char * const scf_property_next_state = SCF_PROPERTY_NEXT_STATE; 148 static const char * const scf_property_contract = SCF_PROPERTY_CONTRACT; 149 150 151 /* 152 * Utility functions 153 */ 154 155 /* 156 * For unexpected libscf errors. The ending newline is necessary to keep 157 * uu_die() from appending the errno error. 158 */ 159 #ifndef NDEBUG 160 void 161 do_scfdie(const char *file, int line) 162 { 163 uu_die(gettext("%s:%d: Unexpected libscf error: %s. Exiting.\n"), 164 file, line, scf_strerror(scf_error())); 165 } 166 #else 167 void 168 scfdie(void) 169 { 170 uu_die(gettext("Unexpected libscf error: %s. Exiting.\n"), 171 scf_strerror(scf_error())); 172 } 173 #endif 174 175 void * 176 safe_malloc(size_t sz) 177 { 178 void *ptr; 179 180 ptr = malloc(sz); 181 if (ptr == NULL) 182 uu_die(gettext("Out of memory")); 183 184 return (ptr); 185 } 186 187 char * 188 safe_strdup(const char *str) 189 { 190 char *cp; 191 192 cp = strdup(str); 193 if (cp == NULL) 194 uu_die(gettext("Out of memory.\n")); 195 196 return (cp); 197 } 198 199 static void 200 sanitize_locale(char *locale) 201 { 202 for (; *locale != '\0'; locale++) 203 if (!isalnum(*locale)) 204 *locale = '_'; 205 } 206 207 /* 208 * FMRI hashtable. For uniquifing listings. 209 */ 210 211 struct ht_elem { 212 const char *fmri; 213 struct ht_elem *next; 214 }; 215 216 static struct ht_elem **ht_buckets; 217 static uint_t ht_buckets_num; 218 static uint_t ht_num; 219 220 static void 221 ht_init() 222 { 223 ht_buckets_num = 8; 224 ht_buckets = safe_malloc(sizeof (*ht_buckets) * ht_buckets_num); 225 bzero(ht_buckets, sizeof (*ht_buckets) * ht_buckets_num); 226 ht_num = 0; 227 } 228 229 static uint_t 230 ht_hash_fmri(const char *fmri) 231 { 232 uint_t h = 0, g; 233 const char *p, *k; 234 235 /* All FMRIs begin with svc:/, so skip that part. */ 236 assert(strncmp(fmri, "svc:/", sizeof ("svc:/") - 1) == 0); 237 k = fmri + sizeof ("svc:/") - 1; 238 239 /* 240 * Generic hash function from uts/common/os/modhash.c. 241 */ 242 for (p = k; *p != '\0'; ++p) { 243 h = (h << 4) + *p; 244 if ((g = (h & 0xf0000000)) != 0) { 245 h ^= (g >> 24); 246 h ^= g; 247 } 248 } 249 250 return (h); 251 } 252 253 static void 254 ht_grow() 255 { 256 uint_t new_ht_buckets_num; 257 struct ht_elem **new_ht_buckets; 258 int i; 259 260 new_ht_buckets_num = ht_buckets_num * 2; 261 assert(new_ht_buckets_num > ht_buckets_num); 262 new_ht_buckets = 263 safe_malloc(sizeof (*new_ht_buckets) * new_ht_buckets_num); 264 bzero(new_ht_buckets, sizeof (*new_ht_buckets) * new_ht_buckets_num); 265 266 for (i = 0; i < ht_buckets_num; ++i) { 267 struct ht_elem *elem, *next; 268 269 for (elem = ht_buckets[i]; elem != NULL; elem = next) { 270 uint_t h; 271 272 next = elem->next; 273 274 h = ht_hash_fmri(elem->fmri); 275 276 elem->next = 277 new_ht_buckets[h & (new_ht_buckets_num - 1)]; 278 new_ht_buckets[h & (new_ht_buckets_num - 1)] = elem; 279 } 280 } 281 282 free(ht_buckets); 283 284 ht_buckets = new_ht_buckets; 285 ht_buckets_num = new_ht_buckets_num; 286 } 287 288 /* 289 * Add an FMRI to the hash table. Returns 1 if it was already there, 290 * 0 otherwise. 291 */ 292 static int 293 ht_add(const char *fmri) 294 { 295 uint_t h; 296 struct ht_elem *elem; 297 298 h = ht_hash_fmri(fmri); 299 300 elem = ht_buckets[h & (ht_buckets_num - 1)]; 301 302 for (; elem != NULL; elem = elem->next) { 303 if (strcmp(elem->fmri, fmri) == 0) 304 return (1); 305 } 306 307 /* Grow when average chain length is over 3. */ 308 if (ht_num > 3 * ht_buckets_num) 309 ht_grow(); 310 311 ++ht_num; 312 313 elem = safe_malloc(sizeof (*elem)); 314 elem->fmri = strdup(fmri); 315 elem->next = ht_buckets[h & (ht_buckets_num - 1)]; 316 ht_buckets[h & (ht_buckets_num - 1)] = elem; 317 318 return (0); 319 } 320 321 322 323 /* 324 * Convenience libscf wrapper functions. 325 */ 326 327 /* 328 * Get the single value of the named property in the given property group, 329 * which must have type ty, and put it in *vp. If ty is SCF_TYPE_ASTRING, vp 330 * is taken to be a char **, and sz is the size of the buffer. sz is unused 331 * otherwise. Return 0 on success, -1 if the property doesn't exist, has the 332 * wrong type, or doesn't have a single value. If flags has EMPTY_OK, don't 333 * complain if the property has no values (but return nonzero). If flags has 334 * MULTI_OK and the property has multiple values, succeed with E2BIG. 335 */ 336 int 337 pg_get_single_val(scf_propertygroup_t *pg, const char *propname, scf_type_t ty, 338 void *vp, size_t sz, uint_t flags) 339 { 340 char *buf; 341 size_t buf_sz; 342 int ret = -1, r; 343 boolean_t multi = B_FALSE; 344 345 assert((flags & ~(EMPTY_OK | MULTI_OK)) == 0); 346 347 if (scf_pg_get_property(pg, propname, g_prop) == -1) { 348 if (scf_error() != SCF_ERROR_NOT_FOUND) 349 scfdie(); 350 351 goto out; 352 } 353 354 if (scf_property_is_type(g_prop, ty) != SCF_SUCCESS) { 355 if (scf_error() == SCF_ERROR_TYPE_MISMATCH) 356 goto misconfigured; 357 scfdie(); 358 } 359 360 if (scf_property_get_value(g_prop, g_val) != SCF_SUCCESS) { 361 switch (scf_error()) { 362 case SCF_ERROR_NOT_FOUND: 363 if (flags & EMPTY_OK) 364 goto out; 365 goto misconfigured; 366 367 case SCF_ERROR_CONSTRAINT_VIOLATED: 368 if (flags & MULTI_OK) { 369 multi = B_TRUE; 370 break; 371 } 372 goto misconfigured; 373 374 default: 375 scfdie(); 376 } 377 } 378 379 switch (ty) { 380 case SCF_TYPE_ASTRING: 381 r = scf_value_get_astring(g_val, vp, sz) > 0 ? SCF_SUCCESS : -1; 382 break; 383 384 case SCF_TYPE_BOOLEAN: 385 r = scf_value_get_boolean(g_val, (uint8_t *)vp); 386 break; 387 388 case SCF_TYPE_COUNT: 389 r = scf_value_get_count(g_val, (uint64_t *)vp); 390 break; 391 392 case SCF_TYPE_INTEGER: 393 r = scf_value_get_integer(g_val, (int64_t *)vp); 394 break; 395 396 case SCF_TYPE_TIME: { 397 int64_t sec; 398 int32_t ns; 399 r = scf_value_get_time(g_val, &sec, &ns); 400 ((struct timeval *)vp)->tv_sec = sec; 401 ((struct timeval *)vp)->tv_usec = ns / 1000; 402 break; 403 } 404 405 case SCF_TYPE_USTRING: 406 r = scf_value_get_ustring(g_val, vp, sz) > 0 ? SCF_SUCCESS : -1; 407 break; 408 409 default: 410 #ifndef NDEBUG 411 uu_warn("%s:%d: Unknown type %d.\n", __FILE__, __LINE__, ty); 412 #endif 413 abort(); 414 } 415 if (r != SCF_SUCCESS) 416 scfdie(); 417 418 ret = multi ? E2BIG : 0; 419 goto out; 420 421 misconfigured: 422 buf_sz = max_scf_fmri_length + 1; 423 buf = safe_malloc(buf_sz); 424 if (scf_property_to_fmri(g_prop, buf, buf_sz) == -1) 425 scfdie(); 426 427 uu_warn(gettext("Property \"%s\" is misconfigured.\n"), buf); 428 429 free(buf); 430 431 out: 432 return (ret); 433 } 434 435 static scf_snapshot_t * 436 get_running_snapshot(scf_instance_t *inst) 437 { 438 scf_snapshot_t *snap; 439 440 snap = scf_snapshot_create(h); 441 if (snap == NULL) 442 scfdie(); 443 444 if (scf_instance_get_snapshot(inst, "running", snap) == 0) 445 return (snap); 446 447 if (scf_error() != SCF_ERROR_NOT_FOUND) 448 scfdie(); 449 450 scf_snapshot_destroy(snap); 451 return (NULL); 452 } 453 454 /* 455 * As pg_get_single_val(), except look the property group up in an 456 * instance. If "use_running" is set, and the running snapshot exists, 457 * do a composed lookup there. Otherwise, do an (optionally composed) 458 * lookup on the current values. Note that lookups using snapshots are 459 * always composed. 460 */ 461 int 462 inst_get_single_val(scf_instance_t *inst, const char *pgname, 463 const char *propname, scf_type_t ty, void *vp, size_t sz, uint_t flags, 464 int use_running, int composed) 465 { 466 scf_snapshot_t *snap = NULL; 467 int r; 468 469 if (use_running) 470 snap = get_running_snapshot(inst); 471 if (composed || use_running) 472 r = scf_instance_get_pg_composed(inst, snap, pgname, g_pg); 473 else 474 r = scf_instance_get_pg(inst, pgname, g_pg); 475 if (snap) 476 scf_snapshot_destroy(snap); 477 if (r == -1) 478 return (-1); 479 480 r = pg_get_single_val(g_pg, propname, ty, vp, sz, flags); 481 482 return (r); 483 } 484 485 static int 486 instance_enabled(scf_instance_t *inst, boolean_t temp) 487 { 488 uint8_t b; 489 490 if (inst_get_single_val(inst, 491 temp ? SCF_PG_GENERAL_OVR : SCF_PG_GENERAL, SCF_PROPERTY_ENABLED, 492 SCF_TYPE_BOOLEAN, &b, 0, 0, 0, 0) != 0) 493 return (-1); 494 495 return (b ? 1 : 0); 496 } 497 498 /* 499 * Get a string property from the restarter property group of the given 500 * instance. Return an empty string on normal problems. 501 */ 502 static void 503 get_restarter_string_prop(scf_instance_t *inst, const char *pname, 504 char *buf, size_t buf_sz) 505 { 506 if (inst_get_single_val(inst, SCF_PG_RESTARTER, pname, 507 SCF_TYPE_ASTRING, buf, buf_sz, 0, 0, 1) != 0) 508 *buf = '\0'; 509 } 510 511 static int 512 get_restarter_time_prop(scf_instance_t *inst, const char *pname, 513 struct timeval *tvp, int ok_if_empty) 514 { 515 int r; 516 517 r = inst_get_single_val(inst, SCF_PG_RESTARTER, pname, SCF_TYPE_TIME, 518 tvp, NULL, ok_if_empty ? EMPTY_OK : 0, 0, 1); 519 520 return (r == 0 ? 0 : -1); 521 } 522 523 static int 524 get_restarter_count_prop(scf_instance_t *inst, const char *pname, uint64_t *cp, 525 uint_t flags) 526 { 527 return (inst_get_single_val(inst, SCF_PG_RESTARTER, pname, 528 SCF_TYPE_COUNT, cp, 0, flags, 0, 1)); 529 } 530 531 532 /* 533 * Generic functions 534 */ 535 536 static int 537 propvals_to_pids(scf_propertygroup_t *pg, const char *pname, pid_t **pidsp, 538 uint_t *np, scf_property_t *prop, scf_value_t *val, scf_iter_t *iter) 539 { 540 scf_type_t ty; 541 int r, fd, err; 542 uint64_t c; 543 ct_stathdl_t ctst; 544 pid_t *pids; 545 uint_t m; 546 547 if (scf_pg_get_property(pg, pname, prop) != 0) { 548 if (scf_error() != SCF_ERROR_NOT_FOUND) 549 scfdie(); 550 551 return (ENOENT); 552 } 553 554 if (scf_property_type(prop, &ty) != 0) 555 scfdie(); 556 557 if (ty != SCF_TYPE_COUNT) 558 return (EINVAL); 559 560 if (scf_iter_property_values(iter, prop) != 0) 561 scfdie(); 562 563 for (;;) { 564 r = scf_iter_next_value(iter, val); 565 if (r == -1) 566 scfdie(); 567 if (r == 0) 568 break; 569 570 if (scf_value_get_count(val, &c) != 0) 571 scfdie(); 572 573 fd = contract_open(c, NULL, "status", O_RDONLY); 574 if (fd < 0) 575 continue; 576 577 err = ct_status_read(fd, CTD_ALL, &ctst); 578 if (err != 0) { 579 uu_warn(gettext("Could not read status of contract " 580 "%ld: %s.\n"), c, strerror(err)); 581 (void) close(fd); 582 continue; 583 } 584 585 (void) close(fd); 586 587 r = ct_pr_status_get_members(ctst, &pids, &m); 588 assert(r == 0); 589 590 if (m == 0) { 591 ct_status_free(ctst); 592 continue; 593 } 594 595 *pidsp = realloc(*pidsp, (*np + m) * sizeof (*pidsp)); 596 if (*pidsp == NULL) 597 uu_die(gettext("Out of memory")); 598 599 bcopy(pids, *pidsp + *np, m * sizeof (*pids)); 600 *np += m; 601 602 ct_status_free(ctst); 603 } 604 605 return (0); 606 } 607 608 static int 609 instance_processes(scf_instance_t *inst, pid_t **pids, uint_t *np) 610 { 611 scf_iter_t *iter; 612 int ret; 613 614 if ((iter = scf_iter_create(h)) == NULL) 615 scfdie(); 616 617 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, g_pg) == 0) { 618 *pids = NULL; 619 *np = 0; 620 621 (void) propvals_to_pids(g_pg, scf_property_contract, pids, np, 622 g_prop, g_val, iter); 623 624 (void) propvals_to_pids(g_pg, SCF_PROPERTY_TRANSIENT_CONTRACT, 625 pids, np, g_prop, g_val, iter); 626 627 ret = 0; 628 } else { 629 if (scf_error() != SCF_ERROR_NOT_FOUND) 630 scfdie(); 631 632 ret = -1; 633 } 634 635 scf_iter_destroy(iter); 636 637 return (ret); 638 } 639 640 static int 641 get_psinfo(pid_t pid, psinfo_t *psip) 642 { 643 char path[100]; 644 int fd; 645 646 (void) snprintf(path, sizeof (path), "/proc/%lu/psinfo", pid); 647 648 fd = open64(path, O_RDONLY); 649 if (fd < 0) 650 return (-1); 651 652 if (read(fd, psip, sizeof (*psip)) < 0) 653 uu_die(gettext("Could not read info for process %lu"), pid); 654 655 (void) close(fd); 656 657 return (0); 658 } 659 660 661 662 /* 663 * Column sprint and sortkey functions 664 */ 665 666 struct column { 667 const char *name; 668 int width; 669 670 /* 671 * This function should write the value for the column into buf, and 672 * grow or allocate buf accordingly. It should always write at least 673 * width bytes, blanking unused bytes with spaces. If the field is 674 * greater than the column width we allow it to overlap other columns. 675 * In particular, it shouldn't write any null bytes. (Though an extra 676 * null byte past the end is currently tolerated.) If the property 677 * group is non-NULL, then we are dealing with a legacy service. 678 */ 679 void (*sprint)(char **, scf_walkinfo_t *); 680 681 int sortkey_width; 682 683 /* 684 * This function should write sortkey_width bytes into buf which will 685 * cause memcmp() to sort it properly. (Unlike sprint() above, 686 * however, an extra null byte may overrun the buffer.) The second 687 * argument controls whether the results are sorted in forward or 688 * reverse order. 689 */ 690 void (*get_sortkey)(char *, int, scf_walkinfo_t *); 691 }; 692 693 static void 694 reverse_bytes(char *buf, size_t len) 695 { 696 int i; 697 698 for (i = 0; i < len; ++i) 699 buf[i] = ~buf[i]; 700 } 701 702 /* CTID */ 703 #define CTID_COLUMN_WIDTH 6 704 705 static void 706 sprint_ctid(char **buf, scf_walkinfo_t *wip) 707 { 708 int r; 709 uint64_t c; 710 size_t newsize = (*buf ? strlen(*buf) : 0) + CTID_COLUMN_WIDTH + 2; 711 char *newbuf = safe_malloc(newsize); 712 713 if (wip->pg != NULL) 714 r = pg_get_single_val(wip->pg, scf_property_contract, 715 SCF_TYPE_COUNT, &c, 0, EMPTY_OK | MULTI_OK); 716 else 717 r = get_restarter_count_prop(wip->inst, scf_property_contract, 718 &c, EMPTY_OK | MULTI_OK); 719 720 if (r == 0) 721 (void) snprintf(newbuf, newsize, "%s%*lu ", 722 *buf ? *buf : "", CTID_COLUMN_WIDTH, (ctid_t)c); 723 else if (r == E2BIG) 724 (void) snprintf(newbuf, newsize, "%s%*lu* ", 725 *buf ? *buf : "", CTID_COLUMN_WIDTH - 1, (ctid_t)c); 726 else 727 (void) snprintf(newbuf, newsize, "%s%*s ", 728 *buf ? *buf : "", CTID_COLUMN_WIDTH, "-"); 729 if (*buf) 730 free(*buf); 731 *buf = newbuf; 732 } 733 734 #define CTID_SORTKEY_WIDTH (sizeof (uint64_t)) 735 736 static void 737 sortkey_ctid(char *buf, int reverse, scf_walkinfo_t *wip) 738 { 739 int r; 740 uint64_t c; 741 742 if (wip->pg != NULL) 743 r = pg_get_single_val(wip->pg, scf_property_contract, 744 SCF_TYPE_COUNT, &c, 0, EMPTY_OK); 745 else 746 r = get_restarter_count_prop(wip->inst, scf_property_contract, 747 &c, EMPTY_OK); 748 749 if (r == 0) { 750 /* 751 * Use the id itself, but it must be big-endian for this to 752 * work. 753 */ 754 c = BE_64(c); 755 756 bcopy(&c, buf, CTID_SORTKEY_WIDTH); 757 } else { 758 bzero(buf, CTID_SORTKEY_WIDTH); 759 } 760 761 if (reverse) 762 reverse_bytes(buf, CTID_SORTKEY_WIDTH); 763 } 764 765 /* DESC */ 766 #define DESC_COLUMN_WIDTH 100 767 768 static void 769 sprint_desc(char **buf, scf_walkinfo_t *wip) 770 { 771 char *x; 772 size_t newsize; 773 char *newbuf; 774 775 if (common_name_buf == NULL) 776 common_name_buf = safe_malloc(max_scf_value_length + 1); 777 778 bzero(common_name_buf, max_scf_value_length + 1); 779 780 if (wip->pg != NULL) { 781 common_name_buf[0] = '-'; 782 } else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale, 783 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 784 1, 1) == -1 && 785 inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C", 786 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 787 1, 1) == -1) { 788 common_name_buf[0] = '-'; 789 } 790 791 /* 792 * Collapse multi-line tm_common_name values into a single line. 793 */ 794 for (x = common_name_buf; *x != '\0'; x++) 795 if (*x == '\n') 796 *x = ' '; 797 798 if (strlen(common_name_buf) > DESC_COLUMN_WIDTH) 799 newsize = (*buf ? strlen(*buf) : 0) + 800 strlen(common_name_buf) + 1; 801 else 802 newsize = (*buf ? strlen(*buf) : 0) + DESC_COLUMN_WIDTH + 1; 803 newbuf = safe_malloc(newsize); 804 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 805 DESC_COLUMN_WIDTH, common_name_buf); 806 if (*buf) 807 free(*buf); 808 *buf = newbuf; 809 } 810 811 /* ARGSUSED */ 812 static void 813 sortkey_desc(char *buf, int reverse, scf_walkinfo_t *wip) 814 { 815 bzero(buf, DESC_COLUMN_WIDTH); 816 } 817 818 /* State columns (STATE, NSTATE, S, N, SN, STA, NSTA) */ 819 820 static char 821 state_to_char(const char *state) 822 { 823 if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0) 824 return ('u'); 825 826 if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0) 827 return ('0'); 828 829 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) 830 return ('1'); 831 832 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) 833 return ('m'); 834 835 if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0) 836 return ('d'); 837 838 if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) 839 return ('D'); 840 841 if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0) 842 return ('L'); 843 844 return ('?'); 845 } 846 847 /* Return true if inst is transitioning. */ 848 static int 849 transitioning(scf_instance_t *inst) 850 { 851 char nstate_name[MAX_SCF_STATE_STRING_SZ]; 852 853 get_restarter_string_prop(inst, scf_property_next_state, nstate_name, 854 sizeof (nstate_name)); 855 856 return (state_to_char(nstate_name) != '?'); 857 } 858 859 /* ARGSUSED */ 860 static void 861 sortkey_states(const char *pname, char *buf, int reverse, scf_walkinfo_t *wip) 862 { 863 char state_name[MAX_SCF_STATE_STRING_SZ]; 864 865 /* 866 * Lower numbers are printed first, so these are arranged from least 867 * interesting ("legacy run") to most interesting (unknown). 868 */ 869 if (wip->pg == NULL) { 870 get_restarter_string_prop(wip->inst, pname, state_name, 871 sizeof (state_name)); 872 873 if (strcmp(state_name, SCF_STATE_STRING_ONLINE) == 0) 874 *buf = 2; 875 else if (strcmp(state_name, SCF_STATE_STRING_DEGRADED) == 0) 876 *buf = 3; 877 else if (strcmp(state_name, SCF_STATE_STRING_OFFLINE) == 0) 878 *buf = 4; 879 else if (strcmp(state_name, SCF_STATE_STRING_MAINT) == 0) 880 *buf = 5; 881 else if (strcmp(state_name, SCF_STATE_STRING_DISABLED) == 0) 882 *buf = 1; 883 else if (strcmp(state_name, SCF_STATE_STRING_UNINIT) == 0) 884 *buf = 6; 885 else 886 *buf = 7; 887 } else 888 *buf = 0; 889 890 if (reverse) 891 *buf = 255 - *buf; 892 } 893 894 static void 895 sprint_state(char **buf, scf_walkinfo_t *wip) 896 { 897 char state_name[MAX_SCF_STATE_STRING_SZ + 1]; 898 size_t newsize; 899 char *newbuf; 900 901 if (wip->pg == NULL) { 902 get_restarter_string_prop(wip->inst, scf_property_state, 903 state_name, sizeof (state_name)); 904 905 /* Don't print blank fields, to ease parsing. */ 906 if (state_name[0] == '\0') { 907 state_name[0] = '-'; 908 state_name[1] = '\0'; 909 } 910 911 if (!opt_nstate_shown && transitioning(wip->inst)) { 912 /* Append an asterisk if nstate is valid. */ 913 (void) strcat(state_name, "*"); 914 } 915 } else 916 (void) strcpy(state_name, SCF_STATE_STRING_LEGACY); 917 918 newsize = (*buf ? strlen(*buf) : 0) + MAX_SCF_STATE_STRING_SZ + 2; 919 newbuf = safe_malloc(newsize); 920 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 921 MAX_SCF_STATE_STRING_SZ + 1, state_name); 922 923 if (*buf) 924 free(*buf); 925 *buf = newbuf; 926 } 927 928 static void 929 sortkey_state(char *buf, int reverse, scf_walkinfo_t *wip) 930 { 931 sortkey_states(scf_property_state, buf, reverse, wip); 932 } 933 934 static void 935 sprint_nstate(char **buf, scf_walkinfo_t *wip) 936 { 937 char next_state_name[MAX_SCF_STATE_STRING_SZ]; 938 boolean_t blank = 0; 939 size_t newsize; 940 char *newbuf; 941 942 if (wip->pg == NULL) { 943 get_restarter_string_prop(wip->inst, scf_property_next_state, 944 next_state_name, sizeof (next_state_name)); 945 946 /* Don't print blank fields, to ease parsing. */ 947 if (next_state_name[0] == '\0' || 948 strcmp(next_state_name, SCF_STATE_STRING_NONE) == 0) 949 blank = 1; 950 } else 951 blank = 1; 952 953 if (blank) { 954 next_state_name[0] = '-'; 955 next_state_name[1] = '\0'; 956 } 957 958 newsize = (*buf ? strlen(*buf) : 0) + MAX_SCF_STATE_STRING_SZ + 1; 959 newbuf = safe_malloc(newsize); 960 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 961 MAX_SCF_STATE_STRING_SZ - 1, next_state_name); 962 if (*buf) 963 free(*buf); 964 *buf = newbuf; 965 } 966 967 static void 968 sortkey_nstate(char *buf, int reverse, scf_walkinfo_t *wip) 969 { 970 sortkey_states(scf_property_next_state, buf, reverse, wip); 971 } 972 973 static void 974 sprint_s(char **buf, scf_walkinfo_t *wip) 975 { 976 char tmp[3]; 977 char state_name[MAX_SCF_STATE_STRING_SZ]; 978 size_t newsize = (*buf ? strlen(*buf) : 0) + 4; 979 char *newbuf = safe_malloc(newsize); 980 981 if (wip->pg == NULL) { 982 get_restarter_string_prop(wip->inst, scf_property_state, 983 state_name, sizeof (state_name)); 984 tmp[0] = state_to_char(state_name); 985 986 if (!opt_nstate_shown && transitioning(wip->inst)) 987 tmp[1] = '*'; 988 else 989 tmp[1] = ' '; 990 } else { 991 tmp[0] = 'L'; 992 tmp[1] = ' '; 993 } 994 tmp[2] = ' '; 995 (void) snprintf(newbuf, newsize, "%s%-*s", *buf ? *buf : "", 996 3, tmp); 997 if (*buf) 998 free(*buf); 999 *buf = newbuf; 1000 } 1001 1002 static void 1003 sprint_n(char **buf, scf_walkinfo_t *wip) 1004 { 1005 char tmp[2]; 1006 size_t newsize = (*buf ? strlen(*buf) : 0) + 3; 1007 char *newbuf = safe_malloc(newsize); 1008 char nstate_name[MAX_SCF_STATE_STRING_SZ]; 1009 1010 if (wip->pg == NULL) { 1011 get_restarter_string_prop(wip->inst, scf_property_next_state, 1012 nstate_name, sizeof (nstate_name)); 1013 1014 if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0) 1015 tmp[0] = '-'; 1016 else 1017 tmp[0] = state_to_char(nstate_name); 1018 } else 1019 tmp[0] = '-'; 1020 1021 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1022 2, tmp); 1023 if (*buf) 1024 free(*buf); 1025 *buf = newbuf; 1026 } 1027 1028 static void 1029 sprint_sn(char **buf, scf_walkinfo_t *wip) 1030 { 1031 char tmp[3]; 1032 size_t newsize = (*buf ? strlen(*buf) : 0) + 4; 1033 char *newbuf = safe_malloc(newsize); 1034 char nstate_name[MAX_SCF_STATE_STRING_SZ]; 1035 char state_name[MAX_SCF_STATE_STRING_SZ]; 1036 1037 if (wip->pg == NULL) { 1038 get_restarter_string_prop(wip->inst, scf_property_state, 1039 state_name, sizeof (state_name)); 1040 get_restarter_string_prop(wip->inst, scf_property_next_state, 1041 nstate_name, sizeof (nstate_name)); 1042 tmp[0] = state_to_char(state_name); 1043 1044 if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0) 1045 tmp[1] = '-'; 1046 else 1047 tmp[1] = state_to_char(nstate_name); 1048 } else { 1049 tmp[0] = 'L'; 1050 tmp[1] = '-'; 1051 } 1052 1053 tmp[2] = ' '; 1054 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1055 3, tmp); 1056 if (*buf) 1057 free(*buf); 1058 *buf = newbuf; 1059 } 1060 1061 /* ARGSUSED */ 1062 static void 1063 sortkey_sn(char *buf, int reverse, scf_walkinfo_t *wip) 1064 { 1065 sortkey_state(buf, reverse, wip); 1066 sortkey_nstate(buf + 1, reverse, wip); 1067 } 1068 1069 static const char * 1070 state_abbrev(const char *state) 1071 { 1072 if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0) 1073 return ("UN"); 1074 if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0) 1075 return ("OFF"); 1076 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) 1077 return ("ON"); 1078 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) 1079 return ("MNT"); 1080 if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0) 1081 return ("DIS"); 1082 if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) 1083 return ("DGD"); 1084 if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0) 1085 return ("LRC"); 1086 1087 return ("?"); 1088 } 1089 1090 static void 1091 sprint_sta(char **buf, scf_walkinfo_t *wip) 1092 { 1093 char state_name[MAX_SCF_STATE_STRING_SZ]; 1094 char sta[5]; 1095 size_t newsize = (*buf ? strlen(*buf) : 0) + 6; 1096 char *newbuf = safe_malloc(newsize); 1097 1098 if (wip->pg == NULL) 1099 get_restarter_string_prop(wip->inst, scf_property_state, 1100 state_name, sizeof (state_name)); 1101 else 1102 (void) strcpy(state_name, SCF_STATE_STRING_LEGACY); 1103 1104 (void) strcpy(sta, state_abbrev(state_name)); 1105 1106 if (wip->pg == NULL && !opt_nstate_shown && transitioning(wip->inst)) 1107 (void) strcat(sta, "*"); 1108 1109 (void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "", sta); 1110 if (*buf) 1111 free(*buf); 1112 *buf = newbuf; 1113 } 1114 1115 static void 1116 sprint_nsta(char **buf, scf_walkinfo_t *wip) 1117 { 1118 char state_name[MAX_SCF_STATE_STRING_SZ]; 1119 size_t newsize = (*buf ? strlen(*buf) : 0) + 6; 1120 char *newbuf = safe_malloc(newsize); 1121 1122 if (wip->pg == NULL) 1123 get_restarter_string_prop(wip->inst, scf_property_next_state, 1124 state_name, sizeof (state_name)); 1125 else 1126 (void) strcpy(state_name, SCF_STATE_STRING_NONE); 1127 1128 if (strcmp(state_name, SCF_STATE_STRING_NONE) == 0) 1129 (void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "", 1130 "-"); 1131 else 1132 (void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "", 1133 state_abbrev(state_name)); 1134 if (*buf) 1135 free(*buf); 1136 *buf = newbuf; 1137 } 1138 1139 /* FMRI */ 1140 #define FMRI_COLUMN_WIDTH 50 1141 static void 1142 sprint_fmri(char **buf, scf_walkinfo_t *wip) 1143 { 1144 char *fmri_buf = safe_malloc(max_scf_fmri_length + 1); 1145 size_t newsize; 1146 char *newbuf; 1147 1148 if (wip->pg == NULL) { 1149 if (scf_instance_to_fmri(wip->inst, fmri_buf, 1150 max_scf_fmri_length + 1) == -1) 1151 scfdie(); 1152 } else { 1153 (void) strcpy(fmri_buf, LEGACY_SCHEME); 1154 if (pg_get_single_val(wip->pg, SCF_LEGACY_PROPERTY_NAME, 1155 SCF_TYPE_ASTRING, fmri_buf + sizeof (LEGACY_SCHEME) - 1, 1156 max_scf_fmri_length + 1 - (sizeof (LEGACY_SCHEME) - 1), 1157 0) != 0) 1158 (void) strcat(fmri_buf, LEGACY_UNKNOWN); 1159 } 1160 1161 if (strlen(fmri_buf) > FMRI_COLUMN_WIDTH) 1162 newsize = (*buf ? strlen(*buf) : 0) + strlen(fmri_buf) + 2; 1163 else 1164 newsize = (*buf ? strlen(*buf) : 0) + FMRI_COLUMN_WIDTH + 2; 1165 newbuf = safe_malloc(newsize); 1166 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1167 FMRI_COLUMN_WIDTH, fmri_buf); 1168 free(fmri_buf); 1169 if (*buf) 1170 free(*buf); 1171 *buf = newbuf; 1172 } 1173 1174 static void 1175 sortkey_fmri(char *buf, int reverse, scf_walkinfo_t *wip) 1176 { 1177 char *tmp = NULL; 1178 1179 sprint_fmri(&tmp, wip); 1180 bcopy(tmp, buf, FMRI_COLUMN_WIDTH); 1181 free(tmp); 1182 if (reverse) 1183 reverse_bytes(buf, FMRI_COLUMN_WIDTH); 1184 } 1185 1186 /* Component columns */ 1187 #define COMPONENT_COLUMN_WIDTH 20 1188 static void 1189 sprint_scope(char **buf, scf_walkinfo_t *wip) 1190 { 1191 char *scope_buf = safe_malloc(max_scf_name_length + 1); 1192 size_t newsize = (*buf ? strlen(*buf) : 0) + COMPONENT_COLUMN_WIDTH + 2; 1193 char *newbuf = safe_malloc(newsize); 1194 1195 assert(wip->scope != NULL); 1196 1197 if (scf_scope_get_name(wip->scope, scope_buf, max_scf_name_length) < 0) 1198 scfdie(); 1199 1200 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1201 COMPONENT_COLUMN_WIDTH, scope_buf); 1202 if (*buf) 1203 free(*buf); 1204 *buf = newbuf; 1205 free(scope_buf); 1206 } 1207 1208 static void 1209 sortkey_scope(char *buf, int reverse, scf_walkinfo_t *wip) 1210 { 1211 char *tmp = NULL; 1212 1213 sprint_scope(&tmp, wip); 1214 bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH); 1215 free(tmp); 1216 if (reverse) 1217 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH); 1218 } 1219 1220 static void 1221 sprint_service(char **buf, scf_walkinfo_t *wip) 1222 { 1223 char *svc_buf = safe_malloc(max_scf_name_length + 1); 1224 char *newbuf; 1225 size_t newsize; 1226 1227 if (wip->pg == NULL) { 1228 if (scf_service_get_name(wip->svc, svc_buf, 1229 max_scf_name_length + 1) < 0) 1230 scfdie(); 1231 } else { 1232 if (pg_get_single_val(wip->pg, "name", SCF_TYPE_ASTRING, 1233 svc_buf, max_scf_name_length + 1, EMPTY_OK) != 0) 1234 (void) strcpy(svc_buf, LEGACY_UNKNOWN); 1235 } 1236 1237 1238 if (strlen(svc_buf) > COMPONENT_COLUMN_WIDTH) 1239 newsize = (*buf ? strlen(*buf) : 0) + strlen(svc_buf) + 2; 1240 else 1241 newsize = (*buf ? strlen(*buf) : 0) + 1242 COMPONENT_COLUMN_WIDTH + 2; 1243 newbuf = safe_malloc(newsize); 1244 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1245 COMPONENT_COLUMN_WIDTH, svc_buf); 1246 free(svc_buf); 1247 if (*buf) 1248 free(*buf); 1249 *buf = newbuf; 1250 } 1251 1252 static void 1253 sortkey_service(char *buf, int reverse, scf_walkinfo_t *wip) 1254 { 1255 char *tmp = NULL; 1256 1257 sprint_service(&tmp, wip); 1258 bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH); 1259 free(tmp); 1260 if (reverse) 1261 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH); 1262 } 1263 1264 /* INST */ 1265 static void 1266 sprint_instance(char **buf, scf_walkinfo_t *wip) 1267 { 1268 char *tmp = safe_malloc(max_scf_name_length + 1); 1269 size_t newsize = (*buf ? strlen(*buf) : 0) + COMPONENT_COLUMN_WIDTH + 2; 1270 char *newbuf = safe_malloc(newsize); 1271 1272 if (wip->pg == NULL) { 1273 if (scf_instance_get_name(wip->inst, tmp, 1274 max_scf_name_length + 1) < 0) 1275 scfdie(); 1276 } else { 1277 tmp[0] = '-'; 1278 tmp[1] = '\0'; 1279 } 1280 1281 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1282 COMPONENT_COLUMN_WIDTH, tmp); 1283 if (*buf) 1284 free(*buf); 1285 *buf = newbuf; 1286 free(tmp); 1287 } 1288 1289 static void 1290 sortkey_instance(char *buf, int reverse, scf_walkinfo_t *wip) 1291 { 1292 char *tmp = NULL; 1293 1294 sprint_instance(&tmp, wip); 1295 bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH); 1296 free(tmp); 1297 if (reverse) 1298 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH); 1299 } 1300 1301 /* STIME */ 1302 #define STIME_COLUMN_WIDTH 8 1303 #define FORMAT_TIME "%k:%M:%S" 1304 #define FORMAT_DATE "%b_%d " 1305 #define FORMAT_YEAR "%Y " 1306 1307 static void 1308 sprint_stime(char **buf, scf_walkinfo_t *wip) 1309 { 1310 int r; 1311 struct timeval tv; 1312 time_t then; 1313 struct tm *tm; 1314 char st_buf[STIME_COLUMN_WIDTH + 1]; 1315 size_t newsize = (*buf ? strlen(*buf) : 0) + STIME_COLUMN_WIDTH + 2; 1316 char *newbuf = safe_malloc(newsize); 1317 1318 if (wip->pg == NULL) { 1319 r = get_restarter_time_prop(wip->inst, 1320 SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0); 1321 } else { 1322 r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP, 1323 SCF_TYPE_TIME, &tv, NULL, 0); 1324 } 1325 1326 if (r != 0) { 1327 (void) snprintf(newbuf, newsize, "%s%-*s", *buf ? *buf : "", 1328 STIME_COLUMN_WIDTH + 1, "?"); 1329 return; 1330 } 1331 1332 then = (time_t)tv.tv_sec; 1333 1334 tm = localtime(&then); 1335 /* 1336 * Print time if started within the past 24 hours, print date 1337 * if within the past 12 months, print year if started greater than 1338 * 12 months ago. 1339 */ 1340 if (now - then < 24 * 60 * 60) 1341 (void) strftime(st_buf, sizeof (st_buf), gettext(FORMAT_TIME), 1342 tm); 1343 else if (now - then < 12 * 30 * 24 * 60 * 60) 1344 (void) strftime(st_buf, sizeof (st_buf), gettext(FORMAT_DATE), 1345 tm); 1346 else 1347 (void) strftime(st_buf, sizeof (st_buf), gettext(FORMAT_YEAR), 1348 tm); 1349 1350 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1351 STIME_COLUMN_WIDTH + 1, st_buf); 1352 if (*buf) 1353 free(*buf); 1354 *buf = newbuf; 1355 } 1356 1357 #define STIME_SORTKEY_WIDTH (sizeof (uint64_t) + sizeof (uint32_t)) 1358 1359 /* ARGSUSED */ 1360 static void 1361 sortkey_stime(char *buf, int reverse, scf_walkinfo_t *wip) 1362 { 1363 struct timeval tv; 1364 int r; 1365 1366 if (wip->pg == NULL) 1367 r = get_restarter_time_prop(wip->inst, 1368 SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0); 1369 else 1370 r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP, 1371 SCF_TYPE_TIME, &tv, NULL, 0); 1372 1373 if (r == 0) { 1374 int64_t sec; 1375 int32_t us; 1376 1377 /* Stick it straight into the buffer. */ 1378 sec = tv.tv_sec; 1379 us = tv.tv_usec; 1380 1381 sec = BE_64(sec); 1382 us = BE_32(us); 1383 bcopy(&sec, buf, sizeof (sec)); 1384 bcopy(&us, buf + sizeof (sec), sizeof (us)); 1385 } else { 1386 bzero(buf, STIME_SORTKEY_WIDTH); 1387 } 1388 1389 if (reverse) 1390 reverse_bytes(buf, STIME_SORTKEY_WIDTH); 1391 } 1392 1393 1394 /* 1395 * Information about columns which can be displayed. If you add something, 1396 * check MAX_COLUMN_NAME_LENGTH_STR & update description_of_column() below. 1397 */ 1398 static const struct column columns[] = { 1399 { "CTID", CTID_COLUMN_WIDTH, sprint_ctid, 1400 CTID_SORTKEY_WIDTH, sortkey_ctid }, 1401 { "DESC", DESC_COLUMN_WIDTH, sprint_desc, 1402 DESC_COLUMN_WIDTH, sortkey_desc }, 1403 { "FMRI", FMRI_COLUMN_WIDTH, sprint_fmri, 1404 FMRI_COLUMN_WIDTH, sortkey_fmri }, 1405 { "INST", COMPONENT_COLUMN_WIDTH, sprint_instance, 1406 COMPONENT_COLUMN_WIDTH, sortkey_instance }, 1407 { "N", 1, sprint_n, 1, sortkey_nstate }, 1408 { "NSTA", 4, sprint_nsta, 1, sortkey_nstate }, 1409 { "NSTATE", MAX_SCF_STATE_STRING_SZ - 1, sprint_nstate, 1410 1, sortkey_nstate }, 1411 { "S", 2, sprint_s, 1, sortkey_state }, 1412 { "SCOPE", COMPONENT_COLUMN_WIDTH, sprint_scope, 1413 COMPONENT_COLUMN_WIDTH, sortkey_scope }, 1414 { "SN", 2, sprint_sn, 2, sortkey_sn }, 1415 { "SVC", COMPONENT_COLUMN_WIDTH, sprint_service, 1416 COMPONENT_COLUMN_WIDTH, sortkey_service }, 1417 { "STA", 4, sprint_sta, 1, sortkey_state }, 1418 { "STATE", MAX_SCF_STATE_STRING_SZ - 1 + 1, sprint_state, 1419 1, sortkey_state }, 1420 { "STIME", STIME_COLUMN_WIDTH, sprint_stime, 1421 STIME_SORTKEY_WIDTH, sortkey_stime }, 1422 }; 1423 1424 #define MAX_COLUMN_NAME_LENGTH_STR "6" 1425 1426 static const int ncolumns = sizeof (columns) / sizeof (columns[0]); 1427 1428 /* 1429 * Necessary thanks to gettext() & xgettext. 1430 */ 1431 static const char * 1432 description_of_column(int c) 1433 { 1434 const char *s = NULL; 1435 1436 switch (c) { 1437 case 0: 1438 s = gettext("contract ID for service (see contract(4))"); 1439 break; 1440 case 1: 1441 s = gettext("human-readable description of the service"); 1442 break; 1443 case 2: 1444 s = gettext("Fault Managed Resource Identifier for service"); 1445 break; 1446 case 3: 1447 s = gettext("portion of the FMRI indicating service instance"); 1448 break; 1449 case 4: 1450 s = gettext("abbreviation for next state (if in transition)"); 1451 break; 1452 case 5: 1453 s = gettext("abbreviation for next state (if in transition)"); 1454 break; 1455 case 6: 1456 s = gettext("name for next state (if in transition)"); 1457 break; 1458 case 7: 1459 s = gettext("abbreviation for current state"); 1460 break; 1461 case 8: 1462 s = gettext("name for scope associated with service"); 1463 break; 1464 case 9: 1465 s = gettext("abbreviation for current state and next state"); 1466 break; 1467 case 10: 1468 s = gettext("portion of the FMRI representing service name"); 1469 break; 1470 case 11: 1471 s = gettext("abbreviation for current state"); 1472 break; 1473 case 12: 1474 s = gettext("name for current state"); 1475 break; 1476 case 13: 1477 s = gettext("time of last state change"); 1478 break; 1479 } 1480 1481 assert(s != NULL); 1482 return (s); 1483 } 1484 1485 1486 static void 1487 print_usage(const char *progname, FILE *f, boolean_t do_exit) 1488 { 1489 (void) fprintf(f, gettext( 1490 "Usage: %1$s [-aHpv] [-o col[,col ... ]] [-R restarter] " 1491 "[-sS col] [<service> ...]\n" 1492 " %1$s -d | -D [-Hpv] [-o col[,col ... ]] [-sS col] " 1493 "[<service> ...]\n" 1494 " %1$s -l <service> ...\n" 1495 " %1$s -x [-v] [<service> ...]\n" 1496 " %1$s -?\n"), progname); 1497 1498 if (do_exit) 1499 exit(UU_EXIT_USAGE); 1500 } 1501 1502 #define argserr(progname) print_usage(progname, stderr, B_TRUE) 1503 1504 static void 1505 print_help(const char *progname) 1506 { 1507 int i; 1508 1509 print_usage(progname, stdout, B_FALSE); 1510 1511 (void) printf(gettext("\n" 1512 "\t-a list all service instances rather than " 1513 "only those that are enabled\n" 1514 "\t-d list dependencies of the specified service(s)\n" 1515 "\t-D list dependents of the specified service(s)\n" 1516 "\t-H omit header line from output\n" 1517 "\t-l list detailed information about the specified service(s)\n" 1518 "\t-o list only the specified columns in the output\n" 1519 "\t-p list process IDs and names associated with each service\n" 1520 "\t-R list only those services with the specified restarter\n" 1521 "\t-s sort output in ascending order by the specified column(s)\n" 1522 "\t-S sort output in descending order by the specified column(s)\n" 1523 "\t-v list verbose information appropriate to the type of output\n" 1524 "\t-x explain the status of services that might require maintenance,\n" 1525 "\t or explain the status of the specified service(s)\n" 1526 "\n\t" 1527 "Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n" 1528 "\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n" 1529 "\n" 1530 "\t%1$s [opts] svc:/network/smtp:sendmail\n" 1531 "\t%1$s [opts] network/smtp:sendmail\n" 1532 "\t%1$s [opts] network/*mail\n" 1533 "\t%1$s [opts] network/smtp\n" 1534 "\t%1$s [opts] smtp:sendmail\n" 1535 "\t%1$s [opts] smtp\n" 1536 "\t%1$s [opts] sendmail\n" 1537 "\n\t" 1538 "Columns for output or sorting can be specified using these names:\n" 1539 "\n"), progname); 1540 1541 for (i = 0; i < ncolumns; i++) { 1542 (void) printf("\t%-" MAX_COLUMN_NAME_LENGTH_STR "s %s\n", 1543 columns[i].name, description_of_column(i)); 1544 } 1545 } 1546 1547 1548 /* 1549 * A getsubopt()-like function which returns an index into the columns table. 1550 * On success, *optionp is set to point to the next sub-option, or the 1551 * terminating null if there are none. 1552 */ 1553 static int 1554 getcolumnopt(char **optionp) 1555 { 1556 char *str = *optionp, *cp; 1557 int i; 1558 1559 assert(optionp != NULL); 1560 assert(*optionp != NULL); 1561 1562 cp = strchr(*optionp, ','); 1563 if (cp != NULL) 1564 *cp = '\0'; 1565 1566 for (i = 0; i < ncolumns; ++i) { 1567 if (strcasecmp(str, columns[i].name) == 0) { 1568 if (cp != NULL) 1569 *optionp = cp + 1; 1570 else 1571 *optionp = strchr(*optionp, '\0'); 1572 1573 return (i); 1574 } 1575 } 1576 1577 return (-1); 1578 } 1579 1580 static void 1581 print_header() 1582 { 1583 int i; 1584 char *line_buf, *cp; 1585 1586 line_buf = safe_malloc(line_sz); 1587 cp = line_buf; 1588 for (i = 0; i < opt_cnum; ++i) { 1589 const struct column * const colp = &columns[opt_columns[i]]; 1590 1591 (void) snprintf(cp, colp->width + 1, "%-*s", colp->width, 1592 colp->name); 1593 cp += colp->width; 1594 *cp++ = ' '; 1595 } 1596 1597 /* Trim the trailing whitespace */ 1598 --cp; 1599 while (*cp == ' ') 1600 --cp; 1601 *(cp+1) = '\0'; 1602 (void) puts(line_buf); 1603 1604 free(line_buf); 1605 } 1606 1607 1608 1609 /* 1610 * Long listing (-l) functions. 1611 */ 1612 1613 static int 1614 pidcmp(const void *l, const void *r) 1615 { 1616 pid_t lp = *(pid_t *)l, rp = *(pid_t *)r; 1617 1618 if (lp < rp) 1619 return (-1); 1620 if (lp > rp) 1621 return (1); 1622 return (0); 1623 } 1624 1625 /* 1626 * This is the strlen() of the longest label ("description"), plus intercolumn 1627 * space. 1628 */ 1629 #define DETAILED_WIDTH (11 + 2) 1630 1631 static void 1632 detailed_list_processes(scf_instance_t *inst) 1633 { 1634 uint64_t c; 1635 pid_t *pids; 1636 uint_t i, n; 1637 psinfo_t psi; 1638 1639 if (get_restarter_count_prop(inst, scf_property_contract, &c, 1640 EMPTY_OK) != 0) 1641 return; 1642 1643 if (instance_processes(inst, &pids, &n) != 0) 1644 return; 1645 1646 qsort(pids, n, sizeof (*pids), pidcmp); 1647 1648 for (i = 0; i < n; ++i) { 1649 (void) printf("%-*s%lu", DETAILED_WIDTH, gettext("process"), 1650 pids[i]); 1651 1652 if (get_psinfo(pids[i], &psi) == 0) 1653 (void) printf(" %.*s", PRARGSZ, psi.pr_psargs); 1654 1655 (void) putchar('\n'); 1656 } 1657 1658 free(pids); 1659 } 1660 1661 /* 1662 * Determines the state of a dependency. If the FMRI specifies a file, then we 1663 * fake up a state based on whether we can access the file. 1664 */ 1665 static void 1666 get_fmri_state(char *fmri, char *state, size_t state_sz) 1667 { 1668 char *lfmri; 1669 const char *svc_name, *inst_name, *pg_name, *path; 1670 scf_service_t *svc; 1671 scf_instance_t *inst; 1672 scf_iter_t *iter; 1673 1674 lfmri = safe_strdup(fmri); 1675 1676 /* 1677 * Check for file:// dependencies 1678 */ 1679 if (scf_parse_file_fmri(lfmri, NULL, &path) == SCF_SUCCESS) { 1680 struct stat64 statbuf; 1681 const char *msg; 1682 1683 if (stat64(path, &statbuf) == 0) 1684 msg = "online"; 1685 else if (errno == ENOENT) 1686 msg = "absent"; 1687 else 1688 msg = "unknown"; 1689 1690 (void) strlcpy(state, msg, state_sz); 1691 return; 1692 } 1693 1694 /* 1695 * scf_parse_file_fmri() may have overwritten part of the string, so 1696 * copy it back. 1697 */ 1698 (void) strcpy(lfmri, fmri); 1699 1700 if (scf_parse_svc_fmri(lfmri, NULL, &svc_name, &inst_name, 1701 &pg_name, NULL) != SCF_SUCCESS) { 1702 free(lfmri); 1703 (void) strlcpy(state, "invalid", state_sz); 1704 return; 1705 } 1706 1707 free(lfmri); 1708 1709 if (svc_name == NULL || pg_name != NULL) { 1710 (void) strlcpy(state, "invalid", state_sz); 1711 return; 1712 } 1713 1714 if (inst_name != NULL) { 1715 /* instance: get state */ 1716 inst = scf_instance_create(h); 1717 if (inst == NULL) 1718 scfdie(); 1719 1720 if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL, 1721 NULL, SCF_DECODE_FMRI_EXACT) == SCF_SUCCESS) 1722 get_restarter_string_prop(inst, scf_property_state, 1723 state, state_sz); 1724 else { 1725 switch (scf_error()) { 1726 case SCF_ERROR_INVALID_ARGUMENT: 1727 (void) strlcpy(state, "invalid", state_sz); 1728 break; 1729 case SCF_ERROR_NOT_FOUND: 1730 (void) strlcpy(state, "absent", state_sz); 1731 break; 1732 1733 default: 1734 scfdie(); 1735 } 1736 } 1737 1738 scf_instance_destroy(inst); 1739 return; 1740 } 1741 1742 /* 1743 * service: If only one instance, use that state. Otherwise, say 1744 * "multiple". 1745 */ 1746 if ((svc = scf_service_create(h)) == NULL || 1747 (inst = scf_instance_create(h)) == NULL || 1748 (iter = scf_iter_create(h)) == NULL) 1749 scfdie(); 1750 1751 if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL, 1752 SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) { 1753 switch (scf_error()) { 1754 case SCF_ERROR_INVALID_ARGUMENT: 1755 (void) strlcpy(state, "invalid", state_sz); 1756 goto out; 1757 case SCF_ERROR_NOT_FOUND: 1758 (void) strlcpy(state, "absent", state_sz); 1759 goto out; 1760 1761 default: 1762 scfdie(); 1763 } 1764 } 1765 1766 if (scf_iter_service_instances(iter, svc) != SCF_SUCCESS) 1767 scfdie(); 1768 1769 switch (scf_iter_next_instance(iter, inst)) { 1770 case 0: 1771 (void) strlcpy(state, "absent", state_sz); 1772 goto out; 1773 1774 case 1: 1775 break; 1776 1777 default: 1778 scfdie(); 1779 } 1780 1781 /* Get the state in case this is the only instance. */ 1782 get_restarter_string_prop(inst, scf_property_state, state, state_sz); 1783 1784 switch (scf_iter_next_instance(iter, inst)) { 1785 case 0: 1786 break; 1787 1788 case 1: 1789 /* Nope, multiple instances. */ 1790 (void) strlcpy(state, "multiple", state_sz); 1791 goto out; 1792 1793 default: 1794 scfdie(); 1795 } 1796 1797 out: 1798 scf_iter_destroy(iter); 1799 scf_instance_destroy(inst); 1800 scf_service_destroy(svc); 1801 } 1802 1803 static void 1804 print_detailed_dependency(scf_propertygroup_t *pg) 1805 { 1806 scf_property_t *eprop; 1807 scf_iter_t *iter; 1808 scf_type_t ty; 1809 char *val_buf; 1810 int i; 1811 1812 if ((eprop = scf_property_create(h)) == NULL || 1813 (iter = scf_iter_create(h)) == NULL) 1814 scfdie(); 1815 1816 val_buf = safe_malloc(max_scf_value_length + 1); 1817 1818 if (scf_pg_get_property(pg, SCF_PROPERTY_ENTITIES, eprop) != 1819 SCF_SUCCESS || 1820 scf_property_type(eprop, &ty) != SCF_SUCCESS || 1821 ty != SCF_TYPE_FMRI) 1822 return; 1823 1824 (void) printf("%-*s", DETAILED_WIDTH, gettext("dependency")); 1825 1826 /* Print the grouping */ 1827 if (pg_get_single_val(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING, 1828 val_buf, max_scf_value_length + 1, 0) == 0) 1829 (void) fputs(val_buf, stdout); 1830 else 1831 (void) putchar('?'); 1832 1833 (void) putchar('/'); 1834 1835 if (pg_get_single_val(pg, SCF_PROPERTY_RESTART_ON, SCF_TYPE_ASTRING, 1836 val_buf, max_scf_value_length + 1, 0) == 0) 1837 (void) fputs(val_buf, stdout); 1838 else 1839 (void) putchar('?'); 1840 1841 /* Print the dependency entities. */ 1842 if (scf_iter_property_values(iter, eprop) == -1) 1843 scfdie(); 1844 1845 while ((i = scf_iter_next_value(iter, g_val)) == 1) { 1846 char state[MAX_SCF_STATE_STRING_SZ]; 1847 1848 if (scf_value_get_astring(g_val, val_buf, 1849 max_scf_value_length + 1) < 0) 1850 scfdie(); 1851 1852 (void) putchar(' '); 1853 (void) fputs(val_buf, stdout); 1854 1855 /* Print the state. */ 1856 state[0] = '-'; 1857 state[1] = '\0'; 1858 1859 get_fmri_state(val_buf, state, sizeof (state)); 1860 1861 (void) printf(" (%s)", state); 1862 } 1863 if (i == -1) 1864 scfdie(); 1865 1866 (void) putchar('\n'); 1867 1868 free(val_buf); 1869 scf_iter_destroy(iter); 1870 scf_property_destroy(eprop); 1871 } 1872 1873 /* ARGSUSED */ 1874 static int 1875 print_detailed(void *unused, scf_walkinfo_t *wip) 1876 { 1877 scf_snapshot_t *snap; 1878 scf_propertygroup_t *rpg; 1879 scf_iter_t *pg_iter; 1880 1881 char *buf; 1882 char *timebuf; 1883 size_t tbsz; 1884 int ret; 1885 uint64_t c; 1886 int temp, perm; 1887 struct timeval tv; 1888 time_t stime; 1889 struct tm *tmp; 1890 1891 const char * const fmt = "%-*s%s\n"; 1892 1893 assert(wip->pg == NULL); 1894 1895 rpg = scf_pg_create(h); 1896 if (rpg == NULL) 1897 scfdie(); 1898 1899 if (first_paragraph) 1900 first_paragraph = 0; 1901 else 1902 (void) putchar('\n'); 1903 1904 buf = safe_malloc(max_scf_fmri_length + 1); 1905 1906 if (scf_instance_to_fmri(wip->inst, buf, max_scf_fmri_length + 1) != -1) 1907 (void) printf(fmt, DETAILED_WIDTH, "fmri", buf); 1908 1909 if (common_name_buf == NULL) 1910 common_name_buf = safe_malloc(max_scf_value_length + 1); 1911 1912 if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale, 1913 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1) 1914 == 0) 1915 (void) printf(fmt, DETAILED_WIDTH, gettext("name"), 1916 common_name_buf); 1917 else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C", 1918 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1) 1919 == 0) 1920 (void) printf(fmt, DETAILED_WIDTH, gettext("name"), 1921 common_name_buf); 1922 1923 /* 1924 * Synthesize an 'enabled' property that hides the enabled_ovr 1925 * implementation from the user. If the service has been temporarily 1926 * set to a state other than its permanent value, alert the user with 1927 * a '(temporary)' message. 1928 */ 1929 perm = instance_enabled(wip->inst, B_FALSE); 1930 temp = instance_enabled(wip->inst, B_TRUE); 1931 if (temp != -1) { 1932 if (temp != perm) 1933 (void) printf(gettext("%-*s%s (temporary)\n"), 1934 DETAILED_WIDTH, gettext("enabled"), 1935 temp ? gettext("true") : gettext("false")); 1936 else 1937 (void) printf(fmt, DETAILED_WIDTH, 1938 gettext("enabled"), temp ? gettext("true") : 1939 gettext("false")); 1940 } else if (perm != -1) { 1941 (void) printf(fmt, DETAILED_WIDTH, gettext("enabled"), 1942 perm ? gettext("true") : gettext("false")); 1943 } 1944 1945 /* 1946 * Property values may be longer than max_scf_fmri_length, but these 1947 * shouldn't be, so we'll just reuse buf. The user can use svcprop if 1948 * he suspects something fishy. 1949 */ 1950 if (scf_instance_get_pg(wip->inst, SCF_PG_RESTARTER, rpg) != 0) { 1951 if (scf_error() != SCF_ERROR_NOT_FOUND) 1952 scfdie(); 1953 1954 scf_pg_destroy(rpg); 1955 rpg = NULL; 1956 } 1957 1958 if (rpg) { 1959 if (pg_get_single_val(rpg, scf_property_state, SCF_TYPE_ASTRING, 1960 buf, max_scf_fmri_length + 1, 0) == 0) 1961 (void) printf(fmt, DETAILED_WIDTH, gettext("state"), 1962 buf); 1963 1964 if (pg_get_single_val(rpg, scf_property_next_state, 1965 SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0) 1966 (void) printf(fmt, DETAILED_WIDTH, 1967 gettext("next_state"), buf); 1968 1969 if (pg_get_single_val(rpg, SCF_PROPERTY_STATE_TIMESTAMP, 1970 SCF_TYPE_TIME, &tv, NULL, 0) == 0) { 1971 stime = tv.tv_sec; 1972 tmp = localtime(&stime); 1973 for (tbsz = 50; ; tbsz *= 2) { 1974 timebuf = safe_malloc(tbsz); 1975 if (strftime(timebuf, tbsz, NULL, tmp) != 0) 1976 break; 1977 free(timebuf); 1978 } 1979 (void) printf(fmt, DETAILED_WIDTH, 1980 gettext("state_time"), 1981 timebuf); 1982 free(timebuf); 1983 } 1984 1985 } 1986 1987 if (pg_get_single_val(rpg, SCF_PROPERTY_ALT_LOGFILE, 1988 SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0) 1989 (void) printf(fmt, DETAILED_WIDTH, gettext("alt_logfile"), 1990 buf); 1991 1992 if (pg_get_single_val(rpg, SCF_PROPERTY_LOGFILE, 1993 SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0) 1994 (void) printf(fmt, DETAILED_WIDTH, gettext("logfile"), buf); 1995 1996 if (inst_get_single_val(wip->inst, SCF_PG_GENERAL, 1997 SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, buf, 1998 max_scf_fmri_length + 1, 0, 0, 1) == 0) 1999 (void) printf(fmt, DETAILED_WIDTH, gettext("restarter"), buf); 2000 else 2001 (void) printf(fmt, DETAILED_WIDTH, gettext("restarter"), 2002 SCF_SERVICE_STARTD); 2003 2004 free(buf); 2005 2006 if (rpg) { 2007 scf_iter_t *iter; 2008 2009 if ((iter = scf_iter_create(h)) == NULL) 2010 scfdie(); 2011 2012 if (scf_pg_get_property(rpg, scf_property_contract, g_prop) == 2013 0) { 2014 if (scf_property_is_type(g_prop, SCF_TYPE_COUNT) == 0) { 2015 (void) printf("%-*s", DETAILED_WIDTH, 2016 "contract_id"); 2017 2018 if (scf_iter_property_values(iter, g_prop) != 0) 2019 scfdie(); 2020 2021 for (;;) { 2022 ret = scf_iter_next_value(iter, g_val); 2023 if (ret == -1) 2024 scfdie(); 2025 if (ret == 0) 2026 break; 2027 2028 if (scf_value_get_count(g_val, &c) != 0) 2029 scfdie(); 2030 (void) printf("%lu ", (ctid_t)c); 2031 } 2032 2033 (void) putchar('\n'); 2034 } else { 2035 if (scf_error() != SCF_ERROR_TYPE_MISMATCH) 2036 scfdie(); 2037 } 2038 } else { 2039 if (scf_error() != SCF_ERROR_NOT_FOUND) 2040 scfdie(); 2041 } 2042 2043 scf_iter_destroy(iter); 2044 } else { 2045 if (scf_error() != SCF_ERROR_NOT_FOUND) 2046 scfdie(); 2047 } 2048 2049 scf_pg_destroy(rpg); 2050 2051 /* Dependencies. */ 2052 if ((pg_iter = scf_iter_create(h)) == NULL) 2053 scfdie(); 2054 2055 snap = get_running_snapshot(wip->inst); 2056 2057 if (scf_iter_instance_pgs_typed_composed(pg_iter, wip->inst, snap, 2058 SCF_GROUP_DEPENDENCY) != SCF_SUCCESS) 2059 scfdie(); 2060 2061 while ((ret = scf_iter_next_pg(pg_iter, g_pg)) == 1) 2062 print_detailed_dependency(g_pg); 2063 if (ret == -1) 2064 scfdie(); 2065 2066 scf_snapshot_destroy(snap); 2067 scf_iter_destroy(pg_iter); 2068 2069 if (opt_processes) 2070 detailed_list_processes(wip->inst); 2071 2072 return (0); 2073 } 2074 2075 /* 2076 * Append a one-lined description of each process in inst's contract(s) and 2077 * return the augmented string. 2078 */ 2079 static char * 2080 add_processes(char *line, scf_instance_t *inst, scf_propertygroup_t *lpg) 2081 { 2082 pid_t *pids = NULL; 2083 uint_t i, n = 0; 2084 2085 if (lpg == NULL) { 2086 if (instance_processes(inst, &pids, &n) != 0) 2087 return (line); 2088 } else { 2089 scf_iter_t *iter; 2090 2091 if ((iter = scf_iter_create(h)) == NULL) 2092 scfdie(); 2093 2094 (void) propvals_to_pids(lpg, scf_property_contract, &pids, &n, 2095 g_prop, g_val, iter); 2096 2097 scf_iter_destroy(iter); 2098 } 2099 2100 if (n == 0) 2101 return (line); 2102 2103 qsort(pids, n, sizeof (*pids), pidcmp); 2104 2105 for (i = 0; i < n; ++i) { 2106 char *cp, stime[9]; 2107 psinfo_t psi; 2108 struct tm *tm; 2109 int len = 1 + 15 + 8 + 3 + 6 + 1 + PRFNSZ; 2110 2111 if (get_psinfo(pids[i], &psi) != 0) 2112 continue; 2113 2114 line = realloc(line, strlen(line) + len); 2115 if (line == NULL) 2116 uu_die(gettext("Out of memory.\n")); 2117 2118 cp = strchr(line, '\0'); 2119 2120 tm = localtime(&psi.pr_start.tv_sec); 2121 2122 /* 2123 * Print time if started within the past 24 hours, print date 2124 * if within the past 12 months, print year if started greater 2125 * than 12 months ago. 2126 */ 2127 if (now - psi.pr_start.tv_sec < 24 * 60 * 60) 2128 (void) strftime(stime, sizeof (stime), gettext(FORMAT_TIME), 2129 tm); 2130 else if (now - psi.pr_start.tv_sec < 12 * 30 * 24 * 60 * 60) 2131 (void) strftime(stime, sizeof (stime), gettext(FORMAT_DATE), 2132 tm); 2133 else 2134 (void) strftime(stime, sizeof (stime), gettext(FORMAT_YEAR), 2135 tm); 2136 2137 (void) snprintf(cp, len, "\n %-8s %6ld %.*s", 2138 stime, pids[i], PRFNSZ, psi.pr_fname); 2139 } 2140 2141 free(pids); 2142 2143 return (line); 2144 } 2145 2146 /*ARGSUSED*/ 2147 static int 2148 list_instance(void *unused, scf_walkinfo_t *wip) 2149 { 2150 struct avl_string *lp; 2151 char *cp; 2152 int i; 2153 uu_avl_index_t idx; 2154 2155 /* 2156 * If the user has specified a restarter, check for a match first 2157 */ 2158 if (restarters != NULL) { 2159 struct pfmri_list *rest; 2160 int match; 2161 char *restarter_fmri; 2162 const char *scope_name, *svc_name, *inst_name, *pg_name; 2163 2164 /* legacy services don't have restarters */ 2165 if (wip->pg != NULL) 2166 return (0); 2167 2168 restarter_fmri = safe_malloc(max_scf_fmri_length + 1); 2169 2170 if (inst_get_single_val(wip->inst, SCF_PG_GENERAL, 2171 SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, restarter_fmri, 2172 max_scf_fmri_length + 1, 0, 0, 1) != 0) 2173 (void) strcpy(restarter_fmri, SCF_SERVICE_STARTD); 2174 2175 if (scf_parse_svc_fmri(restarter_fmri, &scope_name, &svc_name, 2176 &inst_name, &pg_name, NULL) != SCF_SUCCESS) { 2177 free(restarter_fmri); 2178 return (0); 2179 } 2180 2181 match = 0; 2182 for (rest = restarters; rest != NULL; rest = rest->next) { 2183 if (strcmp(rest->scope, scope_name) == 0 && 2184 strcmp(rest->service, svc_name) == 0 && 2185 strcmp(rest->instance, inst_name) == 0) 2186 match = 1; 2187 } 2188 2189 free(restarter_fmri); 2190 2191 if (!match) 2192 return (0); 2193 } 2194 2195 if (wip->pg == NULL && ht_buckets != NULL && ht_add(wip->fmri)) { 2196 /* It was already there. */ 2197 return (0); 2198 } 2199 2200 lp = safe_malloc(sizeof (*lp)); 2201 2202 lp->str = NULL; 2203 for (i = 0; i < opt_cnum; ++i) { 2204 columns[opt_columns[i]].sprint(&lp->str, wip); 2205 } 2206 cp = lp->str + strlen(lp->str); 2207 cp--; 2208 while (*cp == ' ') 2209 cp--; 2210 *(cp+1) = '\0'; 2211 2212 /* If we're supposed to list the processes, too, do that now. */ 2213 if (opt_processes) 2214 lp->str = add_processes(lp->str, wip->inst, wip->pg); 2215 2216 /* Create the sort key. */ 2217 cp = lp->key = safe_malloc(sortkey_sz); 2218 for (i = 0; i < opt_snum; ++i) { 2219 int j = opt_sort[i] & 0xff; 2220 2221 assert(columns[j].get_sortkey != NULL); 2222 columns[j].get_sortkey(cp, opt_sort[i] & ~0xff, wip); 2223 cp += columns[j].sortkey_width; 2224 } 2225 2226 /* Insert into AVL tree. */ 2227 uu_avl_node_init(lp, &lp->node, lines_pool); 2228 (void) uu_avl_find(lines, lp, NULL, &idx); 2229 uu_avl_insert(lines, lp, idx); 2230 2231 return (0); 2232 } 2233 2234 static int 2235 list_if_enabled(void *unused, scf_walkinfo_t *wip) 2236 { 2237 if (wip->pg != NULL || 2238 instance_enabled(wip->inst, B_FALSE) == 1 || 2239 instance_enabled(wip->inst, B_TRUE) == 1) 2240 return (list_instance(unused, wip)); 2241 2242 return (0); 2243 } 2244 2245 /* 2246 * Service FMRI selection: Lookup and call list_instance() for the instances. 2247 * Instance FMRI selection: Lookup and call list_instance(). 2248 * 2249 * Note: This is shoehorned into a walk_dependencies() callback prototype so 2250 * it can be used in list_dependencies. 2251 */ 2252 static int 2253 list_svc_or_inst_fmri(void *complain, scf_walkinfo_t *wip) 2254 { 2255 char *fmri; 2256 const char *svc_name, *inst_name, *pg_name, *save; 2257 scf_iter_t *iter; 2258 int ret; 2259 2260 fmri = safe_strdup(wip->fmri); 2261 2262 if (scf_parse_svc_fmri(fmri, NULL, &svc_name, &inst_name, &pg_name, 2263 NULL) != SCF_SUCCESS) { 2264 if (complain) 2265 uu_warn(gettext("FMRI \"%s\" is invalid.\n"), 2266 wip->fmri); 2267 exit_status = UU_EXIT_FATAL; 2268 free(fmri); 2269 return (0); 2270 } 2271 2272 /* 2273 * Yes, this invalidates *_name, but we only care whether they're NULL 2274 * or not. 2275 */ 2276 free(fmri); 2277 2278 if (svc_name == NULL || pg_name != NULL) { 2279 if (complain) 2280 uu_warn(gettext("FMRI \"%s\" does not designate a " 2281 "service or instance.\n"), wip->fmri); 2282 return (0); 2283 } 2284 2285 if (inst_name != NULL) { 2286 /* instance */ 2287 if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc, 2288 wip->inst, NULL, NULL, 0) != SCF_SUCCESS) { 2289 if (scf_error() != SCF_ERROR_NOT_FOUND) 2290 scfdie(); 2291 2292 if (complain) 2293 uu_warn(gettext( 2294 "Instance \"%s\" does not exist.\n"), 2295 wip->fmri); 2296 return (0); 2297 } 2298 2299 return (list_instance(NULL, wip)); 2300 } 2301 2302 /* service: Walk the instances. */ 2303 if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc, NULL, 2304 NULL, NULL, 0) != SCF_SUCCESS) { 2305 if (scf_error() != SCF_ERROR_NOT_FOUND) 2306 scfdie(); 2307 2308 if (complain) 2309 uu_warn(gettext("Service \"%s\" does not exist.\n"), 2310 wip->fmri); 2311 2312 exit_status = UU_EXIT_FATAL; 2313 2314 return (0); 2315 } 2316 2317 iter = scf_iter_create(h); 2318 if (iter == NULL) 2319 scfdie(); 2320 2321 if (scf_iter_service_instances(iter, wip->svc) != SCF_SUCCESS) 2322 scfdie(); 2323 2324 if ((fmri = malloc(max_scf_fmri_length + 1)) == NULL) { 2325 scf_iter_destroy(iter); 2326 exit_status = UU_EXIT_FATAL; 2327 return (0); 2328 } 2329 2330 save = wip->fmri; 2331 wip->fmri = fmri; 2332 while ((ret = scf_iter_next_instance(iter, wip->inst)) == 1) { 2333 if (scf_instance_to_fmri(wip->inst, fmri, 2334 max_scf_fmri_length + 1) <= 0) 2335 scfdie(); 2336 (void) list_instance(NULL, wip); 2337 } 2338 free(fmri); 2339 wip->fmri = save; 2340 if (ret == -1) 2341 scfdie(); 2342 2343 exit_status = UU_EXIT_OK; 2344 2345 scf_iter_destroy(iter); 2346 2347 return (0); 2348 } 2349 2350 /* 2351 * Dependency selection: Straightforward since each instance lists the 2352 * services it depends on. 2353 */ 2354 2355 static void 2356 walk_dependencies(scf_walkinfo_t *wip, scf_walk_callback callback, void *data) 2357 { 2358 scf_snapshot_t *snap; 2359 scf_iter_t *iter, *viter; 2360 int ret, vret; 2361 char *dep; 2362 2363 assert(wip->inst != NULL); 2364 2365 if ((iter = scf_iter_create(h)) == NULL || 2366 (viter = scf_iter_create(h)) == NULL) 2367 scfdie(); 2368 2369 snap = get_running_snapshot(wip->inst); 2370 2371 if (scf_iter_instance_pgs_typed_composed(iter, wip->inst, snap, 2372 SCF_GROUP_DEPENDENCY) != SCF_SUCCESS) 2373 scfdie(); 2374 2375 dep = safe_malloc(max_scf_value_length + 1); 2376 2377 while ((ret = scf_iter_next_pg(iter, g_pg)) == 1) { 2378 scf_type_t ty; 2379 2380 /* Ignore exclude_any dependencies. */ 2381 if (scf_pg_get_property(g_pg, SCF_PROPERTY_GROUPING, g_prop) != 2382 SCF_SUCCESS) { 2383 if (scf_error() != SCF_ERROR_NOT_FOUND) 2384 scfdie(); 2385 2386 continue; 2387 } 2388 2389 if (scf_property_type(g_prop, &ty) != SCF_SUCCESS) 2390 scfdie(); 2391 2392 if (ty != SCF_TYPE_ASTRING) 2393 continue; 2394 2395 if (scf_property_get_value(g_prop, g_val) != SCF_SUCCESS) { 2396 if (scf_error() != SCF_ERROR_CONSTRAINT_VIOLATED) 2397 scfdie(); 2398 2399 continue; 2400 } 2401 2402 if (scf_value_get_astring(g_val, dep, 2403 max_scf_value_length + 1) < 0) 2404 scfdie(); 2405 2406 if (strcmp(dep, SCF_DEP_EXCLUDE_ALL) == 0) 2407 continue; 2408 2409 if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENTITIES, g_prop) != 2410 SCF_SUCCESS) { 2411 if (scf_error() != SCF_ERROR_NOT_FOUND) 2412 scfdie(); 2413 2414 continue; 2415 } 2416 2417 if (scf_iter_property_values(viter, g_prop) != SCF_SUCCESS) 2418 scfdie(); 2419 2420 while ((vret = scf_iter_next_value(viter, g_val)) == 1) { 2421 if (scf_value_get_astring(g_val, dep, 2422 max_scf_value_length + 1) < 0) 2423 scfdie(); 2424 2425 wip->fmri = dep; 2426 if (callback(data, wip) != 0) 2427 goto out; 2428 } 2429 if (vret == -1) 2430 scfdie(); 2431 } 2432 if (ret == -1) 2433 scfdie(); 2434 2435 out: 2436 scf_iter_destroy(viter); 2437 scf_iter_destroy(iter); 2438 scf_snapshot_destroy(snap); 2439 } 2440 2441 static int 2442 list_dependencies(void *data, scf_walkinfo_t *wip) 2443 { 2444 walk_dependencies(wip, list_svc_or_inst_fmri, data); 2445 return (0); 2446 } 2447 2448 2449 /* 2450 * Dependent selection: The "providing" service's or instance's FMRI is parsed 2451 * into the provider_* variables, the instances are walked, and any instance 2452 * which lists an FMRI which parses to these components is selected. This is 2453 * inefficient in the face of multiple operands, but that should be uncommon. 2454 */ 2455 2456 static char *provider_scope; 2457 static char *provider_svc; 2458 static char *provider_inst; /* NULL for services */ 2459 2460 /*ARGSUSED*/ 2461 static int 2462 check_against_provider(void *arg, scf_walkinfo_t *wip) 2463 { 2464 char *cfmri; 2465 const char *scope_name, *svc_name, *inst_name, *pg_name; 2466 int *matchp = arg; 2467 2468 cfmri = safe_strdup(wip->fmri); 2469 2470 if (scf_parse_svc_fmri(cfmri, &scope_name, &svc_name, &inst_name, 2471 &pg_name, NULL) != SCF_SUCCESS) { 2472 free(cfmri); 2473 return (0); 2474 } 2475 2476 if (svc_name == NULL || pg_name != NULL) { 2477 free(cfmri); 2478 return (0); 2479 } 2480 2481 /* 2482 * If the user has specified an instance, then also match dependencies 2483 * on the service itself. 2484 */ 2485 *matchp = (strcmp(provider_scope, scope_name) == 0 && 2486 strcmp(provider_svc, svc_name) == 0 && 2487 (provider_inst == NULL ? (inst_name == NULL) : 2488 (inst_name == NULL || strcmp(provider_inst, inst_name) == 0))); 2489 2490 free(cfmri); 2491 2492 /* Stop on matches. */ 2493 return (*matchp); 2494 } 2495 2496 static int 2497 list_if_dependent(void *unused, scf_walkinfo_t *wip) 2498 { 2499 /* Only proceed if this instance depends on provider_*. */ 2500 int match = 0; 2501 2502 (void) walk_dependencies(wip, check_against_provider, &match); 2503 2504 if (match) 2505 return (list_instance(unused, wip)); 2506 2507 return (0); 2508 } 2509 2510 /*ARGSUSED*/ 2511 static int 2512 list_dependents(void *unused, scf_walkinfo_t *wip) 2513 { 2514 char *save; 2515 int ret; 2516 2517 if (scf_scope_get_name(wip->scope, provider_scope, 2518 max_scf_fmri_length) <= 0 || 2519 scf_service_get_name(wip->svc, provider_svc, 2520 max_scf_fmri_length) <= 0) 2521 scfdie(); 2522 2523 save = provider_inst; 2524 if (wip->inst == NULL) 2525 provider_inst = NULL; 2526 else if (scf_instance_get_name(wip->inst, provider_inst, 2527 max_scf_fmri_length) <= 0) 2528 scfdie(); 2529 2530 ret = scf_walk_fmri(h, 0, NULL, 0, list_if_dependent, NULL, NULL, 2531 uu_warn); 2532 2533 provider_inst = save; 2534 2535 return (ret); 2536 } 2537 2538 /* 2539 * main() & helpers 2540 */ 2541 2542 static void 2543 add_sort_column(const char *col, int reverse) 2544 { 2545 int i; 2546 2547 ++opt_snum; 2548 2549 opt_sort = realloc(opt_sort, opt_snum * sizeof (*opt_sort)); 2550 if (opt_sort == NULL) 2551 uu_die(gettext("Too many sort criteria: out of memory.\n")); 2552 2553 for (i = 0; i < ncolumns; ++i) { 2554 if (strcasecmp(col, columns[i].name) == 0) 2555 break; 2556 } 2557 2558 if (i < ncolumns) 2559 opt_sort[opt_snum - 1] = (reverse ? i | 0x100 : i); 2560 else 2561 uu_die(gettext("Unrecognized sort column \"%s\".\n"), col); 2562 2563 sortkey_sz += columns[i].sortkey_width; 2564 } 2565 2566 static void 2567 add_restarter(const char *fmri) 2568 { 2569 char *cfmri; 2570 const char *pg_name; 2571 struct pfmri_list *rest; 2572 2573 cfmri = safe_strdup(fmri); 2574 rest = safe_malloc(sizeof (*rest)); 2575 2576 if (scf_parse_svc_fmri(cfmri, &rest->scope, &rest->service, 2577 &rest->instance, &pg_name, NULL) != SCF_SUCCESS) 2578 uu_die(gettext("Restarter FMRI \"%s\" is invalid.\n"), fmri); 2579 2580 if (rest->instance == NULL || pg_name != NULL) 2581 uu_die(gettext("Restarter FMRI \"%s\" does not designate an " 2582 "instance.\n"), fmri); 2583 2584 rest->next = restarters; 2585 restarters = rest; 2586 return; 2587 2588 err: 2589 free(cfmri); 2590 free(rest); 2591 } 2592 2593 /* ARGSUSED */ 2594 static int 2595 line_cmp(const void *l_arg, const void *r_arg, void *private) 2596 { 2597 const struct avl_string *l = l_arg; 2598 const struct avl_string *r = r_arg; 2599 2600 return (memcmp(l->key, r->key, sortkey_sz)); 2601 } 2602 2603 /* ARGSUSED */ 2604 static int 2605 print_line(void *e, void *private) 2606 { 2607 struct avl_string *lp = e; 2608 2609 (void) puts(lp->str); 2610 2611 return (UU_WALK_NEXT); 2612 } 2613 2614 int 2615 main(int argc, char **argv) 2616 { 2617 char opt, opt_mode; 2618 int i, n; 2619 char *columns_str = NULL; 2620 char *cp; 2621 const char *progname; 2622 int err; 2623 2624 int show_all = 0; 2625 int show_header = 1; 2626 2627 const char * const options = "aHpvo:R:s:S:dDl?x"; 2628 2629 (void) setlocale(LC_ALL, ""); 2630 2631 locale = setlocale(LC_MESSAGES, ""); 2632 if (locale) { 2633 locale = safe_strdup(locale); 2634 sanitize_locale(locale); 2635 } 2636 2637 (void) textdomain(TEXT_DOMAIN); 2638 progname = uu_setpname(argv[0]); 2639 2640 exit_status = UU_EXIT_OK; 2641 2642 max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH); 2643 max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 2644 max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH); 2645 2646 if (max_scf_name_length == -1 || max_scf_value_length == -1 || 2647 max_scf_fmri_length == -1) 2648 scfdie(); 2649 2650 now = time(NULL); 2651 assert(now != -1); 2652 2653 /* 2654 * opt_mode is the mode of operation. 0 for plain, 'd' for 2655 * dependencies, 'D' for dependents, and 'l' for detailed (long). We 2656 * need to know now so we know which options are valid. 2657 */ 2658 opt_mode = 0; 2659 while ((opt = getopt(argc, argv, options)) != -1) { 2660 switch (opt) { 2661 case '?': 2662 if (optopt == '?') { 2663 print_help(progname); 2664 return (UU_EXIT_OK); 2665 } else { 2666 argserr(progname); 2667 /* NOTREACHED */ 2668 } 2669 2670 case 'd': 2671 case 'D': 2672 case 'l': 2673 if (opt_mode != 0) 2674 argserr(progname); 2675 2676 opt_mode = opt; 2677 break; 2678 2679 case 'x': 2680 if (opt_mode != 0) 2681 argserr(progname); 2682 2683 opt_mode = opt; 2684 break; 2685 2686 default: 2687 break; 2688 } 2689 } 2690 2691 sortkey_sz = 0; 2692 2693 optind = 1; /* Reset getopt() */ 2694 while ((opt = getopt(argc, argv, options)) != -1) { 2695 switch (opt) { 2696 case 'a': 2697 if (opt_mode != 0) 2698 argserr(progname); 2699 show_all = 1; 2700 break; 2701 2702 case 'H': 2703 if (opt_mode == 'l' || opt_mode == 'x') 2704 argserr(progname); 2705 show_header = 0; 2706 break; 2707 2708 case 'p': 2709 if (opt_mode == 'x') 2710 argserr(progname); 2711 opt_processes = 1; 2712 break; 2713 2714 case 'v': 2715 if (opt_mode == 'l') 2716 argserr(progname); 2717 opt_verbose = 1; 2718 break; 2719 2720 case 'o': 2721 if (opt_mode == 'l' || opt_mode == 'x') 2722 argserr(progname); 2723 columns_str = optarg; 2724 break; 2725 2726 case 'R': 2727 if (opt_mode != 0 || opt_mode == 'x') 2728 argserr(progname); 2729 2730 add_restarter(optarg); 2731 break; 2732 2733 case 's': 2734 case 'S': 2735 if (opt_mode != 0) 2736 argserr(progname); 2737 2738 add_sort_column(optarg, optopt == 'S'); 2739 break; 2740 2741 case 'd': 2742 case 'D': 2743 case 'l': 2744 case 'x': 2745 assert(opt_mode == optopt); 2746 break; 2747 2748 case '?': 2749 argserr(progname); 2750 /* NOTREACHED */ 2751 2752 default: 2753 assert(0); 2754 abort(); 2755 } 2756 } 2757 2758 /* 2759 * -a is only meaningful when given no arguments 2760 */ 2761 if (show_all && optind != argc) 2762 uu_warn(gettext("-a ignored when used with arguments.\n")); 2763 2764 h = scf_handle_create(SCF_VERSION); 2765 if (h == NULL) 2766 scfdie(); 2767 2768 if (scf_handle_bind(h) == -1) 2769 uu_die(gettext("Could not bind to repository server: %s. " 2770 "Exiting.\n"), scf_strerror(scf_error())); 2771 2772 if ((g_pg = scf_pg_create(h)) == NULL || 2773 (g_prop = scf_property_create(h)) == NULL || 2774 (g_val = scf_value_create(h)) == NULL) 2775 scfdie(); 2776 2777 argc -= optind; 2778 argv += optind; 2779 2780 /* 2781 * If we're in long mode, take care of it now before we deal with the 2782 * sorting and the columns, since we won't use them anyway. 2783 */ 2784 if (opt_mode == 'l') { 2785 if (argc == 0) 2786 argserr(progname); 2787 2788 if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE, 2789 print_detailed, NULL, &exit_status, uu_warn)) != 0) { 2790 uu_warn(gettext("failed to iterate over " 2791 "instances: %s\n"), scf_strerror(err)); 2792 exit_status = UU_EXIT_FATAL; 2793 } 2794 2795 return (exit_status); 2796 } 2797 2798 if (opt_mode == 'x') { 2799 explain(opt_verbose, argc, argv); 2800 2801 return (exit_status); 2802 } 2803 2804 2805 if (opt_snum == 0) { 2806 /* Default sort. */ 2807 add_sort_column("state", 0); 2808 add_sort_column("stime", 0); 2809 add_sort_column("fmri", 0); 2810 } 2811 2812 if (columns_str == NULL) { 2813 if (!opt_verbose) 2814 columns_str = safe_strdup("state,stime,fmri"); 2815 else 2816 columns_str = 2817 safe_strdup("state,nstate,stime,ctid,fmri"); 2818 } 2819 2820 /* Decode columns_str into opt_columns. */ 2821 line_sz = 0; 2822 2823 opt_cnum = 1; 2824 for (cp = columns_str; *cp != '\0'; ++cp) 2825 if (*cp == ',') 2826 ++opt_cnum; 2827 2828 opt_columns = malloc(opt_cnum * sizeof (*opt_columns)); 2829 if (opt_columns == NULL) 2830 uu_die(gettext("Too many columns.\n")); 2831 2832 for (n = 0; *columns_str != '\0'; ++n) { 2833 i = getcolumnopt(&columns_str); 2834 if (i == -1) 2835 uu_die(gettext("Unknown column \"%s\".\n"), 2836 columns_str); 2837 2838 if (strcmp(columns[i].name, "N") == 0 || 2839 strcmp(columns[i].name, "SN") == 0 || 2840 strcmp(columns[i].name, "NSTA") == 0 || 2841 strcmp(columns[i].name, "NSTATE") == 0) 2842 opt_nstate_shown = 1; 2843 2844 opt_columns[n] = i; 2845 line_sz += columns[i].width + 1; 2846 } 2847 2848 2849 if ((lines_pool = uu_avl_pool_create("lines_pool", 2850 sizeof (struct avl_string), offsetof(struct avl_string, node), 2851 line_cmp, UU_AVL_DEBUG)) == NULL || 2852 (lines = uu_avl_create(lines_pool, NULL, 0)) == NULL) 2853 uu_die(gettext("Unexpected libuutil error: %s. Exiting.\n"), 2854 uu_strerror(uu_error())); 2855 2856 switch (opt_mode) { 2857 case 0: 2858 ht_init(); 2859 2860 /* Always show all FMRIs when given arguments or restarters */ 2861 if (argc != 0 || restarters != NULL) 2862 show_all = 1; 2863 2864 if ((err = scf_walk_fmri(h, argc, argv, 2865 SCF_WALK_MULTIPLE | SCF_WALK_LEGACY, 2866 show_all ? list_instance : list_if_enabled, NULL, 2867 &exit_status, uu_warn)) != 0) { 2868 uu_warn(gettext("failed to iterate over " 2869 "instances: %s\n"), scf_strerror(err)); 2870 exit_status = UU_EXIT_FATAL; 2871 } 2872 break; 2873 2874 case 'd': 2875 if (argc == 0) 2876 argserr(progname); 2877 2878 if ((err = scf_walk_fmri(h, argc, argv, 2879 SCF_WALK_MULTIPLE, list_dependencies, NULL, 2880 &exit_status, uu_warn)) != 0) { 2881 uu_warn(gettext("failed to iterate over " 2882 "instances: %s\n"), scf_strerror(err)); 2883 exit_status = UU_EXIT_FATAL; 2884 } 2885 break; 2886 2887 case 'D': 2888 if (argc == 0) 2889 argserr(progname); 2890 2891 provider_scope = safe_malloc(max_scf_fmri_length); 2892 provider_svc = safe_malloc(max_scf_fmri_length); 2893 provider_inst = safe_malloc(max_scf_fmri_length); 2894 2895 if ((err = scf_walk_fmri(h, argc, argv, 2896 SCF_WALK_MULTIPLE | SCF_WALK_SERVICE, 2897 list_dependents, NULL, &exit_status, uu_warn)) != 0) { 2898 uu_warn(gettext("failed to iterate over " 2899 "instances: %s\n"), scf_strerror(err)); 2900 exit_status = UU_EXIT_FATAL; 2901 } 2902 2903 free(provider_scope); 2904 free(provider_svc); 2905 free(provider_inst); 2906 break; 2907 2908 default: 2909 assert(0); 2910 abort(); 2911 } 2912 2913 if (show_header) 2914 print_header(); 2915 2916 (void) uu_avl_walk(lines, print_line, NULL, 0); 2917 2918 return (exit_status); 2919 } 2920