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 /* 1601 * sprint_stime() will allocate a new buffer and snprintf the services's 1602 * state timestamp. If the timestamp is unavailable for some reason 1603 * a '-' is given instead. 1604 */ 1605 static void 1606 sprint_stime(char **buf, scf_walkinfo_t *wip) 1607 { 1608 int r; 1609 struct timeval tv; 1610 time_t then; 1611 struct tm *tm; 1612 char st_buf[STIME_COLUMN_WIDTH + 1]; 1613 size_t newsize = (*buf ? strlen(*buf) : 0) + STIME_COLUMN_WIDTH + 2; 1614 char *newbuf = safe_malloc(newsize); 1615 1616 if (wip->pg == NULL) { 1617 r = get_restarter_time_prop(wip->inst, 1618 SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0); 1619 } else { 1620 r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP, 1621 SCF_TYPE_TIME, &tv, NULL, 0); 1622 } 1623 1624 if (r != 0) { 1625 /* 1626 * There's something amiss with our service 1627 * so we'll print a '-' for STIME. 1628 */ 1629 (void) snprintf(newbuf, newsize, "%s%-*s", *buf ? *buf : "", 1630 STIME_COLUMN_WIDTH + 1, "-"); 1631 } else { 1632 /* tv should be valid so we'll format it */ 1633 then = (time_t)tv.tv_sec; 1634 1635 tm = localtime(&then); 1636 /* 1637 * Print time if started within the past 24 hours, print date 1638 * if within the past 12 months or, finally, print year if 1639 * started greater than 12 months ago. 1640 */ 1641 if (now - then < 24 * 60 * 60) { 1642 (void) strftime(st_buf, sizeof (st_buf), 1643 gettext(FORMAT_TIME), tm); 1644 } else if (now - then < 12 * 30 * 24 * 60 * 60) { 1645 (void) strftime(st_buf, sizeof (st_buf), 1646 gettext(FORMAT_DATE), tm); 1647 } else { 1648 (void) strftime(st_buf, sizeof (st_buf), 1649 gettext(FORMAT_YEAR), tm); 1650 } 1651 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1652 STIME_COLUMN_WIDTH + 1, st_buf); 1653 } 1654 if (*buf) 1655 free(*buf); 1656 *buf = newbuf; 1657 } 1658 1659 #define STIME_SORTKEY_WIDTH (sizeof (uint64_t) + sizeof (uint32_t)) 1660 1661 /* ARGSUSED */ 1662 static void 1663 sortkey_stime(char *buf, int reverse, scf_walkinfo_t *wip) 1664 { 1665 struct timeval tv; 1666 int r; 1667 1668 if (wip->pg == NULL) 1669 r = get_restarter_time_prop(wip->inst, 1670 SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0); 1671 else 1672 r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP, 1673 SCF_TYPE_TIME, &tv, NULL, 0); 1674 1675 if (r == 0) { 1676 int64_t sec; 1677 int32_t us; 1678 1679 /* Stick it straight into the buffer. */ 1680 sec = tv.tv_sec; 1681 us = tv.tv_usec; 1682 1683 sec = BE_64(sec); 1684 us = BE_32(us); 1685 bcopy(&sec, buf, sizeof (sec)); 1686 bcopy(&us, buf + sizeof (sec), sizeof (us)); 1687 } else { 1688 bzero(buf, STIME_SORTKEY_WIDTH); 1689 } 1690 1691 if (reverse) 1692 reverse_bytes(buf, STIME_SORTKEY_WIDTH); 1693 } 1694 1695 1696 /* 1697 * Information about columns which can be displayed. If you add something, 1698 * check MAX_COLUMN_NAME_LENGTH_STR & update description_of_column() below. 1699 */ 1700 static const struct column columns[] = { 1701 { "CTID", CTID_COLUMN_WIDTH, sprint_ctid, 1702 CTID_SORTKEY_WIDTH, sortkey_ctid }, 1703 { "DESC", DESC_COLUMN_WIDTH, sprint_desc, 1704 DESC_COLUMN_WIDTH, sortkey_desc }, 1705 { "FMRI", FMRI_COLUMN_WIDTH, sprint_fmri, 1706 FMRI_COLUMN_WIDTH, sortkey_fmri }, 1707 { "INST", COMPONENT_COLUMN_WIDTH, sprint_instance, 1708 COMPONENT_COLUMN_WIDTH, sortkey_instance }, 1709 { "N", 1, sprint_n, 1, sortkey_nstate }, 1710 { "NSTA", 4, sprint_nsta, 1, sortkey_nstate }, 1711 { "NSTATE", MAX_SCF_STATE_STRING_SZ - 1, sprint_nstate, 1712 1, sortkey_nstate }, 1713 { "S", 2, sprint_s, 1, sortkey_state }, 1714 { "SCOPE", COMPONENT_COLUMN_WIDTH, sprint_scope, 1715 COMPONENT_COLUMN_WIDTH, sortkey_scope }, 1716 { "SN", 2, sprint_sn, 2, sortkey_sn }, 1717 { "SVC", COMPONENT_COLUMN_WIDTH, sprint_service, 1718 COMPONENT_COLUMN_WIDTH, sortkey_service }, 1719 { "STA", 4, sprint_sta, 1, sortkey_state }, 1720 { "STATE", MAX_SCF_STATE_STRING_SZ - 1 + 1, sprint_state, 1721 1, sortkey_state }, 1722 { "STIME", STIME_COLUMN_WIDTH, sprint_stime, 1723 STIME_SORTKEY_WIDTH, sortkey_stime }, 1724 }; 1725 1726 #define MAX_COLUMN_NAME_LENGTH_STR "6" 1727 1728 static const int ncolumns = sizeof (columns) / sizeof (columns[0]); 1729 1730 /* 1731 * Necessary thanks to gettext() & xgettext. 1732 */ 1733 static const char * 1734 description_of_column(int c) 1735 { 1736 const char *s = NULL; 1737 1738 switch (c) { 1739 case 0: 1740 s = gettext("contract ID for service (see contract(4))"); 1741 break; 1742 case 1: 1743 s = gettext("human-readable description of the service"); 1744 break; 1745 case 2: 1746 s = gettext("Fault Managed Resource Identifier for service"); 1747 break; 1748 case 3: 1749 s = gettext("portion of the FMRI indicating service instance"); 1750 break; 1751 case 4: 1752 s = gettext("abbreviation for next state (if in transition)"); 1753 break; 1754 case 5: 1755 s = gettext("abbreviation for next state (if in transition)"); 1756 break; 1757 case 6: 1758 s = gettext("name for next state (if in transition)"); 1759 break; 1760 case 7: 1761 s = gettext("abbreviation for current state"); 1762 break; 1763 case 8: 1764 s = gettext("name for scope associated with service"); 1765 break; 1766 case 9: 1767 s = gettext("abbreviation for current state and next state"); 1768 break; 1769 case 10: 1770 s = gettext("portion of the FMRI representing service name"); 1771 break; 1772 case 11: 1773 s = gettext("abbreviation for current state"); 1774 break; 1775 case 12: 1776 s = gettext("name for current state"); 1777 break; 1778 case 13: 1779 s = gettext("time of last state change"); 1780 break; 1781 } 1782 1783 assert(s != NULL); 1784 return (s); 1785 } 1786 1787 1788 static void 1789 print_usage(const char *progname, FILE *f, boolean_t do_exit) 1790 { 1791 (void) fprintf(f, gettext( 1792 "Usage: %1$s [-aHpv] [-o col[,col ... ]] [-R restarter] " 1793 "[-sS col] [<service> ...]\n" 1794 " %1$s -d | -D [-Hpv] [-o col[,col ... ]] [-sS col] " 1795 "[<service> ...]\n" 1796 " %1$s -l <service> ...\n" 1797 " %1$s -x [-v] [<service> ...]\n" 1798 " %1$s -?\n"), progname); 1799 1800 if (do_exit) 1801 exit(UU_EXIT_USAGE); 1802 } 1803 1804 #define argserr(progname) print_usage(progname, stderr, B_TRUE) 1805 1806 static void 1807 print_help(const char *progname) 1808 { 1809 int i; 1810 1811 print_usage(progname, stdout, B_FALSE); 1812 1813 (void) printf(gettext("\n" 1814 "\t-a list all service instances rather than " 1815 "only those that are enabled\n" 1816 "\t-d list dependencies of the specified service(s)\n" 1817 "\t-D list dependents of the specified service(s)\n" 1818 "\t-H omit header line from output\n" 1819 "\t-l list detailed information about the specified service(s)\n" 1820 "\t-o list only the specified columns in the output\n" 1821 "\t-p list process IDs and names associated with each service\n" 1822 "\t-R list only those services with the specified restarter\n" 1823 "\t-s sort output in ascending order by the specified column(s)\n" 1824 "\t-S sort output in descending order by the specified column(s)\n" 1825 "\t-v list verbose information appropriate to the type of output\n" 1826 "\t-x explain the status of services that might require maintenance,\n" 1827 "\t or explain the status of the specified service(s)\n" 1828 "\n\t" 1829 "Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n" 1830 "\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n" 1831 "\n" 1832 "\t%1$s [opts] svc:/network/smtp:sendmail\n" 1833 "\t%1$s [opts] network/smtp:sendmail\n" 1834 "\t%1$s [opts] network/*mail\n" 1835 "\t%1$s [opts] network/smtp\n" 1836 "\t%1$s [opts] smtp:sendmail\n" 1837 "\t%1$s [opts] smtp\n" 1838 "\t%1$s [opts] sendmail\n" 1839 "\n\t" 1840 "Columns for output or sorting can be specified using these names:\n" 1841 "\n"), progname); 1842 1843 for (i = 0; i < ncolumns; i++) { 1844 (void) printf("\t%-" MAX_COLUMN_NAME_LENGTH_STR "s %s\n", 1845 columns[i].name, description_of_column(i)); 1846 } 1847 } 1848 1849 1850 /* 1851 * A getsubopt()-like function which returns an index into the columns table. 1852 * On success, *optionp is set to point to the next sub-option, or the 1853 * terminating null if there are none. 1854 */ 1855 static int 1856 getcolumnopt(char **optionp) 1857 { 1858 char *str = *optionp, *cp; 1859 int i; 1860 1861 assert(optionp != NULL); 1862 assert(*optionp != NULL); 1863 1864 cp = strchr(*optionp, ','); 1865 if (cp != NULL) 1866 *cp = '\0'; 1867 1868 for (i = 0; i < ncolumns; ++i) { 1869 if (strcasecmp(str, columns[i].name) == 0) { 1870 if (cp != NULL) 1871 *optionp = cp + 1; 1872 else 1873 *optionp = strchr(*optionp, '\0'); 1874 1875 return (i); 1876 } 1877 } 1878 1879 return (-1); 1880 } 1881 1882 static void 1883 print_header() 1884 { 1885 int i; 1886 char *line_buf, *cp; 1887 1888 line_buf = safe_malloc(line_sz); 1889 cp = line_buf; 1890 for (i = 0; i < opt_cnum; ++i) { 1891 const struct column * const colp = &columns[opt_columns[i]]; 1892 1893 (void) snprintf(cp, colp->width + 1, "%-*s", colp->width, 1894 colp->name); 1895 cp += colp->width; 1896 *cp++ = ' '; 1897 } 1898 1899 /* Trim the trailing whitespace */ 1900 --cp; 1901 while (*cp == ' ') 1902 --cp; 1903 *(cp+1) = '\0'; 1904 (void) puts(line_buf); 1905 1906 free(line_buf); 1907 } 1908 1909 1910 1911 /* 1912 * Long listing (-l) functions. 1913 */ 1914 1915 static int 1916 pidcmp(const void *l, const void *r) 1917 { 1918 pid_t lp = *(pid_t *)l, rp = *(pid_t *)r; 1919 1920 if (lp < rp) 1921 return (-1); 1922 if (lp > rp) 1923 return (1); 1924 return (0); 1925 } 1926 1927 /* 1928 * This is the strlen() of the longest label ("description"), plus intercolumn 1929 * space. 1930 */ 1931 #define DETAILED_WIDTH (11 + 2) 1932 1933 /* 1934 * Callback routine to print header for contract id. 1935 * Called by ctids_by_restarter and print_detailed. 1936 */ 1937 static void 1938 print_ctid_header() 1939 { 1940 (void) printf("%-*s", DETAILED_WIDTH, "contract_id"); 1941 } 1942 1943 /* 1944 * Callback routine to print a contract id. 1945 * Called by ctids_by_restarter and print_detailed. 1946 */ 1947 static void 1948 print_ctid_detailed(uint64_t c) 1949 { 1950 (void) printf("%lu ", (ctid_t)c); 1951 } 1952 1953 static void 1954 detailed_list_processes(scf_walkinfo_t *wip) 1955 { 1956 uint64_t c; 1957 pid_t *pids; 1958 uint_t i, n; 1959 psinfo_t psi; 1960 1961 if (get_restarter_count_prop(wip->inst, scf_property_contract, &c, 1962 EMPTY_OK) != 0) 1963 return; 1964 1965 if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0) 1966 return; 1967 1968 qsort(pids, n, sizeof (*pids), pidcmp); 1969 1970 for (i = 0; i < n; ++i) { 1971 (void) printf("%-*s%lu", DETAILED_WIDTH, gettext("process"), 1972 pids[i]); 1973 1974 if (get_psinfo(pids[i], &psi) == 0) 1975 (void) printf(" %.*s", PRARGSZ, psi.pr_psargs); 1976 1977 (void) putchar('\n'); 1978 } 1979 1980 free(pids); 1981 } 1982 1983 /* 1984 * Determines the state of a dependency. If the FMRI specifies a file, then we 1985 * fake up a state based on whether we can access the file. 1986 */ 1987 static void 1988 get_fmri_state(char *fmri, char *state, size_t state_sz) 1989 { 1990 char *lfmri; 1991 const char *svc_name, *inst_name, *pg_name, *path; 1992 scf_service_t *svc; 1993 scf_instance_t *inst; 1994 scf_iter_t *iter; 1995 1996 lfmri = safe_strdup(fmri); 1997 1998 /* 1999 * Check for file:// dependencies 2000 */ 2001 if (scf_parse_file_fmri(lfmri, NULL, &path) == SCF_SUCCESS) { 2002 struct stat64 statbuf; 2003 const char *msg; 2004 2005 if (stat64(path, &statbuf) == 0) 2006 msg = "online"; 2007 else if (errno == ENOENT) 2008 msg = "absent"; 2009 else 2010 msg = "unknown"; 2011 2012 (void) strlcpy(state, msg, state_sz); 2013 return; 2014 } 2015 2016 /* 2017 * scf_parse_file_fmri() may have overwritten part of the string, so 2018 * copy it back. 2019 */ 2020 (void) strcpy(lfmri, fmri); 2021 2022 if (scf_parse_svc_fmri(lfmri, NULL, &svc_name, &inst_name, 2023 &pg_name, NULL) != SCF_SUCCESS) { 2024 free(lfmri); 2025 (void) strlcpy(state, "invalid", state_sz); 2026 return; 2027 } 2028 2029 free(lfmri); 2030 2031 if (svc_name == NULL || pg_name != NULL) { 2032 (void) strlcpy(state, "invalid", state_sz); 2033 return; 2034 } 2035 2036 if (inst_name != NULL) { 2037 /* instance: get state */ 2038 inst = scf_instance_create(h); 2039 if (inst == NULL) 2040 scfdie(); 2041 2042 if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL, 2043 NULL, SCF_DECODE_FMRI_EXACT) == SCF_SUCCESS) 2044 get_restarter_string_prop(inst, scf_property_state, 2045 state, state_sz); 2046 else { 2047 switch (scf_error()) { 2048 case SCF_ERROR_INVALID_ARGUMENT: 2049 (void) strlcpy(state, "invalid", state_sz); 2050 break; 2051 case SCF_ERROR_NOT_FOUND: 2052 (void) strlcpy(state, "absent", state_sz); 2053 break; 2054 2055 default: 2056 scfdie(); 2057 } 2058 } 2059 2060 scf_instance_destroy(inst); 2061 return; 2062 } 2063 2064 /* 2065 * service: If only one instance, use that state. Otherwise, say 2066 * "multiple". 2067 */ 2068 if ((svc = scf_service_create(h)) == NULL || 2069 (inst = scf_instance_create(h)) == NULL || 2070 (iter = scf_iter_create(h)) == NULL) 2071 scfdie(); 2072 2073 if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL, 2074 SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) { 2075 switch (scf_error()) { 2076 case SCF_ERROR_INVALID_ARGUMENT: 2077 (void) strlcpy(state, "invalid", state_sz); 2078 goto out; 2079 case SCF_ERROR_NOT_FOUND: 2080 (void) strlcpy(state, "absent", state_sz); 2081 goto out; 2082 2083 default: 2084 scfdie(); 2085 } 2086 } 2087 2088 if (scf_iter_service_instances(iter, svc) != SCF_SUCCESS) 2089 scfdie(); 2090 2091 switch (scf_iter_next_instance(iter, inst)) { 2092 case 0: 2093 (void) strlcpy(state, "absent", state_sz); 2094 goto out; 2095 2096 case 1: 2097 break; 2098 2099 default: 2100 scfdie(); 2101 } 2102 2103 /* Get the state in case this is the only instance. */ 2104 get_restarter_string_prop(inst, scf_property_state, state, state_sz); 2105 2106 switch (scf_iter_next_instance(iter, inst)) { 2107 case 0: 2108 break; 2109 2110 case 1: 2111 /* Nope, multiple instances. */ 2112 (void) strlcpy(state, "multiple", state_sz); 2113 goto out; 2114 2115 default: 2116 scfdie(); 2117 } 2118 2119 out: 2120 scf_iter_destroy(iter); 2121 scf_instance_destroy(inst); 2122 scf_service_destroy(svc); 2123 } 2124 2125 static void 2126 print_detailed_dependency(scf_propertygroup_t *pg) 2127 { 2128 scf_property_t *eprop; 2129 scf_iter_t *iter; 2130 scf_type_t ty; 2131 char *val_buf; 2132 int i; 2133 2134 if ((eprop = scf_property_create(h)) == NULL || 2135 (iter = scf_iter_create(h)) == NULL) 2136 scfdie(); 2137 2138 val_buf = safe_malloc(max_scf_value_length + 1); 2139 2140 if (scf_pg_get_property(pg, SCF_PROPERTY_ENTITIES, eprop) != 2141 SCF_SUCCESS || 2142 scf_property_type(eprop, &ty) != SCF_SUCCESS || 2143 ty != SCF_TYPE_FMRI) 2144 return; 2145 2146 (void) printf("%-*s", DETAILED_WIDTH, gettext("dependency")); 2147 2148 /* Print the grouping */ 2149 if (pg_get_single_val(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING, 2150 val_buf, max_scf_value_length + 1, 0) == 0) 2151 (void) fputs(val_buf, stdout); 2152 else 2153 (void) putchar('?'); 2154 2155 (void) putchar('/'); 2156 2157 if (pg_get_single_val(pg, SCF_PROPERTY_RESTART_ON, SCF_TYPE_ASTRING, 2158 val_buf, max_scf_value_length + 1, 0) == 0) 2159 (void) fputs(val_buf, stdout); 2160 else 2161 (void) putchar('?'); 2162 2163 /* Print the dependency entities. */ 2164 if (scf_iter_property_values(iter, eprop) == -1) 2165 scfdie(); 2166 2167 while ((i = scf_iter_next_value(iter, g_val)) == 1) { 2168 char state[MAX_SCF_STATE_STRING_SZ]; 2169 2170 if (scf_value_get_astring(g_val, val_buf, 2171 max_scf_value_length + 1) < 0) 2172 scfdie(); 2173 2174 (void) putchar(' '); 2175 (void) fputs(val_buf, stdout); 2176 2177 /* Print the state. */ 2178 state[0] = '-'; 2179 state[1] = '\0'; 2180 2181 get_fmri_state(val_buf, state, sizeof (state)); 2182 2183 (void) printf(" (%s)", state); 2184 } 2185 if (i == -1) 2186 scfdie(); 2187 2188 (void) putchar('\n'); 2189 2190 free(val_buf); 2191 scf_iter_destroy(iter); 2192 scf_property_destroy(eprop); 2193 } 2194 2195 /* ARGSUSED */ 2196 static int 2197 print_detailed(void *unused, scf_walkinfo_t *wip) 2198 { 2199 scf_snapshot_t *snap; 2200 scf_propertygroup_t *rpg; 2201 scf_iter_t *pg_iter; 2202 2203 char *buf; 2204 char *timebuf; 2205 size_t tbsz; 2206 int ret; 2207 uint64_t c; 2208 int temp, perm; 2209 struct timeval tv; 2210 time_t stime; 2211 struct tm *tmp; 2212 int restarter_spec; 2213 int restarter_ret; 2214 2215 const char * const fmt = "%-*s%s\n"; 2216 2217 assert(wip->pg == NULL); 2218 2219 rpg = scf_pg_create(h); 2220 if (rpg == NULL) 2221 scfdie(); 2222 2223 if (first_paragraph) 2224 first_paragraph = 0; 2225 else 2226 (void) putchar('\n'); 2227 2228 buf = safe_malloc(max_scf_fmri_length + 1); 2229 2230 if (scf_instance_to_fmri(wip->inst, buf, max_scf_fmri_length + 1) != -1) 2231 (void) printf(fmt, DETAILED_WIDTH, "fmri", buf); 2232 2233 if (common_name_buf == NULL) 2234 common_name_buf = safe_malloc(max_scf_value_length + 1); 2235 2236 if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale, 2237 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1) 2238 == 0) 2239 (void) printf(fmt, DETAILED_WIDTH, gettext("name"), 2240 common_name_buf); 2241 else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C", 2242 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1) 2243 == 0) 2244 (void) printf(fmt, DETAILED_WIDTH, gettext("name"), 2245 common_name_buf); 2246 2247 /* 2248 * Synthesize an 'enabled' property that hides the enabled_ovr 2249 * implementation from the user. If the service has been temporarily 2250 * set to a state other than its permanent value, alert the user with 2251 * a '(temporary)' message. 2252 */ 2253 perm = instance_enabled(wip->inst, B_FALSE); 2254 temp = instance_enabled(wip->inst, B_TRUE); 2255 if (temp != -1) { 2256 if (temp != perm) 2257 (void) printf(gettext("%-*s%s (temporary)\n"), 2258 DETAILED_WIDTH, gettext("enabled"), 2259 temp ? gettext("true") : gettext("false")); 2260 else 2261 (void) printf(fmt, DETAILED_WIDTH, 2262 gettext("enabled"), temp ? gettext("true") : 2263 gettext("false")); 2264 } else if (perm != -1) { 2265 (void) printf(fmt, DETAILED_WIDTH, gettext("enabled"), 2266 perm ? gettext("true") : gettext("false")); 2267 } 2268 2269 /* 2270 * Property values may be longer than max_scf_fmri_length, but these 2271 * shouldn't be, so we'll just reuse buf. The user can use svcprop if 2272 * he suspects something fishy. 2273 */ 2274 if (scf_instance_get_pg(wip->inst, SCF_PG_RESTARTER, rpg) != 0) { 2275 if (scf_error() != SCF_ERROR_NOT_FOUND) 2276 scfdie(); 2277 2278 scf_pg_destroy(rpg); 2279 rpg = NULL; 2280 } 2281 2282 if (rpg) { 2283 if (pg_get_single_val(rpg, scf_property_state, SCF_TYPE_ASTRING, 2284 buf, max_scf_fmri_length + 1, 0) == 0) 2285 (void) printf(fmt, DETAILED_WIDTH, gettext("state"), 2286 buf); 2287 2288 if (pg_get_single_val(rpg, scf_property_next_state, 2289 SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0) 2290 (void) printf(fmt, DETAILED_WIDTH, 2291 gettext("next_state"), buf); 2292 2293 if (pg_get_single_val(rpg, SCF_PROPERTY_STATE_TIMESTAMP, 2294 SCF_TYPE_TIME, &tv, NULL, 0) == 0) { 2295 stime = tv.tv_sec; 2296 tmp = localtime(&stime); 2297 for (tbsz = 50; ; tbsz *= 2) { 2298 timebuf = safe_malloc(tbsz); 2299 if (strftime(timebuf, tbsz, NULL, tmp) != 0) 2300 break; 2301 free(timebuf); 2302 } 2303 (void) printf(fmt, DETAILED_WIDTH, 2304 gettext("state_time"), 2305 timebuf); 2306 free(timebuf); 2307 } 2308 2309 if (pg_get_single_val(rpg, SCF_PROPERTY_ALT_LOGFILE, 2310 SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0) 2311 (void) printf(fmt, DETAILED_WIDTH, 2312 gettext("alt_logfile"), buf); 2313 2314 if (pg_get_single_val(rpg, SCF_PROPERTY_LOGFILE, 2315 SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0) 2316 (void) printf(fmt, DETAILED_WIDTH, gettext("logfile"), 2317 buf); 2318 } 2319 2320 if (inst_get_single_val(wip->inst, SCF_PG_GENERAL, 2321 SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, buf, 2322 max_scf_fmri_length + 1, 0, 0, 1) == 0) 2323 (void) printf(fmt, DETAILED_WIDTH, gettext("restarter"), buf); 2324 else 2325 (void) printf(fmt, DETAILED_WIDTH, gettext("restarter"), 2326 SCF_SERVICE_STARTD); 2327 2328 free(buf); 2329 2330 /* 2331 * Use the restarter specific routine to print the ctids, if available. 2332 * If restarter specific action is available and it fails, then die. 2333 */ 2334 restarter_ret = ctids_by_restarter(wip, &c, 1, 0, 2335 &restarter_spec, print_ctid_header, print_ctid_detailed); 2336 if (restarter_spec == 1) { 2337 if (restarter_ret != 0) 2338 uu_die(gettext("Unable to get restarter for %s"), 2339 wip->fmri); 2340 goto restarter_common; 2341 } 2342 2343 if (rpg) { 2344 scf_iter_t *iter; 2345 2346 if ((iter = scf_iter_create(h)) == NULL) 2347 scfdie(); 2348 2349 if (scf_pg_get_property(rpg, scf_property_contract, g_prop) == 2350 0) { 2351 if (scf_property_is_type(g_prop, SCF_TYPE_COUNT) == 0) { 2352 2353 /* Callback to print ctid header */ 2354 print_ctid_header(); 2355 2356 if (scf_iter_property_values(iter, g_prop) != 0) 2357 scfdie(); 2358 2359 for (;;) { 2360 ret = scf_iter_next_value(iter, g_val); 2361 if (ret == -1) 2362 scfdie(); 2363 if (ret == 0) 2364 break; 2365 2366 if (scf_value_get_count(g_val, &c) != 0) 2367 scfdie(); 2368 2369 /* Callback to print contract id. */ 2370 print_ctid_detailed(c); 2371 } 2372 2373 (void) putchar('\n'); 2374 } else { 2375 if (scf_error() != SCF_ERROR_TYPE_MISMATCH) 2376 scfdie(); 2377 } 2378 } else { 2379 if (scf_error() != SCF_ERROR_NOT_FOUND) 2380 scfdie(); 2381 } 2382 2383 scf_iter_destroy(iter); 2384 } else { 2385 if (scf_error() != SCF_ERROR_NOT_FOUND) 2386 scfdie(); 2387 } 2388 2389 restarter_common: 2390 scf_pg_destroy(rpg); 2391 2392 /* Dependencies. */ 2393 if ((pg_iter = scf_iter_create(h)) == NULL) 2394 scfdie(); 2395 2396 snap = get_running_snapshot(wip->inst); 2397 2398 if (scf_iter_instance_pgs_typed_composed(pg_iter, wip->inst, snap, 2399 SCF_GROUP_DEPENDENCY) != SCF_SUCCESS) 2400 scfdie(); 2401 2402 while ((ret = scf_iter_next_pg(pg_iter, g_pg)) == 1) 2403 print_detailed_dependency(g_pg); 2404 if (ret == -1) 2405 scfdie(); 2406 2407 scf_snapshot_destroy(snap); 2408 scf_iter_destroy(pg_iter); 2409 2410 if (opt_processes) 2411 detailed_list_processes(wip); 2412 2413 return (0); 2414 } 2415 2416 /* 2417 * Append a one-lined description of each process in inst's contract(s) and 2418 * return the augmented string. 2419 */ 2420 static char * 2421 add_processes(scf_walkinfo_t *wip, char *line, scf_propertygroup_t *lpg) 2422 { 2423 pid_t *pids = NULL; 2424 uint_t i, n = 0; 2425 2426 if (lpg == NULL) { 2427 if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0) 2428 return (line); 2429 } else { 2430 /* Legacy services */ 2431 scf_iter_t *iter; 2432 2433 if ((iter = scf_iter_create(h)) == NULL) 2434 scfdie(); 2435 2436 (void) propvals_to_pids(lpg, scf_property_contract, &pids, &n, 2437 g_prop, g_val, iter); 2438 2439 scf_iter_destroy(iter); 2440 } 2441 2442 if (n == 0) 2443 return (line); 2444 2445 qsort(pids, n, sizeof (*pids), pidcmp); 2446 2447 for (i = 0; i < n; ++i) { 2448 char *cp, stime[9]; 2449 psinfo_t psi; 2450 struct tm *tm; 2451 int len = 1 + 15 + 8 + 3 + 6 + 1 + PRFNSZ; 2452 2453 if (get_psinfo(pids[i], &psi) != 0) 2454 continue; 2455 2456 line = realloc(line, strlen(line) + len); 2457 if (line == NULL) 2458 uu_die(gettext("Out of memory.\n")); 2459 2460 cp = strchr(line, '\0'); 2461 2462 tm = localtime(&psi.pr_start.tv_sec); 2463 2464 /* 2465 * Print time if started within the past 24 hours, print date 2466 * if within the past 12 months, print year if started greater 2467 * than 12 months ago. 2468 */ 2469 if (now - psi.pr_start.tv_sec < 24 * 60 * 60) 2470 (void) strftime(stime, sizeof (stime), 2471 gettext(FORMAT_TIME), tm); 2472 else if (now - psi.pr_start.tv_sec < 12 * 30 * 24 * 60 * 60) 2473 (void) strftime(stime, sizeof (stime), 2474 gettext(FORMAT_DATE), tm); 2475 else 2476 (void) strftime(stime, sizeof (stime), 2477 gettext(FORMAT_YEAR), tm); 2478 2479 (void) snprintf(cp, len, "\n %-8s %6ld %.*s", 2480 stime, pids[i], PRFNSZ, psi.pr_fname); 2481 } 2482 2483 free(pids); 2484 2485 return (line); 2486 } 2487 2488 /*ARGSUSED*/ 2489 static int 2490 list_instance(void *unused, scf_walkinfo_t *wip) 2491 { 2492 struct avl_string *lp; 2493 char *cp; 2494 int i; 2495 uu_avl_index_t idx; 2496 2497 /* 2498 * If the user has specified a restarter, check for a match first 2499 */ 2500 if (restarters != NULL) { 2501 struct pfmri_list *rest; 2502 int match; 2503 char *restarter_fmri; 2504 const char *scope_name, *svc_name, *inst_name, *pg_name; 2505 2506 /* legacy services don't have restarters */ 2507 if (wip->pg != NULL) 2508 return (0); 2509 2510 restarter_fmri = safe_malloc(max_scf_fmri_length + 1); 2511 2512 if (inst_get_single_val(wip->inst, SCF_PG_GENERAL, 2513 SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, restarter_fmri, 2514 max_scf_fmri_length + 1, 0, 0, 1) != 0) 2515 (void) strcpy(restarter_fmri, SCF_SERVICE_STARTD); 2516 2517 if (scf_parse_svc_fmri(restarter_fmri, &scope_name, &svc_name, 2518 &inst_name, &pg_name, NULL) != SCF_SUCCESS) { 2519 free(restarter_fmri); 2520 return (0); 2521 } 2522 2523 match = 0; 2524 for (rest = restarters; rest != NULL; rest = rest->next) { 2525 if (strcmp(rest->scope, scope_name) == 0 && 2526 strcmp(rest->service, svc_name) == 0 && 2527 strcmp(rest->instance, inst_name) == 0) 2528 match = 1; 2529 } 2530 2531 free(restarter_fmri); 2532 2533 if (!match) 2534 return (0); 2535 } 2536 2537 if (wip->pg == NULL && ht_buckets != NULL && ht_add(wip->fmri)) { 2538 /* It was already there. */ 2539 return (0); 2540 } 2541 2542 lp = safe_malloc(sizeof (*lp)); 2543 2544 lp->str = NULL; 2545 for (i = 0; i < opt_cnum; ++i) { 2546 columns[opt_columns[i]].sprint(&lp->str, wip); 2547 } 2548 cp = lp->str + strlen(lp->str); 2549 cp--; 2550 while (*cp == ' ') 2551 cp--; 2552 *(cp+1) = '\0'; 2553 2554 /* If we're supposed to list the processes, too, do that now. */ 2555 if (opt_processes) 2556 lp->str = add_processes(wip, lp->str, wip->pg); 2557 2558 /* Create the sort key. */ 2559 cp = lp->key = safe_malloc(sortkey_sz); 2560 for (i = 0; i < opt_snum; ++i) { 2561 int j = opt_sort[i] & 0xff; 2562 2563 assert(columns[j].get_sortkey != NULL); 2564 columns[j].get_sortkey(cp, opt_sort[i] & ~0xff, wip); 2565 cp += columns[j].sortkey_width; 2566 } 2567 2568 /* Insert into AVL tree. */ 2569 uu_avl_node_init(lp, &lp->node, lines_pool); 2570 (void) uu_avl_find(lines, lp, NULL, &idx); 2571 uu_avl_insert(lines, lp, idx); 2572 2573 return (0); 2574 } 2575 2576 static int 2577 list_if_enabled(void *unused, scf_walkinfo_t *wip) 2578 { 2579 if (wip->pg != NULL || 2580 instance_enabled(wip->inst, B_FALSE) == 1 || 2581 instance_enabled(wip->inst, B_TRUE) == 1) 2582 return (list_instance(unused, wip)); 2583 2584 return (0); 2585 } 2586 2587 /* 2588 * Service FMRI selection: Lookup and call list_instance() for the instances. 2589 * Instance FMRI selection: Lookup and call list_instance(). 2590 * 2591 * Note: This is shoehorned into a walk_dependencies() callback prototype so 2592 * it can be used in list_dependencies. 2593 */ 2594 static int 2595 list_svc_or_inst_fmri(void *complain, scf_walkinfo_t *wip) 2596 { 2597 char *fmri; 2598 const char *svc_name, *inst_name, *pg_name, *save; 2599 scf_iter_t *iter; 2600 int ret; 2601 2602 fmri = safe_strdup(wip->fmri); 2603 2604 if (scf_parse_svc_fmri(fmri, NULL, &svc_name, &inst_name, &pg_name, 2605 NULL) != SCF_SUCCESS) { 2606 if (complain) 2607 uu_warn(gettext("FMRI \"%s\" is invalid.\n"), 2608 wip->fmri); 2609 exit_status = UU_EXIT_FATAL; 2610 free(fmri); 2611 return (0); 2612 } 2613 2614 /* 2615 * Yes, this invalidates *_name, but we only care whether they're NULL 2616 * or not. 2617 */ 2618 free(fmri); 2619 2620 if (svc_name == NULL || pg_name != NULL) { 2621 if (complain) 2622 uu_warn(gettext("FMRI \"%s\" does not designate a " 2623 "service or instance.\n"), wip->fmri); 2624 return (0); 2625 } 2626 2627 if (inst_name != NULL) { 2628 /* instance */ 2629 if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc, 2630 wip->inst, NULL, NULL, 0) != SCF_SUCCESS) { 2631 if (scf_error() != SCF_ERROR_NOT_FOUND) 2632 scfdie(); 2633 2634 if (complain) 2635 uu_warn(gettext( 2636 "Instance \"%s\" does not exist.\n"), 2637 wip->fmri); 2638 return (0); 2639 } 2640 2641 return (list_instance(NULL, wip)); 2642 } 2643 2644 /* service: Walk the instances. */ 2645 if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc, NULL, 2646 NULL, NULL, 0) != SCF_SUCCESS) { 2647 if (scf_error() != SCF_ERROR_NOT_FOUND) 2648 scfdie(); 2649 2650 if (complain) 2651 uu_warn(gettext("Service \"%s\" does not exist.\n"), 2652 wip->fmri); 2653 2654 exit_status = UU_EXIT_FATAL; 2655 2656 return (0); 2657 } 2658 2659 iter = scf_iter_create(h); 2660 if (iter == NULL) 2661 scfdie(); 2662 2663 if (scf_iter_service_instances(iter, wip->svc) != SCF_SUCCESS) 2664 scfdie(); 2665 2666 if ((fmri = malloc(max_scf_fmri_length + 1)) == NULL) { 2667 scf_iter_destroy(iter); 2668 exit_status = UU_EXIT_FATAL; 2669 return (0); 2670 } 2671 2672 save = wip->fmri; 2673 wip->fmri = fmri; 2674 while ((ret = scf_iter_next_instance(iter, wip->inst)) == 1) { 2675 if (scf_instance_to_fmri(wip->inst, fmri, 2676 max_scf_fmri_length + 1) <= 0) 2677 scfdie(); 2678 (void) list_instance(NULL, wip); 2679 } 2680 free(fmri); 2681 wip->fmri = save; 2682 if (ret == -1) 2683 scfdie(); 2684 2685 exit_status = UU_EXIT_OK; 2686 2687 scf_iter_destroy(iter); 2688 2689 return (0); 2690 } 2691 2692 /* 2693 * Dependency selection: Straightforward since each instance lists the 2694 * services it depends on. 2695 */ 2696 2697 static void 2698 walk_dependencies(scf_walkinfo_t *wip, scf_walk_callback callback, void *data) 2699 { 2700 scf_snapshot_t *snap; 2701 scf_iter_t *iter, *viter; 2702 int ret, vret; 2703 char *dep; 2704 2705 assert(wip->inst != NULL); 2706 2707 if ((iter = scf_iter_create(h)) == NULL || 2708 (viter = scf_iter_create(h)) == NULL) 2709 scfdie(); 2710 2711 snap = get_running_snapshot(wip->inst); 2712 2713 if (scf_iter_instance_pgs_typed_composed(iter, wip->inst, snap, 2714 SCF_GROUP_DEPENDENCY) != SCF_SUCCESS) 2715 scfdie(); 2716 2717 dep = safe_malloc(max_scf_value_length + 1); 2718 2719 while ((ret = scf_iter_next_pg(iter, g_pg)) == 1) { 2720 scf_type_t ty; 2721 2722 /* Ignore exclude_any dependencies. */ 2723 if (scf_pg_get_property(g_pg, SCF_PROPERTY_GROUPING, g_prop) != 2724 SCF_SUCCESS) { 2725 if (scf_error() != SCF_ERROR_NOT_FOUND) 2726 scfdie(); 2727 2728 continue; 2729 } 2730 2731 if (scf_property_type(g_prop, &ty) != SCF_SUCCESS) 2732 scfdie(); 2733 2734 if (ty != SCF_TYPE_ASTRING) 2735 continue; 2736 2737 if (scf_property_get_value(g_prop, g_val) != SCF_SUCCESS) { 2738 if (scf_error() != SCF_ERROR_CONSTRAINT_VIOLATED) 2739 scfdie(); 2740 2741 continue; 2742 } 2743 2744 if (scf_value_get_astring(g_val, dep, 2745 max_scf_value_length + 1) < 0) 2746 scfdie(); 2747 2748 if (strcmp(dep, SCF_DEP_EXCLUDE_ALL) == 0) 2749 continue; 2750 2751 if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENTITIES, g_prop) != 2752 SCF_SUCCESS) { 2753 if (scf_error() != SCF_ERROR_NOT_FOUND) 2754 scfdie(); 2755 2756 continue; 2757 } 2758 2759 if (scf_iter_property_values(viter, g_prop) != SCF_SUCCESS) 2760 scfdie(); 2761 2762 while ((vret = scf_iter_next_value(viter, g_val)) == 1) { 2763 if (scf_value_get_astring(g_val, dep, 2764 max_scf_value_length + 1) < 0) 2765 scfdie(); 2766 2767 wip->fmri = dep; 2768 if (callback(data, wip) != 0) 2769 goto out; 2770 } 2771 if (vret == -1) 2772 scfdie(); 2773 } 2774 if (ret == -1) 2775 scfdie(); 2776 2777 out: 2778 scf_iter_destroy(viter); 2779 scf_iter_destroy(iter); 2780 scf_snapshot_destroy(snap); 2781 } 2782 2783 static int 2784 list_dependencies(void *data, scf_walkinfo_t *wip) 2785 { 2786 walk_dependencies(wip, list_svc_or_inst_fmri, data); 2787 return (0); 2788 } 2789 2790 2791 /* 2792 * Dependent selection: The "providing" service's or instance's FMRI is parsed 2793 * into the provider_* variables, the instances are walked, and any instance 2794 * which lists an FMRI which parses to these components is selected. This is 2795 * inefficient in the face of multiple operands, but that should be uncommon. 2796 */ 2797 2798 static char *provider_scope; 2799 static char *provider_svc; 2800 static char *provider_inst; /* NULL for services */ 2801 2802 /*ARGSUSED*/ 2803 static int 2804 check_against_provider(void *arg, scf_walkinfo_t *wip) 2805 { 2806 char *cfmri; 2807 const char *scope_name, *svc_name, *inst_name, *pg_name; 2808 int *matchp = arg; 2809 2810 cfmri = safe_strdup(wip->fmri); 2811 2812 if (scf_parse_svc_fmri(cfmri, &scope_name, &svc_name, &inst_name, 2813 &pg_name, NULL) != SCF_SUCCESS) { 2814 free(cfmri); 2815 return (0); 2816 } 2817 2818 if (svc_name == NULL || pg_name != NULL) { 2819 free(cfmri); 2820 return (0); 2821 } 2822 2823 /* 2824 * If the user has specified an instance, then also match dependencies 2825 * on the service itself. 2826 */ 2827 *matchp = (strcmp(provider_scope, scope_name) == 0 && 2828 strcmp(provider_svc, svc_name) == 0 && 2829 (provider_inst == NULL ? (inst_name == NULL) : 2830 (inst_name == NULL || strcmp(provider_inst, inst_name) == 0))); 2831 2832 free(cfmri); 2833 2834 /* Stop on matches. */ 2835 return (*matchp); 2836 } 2837 2838 static int 2839 list_if_dependent(void *unused, scf_walkinfo_t *wip) 2840 { 2841 /* Only proceed if this instance depends on provider_*. */ 2842 int match = 0; 2843 2844 (void) walk_dependencies(wip, check_against_provider, &match); 2845 2846 if (match) 2847 return (list_instance(unused, wip)); 2848 2849 return (0); 2850 } 2851 2852 /*ARGSUSED*/ 2853 static int 2854 list_dependents(void *unused, scf_walkinfo_t *wip) 2855 { 2856 char *save; 2857 int ret; 2858 2859 if (scf_scope_get_name(wip->scope, provider_scope, 2860 max_scf_fmri_length) <= 0 || 2861 scf_service_get_name(wip->svc, provider_svc, 2862 max_scf_fmri_length) <= 0) 2863 scfdie(); 2864 2865 save = provider_inst; 2866 if (wip->inst == NULL) 2867 provider_inst = NULL; 2868 else if (scf_instance_get_name(wip->inst, provider_inst, 2869 max_scf_fmri_length) <= 0) 2870 scfdie(); 2871 2872 ret = scf_walk_fmri(h, 0, NULL, 0, list_if_dependent, NULL, NULL, 2873 uu_warn); 2874 2875 provider_inst = save; 2876 2877 return (ret); 2878 } 2879 2880 /* 2881 * main() & helpers 2882 */ 2883 2884 static void 2885 add_sort_column(const char *col, int reverse) 2886 { 2887 int i; 2888 2889 ++opt_snum; 2890 2891 opt_sort = realloc(opt_sort, opt_snum * sizeof (*opt_sort)); 2892 if (opt_sort == NULL) 2893 uu_die(gettext("Too many sort criteria: out of memory.\n")); 2894 2895 for (i = 0; i < ncolumns; ++i) { 2896 if (strcasecmp(col, columns[i].name) == 0) 2897 break; 2898 } 2899 2900 if (i < ncolumns) 2901 opt_sort[opt_snum - 1] = (reverse ? i | 0x100 : i); 2902 else 2903 uu_die(gettext("Unrecognized sort column \"%s\".\n"), col); 2904 2905 sortkey_sz += columns[i].sortkey_width; 2906 } 2907 2908 static void 2909 add_restarter(const char *fmri) 2910 { 2911 char *cfmri; 2912 const char *pg_name; 2913 struct pfmri_list *rest; 2914 2915 cfmri = safe_strdup(fmri); 2916 rest = safe_malloc(sizeof (*rest)); 2917 2918 if (scf_parse_svc_fmri(cfmri, &rest->scope, &rest->service, 2919 &rest->instance, &pg_name, NULL) != SCF_SUCCESS) 2920 uu_die(gettext("Restarter FMRI \"%s\" is invalid.\n"), fmri); 2921 2922 if (rest->instance == NULL || pg_name != NULL) 2923 uu_die(gettext("Restarter FMRI \"%s\" does not designate an " 2924 "instance.\n"), fmri); 2925 2926 rest->next = restarters; 2927 restarters = rest; 2928 return; 2929 2930 err: 2931 free(cfmri); 2932 free(rest); 2933 } 2934 2935 /* ARGSUSED */ 2936 static int 2937 line_cmp(const void *l_arg, const void *r_arg, void *private) 2938 { 2939 const struct avl_string *l = l_arg; 2940 const struct avl_string *r = r_arg; 2941 2942 return (memcmp(l->key, r->key, sortkey_sz)); 2943 } 2944 2945 /* ARGSUSED */ 2946 static int 2947 print_line(void *e, void *private) 2948 { 2949 struct avl_string *lp = e; 2950 2951 (void) puts(lp->str); 2952 2953 return (UU_WALK_NEXT); 2954 } 2955 2956 int 2957 main(int argc, char **argv) 2958 { 2959 char opt, opt_mode; 2960 int i, n; 2961 char *columns_str = NULL; 2962 char *cp; 2963 const char *progname; 2964 int err; 2965 2966 int show_all = 0; 2967 int show_header = 1; 2968 2969 const char * const options = "aHpvo:R:s:S:dDl?x"; 2970 2971 (void) setlocale(LC_ALL, ""); 2972 2973 locale = setlocale(LC_MESSAGES, ""); 2974 if (locale) { 2975 locale = safe_strdup(locale); 2976 sanitize_locale(locale); 2977 } 2978 2979 (void) textdomain(TEXT_DOMAIN); 2980 progname = uu_setpname(argv[0]); 2981 2982 exit_status = UU_EXIT_OK; 2983 2984 max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH); 2985 max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 2986 max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH); 2987 2988 if (max_scf_name_length == -1 || max_scf_value_length == -1 || 2989 max_scf_fmri_length == -1) 2990 scfdie(); 2991 2992 now = time(NULL); 2993 assert(now != -1); 2994 2995 /* 2996 * opt_mode is the mode of operation. 0 for plain, 'd' for 2997 * dependencies, 'D' for dependents, and 'l' for detailed (long). We 2998 * need to know now so we know which options are valid. 2999 */ 3000 opt_mode = 0; 3001 while ((opt = getopt(argc, argv, options)) != -1) { 3002 switch (opt) { 3003 case '?': 3004 if (optopt == '?') { 3005 print_help(progname); 3006 return (UU_EXIT_OK); 3007 } else { 3008 argserr(progname); 3009 /* NOTREACHED */ 3010 } 3011 3012 case 'd': 3013 case 'D': 3014 case 'l': 3015 if (opt_mode != 0) 3016 argserr(progname); 3017 3018 opt_mode = opt; 3019 break; 3020 3021 case 'x': 3022 if (opt_mode != 0) 3023 argserr(progname); 3024 3025 opt_mode = opt; 3026 break; 3027 3028 default: 3029 break; 3030 } 3031 } 3032 3033 sortkey_sz = 0; 3034 3035 optind = 1; /* Reset getopt() */ 3036 while ((opt = getopt(argc, argv, options)) != -1) { 3037 switch (opt) { 3038 case 'a': 3039 if (opt_mode != 0) 3040 argserr(progname); 3041 show_all = 1; 3042 break; 3043 3044 case 'H': 3045 if (opt_mode == 'l' || opt_mode == 'x') 3046 argserr(progname); 3047 show_header = 0; 3048 break; 3049 3050 case 'p': 3051 if (opt_mode == 'x') 3052 argserr(progname); 3053 opt_processes = 1; 3054 break; 3055 3056 case 'v': 3057 if (opt_mode == 'l') 3058 argserr(progname); 3059 opt_verbose = 1; 3060 break; 3061 3062 case 'o': 3063 if (opt_mode == 'l' || opt_mode == 'x') 3064 argserr(progname); 3065 columns_str = optarg; 3066 break; 3067 3068 case 'R': 3069 if (opt_mode != 0 || opt_mode == 'x') 3070 argserr(progname); 3071 3072 add_restarter(optarg); 3073 break; 3074 3075 case 's': 3076 case 'S': 3077 if (opt_mode != 0) 3078 argserr(progname); 3079 3080 add_sort_column(optarg, optopt == 'S'); 3081 break; 3082 3083 case 'd': 3084 case 'D': 3085 case 'l': 3086 case 'x': 3087 assert(opt_mode == optopt); 3088 break; 3089 3090 case '?': 3091 argserr(progname); 3092 /* NOTREACHED */ 3093 3094 default: 3095 assert(0); 3096 abort(); 3097 } 3098 } 3099 3100 /* 3101 * -a is only meaningful when given no arguments 3102 */ 3103 if (show_all && optind != argc) 3104 uu_warn(gettext("-a ignored when used with arguments.\n")); 3105 3106 h = scf_handle_create(SCF_VERSION); 3107 if (h == NULL) 3108 scfdie(); 3109 3110 if (scf_handle_bind(h) == -1) 3111 uu_die(gettext("Could not bind to repository server: %s. " 3112 "Exiting.\n"), scf_strerror(scf_error())); 3113 3114 if ((g_pg = scf_pg_create(h)) == NULL || 3115 (g_prop = scf_property_create(h)) == NULL || 3116 (g_val = scf_value_create(h)) == NULL) 3117 scfdie(); 3118 3119 argc -= optind; 3120 argv += optind; 3121 3122 /* 3123 * If we're in long mode, take care of it now before we deal with the 3124 * sorting and the columns, since we won't use them anyway. 3125 */ 3126 if (opt_mode == 'l') { 3127 if (argc == 0) 3128 argserr(progname); 3129 3130 if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE, 3131 print_detailed, NULL, &exit_status, uu_warn)) != 0) { 3132 uu_warn(gettext("failed to iterate over " 3133 "instances: %s\n"), scf_strerror(err)); 3134 exit_status = UU_EXIT_FATAL; 3135 } 3136 3137 return (exit_status); 3138 } 3139 3140 if (opt_mode == 'x') { 3141 explain(opt_verbose, argc, argv); 3142 3143 return (exit_status); 3144 } 3145 3146 3147 if (opt_snum == 0) { 3148 /* Default sort. */ 3149 add_sort_column("state", 0); 3150 add_sort_column("stime", 0); 3151 add_sort_column("fmri", 0); 3152 } 3153 3154 if (columns_str == NULL) { 3155 if (!opt_verbose) 3156 columns_str = safe_strdup("state,stime,fmri"); 3157 else 3158 columns_str = 3159 safe_strdup("state,nstate,stime,ctid,fmri"); 3160 } 3161 3162 /* Decode columns_str into opt_columns. */ 3163 line_sz = 0; 3164 3165 opt_cnum = 1; 3166 for (cp = columns_str; *cp != '\0'; ++cp) 3167 if (*cp == ',') 3168 ++opt_cnum; 3169 3170 opt_columns = malloc(opt_cnum * sizeof (*opt_columns)); 3171 if (opt_columns == NULL) 3172 uu_die(gettext("Too many columns.\n")); 3173 3174 for (n = 0; *columns_str != '\0'; ++n) { 3175 i = getcolumnopt(&columns_str); 3176 if (i == -1) 3177 uu_die(gettext("Unknown column \"%s\".\n"), 3178 columns_str); 3179 3180 if (strcmp(columns[i].name, "N") == 0 || 3181 strcmp(columns[i].name, "SN") == 0 || 3182 strcmp(columns[i].name, "NSTA") == 0 || 3183 strcmp(columns[i].name, "NSTATE") == 0) 3184 opt_nstate_shown = 1; 3185 3186 opt_columns[n] = i; 3187 line_sz += columns[i].width + 1; 3188 } 3189 3190 3191 if ((lines_pool = uu_avl_pool_create("lines_pool", 3192 sizeof (struct avl_string), offsetof(struct avl_string, node), 3193 line_cmp, UU_AVL_DEBUG)) == NULL || 3194 (lines = uu_avl_create(lines_pool, NULL, 0)) == NULL) 3195 uu_die(gettext("Unexpected libuutil error: %s. Exiting.\n"), 3196 uu_strerror(uu_error())); 3197 3198 switch (opt_mode) { 3199 case 0: 3200 ht_init(); 3201 3202 /* Always show all FMRIs when given arguments or restarters */ 3203 if (argc != 0 || restarters != NULL) 3204 show_all = 1; 3205 3206 if ((err = scf_walk_fmri(h, argc, argv, 3207 SCF_WALK_MULTIPLE | SCF_WALK_LEGACY, 3208 show_all ? list_instance : list_if_enabled, NULL, 3209 &exit_status, uu_warn)) != 0) { 3210 uu_warn(gettext("failed to iterate over " 3211 "instances: %s\n"), scf_strerror(err)); 3212 exit_status = UU_EXIT_FATAL; 3213 } 3214 break; 3215 3216 case 'd': 3217 if (argc == 0) 3218 argserr(progname); 3219 3220 if ((err = scf_walk_fmri(h, argc, argv, 3221 SCF_WALK_MULTIPLE, list_dependencies, NULL, 3222 &exit_status, uu_warn)) != 0) { 3223 uu_warn(gettext("failed to iterate over " 3224 "instances: %s\n"), scf_strerror(err)); 3225 exit_status = UU_EXIT_FATAL; 3226 } 3227 break; 3228 3229 case 'D': 3230 if (argc == 0) 3231 argserr(progname); 3232 3233 provider_scope = safe_malloc(max_scf_fmri_length); 3234 provider_svc = safe_malloc(max_scf_fmri_length); 3235 provider_inst = safe_malloc(max_scf_fmri_length); 3236 3237 if ((err = scf_walk_fmri(h, argc, argv, 3238 SCF_WALK_MULTIPLE | SCF_WALK_SERVICE, 3239 list_dependents, NULL, &exit_status, uu_warn)) != 0) { 3240 uu_warn(gettext("failed to iterate over " 3241 "instances: %s\n"), scf_strerror(err)); 3242 exit_status = UU_EXIT_FATAL; 3243 } 3244 3245 free(provider_scope); 3246 free(provider_svc); 3247 free(provider_inst); 3248 break; 3249 3250 default: 3251 assert(0); 3252 abort(); 3253 } 3254 3255 if (show_header) 3256 print_header(); 3257 3258 (void) uu_avl_walk(lines, print_line, NULL, 0); 3259 3260 return (exit_status); 3261 } 3262