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