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