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