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