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 /* 28 * Copyright (c) 2011, Joyent, Inc. All rights reserved. 29 */ 30 31 /* 32 * svcprop - report service configuration properties 33 */ 34 35 #include <locale.h> 36 #include <libintl.h> 37 #include <libscf.h> 38 #include <libscf_priv.h> 39 #include <libuutil.h> 40 #include <stddef.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 #include <strings.h> 45 #include <assert.h> 46 #include <zone.h> 47 48 #ifndef TEXT_DOMAIN 49 #define TEXT_DOMAIN "SUNW_OST_OSCMD" 50 #endif /* TEXT_DOMAIN */ 51 52 /* 53 * Error functions. These can change if the quiet (-q) option is used. 54 */ 55 static void (*warn)(const char *, ...) = uu_warn; 56 static void (*die)(const char *, ...) = uu_die; 57 58 /* 59 * Entity encapsulation. This allows me to treat services and instances 60 * similarly, and avoid duplicating process_ent(). 61 */ 62 typedef struct { 63 char type; /* !=0: service, 0: instance */ 64 union { 65 scf_service_t *svc; 66 scf_instance_t *inst; 67 } u; 68 } scf_entityp_t; 69 70 #define ENT_INSTANCE 0 71 72 #define SCF_ENTITY_SET_TO_SERVICE(ent, s) { ent.type = 1; ent.u.svc = s; } 73 74 #define SCF_ENTITY_SET_TO_INSTANCE(ent, i) \ 75 { ent.type = ENT_INSTANCE; ent.u.inst = i; } 76 77 #define scf_entity_get_pg(ent, name, pg) \ 78 (ent.type ? scf_service_get_pg(ent.u.svc, name, pg) : \ 79 scf_instance_get_pg(ent.u.inst, name, pg)) 80 81 #define scf_entity_to_fmri(ent, buf, buf_sz) \ 82 (ent.type ? scf_service_to_fmri(ent.u.svc, buf, buf_sz) : \ 83 scf_instance_to_fmri(ent.u.inst, buf, buf_sz)) 84 85 #define SCF_ENTITY_TYPE_NAME(ent) (ent.type ? "service" : "instance") 86 87 /* 88 * Data structure for -p arguments. Since they may be name or name/name, we 89 * just track the components. 90 */ 91 typedef struct svcprop_prop_node { 92 uu_list_node_t spn_list_node; 93 const char *spn_comp1; 94 const char *spn_comp2; 95 } svcprop_prop_node_t; 96 97 static uu_list_pool_t *prop_pool; 98 static uu_list_t *prop_list; 99 100 static scf_handle_t *hndl; 101 static ssize_t max_scf_name_length; 102 static ssize_t max_scf_value_length; 103 static ssize_t max_scf_fmri_length; 104 105 /* Options */ 106 static int quiet = 0; /* No output. Nothing found, exit(1) */ 107 static int types = 0; /* Display types of properties. */ 108 static int verbose = 0; /* Print not found errors to stderr. */ 109 static int fmris = 0; /* Display full FMRIs for properties. */ 110 static int wait = 0; /* Wait mode. */ 111 static char *snapshot = "running"; /* Snapshot to use. */ 112 static int Cflag = 0; /* C option supplied */ 113 static int cflag = 0; /* c option supplied */ 114 static int sflag = 0; /* s option supplied */ 115 static int return_code; /* main's return code */ 116 117 #define PRINT_NOPROP_ERRORS (!quiet || verbose) 118 119 /* 120 * For unexpected libscf errors. The ending newline is necessary to keep 121 * uu_die() from appending the errno error. 122 */ 123 static void 124 scfdie() 125 { 126 die(gettext("Unexpected libscf error: %s. Exiting.\n"), 127 scf_strerror(scf_error())); 128 } 129 130 static void * 131 safe_malloc(size_t sz) 132 { 133 void *p; 134 135 p = malloc(sz); 136 if (p == NULL) 137 die(gettext("Could not allocate memory")); 138 139 return (p); 140 } 141 142 static void 143 usage() 144 { 145 (void) fprintf(stderr, gettext("Usage: %1$s [-fqtv] " 146 "[-C | -c | -s snapshot] [-z zone] " 147 "[-p [name/]name]... \n" 148 " {FMRI | pattern}...\n" 149 " %1$s -w [-fqtv] [-z zone] [-p [name/]name] " 150 "{FMRI | pattern}\n"), uu_getpname()); 151 exit(UU_EXIT_USAGE); 152 } 153 154 /* 155 * Return an allocated copy of str, with the Bourne shell's metacharacters 156 * escaped by '\'. 157 * 158 * What about unicode? 159 */ 160 static char * 161 quote_for_shell(const char *str) 162 { 163 const char *sp; 164 char *dst, *dp; 165 size_t dst_len; 166 167 const char * const metachars = ";&()|^<>\n \t\\\"\'`"; 168 169 if (str[0] == '\0') 170 return (strdup("\"\"")); 171 172 dst_len = 0; 173 for (sp = str; *sp != '\0'; ++sp) { 174 ++dst_len; 175 176 if (strchr(metachars, *sp) != NULL) 177 ++dst_len; 178 } 179 180 if (sp - str == dst_len) 181 return (strdup(str)); 182 183 dst = safe_malloc(dst_len + 1); 184 185 for (dp = dst, sp = str; *sp != '\0'; ++dp, ++sp) { 186 if (strchr(metachars, *sp) != NULL) 187 *dp++ = '\\'; 188 189 *dp = *sp; 190 } 191 *dp = '\0'; 192 193 return (dst); 194 } 195 196 static void 197 print_value(scf_value_t *val) 198 { 199 char *buf, *qbuf; 200 ssize_t bufsz, r; 201 202 bufsz = scf_value_get_as_string(val, NULL, 0) + 1; 203 if (bufsz - 1 < 0) 204 scfdie(); 205 206 buf = safe_malloc(bufsz); 207 208 r = scf_value_get_as_string(val, buf, bufsz); 209 assert(r + 1 == bufsz); 210 211 qbuf = quote_for_shell(buf); 212 (void) fputs(qbuf, stdout); 213 214 free(qbuf); 215 free(buf); 216 } 217 218 /* 219 * Display a property's values on a line. If types is true, prepend 220 * identification (the FMRI if fmris is true, pg/prop otherwise) and the type 221 * of the property. 222 */ 223 static void 224 display_prop(scf_propertygroup_t *pg, scf_property_t *prop) 225 { 226 scf_value_t *val; 227 scf_iter_t *iter; 228 int ret, first, err; 229 230 const char * const permission_denied_emsg = 231 gettext("Permission denied.\n"); 232 233 if (types) { 234 scf_type_t ty; 235 char *buf; 236 size_t buf_sz; 237 238 if (fmris) { 239 buf_sz = max_scf_fmri_length + 1; 240 buf = safe_malloc(buf_sz); 241 242 if (scf_property_to_fmri(prop, buf, buf_sz) == -1) 243 scfdie(); 244 (void) fputs(buf, stdout); 245 246 free(buf); 247 } else { 248 buf_sz = max_scf_name_length + 1; 249 buf = safe_malloc(buf_sz); 250 251 if (scf_pg_get_name(pg, buf, buf_sz) < 0) 252 scfdie(); 253 (void) fputs(buf, stdout); 254 (void) putchar('/'); 255 256 if (scf_property_get_name(prop, buf, buf_sz) < 0) 257 scfdie(); 258 (void) fputs(buf, stdout); 259 260 free(buf); 261 } 262 263 (void) putchar(' '); 264 265 if (scf_property_type(prop, &ty) == -1) 266 scfdie(); 267 (void) fputs(scf_type_to_string(ty), stdout); 268 (void) putchar(' '); 269 } 270 271 if ((iter = scf_iter_create(hndl)) == NULL || 272 (val = scf_value_create(hndl)) == NULL) 273 scfdie(); 274 275 if (scf_iter_property_values(iter, prop) == -1) 276 scfdie(); 277 278 first = 1; 279 while ((ret = scf_iter_next_value(iter, val)) == 1) { 280 if (first) 281 first = 0; 282 else 283 (void) putchar(' '); 284 print_value(val); 285 } 286 if (ret == -1) { 287 err = scf_error(); 288 if (err == SCF_ERROR_PERMISSION_DENIED) { 289 if (uu_list_numnodes(prop_list) > 0) 290 die(permission_denied_emsg); 291 } else { 292 scfdie(); 293 } 294 } 295 296 (void) putchar('\n'); 297 298 scf_iter_destroy(iter); 299 (void) scf_value_destroy(val); 300 } 301 302 /* 303 * display_prop() all of the properties in the given property group. Force 304 * types to true so identification will be displayed. 305 */ 306 static void 307 display_pg(scf_propertygroup_t *pg) 308 { 309 scf_property_t *prop; 310 scf_iter_t *iter; 311 int ret; 312 313 types = 1; /* Always display types for whole propertygroups. */ 314 315 if ((prop = scf_property_create(hndl)) == NULL || 316 (iter = scf_iter_create(hndl)) == NULL) 317 scfdie(); 318 319 if (scf_iter_pg_properties(iter, pg) == -1) 320 scfdie(); 321 322 while ((ret = scf_iter_next_property(iter, prop)) == 1) 323 display_prop(pg, prop); 324 if (ret == -1) 325 scfdie(); 326 327 scf_iter_destroy(iter); 328 scf_property_destroy(prop); 329 } 330 331 /* 332 * Common code to execute when a nonexistant property is encountered. 333 */ 334 static void 335 noprop_common_action() 336 { 337 if (!PRINT_NOPROP_ERRORS) 338 /* We're not printing errors, so we can cut out early. */ 339 exit(UU_EXIT_FATAL); 340 341 return_code = UU_EXIT_FATAL; 342 } 343 344 /* 345 * Iterate the properties of a service or an instance when no snapshot 346 * is specified. 347 */ 348 static int 349 scf_iter_entity_pgs(scf_iter_t *iter, scf_entityp_t ent) 350 { 351 int ret = 0; 352 353 if (ent.type) { 354 /* 355 * If we are displaying properties for a service, 356 * treat it as though it were a composed, current 357 * lookup. (implicit cflag) However, if a snapshot 358 * was specified, fail. 359 */ 360 if (sflag) 361 die(gettext("Only instances have " 362 "snapshots.\n")); 363 ret = scf_iter_service_pgs(iter, ent.u.svc); 364 } else { 365 if (Cflag) 366 ret = scf_iter_instance_pgs(iter, ent.u.inst); 367 else 368 ret = scf_iter_instance_pgs_composed(iter, ent.u.inst, 369 NULL); 370 } 371 return (ret); 372 } 373 374 /* 375 * Return a snapshot for the supplied instance and snapshot name. 376 */ 377 static scf_snapshot_t * 378 get_snapshot(const scf_instance_t *inst, const char *snapshot) 379 { 380 scf_snapshot_t *snap = scf_snapshot_create(hndl); 381 382 if (snap == NULL) 383 scfdie(); 384 385 if (scf_instance_get_snapshot(inst, snapshot, snap) == -1) { 386 switch (scf_error()) { 387 case SCF_ERROR_INVALID_ARGUMENT: 388 die(gettext("Invalid snapshot name.\n")); 389 /* NOTREACHED */ 390 391 case SCF_ERROR_NOT_FOUND: 392 if (sflag == 0) { 393 scf_snapshot_destroy(snap); 394 snap = NULL; 395 } else 396 die(gettext("No such snapshot.\n")); 397 break; 398 399 default: 400 scfdie(); 401 } 402 } 403 404 return (snap); 405 } 406 407 /* 408 * Entity (service or instance): If there are -p options, 409 * display_{pg,prop}() the named property groups and/or properties. Otherwise 410 * display_pg() all property groups. 411 */ 412 static void 413 process_ent(scf_entityp_t ent) 414 { 415 scf_snapshot_t *snap = NULL; 416 scf_propertygroup_t *pg; 417 scf_property_t *prop; 418 scf_iter_t *iter; 419 svcprop_prop_node_t *spn; 420 int ret, err; 421 422 if (uu_list_numnodes(prop_list) == 0) { 423 if (quiet) 424 return; 425 426 if ((pg = scf_pg_create(hndl)) == NULL || 427 (iter = scf_iter_create(hndl)) == NULL) 428 scfdie(); 429 430 if (cflag || Cflag || ent.type != ENT_INSTANCE) { 431 if (scf_iter_entity_pgs(iter, ent) == -1) 432 scfdie(); 433 } else { 434 if (snapshot != NULL) 435 snap = get_snapshot(ent.u.inst, snapshot); 436 437 if (scf_iter_instance_pgs_composed(iter, ent.u.inst, 438 snap) == -1) 439 scfdie(); 440 if (snap) 441 scf_snapshot_destroy(snap); 442 } 443 444 while ((ret = scf_iter_next_pg(iter, pg)) == 1) 445 display_pg(pg); 446 if (ret == -1) 447 scfdie(); 448 449 /* 450 * In normal usage, i.e. against the running snapshot, 451 * we must iterate over the current non-persistent 452 * pg's. 453 */ 454 if (sflag == 0 && snap != NULL) { 455 scf_iter_reset(iter); 456 if (scf_iter_instance_pgs_composed(iter, ent.u.inst, 457 NULL) == -1) 458 scfdie(); 459 while ((ret = scf_iter_next_pg(iter, pg)) == 1) { 460 uint32_t flags; 461 462 if (scf_pg_get_flags(pg, &flags) == -1) 463 scfdie(); 464 if (flags & SCF_PG_FLAG_NONPERSISTENT) 465 display_pg(pg); 466 } 467 } 468 if (ret == -1) 469 scfdie(); 470 471 scf_iter_destroy(iter); 472 scf_pg_destroy(pg); 473 474 return; 475 } 476 477 if ((pg = scf_pg_create(hndl)) == NULL || 478 (prop = scf_property_create(hndl)) == NULL) 479 scfdie(); 480 481 if (ent.type == ENT_INSTANCE && snapshot != NULL) 482 snap = get_snapshot(ent.u.inst, snapshot); 483 484 for (spn = uu_list_first(prop_list); 485 spn != NULL; 486 spn = uu_list_next(prop_list, spn)) { 487 if (ent.type == ENT_INSTANCE) { 488 if (Cflag) 489 ret = scf_instance_get_pg(ent.u.inst, 490 spn->spn_comp1, pg); 491 else 492 ret = scf_instance_get_pg_composed(ent.u.inst, 493 snap, spn->spn_comp1, pg); 494 err = scf_error(); 495 496 /* 497 * If we didn't find it in the specified snapshot, use 498 * the current values if the pg is nonpersistent. 499 */ 500 if (ret == -1 && !Cflag &&snap != NULL && err == 501 SCF_ERROR_NOT_FOUND) { 502 ret = scf_instance_get_pg_composed( 503 ent.u.inst, NULL, spn->spn_comp1, 504 pg); 505 506 if (ret == 0) { 507 uint32_t flags; 508 509 if (scf_pg_get_flags(pg, &flags) == -1) 510 scfdie(); 511 if ((flags & SCF_PG_FLAG_NONPERSISTENT) 512 == 0) { 513 ret = -1; 514 } 515 } 516 } 517 } else { 518 /* 519 * If we are displaying properties for a service, 520 * treat it as though it were a composed, current 521 * lookup. (implicit cflag) However, if a snapshot 522 * was specified, fail. 523 */ 524 if (sflag) 525 die(gettext("Only instances have " 526 "snapshots.\n")); 527 ret = scf_entity_get_pg(ent, spn->spn_comp1, pg); 528 err = scf_error(); 529 } 530 if (ret == -1) { 531 if (err != SCF_ERROR_NOT_FOUND) 532 scfdie(); 533 534 if (PRINT_NOPROP_ERRORS) { 535 char *buf; 536 537 buf = safe_malloc(max_scf_fmri_length + 1); 538 if (scf_entity_to_fmri(ent, buf, 539 max_scf_fmri_length + 1) == -1) 540 scfdie(); 541 542 uu_warn(gettext("Couldn't find property group " 543 "`%s' for %s `%s'.\n"), spn->spn_comp1, 544 SCF_ENTITY_TYPE_NAME(ent), buf); 545 546 free(buf); 547 } 548 549 noprop_common_action(); 550 551 continue; 552 } 553 554 if (spn->spn_comp2 == NULL) { 555 if (!quiet) 556 display_pg(pg); 557 continue; 558 } 559 560 if (scf_pg_get_property(pg, spn->spn_comp2, prop) == -1) { 561 if (scf_error() != SCF_ERROR_NOT_FOUND) 562 scfdie(); 563 564 if (PRINT_NOPROP_ERRORS) { 565 char *buf; 566 567 buf = safe_malloc(max_scf_fmri_length + 1); 568 if (scf_entity_to_fmri(ent, buf, 569 max_scf_fmri_length + 1) == -1) 570 scfdie(); 571 572 /* FMRI syntax knowledge */ 573 uu_warn(gettext("Couldn't find property " 574 "`%s/%s' for %s `%s'.\n"), spn->spn_comp1, 575 spn->spn_comp2, SCF_ENTITY_TYPE_NAME(ent), 576 buf); 577 578 free(buf); 579 } 580 581 noprop_common_action(); 582 583 continue; 584 } 585 586 if (!quiet) 587 display_prop(pg, prop); 588 } 589 590 scf_property_destroy(prop); 591 scf_pg_destroy(pg); 592 if (snap) 593 scf_snapshot_destroy(snap); 594 } 595 596 /* 597 * Without -p options, just call display_pg(). Otherwise display_prop() the 598 * named properties of the property group. 599 */ 600 static void 601 process_pg(scf_propertygroup_t *pg) 602 { 603 scf_property_t *prop; 604 svcprop_prop_node_t *spn; 605 606 if (uu_list_first(prop_list) == NULL) { 607 if (quiet) 608 return; 609 610 display_pg(pg); 611 return; 612 } 613 614 prop = scf_property_create(hndl); 615 if (prop == NULL) 616 scfdie(); 617 618 for (spn = uu_list_first(prop_list); 619 spn != NULL; 620 spn = uu_list_next(prop_list, spn)) { 621 if (spn->spn_comp2 != NULL) { 622 char *buf; 623 624 buf = safe_malloc(max_scf_fmri_length + 1); 625 if (scf_pg_to_fmri(pg, buf, max_scf_fmri_length + 1) == 626 -1) 627 scfdie(); 628 629 uu_xdie(UU_EXIT_USAGE, gettext("-p argument `%s/%s' " 630 "has too many components for property " 631 "group `%s'.\n"), spn->spn_comp1, spn->spn_comp2, 632 buf); 633 634 free(buf); 635 } 636 637 if (scf_pg_get_property(pg, spn->spn_comp1, prop) == 0) { 638 if (!quiet) 639 display_prop(pg, prop); 640 continue; 641 } 642 643 if (scf_error() != SCF_ERROR_NOT_FOUND) 644 scfdie(); 645 646 if (PRINT_NOPROP_ERRORS) { 647 char *buf; 648 649 buf = safe_malloc(max_scf_fmri_length + 1); 650 if (scf_pg_to_fmri(pg, buf, max_scf_fmri_length + 1) == 651 -1) 652 scfdie(); 653 654 uu_warn(gettext("Couldn't find property `%s' in " 655 "property group `%s'.\n"), spn->spn_comp1, buf); 656 657 free(buf); 658 } 659 660 noprop_common_action(); 661 } 662 } 663 664 /* 665 * If there are -p options, show the error. Otherwise just call 666 * display_prop(). 667 */ 668 static void 669 process_prop(scf_propertygroup_t *pg, scf_property_t *prop) 670 { 671 if (uu_list_first(prop_list) != NULL) { 672 uu_warn(gettext("The -p option cannot be used with property " 673 "operands.\n")); 674 usage(); 675 } 676 677 if (quiet) 678 return; 679 680 display_prop(pg, prop); 681 } 682 683 /* Decode an operand & dispatch. */ 684 /* ARGSUSED */ 685 static int 686 process_fmri(void *unused, scf_walkinfo_t *wip) 687 { 688 scf_entityp_t ent; 689 690 /* Multiple matches imply multiple entities. */ 691 if (wip->count > 1) 692 types = fmris = 1; 693 694 if (wip->prop != NULL) { 695 process_prop(wip->pg, wip->prop); 696 } else if (wip->pg != NULL) { 697 process_pg(wip->pg); 698 } else if (wip->inst != NULL) { 699 SCF_ENTITY_SET_TO_INSTANCE(ent, wip->inst); 700 process_ent(ent); 701 } else { 702 /* scf_walk_fmri() won't let this happen */ 703 assert(wip->svc != NULL); 704 SCF_ENTITY_SET_TO_SERVICE(ent, wip->svc); 705 process_ent(ent); 706 } 707 708 return (0); 709 } 710 711 static void 712 add_prop(char *property) 713 { 714 svcprop_prop_node_t *p, *last; 715 char *slash; 716 717 const char * const invalid_component_emsg = 718 gettext("Invalid component name `%s'.\n"); 719 720 /* FMRI syntax knowledge. */ 721 slash = strchr(property, '/'); 722 if (slash != NULL) { 723 if (strchr(slash + 1, '/') != NULL) { 724 uu_warn(gettext("-p argument `%s' has too many " 725 "components.\n"), property); 726 usage(); 727 } 728 } 729 730 if (slash != NULL) 731 *slash = '\0'; 732 733 p = safe_malloc(sizeof (svcprop_prop_node_t)); 734 uu_list_node_init(p, &p->spn_list_node, prop_pool); 735 736 p->spn_comp1 = property; 737 p->spn_comp2 = (slash == NULL) ? NULL : slash + 1; 738 739 if (uu_check_name(p->spn_comp1, UU_NAME_DOMAIN) == -1) 740 uu_xdie(UU_EXIT_USAGE, invalid_component_emsg, p->spn_comp1); 741 if (p->spn_comp2 != NULL && 742 uu_check_name(p->spn_comp2, UU_NAME_DOMAIN) == -1) 743 uu_xdie(UU_EXIT_USAGE, invalid_component_emsg, p->spn_comp2); 744 745 last = uu_list_last(prop_list); 746 if (last != NULL) { 747 if ((last->spn_comp2 == NULL) ^ (p->spn_comp2 == NULL)) { 748 /* 749 * The -p options have mixed numbers of components. 750 * If they both turn out to be valid, then the 751 * single-component ones will specify property groups, 752 * so we need to turn on types to keep the output of 753 * display_prop() consistent with display_pg(). 754 */ 755 types = 1; 756 } 757 } 758 759 (void) uu_list_insert_after(prop_list, NULL, p); 760 } 761 762 763 /* 764 * Wait for a property group or property change. 765 * 766 * Extract a pg and optionally a property name from fmri & prop_list. 767 * _scf_pg_wait() for the pg, and display_pg(pg) or display_prop(pg, prop) 768 * when it returns. 769 */ 770 /* ARGSUSED */ 771 static int 772 do_wait(void *unused, scf_walkinfo_t *wip) 773 { 774 scf_property_t *prop; 775 scf_propertygroup_t *lpg, *pg; 776 const char *propname; 777 svcprop_prop_node_t *p; 778 779 const char *emsg_not_found = gettext("Not found.\n"); 780 781 if ((lpg = scf_pg_create(hndl)) == NULL || 782 (prop = scf_property_create(hndl)) == NULL) 783 scfdie(); 784 785 if (wip->prop != NULL) { 786 if (uu_list_numnodes(prop_list) > 0) 787 uu_xdie(UU_EXIT_USAGE, gettext("-p cannot be used with " 788 "property FMRIs.\n")); 789 pg = wip->pg; 790 791 assert(strrchr(wip->fmri, '/') != NULL); 792 propname = strrchr(wip->fmri, '/') + 1; 793 794 } else if (wip->pg != NULL) { 795 p = uu_list_first(prop_list); 796 797 if (p != NULL) { 798 if (p->spn_comp2 != NULL) 799 uu_xdie(UU_EXIT_USAGE, gettext("-p argument " 800 "\"%s/%s\" has too many components for " 801 "property group %s.\n"), 802 p->spn_comp1, p->spn_comp2, wip->fmri); 803 804 propname = p->spn_comp1; 805 806 if (scf_pg_get_property(wip->pg, propname, prop) != 807 SCF_SUCCESS) { 808 switch (scf_error()) { 809 case SCF_ERROR_INVALID_ARGUMENT: 810 uu_xdie(UU_EXIT_USAGE, 811 gettext("Invalid property name " 812 "\"%s\".\n"), propname); 813 814 /* NOTREACHED */ 815 816 case SCF_ERROR_NOT_FOUND: 817 die(emsg_not_found); 818 819 /* NOTREACHED */ 820 821 default: 822 scfdie(); 823 } 824 } 825 } else { 826 propname = NULL; 827 } 828 829 pg = wip->pg; 830 831 } else if (wip->inst != NULL) { 832 833 p = uu_list_first(prop_list); 834 if (p == NULL) 835 uu_xdie(UU_EXIT_USAGE, 836 gettext("Cannot wait for an instance.\n")); 837 838 if (scf_instance_get_pg(wip->inst, p->spn_comp1, lpg) != 839 SCF_SUCCESS) { 840 switch (scf_error()) { 841 case SCF_ERROR_INVALID_ARGUMENT: 842 uu_xdie(UU_EXIT_USAGE, gettext("Invalid " 843 "property group name \"%s\".\n"), 844 p->spn_comp1); 845 846 case SCF_ERROR_NOT_FOUND: 847 die(emsg_not_found); 848 849 /* NOTREACHED */ 850 851 default: 852 scfdie(); 853 } 854 } 855 856 propname = p->spn_comp2; 857 858 if (propname != NULL) { 859 if (scf_pg_get_property(lpg, propname, prop) != 860 SCF_SUCCESS) { 861 switch (scf_error()) { 862 case SCF_ERROR_INVALID_ARGUMENT: 863 uu_xdie(UU_EXIT_USAGE, 864 gettext("Invalid property name " 865 "\"%s\".\n"), propname); 866 867 case SCF_ERROR_NOT_FOUND: 868 die(emsg_not_found); 869 870 /* NOTREACHED */ 871 872 default: 873 scfdie(); 874 } 875 } 876 } 877 878 pg = lpg; 879 880 } else if (wip->svc != NULL) { 881 882 p = uu_list_first(prop_list); 883 if (p == NULL) 884 uu_xdie(UU_EXIT_USAGE, 885 gettext("Cannot wait for a service.\n")); 886 887 if (scf_service_get_pg(wip->svc, p->spn_comp1, lpg) != 888 SCF_SUCCESS) { 889 switch (scf_error()) { 890 case SCF_ERROR_INVALID_ARGUMENT: 891 uu_xdie(UU_EXIT_USAGE, gettext("Invalid " 892 "property group name \"%s\".\n"), 893 p->spn_comp1); 894 895 case SCF_ERROR_NOT_FOUND: 896 die(emsg_not_found); 897 898 default: 899 scfdie(); 900 } 901 } 902 903 propname = p->spn_comp2; 904 905 if (propname != NULL) { 906 if (scf_pg_get_property(lpg, propname, prop) != 907 SCF_SUCCESS) { 908 switch (scf_error()) { 909 case SCF_ERROR_INVALID_ARGUMENT: 910 uu_xdie(UU_EXIT_USAGE, 911 gettext("Invalid property name " 912 "\"%s\".\n"), propname); 913 914 /* NOTREACHED */ 915 916 case SCF_ERROR_NOT_FOUND: 917 die(emsg_not_found); 918 919 /* NOTREACHED */ 920 921 default: 922 scfdie(); 923 } 924 } 925 } 926 927 pg = lpg; 928 929 } else { 930 uu_xdie(UU_EXIT_USAGE, gettext("FMRI must specify an entity, " 931 "property group, or property.\n")); 932 } 933 934 for (;;) { 935 int ret; 936 937 ret = _scf_pg_wait(pg, -1); 938 if (ret != SCF_SUCCESS) 939 scfdie(); 940 941 ret = scf_pg_update(pg); 942 if (ret < 0) { 943 if (scf_error() != SCF_ERROR_DELETED) 944 scfdie(); 945 946 die(emsg_not_found); 947 } 948 if (ret == SCF_COMPLETE) 949 break; 950 } 951 952 if (propname != NULL) { 953 if (scf_pg_get_property(pg, propname, prop) == SCF_SUCCESS) { 954 if (!quiet) 955 display_prop(pg, prop); 956 } else { 957 if (scf_error() != SCF_ERROR_NOT_FOUND) 958 scfdie(); 959 960 if (PRINT_NOPROP_ERRORS) 961 uu_warn(emsg_not_found); 962 963 return_code = UU_EXIT_FATAL; 964 } 965 } else { 966 if (!quiet) 967 display_pg(pg); 968 } 969 970 scf_property_destroy(prop); 971 scf_pg_destroy(lpg); 972 973 return (0); 974 } 975 976 /* 977 * These functions replace uu_warn() and uu_die() when the quiet (-q) option is 978 * used, and silently ignore any output. 979 */ 980 981 /*ARGSUSED*/ 982 static void 983 quiet_warn(const char *fmt, ...) 984 { 985 /* Do nothing */ 986 } 987 988 /*ARGSUSED*/ 989 static void 990 quiet_die(const char *fmt, ...) 991 { 992 exit(UU_EXIT_FATAL); 993 } 994 995 int 996 main(int argc, char *argv[]) 997 { 998 int c; 999 scf_walk_callback callback; 1000 int flags; 1001 int err; 1002 1003 (void) setlocale(LC_ALL, ""); 1004 (void) textdomain(TEXT_DOMAIN); 1005 1006 return_code = UU_EXIT_OK; 1007 1008 (void) uu_setpname(argv[0]); 1009 1010 prop_pool = uu_list_pool_create("properties", 1011 sizeof (svcprop_prop_node_t), 1012 offsetof(svcprop_prop_node_t, spn_list_node), NULL, 0); 1013 if (prop_pool == NULL) 1014 uu_die("%s\n", uu_strerror(uu_error())); 1015 1016 prop_list = uu_list_create(prop_pool, NULL, 0); 1017 1018 hndl = scf_handle_create(SCF_VERSION); 1019 if (hndl == NULL) 1020 scfdie(); 1021 1022 while ((c = getopt(argc, argv, "Ccfp:qs:tvwz:")) != -1) { 1023 switch (c) { 1024 case 'C': 1025 if (cflag || sflag || wait) 1026 usage(); /* Not with -c, -s or -w */ 1027 Cflag++; 1028 snapshot = NULL; 1029 break; 1030 1031 case 'c': 1032 if (Cflag || sflag || wait) 1033 usage(); /* Not with -C, -s or -w */ 1034 cflag++; 1035 snapshot = NULL; 1036 break; 1037 1038 case 'f': 1039 types = 1; 1040 fmris = 1; 1041 break; 1042 1043 case 'p': 1044 add_prop(optarg); 1045 break; 1046 1047 case 'q': 1048 quiet = 1; 1049 warn = quiet_warn; 1050 die = quiet_die; 1051 break; 1052 1053 case 's': 1054 if (Cflag || cflag || wait) 1055 usage(); /* Not with -C, -c or -w */ 1056 snapshot = optarg; 1057 sflag++; 1058 break; 1059 1060 case 't': 1061 types = 1; 1062 break; 1063 1064 case 'v': 1065 verbose = 1; 1066 break; 1067 1068 case 'w': 1069 if (Cflag || cflag || sflag) 1070 usage(); /* Not with -C, -c or -s */ 1071 wait = 1; 1072 break; 1073 1074 case 'z': { 1075 scf_value_t *zone; 1076 scf_handle_t *h = hndl; 1077 1078 if (getzoneid() != GLOBAL_ZONEID) 1079 uu_die(gettext("svcprop -z may only be used " 1080 "from the global zone\n")); 1081 1082 if ((zone = scf_value_create(h)) == NULL) 1083 scfdie(); 1084 1085 if (scf_value_set_astring(zone, optarg) != SCF_SUCCESS) 1086 scfdie(); 1087 1088 if (scf_handle_decorate(h, "zone", zone) != SCF_SUCCESS) 1089 uu_die(gettext("invalid zone '%s'\n"), optarg); 1090 1091 scf_value_destroy(zone); 1092 break; 1093 } 1094 1095 case '?': 1096 switch (optopt) { 1097 case 'p': 1098 usage(); 1099 1100 default: 1101 break; 1102 } 1103 1104 /* FALLTHROUGH */ 1105 1106 default: 1107 usage(); 1108 } 1109 } 1110 1111 if (optind == argc) 1112 usage(); 1113 1114 max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH); 1115 max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 1116 max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH); 1117 if (max_scf_name_length == -1 || max_scf_value_length == -1 || 1118 max_scf_fmri_length == -1) 1119 scfdie(); 1120 1121 if (scf_handle_bind(hndl) == -1) 1122 die(gettext("Could not connect to configuration repository: " 1123 "%s.\n"), scf_strerror(scf_error())); 1124 1125 flags = SCF_WALK_PROPERTY | SCF_WALK_SERVICE | SCF_WALK_EXPLICIT; 1126 1127 if (wait) { 1128 if (uu_list_numnodes(prop_list) > 1) 1129 usage(); 1130 1131 if (argc - optind > 1) 1132 usage(); 1133 1134 callback = do_wait; 1135 1136 } else { 1137 callback = process_fmri; 1138 1139 flags |= SCF_WALK_MULTIPLE; 1140 } 1141 1142 if ((err = scf_walk_fmri(hndl, argc - optind, argv + optind, flags, 1143 callback, NULL, &return_code, warn)) != 0) { 1144 warn(gettext("failed to iterate over instances: %s\n"), 1145 scf_strerror(err)); 1146 return_code = UU_EXIT_FATAL; 1147 } 1148 1149 scf_handle_destroy(hndl); 1150 1151 return (return_code); 1152 } 1153