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 2007 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_SCHEME "lrc:" 88 #define LEGACY_UNKNOWN "unknown" 89 90 /* Flags for pg_get_single_val() */ 91 #define EMPTY_OK 0x01 92 #define MULTI_OK 0x02 93 94 95 /* 96 * An AVL-storable node for output lines and the keys to sort them by. 97 */ 98 struct avl_string { 99 uu_avl_node_t node; 100 char *key; 101 char *str; 102 }; 103 104 /* 105 * For lists of parsed restarter FMRIs. 106 */ 107 struct pfmri_list { 108 const char *scope; 109 const char *service; 110 const char *instance; 111 struct pfmri_list *next; 112 }; 113 114 115 /* 116 * Globals 117 */ 118 scf_handle_t *h; 119 static scf_propertygroup_t *g_pg; 120 static scf_property_t *g_prop; 121 static scf_value_t *g_val; 122 123 static size_t line_sz; /* Bytes in the header line. */ 124 static size_t sortkey_sz; /* Bytes in sort keys. */ 125 static uu_avl_pool_t *lines_pool; 126 static uu_avl_t *lines; /* Output lines. */ 127 int exit_status; 128 ssize_t max_scf_name_length; 129 ssize_t max_scf_value_length; 130 ssize_t max_scf_fmri_length; 131 static time_t now; 132 static struct pfmri_list *restarters = NULL; 133 static int first_paragraph = 1; /* For -l mode. */ 134 static char *common_name_buf; /* Sized for maximal length value. */ 135 char *locale; /* Current locale. */ 136 137 /* 138 * Pathname storage for path generated from the fmri. 139 * Used for reading the ctid and (start) pid files for an inetd service. 140 */ 141 static char genfmri_filename[MAXPATHLEN] = ""; 142 143 /* Options */ 144 static int *opt_columns = NULL; /* Indices into columns to display. */ 145 static int opt_cnum = 0; 146 static int opt_processes = 0; /* Print processes? */ 147 static int *opt_sort = NULL; /* Indices into columns to sort. */ 148 static int opt_snum = 0; 149 static int opt_nstate_shown = 0; /* Will nstate be shown? */ 150 static int opt_verbose = 0; 151 152 /* Minimize string constants. */ 153 static const char * const scf_property_state = SCF_PROPERTY_STATE; 154 static const char * const scf_property_next_state = SCF_PROPERTY_NEXT_STATE; 155 static const char * const scf_property_contract = SCF_PROPERTY_CONTRACT; 156 157 158 /* 159 * Utility functions 160 */ 161 162 /* 163 * For unexpected libscf errors. The ending newline is necessary to keep 164 * uu_die() from appending the errno error. 165 */ 166 #ifndef NDEBUG 167 void 168 do_scfdie(const char *file, int line) 169 { 170 uu_die(gettext("%s:%d: Unexpected libscf error: %s. Exiting.\n"), 171 file, line, scf_strerror(scf_error())); 172 } 173 #else 174 void 175 scfdie(void) 176 { 177 uu_die(gettext("Unexpected libscf error: %s. Exiting.\n"), 178 scf_strerror(scf_error())); 179 } 180 #endif 181 182 void * 183 safe_malloc(size_t sz) 184 { 185 void *ptr; 186 187 ptr = malloc(sz); 188 if (ptr == NULL) 189 uu_die(gettext("Out of memory")); 190 191 return (ptr); 192 } 193 194 char * 195 safe_strdup(const char *str) 196 { 197 char *cp; 198 199 cp = strdup(str); 200 if (cp == NULL) 201 uu_die(gettext("Out of memory.\n")); 202 203 return (cp); 204 } 205 206 static void 207 sanitize_locale(char *locale) 208 { 209 for (; *locale != '\0'; locale++) 210 if (!isalnum(*locale)) 211 *locale = '_'; 212 } 213 214 /* 215 * FMRI hashtable. For uniquifing listings. 216 */ 217 218 struct ht_elem { 219 const char *fmri; 220 struct ht_elem *next; 221 }; 222 223 static struct ht_elem **ht_buckets; 224 static uint_t ht_buckets_num; 225 static uint_t ht_num; 226 227 static void 228 ht_init() 229 { 230 ht_buckets_num = 8; 231 ht_buckets = safe_malloc(sizeof (*ht_buckets) * ht_buckets_num); 232 bzero(ht_buckets, sizeof (*ht_buckets) * ht_buckets_num); 233 ht_num = 0; 234 } 235 236 static uint_t 237 ht_hash_fmri(const char *fmri) 238 { 239 uint_t h = 0, g; 240 const char *p, *k; 241 242 /* All FMRIs begin with svc:/, so skip that part. */ 243 assert(strncmp(fmri, "svc:/", sizeof ("svc:/") - 1) == 0); 244 k = fmri + sizeof ("svc:/") - 1; 245 246 /* 247 * Generic hash function from uts/common/os/modhash.c. 248 */ 249 for (p = k; *p != '\0'; ++p) { 250 h = (h << 4) + *p; 251 if ((g = (h & 0xf0000000)) != 0) { 252 h ^= (g >> 24); 253 h ^= g; 254 } 255 } 256 257 return (h); 258 } 259 260 static void 261 ht_grow() 262 { 263 uint_t new_ht_buckets_num; 264 struct ht_elem **new_ht_buckets; 265 int i; 266 267 new_ht_buckets_num = ht_buckets_num * 2; 268 assert(new_ht_buckets_num > ht_buckets_num); 269 new_ht_buckets = 270 safe_malloc(sizeof (*new_ht_buckets) * new_ht_buckets_num); 271 bzero(new_ht_buckets, sizeof (*new_ht_buckets) * new_ht_buckets_num); 272 273 for (i = 0; i < ht_buckets_num; ++i) { 274 struct ht_elem *elem, *next; 275 276 for (elem = ht_buckets[i]; elem != NULL; elem = next) { 277 uint_t h; 278 279 next = elem->next; 280 281 h = ht_hash_fmri(elem->fmri); 282 283 elem->next = 284 new_ht_buckets[h & (new_ht_buckets_num - 1)]; 285 new_ht_buckets[h & (new_ht_buckets_num - 1)] = elem; 286 } 287 } 288 289 free(ht_buckets); 290 291 ht_buckets = new_ht_buckets; 292 ht_buckets_num = new_ht_buckets_num; 293 } 294 295 /* 296 * Add an FMRI to the hash table. Returns 1 if it was already there, 297 * 0 otherwise. 298 */ 299 static int 300 ht_add(const char *fmri) 301 { 302 uint_t h; 303 struct ht_elem *elem; 304 305 h = ht_hash_fmri(fmri); 306 307 elem = ht_buckets[h & (ht_buckets_num - 1)]; 308 309 for (; elem != NULL; elem = elem->next) { 310 if (strcmp(elem->fmri, fmri) == 0) 311 return (1); 312 } 313 314 /* Grow when average chain length is over 3. */ 315 if (ht_num > 3 * ht_buckets_num) 316 ht_grow(); 317 318 ++ht_num; 319 320 elem = safe_malloc(sizeof (*elem)); 321 elem->fmri = strdup(fmri); 322 elem->next = ht_buckets[h & (ht_buckets_num - 1)]; 323 ht_buckets[h & (ht_buckets_num - 1)] = elem; 324 325 return (0); 326 } 327 328 329 330 /* 331 * Convenience libscf wrapper functions. 332 */ 333 334 /* 335 * Get the single value of the named property in the given property group, 336 * which must have type ty, and put it in *vp. If ty is SCF_TYPE_ASTRING, vp 337 * is taken to be a char **, and sz is the size of the buffer. sz is unused 338 * otherwise. Return 0 on success, -1 if the property doesn't exist, has the 339 * wrong type, or doesn't have a single value. If flags has EMPTY_OK, don't 340 * complain if the property has no values (but return nonzero). If flags has 341 * MULTI_OK and the property has multiple values, succeed with E2BIG. 342 */ 343 int 344 pg_get_single_val(scf_propertygroup_t *pg, const char *propname, scf_type_t ty, 345 void *vp, size_t sz, uint_t flags) 346 { 347 char *buf; 348 size_t buf_sz; 349 int ret = -1, r; 350 boolean_t multi = B_FALSE; 351 352 assert((flags & ~(EMPTY_OK | MULTI_OK)) == 0); 353 354 if (scf_pg_get_property(pg, propname, g_prop) == -1) { 355 if (scf_error() != SCF_ERROR_NOT_FOUND) 356 scfdie(); 357 358 goto out; 359 } 360 361 if (scf_property_is_type(g_prop, ty) != SCF_SUCCESS) { 362 if (scf_error() == SCF_ERROR_TYPE_MISMATCH) 363 goto misconfigured; 364 scfdie(); 365 } 366 367 if (scf_property_get_value(g_prop, g_val) != SCF_SUCCESS) { 368 switch (scf_error()) { 369 case SCF_ERROR_NOT_FOUND: 370 if (flags & EMPTY_OK) 371 goto out; 372 goto misconfigured; 373 374 case SCF_ERROR_CONSTRAINT_VIOLATED: 375 if (flags & MULTI_OK) { 376 multi = B_TRUE; 377 break; 378 } 379 goto misconfigured; 380 381 case SCF_ERROR_PERMISSION_DENIED: 382 default: 383 scfdie(); 384 } 385 } 386 387 switch (ty) { 388 case SCF_TYPE_ASTRING: 389 r = scf_value_get_astring(g_val, vp, sz) > 0 ? SCF_SUCCESS : -1; 390 break; 391 392 case SCF_TYPE_BOOLEAN: 393 r = scf_value_get_boolean(g_val, (uint8_t *)vp); 394 break; 395 396 case SCF_TYPE_COUNT: 397 r = scf_value_get_count(g_val, (uint64_t *)vp); 398 break; 399 400 case SCF_TYPE_INTEGER: 401 r = scf_value_get_integer(g_val, (int64_t *)vp); 402 break; 403 404 case SCF_TYPE_TIME: { 405 int64_t sec; 406 int32_t ns; 407 r = scf_value_get_time(g_val, &sec, &ns); 408 ((struct timeval *)vp)->tv_sec = sec; 409 ((struct timeval *)vp)->tv_usec = ns / 1000; 410 break; 411 } 412 413 case SCF_TYPE_USTRING: 414 r = scf_value_get_ustring(g_val, vp, sz) > 0 ? SCF_SUCCESS : -1; 415 break; 416 417 default: 418 #ifndef NDEBUG 419 uu_warn("%s:%d: Unknown type %d.\n", __FILE__, __LINE__, ty); 420 #endif 421 abort(); 422 } 423 if (r != SCF_SUCCESS) 424 scfdie(); 425 426 ret = multi ? E2BIG : 0; 427 goto out; 428 429 misconfigured: 430 buf_sz = max_scf_fmri_length + 1; 431 buf = safe_malloc(buf_sz); 432 if (scf_property_to_fmri(g_prop, buf, buf_sz) == -1) 433 scfdie(); 434 435 uu_warn(gettext("Property \"%s\" is misconfigured.\n"), buf); 436 437 free(buf); 438 439 out: 440 return (ret); 441 } 442 443 static scf_snapshot_t * 444 get_running_snapshot(scf_instance_t *inst) 445 { 446 scf_snapshot_t *snap; 447 448 snap = scf_snapshot_create(h); 449 if (snap == NULL) 450 scfdie(); 451 452 if (scf_instance_get_snapshot(inst, "running", snap) == 0) 453 return (snap); 454 455 if (scf_error() != SCF_ERROR_NOT_FOUND) 456 scfdie(); 457 458 scf_snapshot_destroy(snap); 459 return (NULL); 460 } 461 462 /* 463 * As pg_get_single_val(), except look the property group up in an 464 * instance. If "use_running" is set, and the running snapshot exists, 465 * do a composed lookup there. Otherwise, do an (optionally composed) 466 * lookup on the current values. Note that lookups using snapshots are 467 * always composed. 468 */ 469 int 470 inst_get_single_val(scf_instance_t *inst, const char *pgname, 471 const char *propname, scf_type_t ty, void *vp, size_t sz, uint_t flags, 472 int use_running, int composed) 473 { 474 scf_snapshot_t *snap = NULL; 475 int r; 476 477 if (use_running) 478 snap = get_running_snapshot(inst); 479 if (composed || use_running) 480 r = scf_instance_get_pg_composed(inst, snap, pgname, g_pg); 481 else 482 r = scf_instance_get_pg(inst, pgname, g_pg); 483 if (snap) 484 scf_snapshot_destroy(snap); 485 if (r == -1) 486 return (-1); 487 488 r = pg_get_single_val(g_pg, propname, ty, vp, sz, flags); 489 490 return (r); 491 } 492 493 static int 494 instance_enabled(scf_instance_t *inst, boolean_t temp) 495 { 496 uint8_t b; 497 498 if (inst_get_single_val(inst, 499 temp ? SCF_PG_GENERAL_OVR : SCF_PG_GENERAL, SCF_PROPERTY_ENABLED, 500 SCF_TYPE_BOOLEAN, &b, 0, 0, 0, 0) != 0) 501 return (-1); 502 503 return (b ? 1 : 0); 504 } 505 506 /* 507 * Get a string property from the restarter property group of the given 508 * instance. Return an empty string on normal problems. 509 */ 510 static void 511 get_restarter_string_prop(scf_instance_t *inst, const char *pname, 512 char *buf, size_t buf_sz) 513 { 514 if (inst_get_single_val(inst, SCF_PG_RESTARTER, pname, 515 SCF_TYPE_ASTRING, buf, buf_sz, 0, 0, 1) != 0) 516 *buf = '\0'; 517 } 518 519 static int 520 get_restarter_time_prop(scf_instance_t *inst, const char *pname, 521 struct timeval *tvp, int ok_if_empty) 522 { 523 int r; 524 525 r = inst_get_single_val(inst, SCF_PG_RESTARTER, pname, SCF_TYPE_TIME, 526 tvp, NULL, ok_if_empty ? EMPTY_OK : 0, 0, 1); 527 528 return (r == 0 ? 0 : -1); 529 } 530 531 static int 532 get_restarter_count_prop(scf_instance_t *inst, const char *pname, uint64_t *cp, 533 uint_t flags) 534 { 535 return (inst_get_single_val(inst, SCF_PG_RESTARTER, pname, 536 SCF_TYPE_COUNT, cp, 0, flags, 0, 1)); 537 } 538 539 540 /* 541 * Generic functions 542 */ 543 544 /* 545 * Return an array of pids associated with the given contract id. 546 * Returned pids are added to the end of the pidsp array. 547 */ 548 static void 549 ctid_to_pids(uint64_t c, pid_t **pidsp, uint_t *np) 550 { 551 ct_stathdl_t ctst; 552 uint_t m; 553 int fd; 554 int r, err; 555 pid_t *pids; 556 557 fd = contract_open(c, NULL, "status", O_RDONLY); 558 if (fd < 0) 559 return; 560 561 err = ct_status_read(fd, CTD_ALL, &ctst); 562 if (err != 0) { 563 uu_warn(gettext("Could not read status of contract " 564 "%ld: %s.\n"), c, strerror(err)); 565 (void) close(fd); 566 return; 567 } 568 569 (void) close(fd); 570 571 r = ct_pr_status_get_members(ctst, &pids, &m); 572 assert(r == 0); 573 574 if (m == 0) { 575 ct_status_free(ctst); 576 return; 577 } 578 579 *pidsp = realloc(*pidsp, (*np + m) * sizeof (*pidsp)); 580 if (*pidsp == NULL) 581 uu_die(gettext("Out of memory")); 582 583 bcopy(pids, *pidsp + *np, m * sizeof (*pids)); 584 *np += m; 585 586 ct_status_free(ctst); 587 } 588 589 static int 590 propvals_to_pids(scf_propertygroup_t *pg, const char *pname, pid_t **pidsp, 591 uint_t *np, scf_property_t *prop, scf_value_t *val, scf_iter_t *iter) 592 { 593 scf_type_t ty; 594 uint64_t c; 595 int r; 596 597 if (scf_pg_get_property(pg, pname, prop) != 0) { 598 if (scf_error() != SCF_ERROR_NOT_FOUND) 599 scfdie(); 600 601 return (ENOENT); 602 } 603 604 if (scf_property_type(prop, &ty) != 0) 605 scfdie(); 606 607 if (ty != SCF_TYPE_COUNT) 608 return (EINVAL); 609 610 if (scf_iter_property_values(iter, prop) != 0) 611 scfdie(); 612 613 for (;;) { 614 r = scf_iter_next_value(iter, val); 615 if (r == -1) 616 scfdie(); 617 if (r == 0) 618 break; 619 620 if (scf_value_get_count(val, &c) != 0) 621 scfdie(); 622 623 ctid_to_pids(c, pidsp, np); 624 } 625 626 return (0); 627 } 628 629 /* 630 * Check if instance has general/restarter property that matches 631 * given string. Restarter string must be in canonified form. 632 * Returns 0 for success; -1 otherwise. 633 */ 634 static int 635 check_for_restarter(scf_instance_t *inst, const char *restarter) 636 { 637 char *fmri_buf; 638 char *fmri_buf_canonified = NULL; 639 int ret = -1; 640 641 if (inst == NULL) 642 return (-1); 643 644 /* Get restarter */ 645 fmri_buf = safe_malloc(max_scf_fmri_length + 1); 646 if (inst_get_single_val(inst, SCF_PG_GENERAL, 647 SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, fmri_buf, 648 max_scf_fmri_length + 1, 0, 0, 1) != 0) 649 goto out; 650 651 fmri_buf_canonified = safe_malloc(max_scf_fmri_length + 1); 652 if (scf_canonify_fmri(fmri_buf, fmri_buf_canonified, 653 (max_scf_fmri_length + 1)) < 0) 654 goto out; 655 656 if (strcmp(fmri_buf, restarter) == 0) 657 ret = 0; 658 659 out: 660 free(fmri_buf); 661 if (fmri_buf_canonified) 662 free(fmri_buf_canonified); 663 return (ret); 664 } 665 666 /* 667 * Common code that is used by ctids_by_restarter and pids_by_restarter. 668 * Checks for a common restarter and if one is available, it generates 669 * the appropriate filename using wip->fmri and stores that in the 670 * global genfmri_filename. 671 * 672 * Restarters currently supported are: svc:/network/inetd:default 673 * If a restarter specific action is available, then restarter_spec 674 * is set to 1. If a restarter specific action is not available, then 675 * restarter_spec is set to 0 and a -1 is returned. 676 * 677 * Returns: 678 * 0 if success: restarter specific action found and filename generated 679 * -1 if restarter specific action not found, 680 * if restarter specific action found but an error was encountered 681 * during the generation of the wip->fmri based filename 682 */ 683 static int 684 common_by_restarter(scf_instance_t *inst, const char *fmri, 685 int *restarter_specp) 686 { 687 int ret = -1; 688 int r; 689 690 /* Check for inetd specific restarter */ 691 if (check_for_restarter(inst, "svc:/network/inetd:default") != 0) { 692 *restarter_specp = 0; 693 return (ret); 694 } 695 696 *restarter_specp = 1; 697 698 /* Get the ctid filename associated with this instance */ 699 r = gen_filenms_from_fmri(fmri, "ctid", genfmri_filename, NULL); 700 701 switch (r) { 702 case 0: 703 break; 704 705 case -1: 706 /* 707 * Unable to get filename from fmri. Print warning 708 * and return failure with no ctids. 709 */ 710 uu_warn(gettext("Unable to read contract ids for %s -- " 711 "FMRI is too long\n"), fmri); 712 return (ret); 713 714 case -2: 715 /* 716 * The directory didn't exist, so no contracts. 717 * Return failure with no ctids. 718 */ 719 return (ret); 720 721 default: 722 uu_warn(gettext("%s:%d: gen_filenms_from_fmri() failed with " 723 "unknown error %d\n"), __FILE__, __LINE__, r); 724 abort(); 725 } 726 727 return (0); 728 729 } 730 731 /* 732 * Get or print a contract id using a restarter specific action. 733 * 734 * If the print_flag is not set, this routine gets the single contract 735 * id associated with this instance. 736 * If the print flag is set, then print each contract id found. 737 * 738 * Returns: 739 * 0 if success: restarter specific action found and used with no error 740 * -1 if restarter specific action not found 741 * -1 if restarter specific action found, but there was a failure 742 * -1 if print flag is not set and no contract id is found or multiple 743 * contract ids were found 744 * E2BIG if print flag is not set, MULTI_OK bit in flag is set and multiple 745 * contract ids were found 746 */ 747 static int 748 ctids_by_restarter(scf_walkinfo_t *wip, uint64_t *cp, int print_flag, 749 uint_t flags, int *restarter_specp, void (*callback_header)(), 750 void (*callback_ctid)(uint64_t)) 751 { 752 FILE *fp; 753 int ret = -1; 754 int fscanf_ret; 755 uint64_t cp2; 756 int rest_ret; 757 758 /* Check if callbacks are needed and were passed in */ 759 if (print_flag) { 760 if ((callback_header == NULL) || (callback_ctid == NULL)) 761 return (ret); 762 } 763 764 /* Check for restarter specific action and generation of filename */ 765 rest_ret = common_by_restarter(wip->inst, wip->fmri, restarter_specp); 766 if (rest_ret != 0) 767 return (rest_ret); 768 769 /* 770 * If fopen fails, then ctid file hasn't been created yet. 771 * If print_flag is set, this is ok; otherwise fail. 772 */ 773 if ((fp = fopen(genfmri_filename, "r")) == NULL) { 774 if (print_flag) 775 return (0); 776 goto out; 777 } 778 779 if (print_flag) { 780 /* 781 * Print all contract ids that are found. 782 * First callback to print ctid header. 783 */ 784 callback_header(); 785 786 /* fscanf may not set errno, so be sure to clear it first */ 787 errno = 0; 788 while ((fscanf_ret = fscanf(fp, "%llu", cp)) == 1) { 789 /* Callback to print contract id */ 790 callback_ctid(*cp); 791 errno = 0; 792 } 793 /* EOF is not a failure when no errno. */ 794 if ((fscanf_ret != EOF) || (errno != 0)) { 795 uu_die(gettext("Unable to read ctid file for %s"), 796 wip->fmri); 797 } 798 (void) putchar('\n'); 799 ret = 0; 800 } else { 801 /* Must find 1 ctid or fail */ 802 if (fscanf(fp, "%llu", cp) == 1) { 803 /* If 2nd ctid found - fail */ 804 if (fscanf(fp, "%llu", &cp2) == 1) { 805 if (flags & MULTI_OK) 806 ret = E2BIG; 807 } else { 808 /* Success - found only 1 ctid */ 809 ret = 0; 810 } 811 } 812 } 813 (void) fclose(fp); 814 815 out: 816 return (ret); 817 } 818 819 /* 820 * Get the process ids associated with an instance using a restarter 821 * specific action. 822 * 823 * Returns: 824 * 0 if success: restarter specific action found and used with no error 825 * -1 restarter specific action not found or if failure 826 */ 827 static int 828 pids_by_restarter(scf_instance_t *inst, const char *fmri, 829 pid_t **pids, uint_t *np, int *restarter_specp) 830 { 831 uint64_t c; 832 FILE *fp; 833 int fscanf_ret; 834 int rest_ret; 835 836 /* Check for restarter specific action and generation of filename */ 837 rest_ret = common_by_restarter(inst, fmri, restarter_specp); 838 if (rest_ret != 0) 839 return (rest_ret); 840 841 /* 842 * If fopen fails with ENOENT then the ctid file hasn't been 843 * created yet so return success. 844 * For all other errors - fail with uu_die. 845 */ 846 if ((fp = fopen(genfmri_filename, "r")) == NULL) { 847 if (errno == ENOENT) 848 return (0); 849 uu_die(gettext("Unable to open ctid file for %s"), fmri); 850 } 851 852 /* fscanf may not set errno, so be sure to clear it first */ 853 errno = 0; 854 while ((fscanf_ret = fscanf(fp, "%llu", &c)) == 1) { 855 if (c == 0) { 856 (void) fclose(fp); 857 uu_die(gettext("ctid file for %s has corrupt data"), 858 fmri); 859 } 860 ctid_to_pids(c, pids, np); 861 errno = 0; 862 } 863 /* EOF is not a failure when no errno. */ 864 if ((fscanf_ret != EOF) || (errno != 0)) { 865 uu_die(gettext("Unable to read ctid file for %s"), fmri); 866 } 867 868 (void) fclose(fp); 869 return (0); 870 } 871 872 static int 873 instance_processes(scf_instance_t *inst, const char *fmri, 874 pid_t **pids, uint_t *np) 875 { 876 scf_iter_t *iter; 877 int ret; 878 int restarter_spec; 879 880 /* Use the restarter specific get pids routine, if available. */ 881 ret = pids_by_restarter(inst, fmri, pids, np, &restarter_spec); 882 if (restarter_spec == 1) 883 return (ret); 884 885 if ((iter = scf_iter_create(h)) == NULL) 886 scfdie(); 887 888 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, g_pg) == 0) { 889 *pids = NULL; 890 *np = 0; 891 892 (void) propvals_to_pids(g_pg, scf_property_contract, pids, np, 893 g_prop, g_val, iter); 894 895 (void) propvals_to_pids(g_pg, SCF_PROPERTY_TRANSIENT_CONTRACT, 896 pids, np, g_prop, g_val, iter); 897 898 ret = 0; 899 } else { 900 if (scf_error() != SCF_ERROR_NOT_FOUND) 901 scfdie(); 902 903 ret = -1; 904 } 905 906 scf_iter_destroy(iter); 907 908 return (ret); 909 } 910 911 static int 912 get_psinfo(pid_t pid, psinfo_t *psip) 913 { 914 char path[100]; 915 int fd; 916 917 (void) snprintf(path, sizeof (path), "/proc/%lu/psinfo", pid); 918 919 fd = open64(path, O_RDONLY); 920 if (fd < 0) 921 return (-1); 922 923 if (read(fd, psip, sizeof (*psip)) < 0) 924 uu_die(gettext("Could not read info for process %lu"), pid); 925 926 (void) close(fd); 927 928 return (0); 929 } 930 931 932 933 /* 934 * Column sprint and sortkey functions 935 */ 936 937 struct column { 938 const char *name; 939 int width; 940 941 /* 942 * This function should write the value for the column into buf, and 943 * grow or allocate buf accordingly. It should always write at least 944 * width bytes, blanking unused bytes with spaces. If the field is 945 * greater than the column width we allow it to overlap other columns. 946 * In particular, it shouldn't write any null bytes. (Though an extra 947 * null byte past the end is currently tolerated.) If the property 948 * group is non-NULL, then we are dealing with a legacy service. 949 */ 950 void (*sprint)(char **, scf_walkinfo_t *); 951 952 int sortkey_width; 953 954 /* 955 * This function should write sortkey_width bytes into buf which will 956 * cause memcmp() to sort it properly. (Unlike sprint() above, 957 * however, an extra null byte may overrun the buffer.) The second 958 * argument controls whether the results are sorted in forward or 959 * reverse order. 960 */ 961 void (*get_sortkey)(char *, int, scf_walkinfo_t *); 962 }; 963 964 static void 965 reverse_bytes(char *buf, size_t len) 966 { 967 int i; 968 969 for (i = 0; i < len; ++i) 970 buf[i] = ~buf[i]; 971 } 972 973 /* CTID */ 974 #define CTID_COLUMN_WIDTH 6 975 976 static void 977 sprint_ctid(char **buf, scf_walkinfo_t *wip) 978 { 979 int r; 980 uint64_t c; 981 size_t newsize = (*buf ? strlen(*buf) : 0) + CTID_COLUMN_WIDTH + 2; 982 char *newbuf = safe_malloc(newsize); 983 int restarter_spec; 984 985 /* 986 * Use the restarter specific get pids routine, if available. 987 * Only check for non-legacy services (wip->pg == 0). 988 */ 989 if (wip->pg != NULL) { 990 r = pg_get_single_val(wip->pg, scf_property_contract, 991 SCF_TYPE_COUNT, &c, 0, EMPTY_OK | MULTI_OK); 992 } else { 993 r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec, 994 NULL, NULL); 995 if (restarter_spec == 0) { 996 /* No restarter specific routine */ 997 r = get_restarter_count_prop(wip->inst, 998 scf_property_contract, &c, EMPTY_OK | MULTI_OK); 999 } 1000 } 1001 1002 if (r == 0) 1003 (void) snprintf(newbuf, newsize, "%s%*lu ", 1004 *buf ? *buf : "", CTID_COLUMN_WIDTH, (ctid_t)c); 1005 else if (r == E2BIG) 1006 (void) snprintf(newbuf, newsize, "%s%*lu* ", 1007 *buf ? *buf : "", CTID_COLUMN_WIDTH - 1, (ctid_t)c); 1008 else 1009 (void) snprintf(newbuf, newsize, "%s%*s ", 1010 *buf ? *buf : "", CTID_COLUMN_WIDTH, "-"); 1011 if (*buf) 1012 free(*buf); 1013 *buf = newbuf; 1014 } 1015 1016 #define CTID_SORTKEY_WIDTH (sizeof (uint64_t)) 1017 1018 static void 1019 sortkey_ctid(char *buf, int reverse, scf_walkinfo_t *wip) 1020 { 1021 int r; 1022 uint64_t c; 1023 int restarter_spec; 1024 1025 /* 1026 * Use the restarter specific get pids routine, if available. 1027 * Only check for non-legacy services (wip->pg == 0). 1028 */ 1029 if (wip->pg != NULL) { 1030 r = pg_get_single_val(wip->pg, scf_property_contract, 1031 SCF_TYPE_COUNT, &c, 0, EMPTY_OK); 1032 } else { 1033 r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec, 1034 NULL, NULL); 1035 if (restarter_spec == 0) { 1036 /* No restarter specific routine */ 1037 r = get_restarter_count_prop(wip->inst, 1038 scf_property_contract, &c, EMPTY_OK); 1039 } 1040 } 1041 1042 if (r == 0) { 1043 /* 1044 * Use the id itself, but it must be big-endian for this to 1045 * work. 1046 */ 1047 c = BE_64(c); 1048 1049 bcopy(&c, buf, CTID_SORTKEY_WIDTH); 1050 } else { 1051 bzero(buf, CTID_SORTKEY_WIDTH); 1052 } 1053 1054 if (reverse) 1055 reverse_bytes(buf, CTID_SORTKEY_WIDTH); 1056 } 1057 1058 /* DESC */ 1059 #define DESC_COLUMN_WIDTH 100 1060 1061 static void 1062 sprint_desc(char **buf, scf_walkinfo_t *wip) 1063 { 1064 char *x; 1065 size_t newsize; 1066 char *newbuf; 1067 1068 if (common_name_buf == NULL) 1069 common_name_buf = safe_malloc(max_scf_value_length + 1); 1070 1071 bzero(common_name_buf, max_scf_value_length + 1); 1072 1073 if (wip->pg != NULL) { 1074 common_name_buf[0] = '-'; 1075 } else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale, 1076 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1077 1, 1) == -1 && 1078 inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C", 1079 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1080 1, 1) == -1) { 1081 common_name_buf[0] = '-'; 1082 } 1083 1084 /* 1085 * Collapse multi-line tm_common_name values into a single line. 1086 */ 1087 for (x = common_name_buf; *x != '\0'; x++) 1088 if (*x == '\n') 1089 *x = ' '; 1090 1091 if (strlen(common_name_buf) > DESC_COLUMN_WIDTH) 1092 newsize = (*buf ? strlen(*buf) : 0) + 1093 strlen(common_name_buf) + 1; 1094 else 1095 newsize = (*buf ? strlen(*buf) : 0) + DESC_COLUMN_WIDTH + 1; 1096 newbuf = safe_malloc(newsize); 1097 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1098 DESC_COLUMN_WIDTH, common_name_buf); 1099 if (*buf) 1100 free(*buf); 1101 *buf = newbuf; 1102 } 1103 1104 /* ARGSUSED */ 1105 static void 1106 sortkey_desc(char *buf, int reverse, scf_walkinfo_t *wip) 1107 { 1108 bzero(buf, DESC_COLUMN_WIDTH); 1109 } 1110 1111 /* State columns (STATE, NSTATE, S, N, SN, STA, NSTA) */ 1112 1113 static char 1114 state_to_char(const char *state) 1115 { 1116 if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0) 1117 return ('u'); 1118 1119 if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0) 1120 return ('0'); 1121 1122 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) 1123 return ('1'); 1124 1125 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) 1126 return ('m'); 1127 1128 if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0) 1129 return ('d'); 1130 1131 if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) 1132 return ('D'); 1133 1134 if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0) 1135 return ('L'); 1136 1137 return ('?'); 1138 } 1139 1140 /* Return true if inst is transitioning. */ 1141 static int 1142 transitioning(scf_instance_t *inst) 1143 { 1144 char nstate_name[MAX_SCF_STATE_STRING_SZ]; 1145 1146 get_restarter_string_prop(inst, scf_property_next_state, nstate_name, 1147 sizeof (nstate_name)); 1148 1149 return (state_to_char(nstate_name) != '?'); 1150 } 1151 1152 /* ARGSUSED */ 1153 static void 1154 sortkey_states(const char *pname, char *buf, int reverse, scf_walkinfo_t *wip) 1155 { 1156 char state_name[MAX_SCF_STATE_STRING_SZ]; 1157 1158 /* 1159 * Lower numbers are printed first, so these are arranged from least 1160 * interesting ("legacy run") to most interesting (unknown). 1161 */ 1162 if (wip->pg == NULL) { 1163 get_restarter_string_prop(wip->inst, pname, state_name, 1164 sizeof (state_name)); 1165 1166 if (strcmp(state_name, SCF_STATE_STRING_ONLINE) == 0) 1167 *buf = 2; 1168 else if (strcmp(state_name, SCF_STATE_STRING_DEGRADED) == 0) 1169 *buf = 3; 1170 else if (strcmp(state_name, SCF_STATE_STRING_OFFLINE) == 0) 1171 *buf = 4; 1172 else if (strcmp(state_name, SCF_STATE_STRING_MAINT) == 0) 1173 *buf = 5; 1174 else if (strcmp(state_name, SCF_STATE_STRING_DISABLED) == 0) 1175 *buf = 1; 1176 else if (strcmp(state_name, SCF_STATE_STRING_UNINIT) == 0) 1177 *buf = 6; 1178 else 1179 *buf = 7; 1180 } else 1181 *buf = 0; 1182 1183 if (reverse) 1184 *buf = 255 - *buf; 1185 } 1186 1187 static void 1188 sprint_state(char **buf, scf_walkinfo_t *wip) 1189 { 1190 char state_name[MAX_SCF_STATE_STRING_SZ + 1]; 1191 size_t newsize; 1192 char *newbuf; 1193 1194 if (wip->pg == NULL) { 1195 get_restarter_string_prop(wip->inst, scf_property_state, 1196 state_name, sizeof (state_name)); 1197 1198 /* Don't print blank fields, to ease parsing. */ 1199 if (state_name[0] == '\0') { 1200 state_name[0] = '-'; 1201 state_name[1] = '\0'; 1202 } 1203 1204 if (!opt_nstate_shown && transitioning(wip->inst)) { 1205 /* Append an asterisk if nstate is valid. */ 1206 (void) strcat(state_name, "*"); 1207 } 1208 } else 1209 (void) strcpy(state_name, SCF_STATE_STRING_LEGACY); 1210 1211 newsize = (*buf ? strlen(*buf) : 0) + MAX_SCF_STATE_STRING_SZ + 2; 1212 newbuf = safe_malloc(newsize); 1213 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1214 MAX_SCF_STATE_STRING_SZ + 1, state_name); 1215 1216 if (*buf) 1217 free(*buf); 1218 *buf = newbuf; 1219 } 1220 1221 static void 1222 sortkey_state(char *buf, int reverse, scf_walkinfo_t *wip) 1223 { 1224 sortkey_states(scf_property_state, buf, reverse, wip); 1225 } 1226 1227 static void 1228 sprint_nstate(char **buf, scf_walkinfo_t *wip) 1229 { 1230 char next_state_name[MAX_SCF_STATE_STRING_SZ]; 1231 boolean_t blank = 0; 1232 size_t newsize; 1233 char *newbuf; 1234 1235 if (wip->pg == NULL) { 1236 get_restarter_string_prop(wip->inst, scf_property_next_state, 1237 next_state_name, sizeof (next_state_name)); 1238 1239 /* Don't print blank fields, to ease parsing. */ 1240 if (next_state_name[0] == '\0' || 1241 strcmp(next_state_name, SCF_STATE_STRING_NONE) == 0) 1242 blank = 1; 1243 } else 1244 blank = 1; 1245 1246 if (blank) { 1247 next_state_name[0] = '-'; 1248 next_state_name[1] = '\0'; 1249 } 1250 1251 newsize = (*buf ? strlen(*buf) : 0) + MAX_SCF_STATE_STRING_SZ + 1; 1252 newbuf = safe_malloc(newsize); 1253 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1254 MAX_SCF_STATE_STRING_SZ - 1, next_state_name); 1255 if (*buf) 1256 free(*buf); 1257 *buf = newbuf; 1258 } 1259 1260 static void 1261 sortkey_nstate(char *buf, int reverse, scf_walkinfo_t *wip) 1262 { 1263 sortkey_states(scf_property_next_state, buf, reverse, wip); 1264 } 1265 1266 static void 1267 sprint_s(char **buf, scf_walkinfo_t *wip) 1268 { 1269 char tmp[3]; 1270 char state_name[MAX_SCF_STATE_STRING_SZ]; 1271 size_t newsize = (*buf ? strlen(*buf) : 0) + 4; 1272 char *newbuf = safe_malloc(newsize); 1273 1274 if (wip->pg == NULL) { 1275 get_restarter_string_prop(wip->inst, scf_property_state, 1276 state_name, sizeof (state_name)); 1277 tmp[0] = state_to_char(state_name); 1278 1279 if (!opt_nstate_shown && transitioning(wip->inst)) 1280 tmp[1] = '*'; 1281 else 1282 tmp[1] = ' '; 1283 } else { 1284 tmp[0] = 'L'; 1285 tmp[1] = ' '; 1286 } 1287 tmp[2] = ' '; 1288 (void) snprintf(newbuf, newsize, "%s%-*s", *buf ? *buf : "", 1289 3, tmp); 1290 if (*buf) 1291 free(*buf); 1292 *buf = newbuf; 1293 } 1294 1295 static void 1296 sprint_n(char **buf, scf_walkinfo_t *wip) 1297 { 1298 char tmp[2]; 1299 size_t newsize = (*buf ? strlen(*buf) : 0) + 3; 1300 char *newbuf = safe_malloc(newsize); 1301 char nstate_name[MAX_SCF_STATE_STRING_SZ]; 1302 1303 if (wip->pg == NULL) { 1304 get_restarter_string_prop(wip->inst, scf_property_next_state, 1305 nstate_name, sizeof (nstate_name)); 1306 1307 if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0) 1308 tmp[0] = '-'; 1309 else 1310 tmp[0] = state_to_char(nstate_name); 1311 } else 1312 tmp[0] = '-'; 1313 1314 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1315 2, tmp); 1316 if (*buf) 1317 free(*buf); 1318 *buf = newbuf; 1319 } 1320 1321 static void 1322 sprint_sn(char **buf, scf_walkinfo_t *wip) 1323 { 1324 char tmp[3]; 1325 size_t newsize = (*buf ? strlen(*buf) : 0) + 4; 1326 char *newbuf = safe_malloc(newsize); 1327 char nstate_name[MAX_SCF_STATE_STRING_SZ]; 1328 char state_name[MAX_SCF_STATE_STRING_SZ]; 1329 1330 if (wip->pg == NULL) { 1331 get_restarter_string_prop(wip->inst, scf_property_state, 1332 state_name, sizeof (state_name)); 1333 get_restarter_string_prop(wip->inst, scf_property_next_state, 1334 nstate_name, sizeof (nstate_name)); 1335 tmp[0] = state_to_char(state_name); 1336 1337 if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0) 1338 tmp[1] = '-'; 1339 else 1340 tmp[1] = state_to_char(nstate_name); 1341 } else { 1342 tmp[0] = 'L'; 1343 tmp[1] = '-'; 1344 } 1345 1346 tmp[2] = ' '; 1347 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "", 1348 3, tmp); 1349 if (*buf) 1350 free(*buf); 1351 *buf = newbuf; 1352 } 1353 1354 /* ARGSUSED */ 1355 static void 1356 sortkey_sn(char *buf, int reverse, scf_walkinfo_t *wip) 1357 { 1358 sortkey_state(buf, reverse, wip); 1359 sortkey_nstate(buf + 1, reverse, wip); 1360 } 1361 1362 static const char * 1363 state_abbrev(const char *state) 1364 { 1365 if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0) 1366 return ("UN"); 1367 if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0) 1368 return ("OFF"); 1369 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) 1370 return ("ON"); 1371 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) 1372 return ("MNT"); 1373 if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0) 1374 return ("DIS"); 1375 if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) 1376 return ("DGD"); 1377 if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0) 1378 return ("LRC"); 1379 1380 return ("?"); 1381 } 1382 1383 static void 1384 sprint_sta(char **buf, scf_walkinfo_t *wip) 1385 { 1386 char state_name[MAX_SCF_STATE_STRING_SZ]; 1387 char sta[5]; 1388 size_t newsize = (*buf ? strlen(*buf) : 0) + 6; 1389 char *newbuf = safe_malloc(newsize); 1390 1391 if (wip->pg == NULL) 1392 get_restarter_string_prop(wip->inst, scf_property_state, 1393 state_name, sizeof (state_name)); 1394 else 1395 (void) strcpy(state_name, SCF_STATE_STRING_LEGACY); 1396 1397 (void) strcpy(sta, state_abbrev(state_name)); 1398 1399 if (wip->pg == NULL && !opt_nstate_shown && transitioning(wip->inst)) 1400 (void) strcat(sta, "*"); 1401 1402 (void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "", sta); 1403 if (*buf) 1404 free(*buf); 1405 *buf = newbuf; 1406 } 1407 1408 static void 1409 sprint_nsta(char **buf, scf_walkinfo_t *wip) 1410 { 1411 char state_name[MAX_SCF_STATE_STRING_SZ]; 1412 size_t newsize = (*buf ? strlen(*buf) : 0) + 6; 1413 char *newbuf = safe_malloc(newsize); 1414 1415 if (wip->pg == NULL) 1416 get_restarter_string_prop(wip->inst, scf_property_next_state, 1417 state_name, sizeof (state_name)); 1418 else 1419 (void) strcpy(state_name, SCF_STATE_STRING_NONE); 1420 1421 if (strcmp(state_name, SCF_STATE_STRING_NONE) == 0) 1422 (void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "", 1423 "-"); 1424 else 1425 (void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "", 1426 state_abbrev(state_name)); 1427 if (*buf) 1428 free(*buf); 1429 *buf = newbuf; 1430 } 1431 1432 /* FMRI */ 1433 #define FMRI_COLUMN_WIDTH 50 1434 static void 1435 sprint_fmri(char **buf, scf_walkinfo_t *wip) 1436 { 1437 char *fmri_buf = safe_malloc(max_scf_fmri_length + 1); 1438 size_t newsize; 1439 char *newbuf; 1440 1441 if (wip->pg == NULL) { 1442 if (scf_instance_to_fmri(wip->inst, fmri_buf, 1443 max_scf_fmri_length + 1) == -1) 1444 scfdie(); 1445 } else { 1446 (void) strcpy(fmri_buf, LEGACY_SCHEME); 1447 if (pg_get_single_val(wip->pg, SCF_LEGACY_PROPERTY_NAME, 1448 SCF_TYPE_ASTRING, fmri_buf + sizeof (LEGACY_SCHEME) - 1, 1449 max_scf_fmri_length + 1 - (sizeof (LEGACY_SCHEME) - 1), 1450 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