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