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