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