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