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