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