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