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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * svcadm - request adminstrative actions for service instances 30 */ 31 32 #include <locale.h> 33 #include <libintl.h> 34 #include <libscf.h> 35 #include <libscf_priv.h> 36 #include <libuutil.h> 37 #include <stddef.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include <assert.h> 43 #include <errno.h> 44 45 #ifndef TEXT_DOMAIN 46 #define TEXT_DOMAIN "SUNW_OST_OSCMD" 47 #endif /* TEXT_DOMAIN */ 48 49 /* Must be a power of two */ 50 #define HT_BUCKETS 64 51 52 /* 53 * Exit codes for enable and disable -s. 54 */ 55 #define EXIT_SVC_FAILURE 3 56 #define EXIT_DEP_FAILURE 4 57 58 /* 59 * How long we will wait (in seconds) for a service to change state 60 * before re-checking its dependencies. 61 */ 62 #define WAIT_INTERVAL 3 63 64 #ifndef NDEBUG 65 #define bad_error(func, err) { \ 66 uu_warn("%s:%d: %s() failed with unexpected error %d.\n", \ 67 __FILE__, __LINE__, (func), (err)); \ 68 abort(); \ 69 } 70 #else 71 #define bad_error(func, err) abort() 72 #endif 73 74 75 struct ht_elt { 76 struct ht_elt *next; 77 boolean_t active; 78 char str[1]; 79 }; 80 81 82 scf_handle_t *h; 83 ssize_t max_scf_fmri_sz; 84 static const char *emsg_permission_denied; 85 static const char *emsg_nomem; 86 static const char *emsg_create_pg_perm_denied; 87 static const char *emsg_pg_perm_denied; 88 static const char *emsg_prop_perm_denied; 89 static const char *emsg_no_service; 90 91 static int exit_status = 0; 92 static int verbose = 0; 93 static char *scratch_fmri; 94 95 static struct ht_elt **visited; 96 97 void do_scfdie(int lineno) __NORETURN; 98 static void usage_milestone(void) __NORETURN; 99 100 /* 101 * Visitors from synch.c, needed for enable -s and disable -s. 102 */ 103 extern int is_enabled(scf_instance_t *); 104 extern int has_potential(scf_instance_t *, int); 105 106 void 107 do_scfdie(int lineno) 108 { 109 scf_error_t err; 110 111 switch (err = scf_error()) { 112 case SCF_ERROR_CONNECTION_BROKEN: 113 uu_die(gettext("Connection to repository server broken. " 114 "Exiting.\n")); 115 /* NOTREACHED */ 116 117 case SCF_ERROR_BACKEND_READONLY: 118 uu_die(gettext("Repository is read-only. Exiting.\n")); 119 /* NOTREACHED */ 120 121 default: 122 #ifdef NDEBUG 123 uu_die(gettext("Unexpected libscf error: %s. Exiting.\n"), 124 scf_strerror(err)); 125 #else 126 uu_die("Unexpected libscf error on line %d: %s.\n", lineno, 127 scf_strerror(err)); 128 #endif 129 } 130 } 131 132 #define scfdie() do_scfdie(__LINE__) 133 134 static void 135 usage() 136 { 137 (void) fprintf(stderr, gettext( 138 "Usage: %1$s [-v] [cmd [args ... ]]\n\n" 139 "\t%1$s enable [-rst] <service> ...\t- enable and online service(s)\n" 140 "\t%1$s disable [-st] <service> ...\t- disable and offline service(s)\n" 141 "\t%1$s restart <service> ...\t\t- restart specified service(s)\n" 142 "\t%1$s refresh <service> ...\t\t- re-read service configuration\n" 143 "\t%1$s mark [-It] <state> <service> ...\t- set maintenance state\n" 144 "\t%1$s clear <service> ...\t\t- clear maintenance state\n" 145 "\t%1$s milestone [-d] <milestone>\t- advance to a service milestone\n" 146 "\n\t" 147 "Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n" 148 "\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n" 149 "\n" 150 "\t%1$s <cmd> svc:/network/smtp:sendmail\n" 151 "\t%1$s <cmd> network/smtp:sendmail\n" 152 "\t%1$s <cmd> network/*mail\n" 153 "\t%1$s <cmd> network/smtp\n" 154 "\t%1$s <cmd> smtp:sendmail\n" 155 "\t%1$s <cmd> smtp\n" 156 "\t%1$s <cmd> sendmail\n"), uu_getpname()); 157 158 exit(UU_EXIT_USAGE); 159 } 160 161 162 /* 163 * FMRI hash table for recursive enable. 164 */ 165 166 static uint32_t 167 hash_fmri(const char *str) 168 { 169 uint32_t h = 0, g; 170 const char *p; 171 172 /* Generic hash function from uts/common/os/modhash.c . */ 173 for (p = str; *p != '\0'; ++p) { 174 h = (h << 4) + *p; 175 if ((g = (h & 0xf0000000)) != 0) { 176 h ^= (g >> 24); 177 h ^= g; 178 } 179 } 180 181 return (h); 182 } 183 184 /* 185 * Return 1 if str has been visited, 0 if it has not, and -1 if memory could not 186 * be allocated. 187 */ 188 static int 189 visited_find_or_add(const char *str, struct ht_elt **hep) 190 { 191 uint32_t h; 192 uint_t i; 193 struct ht_elt *he; 194 195 h = hash_fmri(str); 196 i = h & (HT_BUCKETS - 1); 197 198 for (he = visited[i]; he != NULL; he = he->next) { 199 if (strcmp(he->str, str) == 0) { 200 if (hep) 201 *hep = he; 202 return (1); 203 } 204 } 205 206 he = malloc(offsetof(struct ht_elt, str) + strlen(str) + 1); 207 if (he == NULL) 208 return (-1); 209 210 (void) strcpy(he->str, str); 211 212 he->next = visited[i]; 213 visited[i] = he; 214 215 if (hep) 216 *hep = he; 217 return (0); 218 } 219 220 221 /* 222 * Returns 0, ECANCELED if pg is deleted, ENOENT if propname doesn't exist, 223 * EINVAL if the property is not of boolean type or has no values, and E2BIG 224 * if it has more than one value. *bp is set if 0 or E2BIG is returned. 225 */ 226 int 227 get_bool_prop(scf_propertygroup_t *pg, const char *propname, uint8_t *bp) 228 { 229 scf_property_t *prop; 230 scf_value_t *val; 231 int ret; 232 233 if ((prop = scf_property_create(h)) == NULL || 234 (val = scf_value_create(h)) == NULL) 235 scfdie(); 236 237 if (scf_pg_get_property(pg, propname, prop) != 0) { 238 switch (scf_error()) { 239 case SCF_ERROR_DELETED: 240 ret = ECANCELED; 241 goto out; 242 243 case SCF_ERROR_NOT_FOUND: 244 ret = ENOENT; 245 goto out; 246 247 case SCF_ERROR_NOT_SET: 248 assert(0); 249 abort(); 250 /* NOTREACHED */ 251 252 default: 253 scfdie(); 254 } 255 } 256 257 if (scf_property_get_value(prop, val) == 0) { 258 ret = 0; 259 } else { 260 switch (scf_error()) { 261 case SCF_ERROR_DELETED: 262 ret = ENOENT; 263 goto out; 264 265 case SCF_ERROR_NOT_FOUND: 266 ret = EINVAL; 267 goto out; 268 269 case SCF_ERROR_CONSTRAINT_VIOLATED: 270 ret = E2BIG; 271 break; 272 273 case SCF_ERROR_NOT_SET: 274 assert(0); 275 abort(); 276 /* NOTREACHED */ 277 278 default: 279 scfdie(); 280 } 281 } 282 283 if (scf_value_get_boolean(val, bp) != 0) { 284 if (scf_error() != SCF_ERROR_TYPE_MISMATCH) 285 scfdie(); 286 287 ret = EINVAL; 288 goto out; 289 } 290 291 out: 292 scf_value_destroy(val); 293 scf_property_destroy(prop); 294 return (ret); 295 } 296 297 /* 298 * Returns 0, EPERM, or EROFS. 299 */ 300 static int 301 set_bool_prop(scf_propertygroup_t *pg, const char *propname, boolean_t b) 302 { 303 scf_value_t *v; 304 scf_transaction_t *tx; 305 scf_transaction_entry_t *ent; 306 int ret = 0, r; 307 308 if ((tx = scf_transaction_create(h)) == NULL || 309 (ent = scf_entry_create(h)) == NULL || 310 (v = scf_value_create(h)) == NULL) 311 scfdie(); 312 313 scf_value_set_boolean(v, b); 314 315 for (;;) { 316 if (scf_transaction_start(tx, pg) == -1) { 317 switch (scf_error()) { 318 case SCF_ERROR_PERMISSION_DENIED: 319 ret = EPERM; 320 goto out; 321 322 case SCF_ERROR_BACKEND_READONLY: 323 ret = EROFS; 324 goto out; 325 326 default: 327 scfdie(); 328 } 329 } 330 331 if (scf_transaction_property_change_type(tx, ent, propname, 332 SCF_TYPE_BOOLEAN) != 0) { 333 if (scf_error() != SCF_ERROR_NOT_FOUND) 334 scfdie(); 335 336 if (scf_transaction_property_new(tx, ent, propname, 337 SCF_TYPE_BOOLEAN) != 0) 338 scfdie(); 339 } 340 341 r = scf_entry_add_value(ent, v); 342 assert(r == 0); 343 344 r = scf_transaction_commit(tx); 345 if (r == 1) 346 break; 347 348 scf_transaction_reset(tx); 349 350 if (r != 0) { 351 switch (scf_error()) { 352 case SCF_ERROR_PERMISSION_DENIED: 353 ret = EPERM; 354 goto out; 355 356 case SCF_ERROR_BACKEND_READONLY: 357 ret = EROFS; 358 goto out; 359 360 default: 361 scfdie(); 362 } 363 } 364 365 if (scf_pg_update(pg) == -1) 366 scfdie(); 367 } 368 369 out: 370 scf_transaction_destroy(tx); 371 scf_entry_destroy(ent); 372 scf_value_destroy(v); 373 return (ret); 374 } 375 376 /* 377 * Gets the single astring value of the propname property of pg. prop & v are 378 * scratch space. Returns the length of the string on success or 379 * -ENOENT - pg has no property named propname 380 * -E2BIG - property has no values or multiple values 381 * -EINVAL - property type is not compatible with astring 382 */ 383 ssize_t 384 get_astring_prop(const scf_propertygroup_t *pg, const char *propname, 385 scf_property_t *prop, scf_value_t *v, char *buf, size_t bufsz) 386 { 387 ssize_t sz; 388 389 if (scf_pg_get_property(pg, propname, prop) != 0) { 390 if (scf_error() != SCF_ERROR_NOT_FOUND) 391 scfdie(); 392 393 return (-ENOENT); 394 } 395 396 if (scf_property_get_value(prop, v) != 0) { 397 switch (scf_error()) { 398 case SCF_ERROR_NOT_FOUND: 399 case SCF_ERROR_CONSTRAINT_VIOLATED: 400 return (-E2BIG); 401 402 default: 403 scfdie(); 404 } 405 } 406 407 sz = scf_value_get_astring(v, buf, bufsz); 408 if (sz < 0) { 409 if (scf_error() != SCF_ERROR_TYPE_MISMATCH) 410 scfdie(); 411 412 return (-EINVAL); 413 } 414 415 return (sz); 416 } 417 418 /* 419 * Returns 420 * 0 - success 421 * ECANCELED - pg was deleted 422 * EPERM - permission denied 423 * EACCES - access denied 424 * EROFS - readonly 425 */ 426 static int 427 delete_prop(scf_propertygroup_t *pg, const char *propname) 428 { 429 scf_transaction_t *tx; 430 scf_transaction_entry_t *ent; 431 int ret = 0, r; 432 433 if ((tx = scf_transaction_create(h)) == NULL || 434 (ent = scf_entry_create(h)) == NULL) 435 scfdie(); 436 437 for (;;) { 438 if (scf_transaction_start(tx, pg) == -1) { 439 switch (scf_error()) { 440 case SCF_ERROR_DELETED: 441 ret = ECANCELED; 442 goto out; 443 444 case SCF_ERROR_PERMISSION_DENIED: 445 ret = EPERM; 446 goto out; 447 448 case SCF_ERROR_BACKEND_ACCESS: 449 ret = EACCES; 450 goto out; 451 452 case SCF_ERROR_BACKEND_READONLY: 453 ret = EROFS; 454 goto out; 455 456 case SCF_ERROR_CONNECTION_BROKEN: 457 case SCF_ERROR_HANDLE_MISMATCH: 458 case SCF_ERROR_NOT_BOUND: 459 case SCF_ERROR_NOT_SET: 460 case SCF_ERROR_IN_USE: 461 default: 462 scfdie(); 463 } 464 } 465 466 if (scf_transaction_property_delete(tx, ent, propname) == -1) 467 switch (scf_error()) { 468 case SCF_ERROR_DELETED: 469 ret = ECANCELED; 470 goto out; 471 472 case SCF_ERROR_NOT_FOUND: 473 ret = 0; 474 goto out; 475 476 case SCF_ERROR_HANDLE_MISMATCH: 477 case SCF_ERROR_NOT_BOUND: 478 case SCF_ERROR_CONNECTION_BROKEN: 479 case SCF_ERROR_INVALID_ARGUMENT: 480 case SCF_ERROR_NOT_SET: 481 default: 482 scfdie(); 483 } 484 485 r = scf_transaction_commit(tx); 486 if (r == 1) 487 break; 488 489 scf_transaction_reset(tx); 490 491 if (r != 0) { 492 switch (scf_error()) { 493 case SCF_ERROR_DELETED: 494 ret = ECANCELED; 495 goto out; 496 497 case SCF_ERROR_PERMISSION_DENIED: 498 ret = EPERM; 499 goto out; 500 501 case SCF_ERROR_BACKEND_ACCESS: 502 ret = EACCES; 503 goto out; 504 505 case SCF_ERROR_BACKEND_READONLY: 506 ret = EROFS; 507 goto out; 508 509 case SCF_ERROR_INVALID_ARGUMENT: 510 case SCF_ERROR_NOT_SET: 511 case SCF_ERROR_NOT_BOUND: 512 case SCF_ERROR_CONNECTION_BROKEN: 513 default: 514 scfdie(); 515 } 516 } 517 518 if (scf_pg_update(pg) == -1) { 519 if (scf_error() != SCF_ERROR_DELETED) 520 scfdie(); 521 522 ret = ECANCELED; 523 goto out; 524 } 525 } 526 527 out: 528 scf_transaction_destroy(tx); 529 scf_entry_destroy(ent); 530 return (ret); 531 } 532 533 /* 534 * Returns 0 or EPERM. 535 */ 536 static int 537 pg_get_or_add(scf_instance_t *inst, const char *pgname, const char *pgtype, 538 uint32_t pgflags, scf_propertygroup_t *pg) 539 { 540 again: 541 if (scf_instance_get_pg(inst, pgname, pg) == 0) 542 return (0); 543 544 if (scf_error() != SCF_ERROR_NOT_FOUND) 545 scfdie(); 546 547 if (scf_instance_add_pg(inst, pgname, pgtype, pgflags, pg) == 0) 548 return (0); 549 550 switch (scf_error()) { 551 case SCF_ERROR_EXISTS: 552 goto again; 553 554 case SCF_ERROR_PERMISSION_DENIED: 555 return (EPERM); 556 557 default: 558 scfdie(); 559 /* NOTREACHED */ 560 } 561 } 562 563 /* 564 * Enable or disable inst, per enable. If temp is true, set 565 * general_ovr/enabled. Otherwise set general/enabled and delete 566 * general_ovr/enabled if it exists (order is important here: we don't want the 567 * enabled status to glitch). 568 */ 569 static void 570 set_inst_enabled(const char *fmri, scf_instance_t *inst, boolean_t temp, 571 boolean_t enable) 572 { 573 scf_propertygroup_t *pg; 574 uint8_t b; 575 const char *pgname = NULL; /* For emsg_pg_perm_denied */ 576 int r; 577 578 pg = scf_pg_create(h); 579 if (pg == NULL) 580 scfdie(); 581 582 /* 583 * An instance's configuration is incomplete if general/enabled 584 * doesn't exist. Create both the property group and property 585 * here if they don't exist. 586 */ 587 pgname = SCF_PG_GENERAL; 588 if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE, 589 SCF_PG_GENERAL_FLAGS, pg) != 0) 590 goto eperm; 591 592 if (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b) != 0) { 593 /* Create and set state to disabled */ 594 switch (set_bool_prop(pg, SCF_PROPERTY_ENABLED, B_FALSE) != 0) { 595 case 0: 596 break; 597 598 case EPERM: 599 goto eperm; 600 601 case EROFS: 602 /* Shouldn't happen, but it can. */ 603 if (!verbose) 604 uu_warn(gettext("%s: Repository read-only.\n"), 605 fmri); 606 else 607 uu_warn(gettext("%s: Could not set %s/%s " 608 "(repository read-only).\n"), fmri, 609 SCF_PG_GENERAL, SCF_PROPERTY_ENABLED); 610 goto out; 611 612 default: 613 assert(0); 614 abort(); 615 } 616 } 617 618 if (temp) { 619 /* Set general_ovr/enabled */ 620 pgname = SCF_PG_GENERAL_OVR; 621 if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_OVR_TYPE, 622 SCF_PG_GENERAL_OVR_FLAGS, pg) != 0) 623 goto eperm; 624 625 switch (set_bool_prop(pg, SCF_PROPERTY_ENABLED, enable) != 0) { 626 case 0: 627 break; 628 629 case EPERM: 630 goto eperm; 631 632 case EROFS: 633 /* Shouldn't happen, but it can. */ 634 if (!verbose) 635 uu_warn(gettext("%s: Repository read-only.\n"), 636 fmri); 637 else 638 uu_warn(gettext("%s: Could not set %s/%s " 639 "(repository read-only).\n"), fmri, 640 SCF_PG_GENERAL_OVR, SCF_PROPERTY_ENABLED); 641 goto out; 642 643 default: 644 assert(0); 645 abort(); 646 } 647 648 if (verbose) 649 (void) printf(enable ? 650 gettext("%s temporarily enabled.\n") : 651 gettext("%s temporarily disabled.\n"), fmri); 652 } else { 653 again: 654 /* 655 * Both pg and property should exist since we created 656 * them earlier. However, there's still a chance that 657 * someone may have deleted the property out from under 658 * us. 659 */ 660 if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE, 661 SCF_PG_GENERAL_FLAGS, pg) != 0) 662 goto eperm; 663 664 switch (set_bool_prop(pg, SCF_PROPERTY_ENABLED, enable)) { 665 case 0: 666 break; 667 668 case EPERM: 669 goto eperm; 670 671 case EROFS: 672 /* 673 * If general/enabled is already set the way we want, 674 * proceed. 675 */ 676 switch (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b)) { 677 case 0: 678 if ((b != 0) == (enable != B_FALSE)) 679 break; 680 /* FALLTHROUGH */ 681 682 case ENOENT: 683 case EINVAL: 684 case E2BIG: 685 if (!verbose) 686 uu_warn(gettext("%s: Repository " 687 "read-only.\n"), fmri); 688 else 689 uu_warn(gettext("%s: Could not set " 690 "%s/%s (repository read-only).\n"), 691 fmri, SCF_PG_GENERAL, 692 SCF_PROPERTY_ENABLED); 693 goto out; 694 695 case ECANCELED: 696 goto again; 697 698 default: 699 assert(0); 700 abort(); 701 } 702 break; 703 704 default: 705 assert(0); 706 abort(); 707 } 708 709 pgname = SCF_PG_GENERAL_OVR; 710 if (scf_instance_get_pg(inst, pgname, pg) == 0) { 711 r = delete_prop(pg, SCF_PROPERTY_ENABLED); 712 switch (r) { 713 case 0: 714 break; 715 716 case ECANCELED: 717 uu_warn(emsg_no_service, fmri); 718 goto out; 719 720 case EPERM: 721 goto eperm; 722 723 case EACCES: 724 uu_warn(gettext("Could not delete %s/%s " 725 "property of %s: backend access denied.\n"), 726 pgname, SCF_PROPERTY_ENABLED, fmri); 727 goto out; 728 729 case EROFS: 730 uu_warn(gettext("Could not delete %s/%s " 731 "property of %s: backend is read-only.\n"), 732 pgname, SCF_PROPERTY_ENABLED, fmri); 733 goto out; 734 735 default: 736 bad_error("delete_prop", r); 737 } 738 } else { 739 switch (scf_error()) { 740 case SCF_ERROR_DELETED: 741 /* Print something? */ 742 743 case SCF_ERROR_NOT_FOUND: 744 break; 745 746 case SCF_ERROR_HANDLE_MISMATCH: 747 case SCF_ERROR_INVALID_ARGUMENT: 748 case SCF_ERROR_NOT_SET: 749 assert(0); 750 abort(); 751 /* NOTREACHED */ 752 753 case SCF_ERROR_CONNECTION_BROKEN: 754 default: 755 scfdie(); 756 } 757 } 758 759 if (verbose) 760 (void) printf(enable ? gettext("%s enabled.\n") : 761 gettext("%s disabled.\n"), fmri); 762 } 763 764 scf_pg_destroy(pg); 765 return; 766 767 eperm: 768 assert(pgname != NULL); 769 if (!verbose) 770 uu_warn(emsg_permission_denied, fmri); 771 else 772 uu_warn(emsg_pg_perm_denied, fmri, pgname); 773 774 out: 775 scf_pg_destroy(pg); 776 exit_status = 1; 777 } 778 779 /* 780 * Set inst to the instance which corresponds to fmri. If fmri identifies 781 * a service with a single instance, get that instance. 782 * 783 * Fails with 784 * ENOTSUP - fmri has an unsupported scheme 785 * EINVAL - fmri is invalid 786 * ENOTDIR - fmri does not identify a service or instance 787 * ENOENT - could not locate instance 788 * E2BIG - fmri is a service with multiple instances (warning not printed) 789 */ 790 static int 791 get_inst_mult(const char *fmri, scf_instance_t *inst) 792 { 793 char *cfmri; 794 const char *svc_name, *inst_name, *pg_name; 795 scf_service_t *svc; 796 scf_instance_t *inst2; 797 scf_iter_t *iter; 798 int ret; 799 800 if (strncmp(fmri, "lrc:", sizeof ("lrc:") - 1) == 0) { 801 uu_warn(gettext("FMRI \"%s\" is a legacy service.\n"), fmri); 802 exit_status = 1; 803 return (ENOTSUP); 804 } 805 806 cfmri = strdup(fmri); 807 if (cfmri == NULL) 808 uu_die(emsg_nomem); 809 810 if (scf_parse_svc_fmri(cfmri, NULL, &svc_name, &inst_name, &pg_name, 811 NULL) != SCF_SUCCESS) { 812 free(cfmri); 813 uu_warn(gettext("FMRI \"%s\" is invalid.\n"), fmri); 814 exit_status = 1; 815 return (EINVAL); 816 } 817 818 free(cfmri); 819 820 if (svc_name == NULL || pg_name != NULL) { 821 uu_warn(gettext( 822 "FMRI \"%s\" does not designate a service or instance.\n"), 823 fmri); 824 exit_status = 1; 825 return (ENOTDIR); 826 } 827 828 if (inst_name != NULL) { 829 if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL, 830 NULL, SCF_DECODE_FMRI_EXACT) == 0) 831 return (0); 832 833 if (scf_error() != SCF_ERROR_NOT_FOUND) 834 scfdie(); 835 836 uu_warn(gettext("No such instance \"%s\".\n"), fmri); 837 exit_status = 1; 838 839 return (ENOENT); 840 } 841 842 if ((svc = scf_service_create(h)) == NULL || 843 (inst2 = scf_instance_create(h)) == NULL || 844 (iter = scf_iter_create(h)) == NULL) 845 scfdie(); 846 847 if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL, 848 SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) { 849 if (scf_error() != SCF_ERROR_NOT_FOUND) 850 scfdie(); 851 852 uu_warn(emsg_no_service, fmri); 853 exit_status = 1; 854 855 ret = ENOENT; 856 goto out; 857 } 858 859 /* If the service has only one child, use it. */ 860 if (scf_iter_service_instances(iter, svc) != SCF_SUCCESS) 861 scfdie(); 862 863 ret = scf_iter_next_instance(iter, inst); 864 if (ret < 0) 865 scfdie(); 866 if (ret != 1) { 867 uu_warn(gettext("Service \"%s\" has no instances.\n"), 868 fmri); 869 exit_status = 1; 870 ret = ENOENT; 871 goto out; 872 } 873 874 ret = scf_iter_next_instance(iter, inst2); 875 if (ret < 0) 876 scfdie(); 877 878 if (ret != 0) { 879 ret = E2BIG; 880 goto out; 881 } 882 883 ret = 0; 884 885 out: 886 scf_iter_destroy(iter); 887 scf_instance_destroy(inst2); 888 scf_service_destroy(svc); 889 return (ret); 890 } 891 892 /* 893 * Same as get_inst_mult(), but on E2BIG prints a warning and returns ENOENT. 894 */ 895 static int 896 get_inst(const char *fmri, scf_instance_t *inst) 897 { 898 int r; 899 900 r = get_inst_mult(fmri, inst); 901 if (r != E2BIG) 902 return (r); 903 904 uu_warn(gettext("operation on service %s is ambiguous; " 905 "instance specification needed.\n"), fmri); 906 return (ENOENT); 907 } 908 909 static char * 910 inst_get_fmri(const scf_instance_t *inst) 911 { 912 ssize_t sz; 913 914 sz = scf_instance_to_fmri(inst, scratch_fmri, max_scf_fmri_sz); 915 if (sz < 0) 916 scfdie(); 917 if (sz >= max_scf_fmri_sz) 918 uu_die(gettext("scf_instance_to_fmri() returned unexpectedly " 919 "long value.\n")); 920 921 return (scratch_fmri); 922 } 923 924 static ssize_t 925 dep_get_astring(const char *fmri, const char *pgname, 926 const scf_propertygroup_t *pg, const char *propname, scf_property_t *prop, 927 scf_value_t *v, char *buf, size_t bufsz) 928 { 929 ssize_t sz; 930 931 sz = get_astring_prop(pg, propname, prop, v, buf, bufsz); 932 if (sz >= 0) 933 return (sz); 934 935 switch (-sz) { 936 case ENOENT: 937 uu_warn(gettext("\"%s\" is misconfigured (\"%s\" dependency " 938 "lacks \"%s\" property.)\n"), fmri, pgname, propname); 939 return (-1); 940 941 case E2BIG: 942 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" property " 943 "is not single-valued.)\n"), fmri, pgname, propname); 944 return (-1); 945 946 case EINVAL: 947 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" property " 948 "is not of astring type.)\n"), fmri, pgname, propname); 949 return (-1); 950 951 default: 952 assert(0); 953 abort(); 954 /* NOTREACHED */ 955 } 956 } 957 958 static boolean_t 959 multiple_instances(scf_iter_t *iter, scf_value_t *v, char *buf) 960 { 961 int count = 0, r; 962 boolean_t ret; 963 scf_instance_t *inst; 964 965 inst = scf_instance_create(h); 966 if (inst == NULL) 967 scfdie(); 968 969 for (;;) { 970 r = scf_iter_next_value(iter, v); 971 if (r == 0) { 972 ret = B_FALSE; 973 goto out; 974 } 975 if (r != 1) 976 scfdie(); 977 978 if (scf_value_get_astring(v, buf, max_scf_fmri_sz) < 0) 979 scfdie(); 980 981 switch (get_inst_mult(buf, inst)) { 982 case 0: 983 ++count; 984 if (count > 1) { 985 ret = B_TRUE; 986 goto out; 987 } 988 break; 989 990 case ENOTSUP: 991 case EINVAL: 992 case ENOTDIR: 993 case ENOENT: 994 continue; 995 996 case E2BIG: 997 ret = B_TRUE; 998 goto out; 999 1000 default: 1001 assert(0); 1002 abort(); 1003 } 1004 } 1005 1006 out: 1007 scf_instance_destroy(inst); 1008 return (ret); 1009 } 1010 1011 /* 1012 * Enable the service or instance identified by fmri and its dependencies, 1013 * recursively. Specifically, call get_inst(fmri), enable the result, and 1014 * recurse on its restarter and the dependencies. To avoid duplication of 1015 * effort or looping around a dependency cycle, each FMRI is entered into the 1016 * "visited" hash table. While recursing, the hash table entry is marked 1017 * "active", so that if we come upon it again, we know we've hit a cycle. 1018 * exclude_all and optional_all dependencies are ignored. require_any 1019 * dependencies are followed only if they comprise a single service; otherwise 1020 * the user is warned. 1021 * 1022 * fmri must point to a writable max_scf_fmri_sz buffer. Returns EINVAL if fmri 1023 * is invalid, E2BIG if fmri identifies a service with multiple instances, ELOOP 1024 * on cycle detection, or 0 on success. 1025 */ 1026 static int 1027 enable_fmri_rec(char *fmri, boolean_t temp) 1028 { 1029 scf_instance_t *inst; 1030 scf_snapshot_t *snap; 1031 scf_propertygroup_t *pg; 1032 scf_property_t *prop; 1033 scf_value_t *v; 1034 scf_iter_t *pg_iter, *val_iter; 1035 scf_type_t ty; 1036 char *buf, *pgname; 1037 ssize_t name_sz, len, sz; 1038 int ret; 1039 struct ht_elt *he; 1040 1041 len = scf_canonify_fmri(fmri, fmri, max_scf_fmri_sz); 1042 if (len < 0) { 1043 assert(scf_error() == SCF_ERROR_INVALID_ARGUMENT); 1044 return (EINVAL); 1045 } 1046 assert(len < max_scf_fmri_sz); 1047 1048 switch (visited_find_or_add(fmri, &he)) { 1049 case 0: 1050 he->active = B_TRUE; 1051 break; 1052 1053 case 1: 1054 return (he->active ? ELOOP : 0); 1055 1056 case -1: 1057 uu_die(emsg_nomem); 1058 1059 default: 1060 assert(0); 1061 abort(); 1062 } 1063 1064 inst = scf_instance_create(h); 1065 if (inst == NULL) 1066 scfdie(); 1067 1068 switch (get_inst_mult(fmri, inst)) { 1069 case 0: 1070 break; 1071 1072 case E2BIG: 1073 he->active = B_FALSE; 1074 return (E2BIG); 1075 1076 default: 1077 he->active = B_FALSE; 1078 return (0); 1079 } 1080 1081 set_inst_enabled(fmri, inst, temp, B_TRUE); 1082 1083 if ((snap = scf_snapshot_create(h)) == NULL || 1084 (pg = scf_pg_create(h)) == NULL || 1085 (prop = scf_property_create(h)) == NULL || 1086 (v = scf_value_create(h)) == NULL || 1087 (pg_iter = scf_iter_create(h)) == NULL || 1088 (val_iter = scf_iter_create(h)) == NULL) 1089 scfdie(); 1090 1091 buf = malloc(max_scf_fmri_sz); 1092 if (buf == NULL) 1093 uu_die(emsg_nomem); 1094 1095 name_sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH); 1096 if (name_sz < 0) 1097 scfdie(); 1098 ++name_sz; 1099 pgname = malloc(name_sz); 1100 if (pgname == NULL) 1101 uu_die(emsg_nomem); 1102 1103 if (scf_instance_get_snapshot(inst, "running", snap) != 0) { 1104 if (scf_error() != SCF_ERROR_NOT_FOUND) 1105 scfdie(); 1106 1107 scf_snapshot_destroy(snap); 1108 snap = NULL; 1109 } 1110 1111 /* Enable restarter */ 1112 if (scf_instance_get_pg_composed(inst, snap, SCF_PG_GENERAL, pg) != 0) { 1113 if (scf_error() != SCF_ERROR_NOT_FOUND) 1114 scfdie(); 1115 1116 uu_warn(gettext("\"%s\" is misconfigured (lacks \"%s\" " 1117 "property group).\n"), fmri, SCF_PG_GENERAL); 1118 ret = 0; 1119 goto out; 1120 } 1121 1122 sz = get_astring_prop(pg, SCF_PROPERTY_RESTARTER, prop, v, buf, 1123 max_scf_fmri_sz); 1124 if (sz > max_scf_fmri_sz) { 1125 uu_warn(gettext("\"%s\" is misconfigured (the value of " 1126 "\"%s/%s\" is too long).\n"), fmri, SCF_PG_GENERAL, 1127 SCF_PROPERTY_RESTARTER); 1128 ret = 0; 1129 goto out; 1130 } else if (sz >= 0) { 1131 switch (enable_fmri_rec(buf, temp)) { 1132 case 0: 1133 break; 1134 1135 case EINVAL: 1136 uu_warn(gettext("Restarter FMRI for \"%s\" is " 1137 "invalid.\n"), fmri); 1138 break; 1139 1140 case E2BIG: 1141 uu_warn(gettext("Restarter FMRI for \"%s\" identifies " 1142 "a service with multiple instances.\n"), fmri); 1143 break; 1144 1145 case ELOOP: 1146 ret = ELOOP; 1147 goto out; 1148 1149 default: 1150 assert(0); 1151 abort(); 1152 } 1153 } else if (sz < 0) { 1154 switch (-sz) { 1155 case ENOENT: 1156 break; 1157 1158 case E2BIG: 1159 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" " 1160 "property is not single-valued).\n"), fmri, 1161 SCF_PG_GENERAL, SCF_PROPERTY_RESTARTER); 1162 ret = 0; 1163 goto out; 1164 1165 case EINVAL: 1166 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" " 1167 "property is not of astring type).\n"), fmri, 1168 SCF_PG_GENERAL, SCF_PROPERTY_RESTARTER); 1169 ret = 0; 1170 goto out; 1171 1172 default: 1173 assert(0); 1174 abort(); 1175 } 1176 } 1177 1178 if (scf_iter_instance_pgs_typed_composed(pg_iter, inst, snap, 1179 SCF_GROUP_DEPENDENCY) == -1) 1180 scfdie(); 1181 1182 while (scf_iter_next_pg(pg_iter, pg) > 0) { 1183 len = scf_pg_get_name(pg, pgname, name_sz); 1184 if (len < 0) 1185 scfdie(); 1186 assert(len < name_sz); 1187 1188 if (dep_get_astring(fmri, pgname, pg, SCF_PROPERTY_TYPE, prop, 1189 v, buf, max_scf_fmri_sz) < 0) 1190 continue; 1191 1192 if (strcmp(buf, "service") != 0) 1193 continue; 1194 1195 if (dep_get_astring(fmri, pgname, pg, SCF_PROPERTY_GROUPING, 1196 prop, v, buf, max_scf_fmri_sz) < 0) 1197 continue; 1198 1199 if (strcmp(buf, SCF_DEP_EXCLUDE_ALL) == 0 || 1200 strcmp(buf, SCF_DEP_OPTIONAL_ALL) == 0) 1201 continue; 1202 1203 if (strcmp(buf, SCF_DEP_REQUIRE_ALL) != 0 && 1204 strcmp(buf, SCF_DEP_REQUIRE_ANY) != 0) { 1205 uu_warn(gettext("Dependency \"%s\" of \"%s\" has " 1206 "unknown type \"%s\".\n"), pgname, fmri, buf); 1207 continue; 1208 } 1209 1210 if (scf_pg_get_property(pg, SCF_PROPERTY_ENTITIES, prop) == 1211 -1) { 1212 if (scf_error() != SCF_ERROR_NOT_FOUND) 1213 scfdie(); 1214 1215 uu_warn(gettext("\"%s\" is misconfigured (\"%s\" " 1216 "dependency lacks \"%s\" property.)\n"), fmri, 1217 pgname, SCF_PROPERTY_ENTITIES); 1218 continue; 1219 } 1220 1221 if (scf_property_type(prop, &ty) != SCF_SUCCESS) 1222 scfdie(); 1223 1224 if (ty != SCF_TYPE_FMRI) { 1225 uu_warn(gettext("\"%s\" is misconfigured (property " 1226 "\"%s/%s\" is not of fmri type).\n"), fmri, pgname, 1227 SCF_PROPERTY_ENTITIES); 1228 continue; 1229 } 1230 1231 if (scf_iter_property_values(val_iter, prop) == -1) 1232 scfdie(); 1233 1234 if (strcmp(buf, SCF_DEP_REQUIRE_ANY) == 0) { 1235 if (multiple_instances(val_iter, v, buf)) { 1236 (void) printf(gettext("%s requires one of:\n"), 1237 fmri); 1238 1239 if (scf_iter_property_values(val_iter, prop) != 1240 0) 1241 scfdie(); 1242 1243 for (;;) { 1244 int r; 1245 1246 r = scf_iter_next_value(val_iter, v); 1247 if (r == 0) 1248 break; 1249 if (r != 1) 1250 scfdie(); 1251 1252 if (scf_value_get_astring(v, buf, 1253 max_scf_fmri_sz) < 0) 1254 scfdie(); 1255 1256 (void) fputs(" ", stdout); 1257 (void) puts(buf); 1258 } 1259 1260 continue; 1261 } 1262 1263 /* 1264 * Since there's only one instance, we can enable it. 1265 * Reset val_iter and continue. 1266 */ 1267 if (scf_iter_property_values(val_iter, prop) != 0) 1268 scfdie(); 1269 } 1270 1271 for (;;) { 1272 ret = scf_iter_next_value(val_iter, v); 1273 if (ret == 0) 1274 break; 1275 if (ret != 1) 1276 scfdie(); 1277 1278 if (scf_value_get_astring(v, buf, max_scf_fmri_sz) == 1279 -1) 1280 scfdie(); 1281 1282 switch (enable_fmri_rec(buf, temp)) { 1283 case 0: 1284 break; 1285 1286 case EINVAL: 1287 uu_warn(gettext("\"%s\" dependency of \"%s\" " 1288 "has invalid FMRI \"%s\".\n"), pgname, 1289 fmri, buf); 1290 break; 1291 1292 case E2BIG: 1293 uu_warn(gettext("%s depends on %s, which has " 1294 "multiple instances.\n"), fmri, buf); 1295 break; 1296 1297 case ELOOP: 1298 ret = ELOOP; 1299 goto out; 1300 1301 default: 1302 assert(0); 1303 abort(); 1304 } 1305 } 1306 } 1307 1308 ret = 0; 1309 1310 out: 1311 he->active = B_FALSE; 1312 1313 free(buf); 1314 free(pgname); 1315 1316 (void) scf_value_destroy(v); 1317 scf_property_destroy(prop); 1318 scf_pg_destroy(pg); 1319 scf_snapshot_destroy(snap); 1320 scf_iter_destroy(pg_iter); 1321 scf_iter_destroy(val_iter); 1322 1323 return (ret); 1324 } 1325 1326 /* 1327 * fmri here is only used for verbose messages. 1328 */ 1329 static void 1330 set_inst_action(const char *fmri, const scf_instance_t *inst, 1331 const char *action) 1332 { 1333 scf_transaction_t *tx; 1334 scf_transaction_entry_t *ent; 1335 scf_propertygroup_t *pg; 1336 scf_property_t *prop; 1337 scf_value_t *v; 1338 int ret; 1339 int64_t t; 1340 hrtime_t timestamp; 1341 1342 const char * const scf_pg_restarter_actions = SCF_PG_RESTARTER_ACTIONS; 1343 1344 if ((pg = scf_pg_create(h)) == NULL || 1345 (prop = scf_property_create(h)) == NULL || 1346 (v = scf_value_create(h)) == NULL || 1347 (tx = scf_transaction_create(h)) == NULL || 1348 (ent = scf_entry_create(h)) == NULL) 1349 scfdie(); 1350 1351 if (scf_instance_get_pg(inst, scf_pg_restarter_actions, pg) == -1) { 1352 if (scf_error() != SCF_ERROR_NOT_FOUND) 1353 scfdie(); 1354 1355 /* Try creating the restarter_actions property group. */ 1356 if (scf_instance_add_pg(inst, scf_pg_restarter_actions, 1357 SCF_PG_RESTARTER_ACTIONS_TYPE, 1358 SCF_PG_RESTARTER_ACTIONS_FLAGS, pg) == -1) { 1359 switch (scf_error()) { 1360 case SCF_ERROR_EXISTS: 1361 /* Someone must have added it. */ 1362 break; 1363 1364 case SCF_ERROR_PERMISSION_DENIED: 1365 if (!verbose) 1366 uu_warn(emsg_permission_denied, fmri); 1367 else 1368 uu_warn(emsg_create_pg_perm_denied, 1369 fmri, scf_pg_restarter_actions); 1370 goto out; 1371 1372 default: 1373 scfdie(); 1374 } 1375 } 1376 } 1377 1378 /* 1379 * If we lose the transaction race and need to retry, there are 2 1380 * potential other winners: 1381 * - another process setting actions 1382 * - the restarter marking the action complete 1383 * Therefore, re-read the property every time through the loop before 1384 * making any decisions based on their values. 1385 */ 1386 do { 1387 timestamp = gethrtime(); 1388 1389 if (scf_transaction_start(tx, pg) == -1) { 1390 if (scf_error() != SCF_ERROR_PERMISSION_DENIED) 1391 scfdie(); 1392 1393 if (!verbose) 1394 uu_warn(emsg_permission_denied, fmri); 1395 else 1396 uu_warn(emsg_pg_perm_denied, fmri, 1397 scf_pg_restarter_actions); 1398 goto out; 1399 } 1400 1401 if (scf_pg_get_property(pg, action, prop) == -1) { 1402 if (scf_error() != SCF_ERROR_NOT_FOUND) 1403 scfdie(); 1404 if (scf_transaction_property_new(tx, ent, 1405 action, SCF_TYPE_INTEGER) == -1) 1406 scfdie(); 1407 goto action_set; 1408 } else { 1409 if (scf_transaction_property_change_type(tx, ent, 1410 action, SCF_TYPE_INTEGER) == -1) 1411 scfdie(); 1412 } 1413 1414 if (scf_property_get_value(prop, v) == -1) { 1415 switch (scf_error()) { 1416 case SCF_ERROR_CONSTRAINT_VIOLATED: 1417 case SCF_ERROR_NOT_FOUND: 1418 /* Misconfigured, so set anyway. */ 1419 goto action_set; 1420 1421 default: 1422 scfdie(); 1423 } 1424 } else { 1425 if (scf_value_get_integer(v, &t) == -1) { 1426 assert(scf_error() == SCF_ERROR_TYPE_MISMATCH); 1427 goto action_set; 1428 } 1429 if (t > timestamp) 1430 break; 1431 } 1432 1433 action_set: 1434 scf_value_set_integer(v, timestamp); 1435 if (scf_entry_add_value(ent, v) == -1) 1436 scfdie(); 1437 1438 ret = scf_transaction_commit(tx); 1439 if (ret == -1) { 1440 if (scf_error() != SCF_ERROR_PERMISSION_DENIED) 1441 scfdie(); 1442 1443 if (!verbose) 1444 uu_warn(emsg_permission_denied, fmri); 1445 else 1446 uu_warn(emsg_prop_perm_denied, fmri, 1447 scf_pg_restarter_actions, action); 1448 scf_transaction_reset(tx); 1449 goto out; 1450 } 1451 1452 scf_transaction_reset(tx); 1453 1454 if (ret == 0) { 1455 if (scf_pg_update(pg) == -1) 1456 scfdie(); 1457 } 1458 } while (ret == 0); 1459 1460 if (verbose) 1461 (void) printf(gettext("Action %s set for %s.\n"), action, fmri); 1462 1463 out: 1464 scf_value_destroy(v); 1465 scf_entry_destroy(ent); 1466 scf_transaction_destroy(tx); 1467 scf_property_destroy(prop); 1468 scf_pg_destroy(pg); 1469 } 1470 1471 /* 1472 * Get the state of inst. state should point to a buffer of 1473 * MAX_SCF_STATE_STRING_SZ bytes. Returns 0 on success or -1 if 1474 * no restarter property group 1475 * no state property 1476 * state property is misconfigured (wrong type, not single-valued) 1477 * state value is too long 1478 * In these cases, fmri is used to print a warning. 1479 * 1480 * If pgp is non-NULL, a successful call to inst_get_state will store 1481 * the SCF_PG_RESTARTER property group in *pgp, and the caller will be 1482 * responsible for calling scf_pg_destroy on the property group. 1483 */ 1484 int 1485 inst_get_state(scf_instance_t *inst, char *state, const char *fmri, 1486 scf_propertygroup_t **pgp) 1487 { 1488 scf_propertygroup_t *pg; 1489 scf_property_t *prop; 1490 scf_value_t *val; 1491 int ret = -1; 1492 ssize_t szret; 1493 1494 if ((pg = scf_pg_create(h)) == NULL || 1495 (prop = scf_property_create(h)) == NULL || 1496 (val = scf_value_create(h)) == NULL) 1497 scfdie(); 1498 1499 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) != SCF_SUCCESS) { 1500 if (scf_error() != SCF_ERROR_NOT_FOUND) 1501 scfdie(); 1502 1503 uu_warn(gettext("%s is misconfigured (lacks \"%s\" property " 1504 "group).\n"), fmri ? fmri : inst_get_fmri(inst), 1505 SCF_PG_RESTARTER); 1506 goto out; 1507 } 1508 1509 szret = get_astring_prop(pg, SCF_PROPERTY_STATE, prop, val, state, 1510 MAX_SCF_STATE_STRING_SZ); 1511 if (szret < 0) { 1512 switch (-szret) { 1513 case ENOENT: 1514 uu_warn(gettext("%s is misconfigured (\"%s\" property " 1515 "group lacks \"%s\" property).\n"), 1516 fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER, 1517 SCF_PROPERTY_STATE); 1518 goto out; 1519 1520 case E2BIG: 1521 uu_warn(gettext("%s is misconfigured (\"%s/%s\" " 1522 "property is not single-valued).\n"), 1523 fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER, 1524 SCF_PROPERTY_STATE); 1525 goto out; 1526 1527 case EINVAL: 1528 uu_warn(gettext("%s is misconfigured (\"%s/%s\" " 1529 "property is not of type astring).\n"), 1530 fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER, 1531 SCF_PROPERTY_STATE); 1532 goto out; 1533 1534 default: 1535 assert(0); 1536 abort(); 1537 } 1538 } 1539 if (szret >= MAX_SCF_STATE_STRING_SZ) { 1540 uu_warn(gettext("%s is misconfigured (\"%s/%s\" property value " 1541 "is too long).\n"), fmri ? fmri : inst_get_fmri(inst), 1542 SCF_PG_RESTARTER, SCF_PROPERTY_STATE); 1543 goto out; 1544 } 1545 1546 ret = 0; 1547 if (pgp) 1548 *pgp = pg; 1549 1550 out: 1551 (void) scf_value_destroy(val); 1552 scf_property_destroy(prop); 1553 if (ret || pgp == NULL) 1554 scf_pg_destroy(pg); 1555 return (ret); 1556 } 1557 1558 static void 1559 set_astring_prop(const char *fmri, const char *pgname, const char *pgtype, 1560 uint32_t pgflags, const char *propname, const char *str) 1561 { 1562 scf_instance_t *inst; 1563 scf_propertygroup_t *pg; 1564 scf_property_t *prop; 1565 scf_value_t *val; 1566 scf_transaction_t *tx; 1567 scf_transaction_entry_t *txent; 1568 int ret; 1569 1570 inst = scf_instance_create(h); 1571 if (inst == NULL) 1572 scfdie(); 1573 1574 if (get_inst(fmri, inst) != 0) 1575 return; 1576 1577 if ((pg = scf_pg_create(h)) == NULL || 1578 (prop = scf_property_create(h)) == NULL || 1579 (val = scf_value_create(h)) == NULL || 1580 (tx = scf_transaction_create(h)) == NULL || 1581 (txent = scf_entry_create(h)) == NULL) 1582 scfdie(); 1583 1584 if (scf_instance_get_pg(inst, pgname, pg) != SCF_SUCCESS) { 1585 if (scf_error() != SCF_ERROR_NOT_FOUND) 1586 scfdie(); 1587 1588 if (scf_instance_add_pg(inst, pgname, pgtype, pgflags, pg) != 1589 SCF_SUCCESS) { 1590 switch (scf_error()) { 1591 case SCF_ERROR_EXISTS: 1592 if (scf_instance_get_pg(inst, pgname, pg) != 1593 SCF_SUCCESS) { 1594 if (scf_error() != SCF_ERROR_NOT_FOUND) 1595 scfdie(); 1596 1597 uu_warn(gettext("Repository write " 1598 "contention.\n")); 1599 goto out; 1600 } 1601 break; 1602 1603 case SCF_ERROR_PERMISSION_DENIED: 1604 if (!verbose) 1605 uu_warn(emsg_permission_denied, fmri); 1606 else 1607 uu_warn(emsg_create_pg_perm_denied, 1608 fmri, pgname); 1609 goto out; 1610 1611 default: 1612 scfdie(); 1613 } 1614 } 1615 } 1616 1617 do { 1618 if (scf_transaction_start(tx, pg) != SCF_SUCCESS) { 1619 if (scf_error() != SCF_ERROR_PERMISSION_DENIED) 1620 scfdie(); 1621 1622 if (!verbose) 1623 uu_warn(emsg_permission_denied, fmri); 1624 else 1625 uu_warn(emsg_pg_perm_denied, fmri, pgname); 1626 goto out; 1627 } 1628 1629 if (scf_transaction_property_change_type(tx, txent, propname, 1630 SCF_TYPE_ASTRING) != 0) { 1631 if (scf_error() != SCF_ERROR_NOT_FOUND) 1632 scfdie(); 1633 1634 if (scf_transaction_property_new(tx, txent, propname, 1635 SCF_TYPE_ASTRING) != 0) 1636 scfdie(); 1637 } 1638 1639 if (scf_value_set_astring(val, str) != SCF_SUCCESS) 1640 scfdie(); 1641 1642 if (scf_entry_add_value(txent, val) != SCF_SUCCESS) 1643 scfdie(); 1644 1645 ret = scf_transaction_commit(tx); 1646 if (ret == -1) { 1647 if (scf_error() != SCF_ERROR_PERMISSION_DENIED) 1648 scfdie(); 1649 1650 if (!verbose) 1651 uu_warn(emsg_permission_denied, fmri); 1652 else 1653 uu_warn(emsg_prop_perm_denied, fmri, pgname, 1654 propname); 1655 goto out; 1656 } 1657 1658 if (ret == 0) { 1659 scf_transaction_reset(tx); 1660 1661 if (scf_pg_update(pg) != SCF_SUCCESS) 1662 scfdie(); 1663 } 1664 } while (ret == 0); 1665 1666 out: 1667 scf_transaction_destroy(tx); 1668 scf_entry_destroy(txent); 1669 scf_value_destroy(val); 1670 scf_property_destroy(prop); 1671 scf_pg_destroy(pg); 1672 scf_instance_destroy(inst); 1673 } 1674 1675 1676 /* 1677 * Flags to control enable and disable actions. 1678 */ 1679 #define SET_ENABLED 0x1 1680 #define SET_TEMPORARY 0x2 1681 #define SET_RECURSIVE 0x4 1682 1683 static int 1684 set_fmri_enabled(void *data, scf_walkinfo_t *wip) 1685 { 1686 int flags = (int)data; 1687 1688 assert(wip->inst != NULL); 1689 assert(wip->pg == NULL); 1690 1691 if (flags & SET_RECURSIVE) { 1692 char *fmri_buf = malloc(max_scf_fmri_sz); 1693 if (fmri_buf == NULL) 1694 uu_die(emsg_nomem); 1695 1696 visited = calloc(HT_BUCKETS, sizeof (*visited)); 1697 if (visited == NULL) 1698 uu_die(emsg_nomem); 1699 1700 /* scf_walk_fmri() guarantees that fmri isn't too long */ 1701 assert(strlen(wip->fmri) <= max_scf_fmri_sz); 1702 (void) strlcpy(fmri_buf, wip->fmri, max_scf_fmri_sz); 1703 1704 switch (enable_fmri_rec(fmri_buf, (flags & SET_TEMPORARY))) { 1705 case E2BIG: 1706 uu_warn(gettext("operation on service %s is ambiguous; " 1707 "instance specification needed.\n"), fmri_buf); 1708 break; 1709 1710 case ELOOP: 1711 uu_warn(gettext("%s: Dependency cycle detected.\n"), 1712 fmri_buf); 1713 } 1714 1715 free(visited); 1716 free(fmri_buf); 1717 1718 } else { 1719 set_inst_enabled(wip->fmri, wip->inst, 1720 (flags & SET_TEMPORARY) != 0, (flags & SET_ENABLED) != 0); 1721 } 1722 1723 return (0); 1724 } 1725 1726 /* ARGSUSED */ 1727 static int 1728 wait_fmri_enabled(void *data, scf_walkinfo_t *wip) 1729 { 1730 scf_propertygroup_t *pg = NULL; 1731 char state[MAX_SCF_STATE_STRING_SZ]; 1732 1733 assert(wip->inst != NULL); 1734 assert(wip->pg == NULL); 1735 1736 do { 1737 if (pg) 1738 scf_pg_destroy(pg); 1739 if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) { 1740 exit_status = EXIT_SVC_FAILURE; 1741 return (0); 1742 } 1743 1744 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0 || 1745 strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) { 1746 /* 1747 * We're done. 1748 */ 1749 goto out; 1750 } 1751 1752 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) { 1753 /* 1754 * The service is ill. 1755 */ 1756 uu_warn(gettext("Instance \"%s\" is in maintenance" 1757 " state.\n"), wip->fmri); 1758 exit_status = EXIT_SVC_FAILURE; 1759 goto out; 1760 } 1761 1762 if (!is_enabled(wip->inst)) { 1763 /* 1764 * Someone stepped in and disabled the service. 1765 */ 1766 uu_warn(gettext("Instance \"%s\" has been disabled" 1767 " by another entity.\n"), wip->fmri); 1768 exit_status = EXIT_SVC_FAILURE; 1769 goto out; 1770 } 1771 1772 if (!has_potential(wip->inst, B_FALSE)) { 1773 /* 1774 * Our dependencies aren't met. We'll never 1775 * amount to anything. 1776 */ 1777 uu_warn(gettext("Instance \"%s\" has unsatisfied" 1778 " dependencies.\n"), wip->fmri); 1779 /* 1780 * EXIT_SVC_FAILURE takes precedence over 1781 * EXIT_DEP_FAILURE 1782 */ 1783 if (exit_status == 0) 1784 exit_status = EXIT_DEP_FAILURE; 1785 goto out; 1786 } 1787 } while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0); 1788 scfdie(); 1789 /* NOTREACHED */ 1790 1791 out: 1792 scf_pg_destroy(pg); 1793 return (0); 1794 } 1795 1796 /* ARGSUSED */ 1797 static int 1798 wait_fmri_disabled(void *data, scf_walkinfo_t *wip) 1799 { 1800 scf_propertygroup_t *pg = NULL; 1801 char state[MAX_SCF_STATE_STRING_SZ]; 1802 1803 assert(wip->inst != NULL); 1804 assert(wip->pg == NULL); 1805 1806 do { 1807 if (pg) 1808 scf_pg_destroy(pg); 1809 if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) { 1810 exit_status = EXIT_SVC_FAILURE; 1811 return (0); 1812 } 1813 1814 if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0) { 1815 /* 1816 * We're done. 1817 */ 1818 goto out; 1819 } 1820 1821 if (is_enabled(wip->inst)) { 1822 /* 1823 * Someone stepped in and enabled the service. 1824 */ 1825 uu_warn(gettext("Instance \"%s\" has been enabled" 1826 " by another entity.\n"), wip->fmri); 1827 exit_status = EXIT_SVC_FAILURE; 1828 goto out; 1829 } 1830 1831 if (!has_potential(wip->inst, B_TRUE)) { 1832 /* 1833 * Our restarter is hopeless. 1834 */ 1835 uu_warn(gettext("Restarter for instance \"%s\" is" 1836 " unavailable.\n"), wip->fmri); 1837 /* 1838 * EXIT_SVC_FAILURE takes precedence over 1839 * EXIT_DEP_FAILURE 1840 */ 1841 if (exit_status == 0) 1842 exit_status = EXIT_DEP_FAILURE; 1843 goto out; 1844 } 1845 1846 } while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0); 1847 scfdie(); 1848 /* NOTREACHED */ 1849 1850 out: 1851 scf_pg_destroy(pg); 1852 return (0); 1853 } 1854 1855 /* ARGSUSED */ 1856 static int 1857 clear_instance(void *data, scf_walkinfo_t *wip) 1858 { 1859 char state[MAX_SCF_STATE_STRING_SZ]; 1860 1861 assert(wip->inst != NULL); 1862 assert(wip->pg == NULL); 1863 1864 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0) 1865 return (0); 1866 1867 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) { 1868 set_inst_action(wip->fmri, wip->inst, SCF_PROPERTY_MAINT_OFF); 1869 } else if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 1870 0) { 1871 set_inst_action(wip->fmri, wip->inst, SCF_PROPERTY_RESTORE); 1872 } else { 1873 uu_warn(gettext("Instance \"%s\" is not in a " 1874 "maintenance or degraded state.\n"), wip->fmri); 1875 1876 exit_status = 1; 1877 } 1878 1879 return (0); 1880 } 1881 1882 static int 1883 set_fmri_action(void *action, scf_walkinfo_t *wip) 1884 { 1885 assert(wip->inst != NULL && wip->pg == NULL); 1886 1887 set_inst_action(wip->fmri, wip->inst, action); 1888 1889 return (0); 1890 } 1891 1892 /* 1893 * Flags to control 'mark' action. 1894 */ 1895 #define MARK_IMMEDIATE 0x1 1896 #define MARK_TEMPORARY 0x2 1897 1898 static int 1899 force_degraded(void *data, scf_walkinfo_t *wip) 1900 { 1901 int flags = (int)data; 1902 char state[MAX_SCF_STATE_STRING_SZ]; 1903 1904 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0) { 1905 exit_status = 1; 1906 return (0); 1907 } 1908 1909 if (strcmp(state, SCF_STATE_STRING_ONLINE) != 0) { 1910 uu_warn(gettext("Instance \"%s\" is not online.\n"), wip->fmri); 1911 exit_status = 1; 1912 return (0); 1913 } 1914 1915 set_inst_action(wip->fmri, wip->inst, (flags & MARK_IMMEDIATE) ? 1916 SCF_PROPERTY_DEGRADE_IMMEDIATE : SCF_PROPERTY_DEGRADED); 1917 1918 return (0); 1919 } 1920 1921 static int 1922 force_maintenance(void *data, scf_walkinfo_t *wip) 1923 { 1924 int flags = (int)data; 1925 const char *prop; 1926 1927 if (flags & MARK_IMMEDIATE) { 1928 prop = (flags & MARK_TEMPORARY) ? 1929 SCF_PROPERTY_MAINT_ON_IMMTEMP : 1930 SCF_PROPERTY_MAINT_ON_IMMEDIATE; 1931 } else { 1932 prop = (flags & MARK_TEMPORARY) ? 1933 SCF_PROPERTY_MAINT_ON_TEMPORARY : 1934 SCF_PROPERTY_MAINT_ON; 1935 } 1936 1937 set_inst_action(wip->fmri, wip->inst, prop); 1938 1939 return (0); 1940 } 1941 1942 static void 1943 set_milestone(const char *fmri, boolean_t temporary) 1944 { 1945 scf_instance_t *inst; 1946 scf_propertygroup_t *pg; 1947 int r; 1948 1949 if (temporary) { 1950 set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS_OVR, 1951 SCF_PG_OPTIONS_OVR_TYPE, SCF_PG_OPTIONS_OVR_FLAGS, 1952 SCF_PROPERTY_MILESTONE, fmri); 1953 return; 1954 } 1955 1956 if ((inst = scf_instance_create(h)) == NULL || 1957 (pg = scf_pg_create(h)) == NULL) 1958 scfdie(); 1959 1960 if (get_inst(SCF_SERVICE_STARTD, inst) != 0) { 1961 scf_instance_destroy(inst); 1962 return; 1963 } 1964 1965 /* 1966 * Set the persistent milestone before deleting the override so we don't 1967 * glitch. 1968 */ 1969 set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS, 1970 SCF_PG_OPTIONS_TYPE, SCF_PG_OPTIONS_FLAGS, SCF_PROPERTY_MILESTONE, 1971 fmri); 1972 1973 if (scf_instance_get_pg(inst, SCF_PG_OPTIONS_OVR, pg) == 0) { 1974 r = delete_prop(pg, SCF_PROPERTY_MILESTONE); 1975 switch (r) { 1976 case 0: 1977 break; 1978 1979 case ECANCELED: 1980 uu_warn(emsg_no_service, fmri); 1981 exit_status = 1; 1982 goto out; 1983 1984 case EPERM: 1985 uu_warn(gettext("Could not delete %s/%s property of " 1986 "%s: permission denied.\n"), SCF_PG_OPTIONS_OVR, 1987 SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD); 1988 exit_status = 1; 1989 goto out; 1990 1991 case EACCES: 1992 uu_warn(gettext("Could not delete %s/%s property of " 1993 "%s: access denied.\n"), SCF_PG_OPTIONS_OVR, 1994 SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD); 1995 exit_status = 1; 1996 goto out; 1997 1998 case EROFS: 1999 uu_warn(gettext("Could not delete %s/%s property of " 2000 "%s: backend read-only.\n"), SCF_PG_OPTIONS_OVR, 2001 SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD); 2002 exit_status = 1; 2003 goto out; 2004 2005 default: 2006 bad_error("delete_prop", r); 2007 } 2008 } else { 2009 switch (scf_error()) { 2010 case SCF_ERROR_NOT_FOUND: 2011 break; 2012 2013 case SCF_ERROR_DELETED: 2014 uu_warn(emsg_no_service, fmri); 2015 exit_status = 1; 2016 goto out; 2017 2018 case SCF_ERROR_CONNECTION_BROKEN: 2019 case SCF_ERROR_HANDLE_MISMATCH: 2020 case SCF_ERROR_NOT_BOUND: 2021 case SCF_ERROR_INVALID_ARGUMENT: 2022 case SCF_ERROR_NOT_SET: 2023 default: 2024 scfdie(); 2025 } 2026 } 2027 2028 out: 2029 scf_pg_destroy(pg); 2030 scf_instance_destroy(inst); 2031 } 2032 2033 static char const *milestones[] = { 2034 SCF_MILESTONE_SINGLE_USER, 2035 SCF_MILESTONE_MULTI_USER, 2036 SCF_MILESTONE_MULTI_USER_SERVER, 2037 NULL 2038 }; 2039 2040 static void 2041 usage_milestone(void) 2042 { 2043 const char **ms; 2044 2045 (void) fprintf(stderr, gettext( 2046 "Usage: svcadm milestone [-d] <milestone>\n\n" 2047 "\t-d\tmake the specified milestone the default for system boot\n\n" 2048 "\tMilestones can be specified using an FMRI or abbreviation.\n" 2049 "\tThe major milestones are as follows:\n\n" 2050 "\tall\n" 2051 "\tnone\n")); 2052 2053 for (ms = milestones; *ms != NULL; ms++) 2054 (void) fprintf(stderr, "\t%s\n", *ms); 2055 2056 exit(UU_EXIT_USAGE); 2057 } 2058 2059 static const char * 2060 validate_milestone(const char *milestone) 2061 { 2062 const char **ms; 2063 const char *tmp; 2064 size_t len; 2065 2066 if (strcmp(milestone, "all") == 0) 2067 return (milestone); 2068 2069 if (strcmp(milestone, "none") == 0) 2070 return (milestone); 2071 2072 /* 2073 * Determine if this is a full or partial milestone 2074 */ 2075 for (ms = milestones; *ms != NULL; ms++) { 2076 if ((tmp = strstr(*ms, milestone)) != NULL) { 2077 len = strlen(milestone); 2078 2079 /* 2080 * The beginning of the string must align with the start 2081 * of a milestone fmri, or on the boundary between 2082 * elements. The end of the string must align with the 2083 * end of the milestone, or at the instance boundary. 2084 */ 2085 if ((tmp == *ms || tmp[-1] == '/') && 2086 (tmp[len] == '\0' || tmp[len] == ':')) 2087 return (*ms); 2088 } 2089 } 2090 2091 (void) fprintf(stderr, 2092 gettext("\"%s\" is not a valid major milestone.\n"), milestone); 2093 2094 usage_milestone(); 2095 /* NOTREACHED */ 2096 } 2097 2098 /*ARGSUSED*/ 2099 static void 2100 quiet(const char *fmt, ...) 2101 { 2102 /* Do nothing */ 2103 } 2104 2105 int 2106 main(int argc, char *argv[]) 2107 { 2108 int o; 2109 int err; 2110 2111 (void) setlocale(LC_ALL, ""); 2112 (void) textdomain(TEXT_DOMAIN); 2113 2114 (void) uu_setpname(argv[0]); 2115 2116 if (argc < 2) 2117 usage(); 2118 2119 max_scf_fmri_sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH); 2120 if (max_scf_fmri_sz < 0) 2121 scfdie(); 2122 ++max_scf_fmri_sz; 2123 2124 scratch_fmri = malloc(max_scf_fmri_sz); 2125 if (scratch_fmri == NULL) 2126 uu_die(emsg_nomem); 2127 2128 h = scf_handle_create(SCF_VERSION); 2129 if (h == NULL) 2130 scfdie(); 2131 2132 if (scf_handle_bind(h) == -1) 2133 uu_die(gettext("Couldn't bind to svc.configd.\n")); 2134 2135 while ((o = getopt(argc, argv, "v")) != -1) { 2136 if (o == 'v') 2137 verbose = 1; 2138 else 2139 usage(); 2140 } 2141 2142 if (optind >= argc) 2143 usage(); 2144 2145 emsg_permission_denied = gettext("%s: Permission denied.\n"); 2146 emsg_nomem = gettext("Out of memory.\n"); 2147 emsg_create_pg_perm_denied = gettext("%s: Couldn't create \"%s\" " 2148 "property group (permission denied).\n"); 2149 emsg_pg_perm_denied = gettext("%s: Couldn't modify \"%s\" property " 2150 "group (permission denied).\n"); 2151 emsg_prop_perm_denied = gettext("%s: Couldn't modify \"%s/%s\" " 2152 "property (permission denied).\n"); 2153 emsg_no_service = gettext("No such service \"%s\".\n"); 2154 2155 if (strcmp(argv[optind], "enable") == 0) { 2156 int flags = SET_ENABLED; 2157 int wait = 0; 2158 int error = 0; 2159 2160 ++optind; 2161 2162 while ((o = getopt(argc, argv, "rst")) != -1) { 2163 if (o == 'r') 2164 flags |= SET_RECURSIVE; 2165 else if (o == 't') 2166 flags |= SET_TEMPORARY; 2167 else if (o == 's') 2168 wait = 1; 2169 else if (o == '?') 2170 usage(); 2171 else { 2172 assert(0); 2173 abort(); 2174 } 2175 } 2176 argc -= optind; 2177 argv += optind; 2178 2179 if (argc <= 0) 2180 usage(); 2181 2182 /* 2183 * We want to continue with -s processing if we had 2184 * invalid options, but not if an enable failed. We 2185 * squelch output the second time we walk fmris; we saw 2186 * the errors the first time. 2187 */ 2188 if ((err = scf_walk_fmri(h, argc, argv, 0, set_fmri_enabled, 2189 (void *)flags, &error, uu_warn)) != 0) { 2190 2191 uu_warn(gettext("failed to iterate over " 2192 "instances: %s\n"), scf_strerror(err)); 2193 exit_status = UU_EXIT_FATAL; 2194 2195 } else if (wait && exit_status == 0 && 2196 (err = scf_walk_fmri(h, argc, argv, 0, wait_fmri_enabled, 2197 (void *)flags, &error, quiet)) != 0) { 2198 2199 uu_warn(gettext("failed to iterate over " 2200 "instances: %s\n"), scf_strerror(err)); 2201 exit_status = UU_EXIT_FATAL; 2202 } 2203 2204 if (error > 0) 2205 exit_status = error; 2206 2207 } else if (strcmp(argv[optind], "disable") == 0) { 2208 int flags = 0; 2209 int wait = 0; 2210 int error = 0; 2211 2212 ++optind; 2213 2214 while ((o = getopt(argc, argv, "st")) != -1) { 2215 if (o == 't') 2216 flags |= SET_TEMPORARY; 2217 else if (o == 's') 2218 wait = 1; 2219 else if (o == '?') 2220 usage(); 2221 else { 2222 assert(0); 2223 abort(); 2224 } 2225 } 2226 argc -= optind; 2227 argv += optind; 2228 2229 if (argc <= 0) 2230 usage(); 2231 2232 /* 2233 * We want to continue with -s processing if we had 2234 * invalid options, but not if a disable failed. We 2235 * squelch output the second time we walk fmris; we saw 2236 * the errors the first time. 2237 */ 2238 if ((err = scf_walk_fmri(h, argc, argv, 0, set_fmri_enabled, 2239 (void *)flags, &exit_status, uu_warn)) != 0) { 2240 2241 uu_warn(gettext("failed to iterate over " 2242 "instances: %s\n"), scf_strerror(err)); 2243 exit_status = UU_EXIT_FATAL; 2244 2245 } else if (wait && exit_status == 0 && 2246 (err = scf_walk_fmri(h, argc, argv, 0, wait_fmri_disabled, 2247 (void *)flags, &error, quiet)) != 0) { 2248 2249 uu_warn(gettext("failed to iterate over " 2250 "instances: %s\n"), scf_strerror(err)); 2251 exit_status = UU_EXIT_FATAL; 2252 } 2253 2254 if (error > 0) 2255 exit_status = error; 2256 2257 } else if (strcmp(argv[optind], "restart") == 0) { 2258 ++optind; 2259 2260 if (optind >= argc) 2261 usage(); 2262 2263 if ((err = scf_walk_fmri(h, argc - optind, argv + optind, 0, 2264 set_fmri_action, (void *)SCF_PROPERTY_RESTART, 2265 &exit_status, uu_warn)) != 0) { 2266 uu_warn(gettext("failed to iterate over " 2267 "instances: %s\n"), scf_strerror(err)); 2268 exit_status = UU_EXIT_FATAL; 2269 } 2270 2271 } else if (strcmp(argv[optind], "refresh") == 0) { 2272 ++optind; 2273 2274 if (optind >= argc) 2275 usage(); 2276 2277 if ((err = scf_walk_fmri(h, argc - optind, argv + optind, 0, 2278 set_fmri_action, (void *)SCF_PROPERTY_REFRESH, 2279 &exit_status, uu_warn)) != 0) { 2280 uu_warn(gettext("failed to iterate over " 2281 "instances: %s\n"), scf_strerror(scf_error())); 2282 exit_status = UU_EXIT_FATAL; 2283 } 2284 2285 } else if (strcmp(argv[optind], "mark") == 0) { 2286 int flags = 0; 2287 scf_walk_callback callback; 2288 2289 ++optind; 2290 2291 while ((o = getopt(argc, argv, "It")) != -1) { 2292 if (o == 'I') 2293 flags |= MARK_IMMEDIATE; 2294 else if (o == 't') 2295 flags |= MARK_TEMPORARY; 2296 else if (o == '?') 2297 usage(); 2298 else { 2299 assert(0); 2300 abort(); 2301 } 2302 } 2303 2304 if (argc - optind < 2) 2305 usage(); 2306 2307 if (strcmp(argv[optind], "degraded") == 0) { 2308 if (flags & MARK_TEMPORARY) 2309 uu_xdie(UU_EXIT_USAGE, gettext("-t may not be " 2310 "used with degraded.\n")); 2311 callback = force_degraded; 2312 2313 } else if (strcmp(argv[optind], "maintenance") == 0) { 2314 callback = force_maintenance; 2315 } else { 2316 usage(); 2317 } 2318 2319 if ((err = scf_walk_fmri(h, argc - optind - 1, 2320 argv + optind + 1, 0, callback, NULL, &exit_status, 2321 uu_warn)) != 0) { 2322 uu_warn(gettext("failed to iterate over " 2323 "instances: %s\n"), 2324 scf_strerror(err)); 2325 exit_status = UU_EXIT_FATAL; 2326 } 2327 2328 } else if (strcmp(argv[optind], "clear") == 0) { 2329 ++optind; 2330 2331 if (optind >= argc) 2332 usage(); 2333 2334 if ((err = scf_walk_fmri(h, argc - optind, argv + optind, 0, 2335 clear_instance, NULL, &exit_status, uu_warn)) != 0) { 2336 uu_warn(gettext("failed to iterate over " 2337 "instances: %s\n"), scf_strerror(err)); 2338 exit_status = UU_EXIT_FATAL; 2339 } 2340 2341 } else if (strcmp(argv[optind], "milestone") == 0) { 2342 boolean_t temporary = B_TRUE; 2343 const char *milestone; 2344 2345 ++optind; 2346 2347 while ((o = getopt(argc, argv, "d")) != -1) { 2348 if (o == 'd') 2349 temporary = B_FALSE; 2350 else if (o == '?') 2351 usage_milestone(); 2352 else { 2353 assert(0); 2354 abort(); 2355 } 2356 } 2357 2358 if (optind >= argc) 2359 usage_milestone(); 2360 2361 milestone = validate_milestone(argv[optind]); 2362 2363 set_milestone(milestone, temporary); 2364 } else if (strcmp(argv[optind], "_smf_backup") == 0) { 2365 const char *reason = NULL; 2366 2367 ++optind; 2368 2369 if (optind != argc - 1) 2370 usage(); 2371 2372 if ((err = _scf_request_backup(h, argv[optind])) != 2373 SCF_SUCCESS) { 2374 switch (scf_error()) { 2375 case SCF_ERROR_NOT_BOUND: 2376 case SCF_ERROR_CONNECTION_BROKEN: 2377 case SCF_ERROR_BACKEND_READONLY: 2378 scfdie(); 2379 break; 2380 2381 case SCF_ERROR_PERMISSION_DENIED: 2382 case SCF_ERROR_INVALID_ARGUMENT: 2383 reason = scf_strerror(scf_error()); 2384 break; 2385 2386 case SCF_ERROR_INTERNAL: 2387 reason = 2388 "unknown error (see console for details)"; 2389 break; 2390 } 2391 uu_warn("failed to backup repository: %s\n", reason); 2392 exit_status = UU_EXIT_FATAL; 2393 } 2394 } else { 2395 usage(); 2396 } 2397 2398 if (scf_handle_unbind(h) == -1) 2399 scfdie(); 2400 scf_handle_destroy(h); 2401 2402 return (exit_status); 2403 } 2404