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