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