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 goto out; 1343 1344 if (scf_instance_get_pg(inst, scf_pg_restarter_actions, pg) == -1) { 1345 if (scf_error() != SCF_ERROR_NOT_FOUND) 1346 scfdie(); 1347 1348 /* Try creating the restarter_actions property group. */ 1349 if (scf_instance_add_pg(inst, scf_pg_restarter_actions, 1350 SCF_PG_RESTARTER_ACTIONS_TYPE, 1351 SCF_PG_RESTARTER_ACTIONS_FLAGS, pg) == -1) { 1352 switch (scf_error()) { 1353 case SCF_ERROR_EXISTS: 1354 /* Someone must have added it. */ 1355 break; 1356 1357 case SCF_ERROR_PERMISSION_DENIED: 1358 if (!verbose) 1359 uu_warn(emsg_permission_denied, fmri); 1360 else 1361 uu_warn(emsg_create_pg_perm_denied, 1362 fmri, scf_pg_restarter_actions); 1363 goto out; 1364 1365 default: 1366 scfdie(); 1367 } 1368 } 1369 } 1370 1371 /* 1372 * If we lose the transaction race and need to retry, there are 2 1373 * potential other winners: 1374 * - another process setting actions 1375 * - the restarter marking the action complete 1376 * Therefore, re-read the property every time through the loop before 1377 * making any decisions based on their values. 1378 */ 1379 do { 1380 timestamp = gethrtime(); 1381 1382 if (scf_transaction_start(tx, pg) == -1) { 1383 if (scf_error() != SCF_ERROR_PERMISSION_DENIED) 1384 scfdie(); 1385 1386 if (!verbose) 1387 uu_warn(emsg_permission_denied, fmri); 1388 else 1389 uu_warn(emsg_pg_perm_denied, fmri, 1390 scf_pg_restarter_actions); 1391 goto out; 1392 } 1393 1394 if (scf_pg_get_property(pg, action, prop) == -1) { 1395 if (scf_error() != SCF_ERROR_NOT_FOUND) 1396 scfdie(); 1397 if (scf_transaction_property_new(tx, ent, 1398 action, SCF_TYPE_INTEGER) == -1) 1399 scfdie(); 1400 goto action_set; 1401 } else { 1402 if (scf_transaction_property_change_type(tx, ent, 1403 action, SCF_TYPE_INTEGER) == -1) 1404 scfdie(); 1405 } 1406 1407 if (scf_property_get_value(prop, v) == -1) { 1408 switch (scf_error()) { 1409 case SCF_ERROR_CONSTRAINT_VIOLATED: 1410 case SCF_ERROR_NOT_FOUND: 1411 /* Misconfigured, so set anyway. */ 1412 goto action_set; 1413 1414 default: 1415 scfdie(); 1416 } 1417 } else { 1418 if (scf_value_get_integer(v, &t) == -1) { 1419 assert(scf_error() == SCF_ERROR_TYPE_MISMATCH); 1420 goto action_set; 1421 } 1422 if (t > timestamp) 1423 break; 1424 } 1425 1426 action_set: 1427 scf_value_set_integer(v, timestamp); 1428 if (scf_entry_add_value(ent, v) == -1) 1429 scfdie(); 1430 1431 ret = scf_transaction_commit(tx); 1432 if (ret == -1) { 1433 if (scf_error() != SCF_ERROR_PERMISSION_DENIED) 1434 scfdie(); 1435 1436 if (!verbose) 1437 uu_warn(emsg_permission_denied, fmri); 1438 else 1439 uu_warn(emsg_prop_perm_denied, fmri, 1440 scf_pg_restarter_actions, action); 1441 scf_transaction_reset(tx); 1442 goto out; 1443 } 1444 1445 scf_transaction_reset(tx); 1446 1447 if (ret == 0) { 1448 if (scf_pg_update(pg) == -1) 1449 scfdie(); 1450 } 1451 } while (ret == 0); 1452 1453 if (verbose) 1454 (void) printf(gettext("Action %s set for %s.\n"), action, fmri); 1455 1456 out: 1457 scf_value_destroy(v); 1458 scf_entry_destroy(ent); 1459 scf_transaction_destroy(tx); 1460 scf_property_destroy(prop); 1461 scf_pg_destroy(pg); 1462 } 1463 1464 /* 1465 * Get the state of inst. state should point to a buffer of 1466 * MAX_SCF_STATE_STRING_SZ bytes. Returns 0 on success or -1 if 1467 * no restarter property group 1468 * no state property 1469 * state property is misconfigured (wrong type, not single-valued) 1470 * state value is too long 1471 * In these cases, fmri is used to print a warning. 1472 * 1473 * If pgp is non-NULL, a successful call to inst_get_state will store 1474 * the SCF_PG_RESTARTER property group in *pgp, and the caller will be 1475 * responsible for calling scf_pg_destroy on the property group. 1476 */ 1477 int 1478 inst_get_state(scf_instance_t *inst, char *state, const char *fmri, 1479 scf_propertygroup_t **pgp) 1480 { 1481 scf_propertygroup_t *pg; 1482 scf_property_t *prop; 1483 scf_value_t *val; 1484 int ret = -1; 1485 ssize_t szret; 1486 1487 if ((pg = scf_pg_create(h)) == NULL || 1488 (prop = scf_property_create(h)) == NULL || 1489 (val = scf_value_create(h)) == NULL) 1490 scfdie(); 1491 1492 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) != SCF_SUCCESS) { 1493 if (scf_error() != SCF_ERROR_NOT_FOUND) 1494 scfdie(); 1495 1496 uu_warn(gettext("%s is misconfigured (lacks \"%s\" property " 1497 "group).\n"), fmri ? fmri : inst_get_fmri(inst), 1498 SCF_PG_RESTARTER); 1499 goto out; 1500 } 1501 1502 szret = get_astring_prop(pg, SCF_PROPERTY_STATE, prop, val, state, 1503 MAX_SCF_STATE_STRING_SZ); 1504 if (szret < 0) { 1505 switch (-szret) { 1506 case ENOENT: 1507 uu_warn(gettext("%s is misconfigured (\"%s\" property " 1508 "group lacks \"%s\" property).\n"), 1509 fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER, 1510 SCF_PROPERTY_STATE); 1511 goto out; 1512 1513 case E2BIG: 1514 uu_warn(gettext("%s is misconfigured (\"%s/%s\" " 1515 "property is not single-valued).\n"), 1516 fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER, 1517 SCF_PROPERTY_STATE); 1518 goto out; 1519 1520 case EINVAL: 1521 uu_warn(gettext("%s is misconfigured (\"%s/%s\" " 1522 "property is not of type astring).\n"), 1523 fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER, 1524 SCF_PROPERTY_STATE); 1525 goto out; 1526 1527 default: 1528 assert(0); 1529 abort(); 1530 } 1531 } 1532 if (szret >= MAX_SCF_STATE_STRING_SZ) { 1533 uu_warn(gettext("%s is misconfigured (\"%s/%s\" property value " 1534 "is too long).\n"), fmri ? fmri : inst_get_fmri(inst), 1535 SCF_PG_RESTARTER, SCF_PROPERTY_STATE); 1536 goto out; 1537 } 1538 1539 ret = 0; 1540 if (pgp) 1541 *pgp = pg; 1542 1543 out: 1544 (void) scf_value_destroy(val); 1545 scf_property_destroy(prop); 1546 if (ret || pgp == NULL) 1547 scf_pg_destroy(pg); 1548 return (ret); 1549 } 1550 1551 static void 1552 set_astring_prop(const char *fmri, const char *pgname, const char *pgtype, 1553 uint32_t pgflags, const char *propname, const char *str) 1554 { 1555 scf_instance_t *inst; 1556 scf_propertygroup_t *pg; 1557 scf_property_t *prop; 1558 scf_value_t *val; 1559 scf_transaction_t *tx; 1560 scf_transaction_entry_t *txent; 1561 int ret; 1562 1563 inst = scf_instance_create(h); 1564 if (inst == NULL) 1565 scfdie(); 1566 1567 if (get_inst(fmri, inst) != 0) 1568 return; 1569 1570 if ((pg = scf_pg_create(h)) == NULL || 1571 (prop = scf_property_create(h)) == NULL || 1572 (val = scf_value_create(h)) == NULL || 1573 (tx = scf_transaction_create(h)) == NULL || 1574 (txent = scf_entry_create(h)) == NULL) 1575 scfdie(); 1576 1577 if (scf_instance_get_pg(inst, pgname, pg) != SCF_SUCCESS) { 1578 if (scf_error() != SCF_ERROR_NOT_FOUND) 1579 scfdie(); 1580 1581 if (scf_instance_add_pg(inst, pgname, pgtype, pgflags, pg) != 1582 SCF_SUCCESS) { 1583 switch (scf_error()) { 1584 case SCF_ERROR_EXISTS: 1585 if (scf_instance_get_pg(inst, pgname, pg) != 1586 SCF_SUCCESS) { 1587 if (scf_error() != SCF_ERROR_NOT_FOUND) 1588 scfdie(); 1589 1590 uu_warn(gettext("Repository write " 1591 "contention.\n")); 1592 goto out; 1593 } 1594 break; 1595 1596 case SCF_ERROR_PERMISSION_DENIED: 1597 if (!verbose) 1598 uu_warn(emsg_permission_denied, fmri); 1599 else 1600 uu_warn(emsg_create_pg_perm_denied, 1601 fmri, pgname); 1602 goto out; 1603 1604 default: 1605 scfdie(); 1606 } 1607 } 1608 } 1609 1610 do { 1611 if (scf_transaction_start(tx, pg) != SCF_SUCCESS) { 1612 if (scf_error() != SCF_ERROR_PERMISSION_DENIED) 1613 scfdie(); 1614 1615 if (!verbose) 1616 uu_warn(emsg_permission_denied, fmri); 1617 else 1618 uu_warn(emsg_pg_perm_denied, fmri, pgname); 1619 goto out; 1620 } 1621 1622 if (scf_transaction_property_change_type(tx, txent, propname, 1623 SCF_TYPE_ASTRING) != 0) { 1624 if (scf_error() != SCF_ERROR_NOT_FOUND) 1625 scfdie(); 1626 1627 if (scf_transaction_property_new(tx, txent, propname, 1628 SCF_TYPE_ASTRING) != 0) 1629 scfdie(); 1630 } 1631 1632 if (scf_value_set_astring(val, str) != SCF_SUCCESS) 1633 scfdie(); 1634 1635 if (scf_entry_add_value(txent, val) != SCF_SUCCESS) 1636 scfdie(); 1637 1638 ret = scf_transaction_commit(tx); 1639 if (ret == -1) { 1640 if (scf_error() != SCF_ERROR_PERMISSION_DENIED) 1641 scfdie(); 1642 1643 if (!verbose) 1644 uu_warn(emsg_permission_denied, fmri); 1645 else 1646 uu_warn(emsg_prop_perm_denied, fmri, pgname, 1647 propname); 1648 goto out; 1649 } 1650 1651 if (ret == 0) { 1652 scf_transaction_reset(tx); 1653 1654 if (scf_pg_update(pg) == -1) 1655 scfdie(); 1656 } 1657 } while (ret == 0); 1658 1659 out: 1660 scf_transaction_destroy(tx); 1661 scf_entry_destroy(txent); 1662 scf_value_destroy(val); 1663 scf_property_destroy(prop); 1664 scf_pg_destroy(pg); 1665 scf_instance_destroy(inst); 1666 } 1667 1668 1669 /* 1670 * Flags to control enable and disable actions. 1671 */ 1672 #define SET_ENABLED 0x1 1673 #define SET_TEMPORARY 0x2 1674 #define SET_RECURSIVE 0x4 1675 1676 static int 1677 set_fmri_enabled(void *data, scf_walkinfo_t *wip) 1678 { 1679 int flags = (int)data; 1680 1681 assert(wip->inst != NULL); 1682 assert(wip->pg == NULL); 1683 1684 if (flags & SET_RECURSIVE) { 1685 char *fmri_buf = malloc(max_scf_fmri_sz); 1686 if (fmri_buf == NULL) 1687 uu_die(emsg_nomem); 1688 1689 visited = calloc(HT_BUCKETS, sizeof (*visited)); 1690 if (visited == NULL) 1691 uu_die(emsg_nomem); 1692 1693 /* scf_walk_fmri() guarantees that fmri isn't too long */ 1694 assert(strlen(wip->fmri) <= max_scf_fmri_sz); 1695 (void) strlcpy(fmri_buf, wip->fmri, max_scf_fmri_sz); 1696 1697 switch (enable_fmri_rec(fmri_buf, (flags & SET_TEMPORARY))) { 1698 case E2BIG: 1699 uu_warn(gettext("operation on service %s is ambiguous; " 1700 "instance specification needed.\n"), fmri_buf); 1701 break; 1702 1703 case ELOOP: 1704 uu_warn(gettext("%s: Dependency cycle detected.\n"), 1705 fmri_buf); 1706 } 1707 1708 free(visited); 1709 free(fmri_buf); 1710 1711 } else { 1712 set_inst_enabled(wip->fmri, wip->inst, 1713 (flags & SET_TEMPORARY) != 0, (flags & SET_ENABLED) != 0); 1714 } 1715 1716 return (0); 1717 } 1718 1719 /* ARGSUSED */ 1720 static int 1721 wait_fmri_enabled(void *data, scf_walkinfo_t *wip) 1722 { 1723 scf_propertygroup_t *pg = NULL; 1724 char state[MAX_SCF_STATE_STRING_SZ]; 1725 1726 assert(wip->inst != NULL); 1727 assert(wip->pg == NULL); 1728 1729 do { 1730 if (pg) 1731 scf_pg_destroy(pg); 1732 if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) { 1733 exit_status = EXIT_SVC_FAILURE; 1734 return (0); 1735 } 1736 1737 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0 || 1738 strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) { 1739 /* 1740 * We're done. 1741 */ 1742 goto out; 1743 } 1744 1745 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) { 1746 /* 1747 * The service is ill. 1748 */ 1749 uu_warn(gettext("Instance \"%s\" is in maintenance" 1750 " state.\n"), wip->fmri); 1751 exit_status = EXIT_SVC_FAILURE; 1752 goto out; 1753 } 1754 1755 if (!is_enabled(wip->inst)) { 1756 /* 1757 * Someone stepped in and disabled the service. 1758 */ 1759 uu_warn(gettext("Instance \"%s\" has been disabled" 1760 " by another entity.\n"), wip->fmri); 1761 exit_status = EXIT_SVC_FAILURE; 1762 goto out; 1763 } 1764 1765 if (!has_potential(wip->inst, B_FALSE)) { 1766 /* 1767 * Our dependencies aren't met. We'll never 1768 * amount to anything. 1769 */ 1770 uu_warn(gettext("Instance \"%s\" has unsatisfied" 1771 " dependencies.\n"), wip->fmri); 1772 /* 1773 * EXIT_SVC_FAILURE takes precedence over 1774 * EXIT_DEP_FAILURE 1775 */ 1776 if (exit_status == 0) 1777 exit_status = EXIT_DEP_FAILURE; 1778 goto out; 1779 } 1780 } while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0); 1781 scfdie(); 1782 /* NOTREACHED */ 1783 1784 out: 1785 scf_pg_destroy(pg); 1786 return (0); 1787 } 1788 1789 /* ARGSUSED */ 1790 static int 1791 wait_fmri_disabled(void *data, scf_walkinfo_t *wip) 1792 { 1793 scf_propertygroup_t *pg = NULL; 1794 char state[MAX_SCF_STATE_STRING_SZ]; 1795 1796 assert(wip->inst != NULL); 1797 assert(wip->pg == NULL); 1798 1799 do { 1800 if (pg) 1801 scf_pg_destroy(pg); 1802 if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) { 1803 exit_status = EXIT_SVC_FAILURE; 1804 return (0); 1805 } 1806 1807 if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0) { 1808 /* 1809 * We're done. 1810 */ 1811 goto out; 1812 } 1813 1814 if (is_enabled(wip->inst)) { 1815 /* 1816 * Someone stepped in and enabled the service. 1817 */ 1818 uu_warn(gettext("Instance \"%s\" has been enabled" 1819 " by another entity.\n"), wip->fmri); 1820 exit_status = EXIT_SVC_FAILURE; 1821 goto out; 1822 } 1823 1824 if (!has_potential(wip->inst, B_TRUE)) { 1825 /* 1826 * Our restarter is hopeless. 1827 */ 1828 uu_warn(gettext("Restarter for instance \"%s\" is" 1829 " unavailable.\n"), wip->fmri); 1830 /* 1831 * EXIT_SVC_FAILURE takes precedence over 1832 * EXIT_DEP_FAILURE 1833 */ 1834 if (exit_status == 0) 1835 exit_status = EXIT_DEP_FAILURE; 1836 goto out; 1837 } 1838 1839 } while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0); 1840 scfdie(); 1841 /* NOTREACHED */ 1842 1843 out: 1844 scf_pg_destroy(pg); 1845 return (0); 1846 } 1847 1848 /* ARGSUSED */ 1849 static int 1850 clear_instance(void *data, scf_walkinfo_t *wip) 1851 { 1852 char state[MAX_SCF_STATE_STRING_SZ]; 1853 1854 assert(wip->inst != NULL); 1855 assert(wip->pg == NULL); 1856 1857 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0) 1858 return (0); 1859 1860 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) { 1861 set_inst_action(wip->fmri, wip->inst, SCF_PROPERTY_MAINT_OFF); 1862 } else if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 1863 0) { 1864 set_inst_action(wip->fmri, wip->inst, SCF_PROPERTY_RESTORE); 1865 } else { 1866 uu_warn(gettext("Instance \"%s\" is not in a " 1867 "maintenance or degraded state.\n"), wip->fmri); 1868 1869 exit_status = 1; 1870 } 1871 1872 return (0); 1873 } 1874 1875 static int 1876 set_fmri_action(void *action, scf_walkinfo_t *wip) 1877 { 1878 assert(wip->inst != NULL && wip->pg == NULL); 1879 1880 set_inst_action(wip->fmri, wip->inst, action); 1881 1882 return (0); 1883 } 1884 1885 /* 1886 * Flags to control 'mark' action. 1887 */ 1888 #define MARK_IMMEDIATE 0x1 1889 #define MARK_TEMPORARY 0x2 1890 1891 static int 1892 force_degraded(void *data, scf_walkinfo_t *wip) 1893 { 1894 int flags = (int)data; 1895 char state[MAX_SCF_STATE_STRING_SZ]; 1896 1897 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0) { 1898 exit_status = 1; 1899 return (0); 1900 } 1901 1902 if (strcmp(state, SCF_STATE_STRING_ONLINE) != 0) { 1903 uu_warn(gettext("Instance \"%s\" is not online.\n"), wip->fmri); 1904 exit_status = 1; 1905 return (0); 1906 } 1907 1908 set_inst_action(wip->fmri, wip->inst, (flags & MARK_IMMEDIATE) ? 1909 SCF_PROPERTY_DEGRADE_IMMEDIATE : SCF_PROPERTY_DEGRADED); 1910 1911 return (0); 1912 } 1913 1914 static int 1915 force_maintenance(void *data, scf_walkinfo_t *wip) 1916 { 1917 int flags = (int)data; 1918 const char *prop; 1919 1920 if (flags & MARK_IMMEDIATE) { 1921 prop = (flags & MARK_TEMPORARY) ? 1922 SCF_PROPERTY_MAINT_ON_IMMTEMP : 1923 SCF_PROPERTY_MAINT_ON_IMMEDIATE; 1924 } else { 1925 prop = (flags & MARK_TEMPORARY) ? 1926 SCF_PROPERTY_MAINT_ON_TEMPORARY : 1927 SCF_PROPERTY_MAINT_ON; 1928 } 1929 1930 set_inst_action(wip->fmri, wip->inst, prop); 1931 1932 return (0); 1933 } 1934 1935 static void 1936 set_milestone(const char *fmri, boolean_t temporary) 1937 { 1938 scf_instance_t *inst; 1939 scf_propertygroup_t *pg; 1940 int r; 1941 1942 if (temporary) { 1943 set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS_OVR, 1944 SCF_PG_OPTIONS_OVR_TYPE, SCF_PG_OPTIONS_OVR_FLAGS, 1945 SCF_PROPERTY_MILESTONE, fmri); 1946 return; 1947 } 1948 1949 if ((inst = scf_instance_create(h)) == NULL || 1950 (pg = scf_pg_create(h)) == NULL) 1951 scfdie(); 1952 1953 if (get_inst(SCF_SERVICE_STARTD, inst) != 0) { 1954 scf_instance_destroy(inst); 1955 return; 1956 } 1957 1958 /* 1959 * Set the persistent milestone before deleting the override so we don't 1960 * glitch. 1961 */ 1962 set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS, 1963 SCF_PG_OPTIONS_TYPE, SCF_PG_OPTIONS_FLAGS, SCF_PROPERTY_MILESTONE, 1964 fmri); 1965 1966 r = scf_instance_delete_prop(inst, SCF_PG_OPTIONS_OVR, 1967 SCF_PROPERTY_MILESTONE); 1968 switch (r) { 1969 case 0: 1970 break; 1971 1972 case ECANCELED: 1973 uu_warn(emsg_no_service, fmri); 1974 exit_status = 1; 1975 goto out; 1976 1977 case EPERM: 1978 uu_warn(gettext("Could not delete %s/%s property of " 1979 "%s: permission denied.\n"), SCF_PG_OPTIONS_OVR, 1980 SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD); 1981 exit_status = 1; 1982 goto out; 1983 1984 case EACCES: 1985 uu_warn(gettext("Could not delete %s/%s property of " 1986 "%s: access denied.\n"), SCF_PG_OPTIONS_OVR, 1987 SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD); 1988 exit_status = 1; 1989 goto out; 1990 1991 case EROFS: 1992 uu_warn(gettext("Could not delete %s/%s property of " 1993 "%s: backend read-only.\n"), SCF_PG_OPTIONS_OVR, 1994 SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD); 1995 exit_status = 1; 1996 goto out; 1997 1998 default: 1999 bad_error("scf_instance_delete_prop", r); 2000 } 2001 2002 out: 2003 scf_pg_destroy(pg); 2004 scf_instance_destroy(inst); 2005 } 2006 2007 static char const *milestones[] = { 2008 SCF_MILESTONE_SINGLE_USER, 2009 SCF_MILESTONE_MULTI_USER, 2010 SCF_MILESTONE_MULTI_USER_SERVER, 2011 NULL 2012 }; 2013 2014 static void 2015 usage_milestone(void) 2016 { 2017 const char **ms; 2018 2019 (void) fprintf(stderr, gettext( 2020 "Usage: svcadm milestone [-d] <milestone>\n\n" 2021 "\t-d\tmake the specified milestone the default for system boot\n\n" 2022 "\tMilestones can be specified using an FMRI or abbreviation.\n" 2023 "\tThe major milestones are as follows:\n\n" 2024 "\tall\n" 2025 "\tnone\n")); 2026 2027 for (ms = milestones; *ms != NULL; ms++) 2028 (void) fprintf(stderr, "\t%s\n", *ms); 2029 2030 exit(UU_EXIT_USAGE); 2031 } 2032 2033 static const char * 2034 validate_milestone(const char *milestone) 2035 { 2036 const char **ms; 2037 const char *tmp; 2038 size_t len; 2039 2040 if (strcmp(milestone, "all") == 0) 2041 return (milestone); 2042 2043 if (strcmp(milestone, "none") == 0) 2044 return (milestone); 2045 2046 /* 2047 * Determine if this is a full or partial milestone 2048 */ 2049 for (ms = milestones; *ms != NULL; ms++) { 2050 if ((tmp = strstr(*ms, milestone)) != NULL) { 2051 len = strlen(milestone); 2052 2053 /* 2054 * The beginning of the string must align with the start 2055 * of a milestone fmri, or on the boundary between 2056 * elements. The end of the string must align with the 2057 * end of the milestone, or at the instance boundary. 2058 */ 2059 if ((tmp == *ms || tmp[-1] == '/') && 2060 (tmp[len] == '\0' || tmp[len] == ':')) 2061 return (*ms); 2062 } 2063 } 2064 2065 (void) fprintf(stderr, 2066 gettext("\"%s\" is not a valid major milestone.\n"), milestone); 2067 2068 usage_milestone(); 2069 /* NOTREACHED */ 2070 } 2071 2072 /*ARGSUSED*/ 2073 static void 2074 quiet(const char *fmt, ...) 2075 { 2076 /* Do nothing */ 2077 } 2078 2079 int 2080 main(int argc, char *argv[]) 2081 { 2082 int o; 2083 int err; 2084 int sw_back; 2085 2086 (void) setlocale(LC_ALL, ""); 2087 (void) textdomain(TEXT_DOMAIN); 2088 2089 (void) uu_setpname(argv[0]); 2090 2091 if (argc < 2) 2092 usage(); 2093 2094 max_scf_fmri_sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH); 2095 if (max_scf_fmri_sz < 0) 2096 scfdie(); 2097 ++max_scf_fmri_sz; 2098 2099 scratch_fmri = malloc(max_scf_fmri_sz); 2100 if (scratch_fmri == NULL) 2101 uu_die(emsg_nomem); 2102 2103 h = scf_handle_create(SCF_VERSION); 2104 if (h == NULL) 2105 scfdie(); 2106 2107 while ((o = getopt(argc, argv, "vz:")) != -1) { 2108 switch (o) { 2109 case 'v': 2110 verbose = 1; 2111 break; 2112 2113 case 'z': { 2114 scf_value_t *zone; 2115 2116 if (getzoneid() != GLOBAL_ZONEID) 2117 uu_die(gettext("svcadm -z may only be used " 2118 "from the global zone\n")); 2119 2120 if ((zone = scf_value_create(h)) == NULL) 2121 scfdie(); 2122 2123 if (scf_value_set_astring(zone, optarg) != SCF_SUCCESS) 2124 scfdie(); 2125 2126 if (scf_handle_decorate(h, "zone", zone) != SCF_SUCCESS) 2127 uu_die(gettext("invalid zone '%s'\n"), optarg); 2128 2129 scf_value_destroy(zone); 2130 break; 2131 } 2132 2133 default: 2134 usage(); 2135 } 2136 } 2137 2138 if (scf_handle_bind(h) == -1) 2139 uu_die(gettext("Couldn't bind to configuration repository: " 2140 "%s.\n"), scf_strerror(scf_error())); 2141 2142 if (optind >= argc) 2143 usage(); 2144 2145 emsg_permission_denied = gettext("%s: Permission denied.\n"); 2146 emsg_nomem = gettext("Out of memory.\n"); 2147 emsg_create_pg_perm_denied = gettext("%s: Couldn't create \"%s\" " 2148 "property group (permission denied).\n"); 2149 emsg_pg_perm_denied = gettext("%s: Couldn't modify \"%s\" property " 2150 "group (permission denied).\n"); 2151 emsg_prop_perm_denied = gettext("%s: Couldn't modify \"%s/%s\" " 2152 "property (permission denied).\n"); 2153 emsg_no_service = gettext("No such service \"%s\".\n"); 2154 2155 if (strcmp(argv[optind], "enable") == 0) { 2156 int flags = SET_ENABLED; 2157 int wait = 0; 2158 int error = 0; 2159 2160 ++optind; 2161 2162 while ((o = getopt(argc, argv, "rst")) != -1) { 2163 if (o == 'r') 2164 flags |= SET_RECURSIVE; 2165 else if (o == 't') 2166 flags |= SET_TEMPORARY; 2167 else if (o == 's') 2168 wait = 1; 2169 else if (o == '?') 2170 usage(); 2171 else { 2172 assert(0); 2173 abort(); 2174 } 2175 } 2176 argc -= optind; 2177 argv += optind; 2178 2179 if (argc <= 0) 2180 usage(); 2181 2182 /* 2183 * We want to continue with -s processing if we had 2184 * invalid options, but not if an enable failed. We 2185 * squelch output the second time we walk fmris; we saw 2186 * the errors the first time. 2187 */ 2188 if ((err = scf_walk_fmri(h, argc, argv, 0, set_fmri_enabled, 2189 (void *)flags, &error, uu_warn)) != 0) { 2190 2191 uu_warn(gettext("failed to iterate over " 2192 "instances: %s\n"), scf_strerror(err)); 2193 exit_status = UU_EXIT_FATAL; 2194 2195 } else if (wait && exit_status == 0 && 2196 (err = scf_walk_fmri(h, argc, argv, 0, wait_fmri_enabled, 2197 (void *)flags, &error, quiet)) != 0) { 2198 2199 uu_warn(gettext("failed to iterate over " 2200 "instances: %s\n"), scf_strerror(err)); 2201 exit_status = UU_EXIT_FATAL; 2202 } 2203 2204 if (error > 0) 2205 exit_status = error; 2206 2207 } else if (strcmp(argv[optind], "disable") == 0) { 2208 int flags = 0; 2209 int wait = 0; 2210 int error = 0; 2211 2212 ++optind; 2213 2214 while ((o = getopt(argc, argv, "st")) != -1) { 2215 if (o == 't') 2216 flags |= SET_TEMPORARY; 2217 else if (o == 's') 2218 wait = 1; 2219 else if (o == '?') 2220 usage(); 2221 else { 2222 assert(0); 2223 abort(); 2224 } 2225 } 2226 argc -= optind; 2227 argv += optind; 2228 2229 if (argc <= 0) 2230 usage(); 2231 2232 /* 2233 * We want to continue with -s processing if we had 2234 * invalid options, but not if a disable failed. We 2235 * squelch output the second time we walk fmris; we saw 2236 * the errors the first time. 2237 */ 2238 if ((err = scf_walk_fmri(h, argc, argv, 0, set_fmri_enabled, 2239 (void *)flags, &exit_status, uu_warn)) != 0) { 2240 2241 uu_warn(gettext("failed to iterate over " 2242 "instances: %s\n"), scf_strerror(err)); 2243 exit_status = UU_EXIT_FATAL; 2244 2245 } else if (wait && exit_status == 0 && 2246 (err = scf_walk_fmri(h, argc, argv, 0, wait_fmri_disabled, 2247 (void *)flags, &error, quiet)) != 0) { 2248 2249 uu_warn(gettext("failed to iterate over " 2250 "instances: %s\n"), scf_strerror(err)); 2251 exit_status = UU_EXIT_FATAL; 2252 } 2253 2254 if (error > 0) 2255 exit_status = error; 2256 2257 } else if (strcmp(argv[optind], "restart") == 0) { 2258 ++optind; 2259 2260 if (optind >= argc) 2261 usage(); 2262 2263 if ((err = scf_walk_fmri(h, argc - optind, argv + optind, 0, 2264 set_fmri_action, (void *)SCF_PROPERTY_RESTART, 2265 &exit_status, uu_warn)) != 0) { 2266 uu_warn(gettext("failed to iterate over " 2267 "instances: %s\n"), scf_strerror(err)); 2268 exit_status = UU_EXIT_FATAL; 2269 } 2270 2271 } else if (strcmp(argv[optind], "refresh") == 0) { 2272 ++optind; 2273 2274 if (optind >= argc) 2275 usage(); 2276 2277 if ((err = scf_walk_fmri(h, argc - optind, argv + optind, 0, 2278 set_fmri_action, (void *)SCF_PROPERTY_REFRESH, 2279 &exit_status, uu_warn)) != 0) { 2280 uu_warn(gettext("failed to iterate over " 2281 "instances: %s\n"), scf_strerror(scf_error())); 2282 exit_status = UU_EXIT_FATAL; 2283 } 2284 2285 } else if (strcmp(argv[optind], "mark") == 0) { 2286 int flags = 0; 2287 scf_walk_callback callback; 2288 2289 ++optind; 2290 2291 while ((o = getopt(argc, argv, "It")) != -1) { 2292 if (o == 'I') 2293 flags |= MARK_IMMEDIATE; 2294 else if (o == 't') 2295 flags |= MARK_TEMPORARY; 2296 else if (o == '?') 2297 usage(); 2298 else { 2299 assert(0); 2300 abort(); 2301 } 2302 } 2303 2304 if (argc - optind < 2) 2305 usage(); 2306 2307 if (strcmp(argv[optind], "degraded") == 0) { 2308 if (flags & MARK_TEMPORARY) 2309 uu_xdie(UU_EXIT_USAGE, gettext("-t may not be " 2310 "used with degraded.\n")); 2311 callback = force_degraded; 2312 2313 } else if (strcmp(argv[optind], "maintenance") == 0) { 2314 callback = force_maintenance; 2315 } else { 2316 usage(); 2317 } 2318 2319 if ((err = scf_walk_fmri(h, argc - optind - 1, 2320 argv + optind + 1, 0, callback, NULL, &exit_status, 2321 uu_warn)) != 0) { 2322 uu_warn(gettext("failed to iterate over " 2323 "instances: %s\n"), 2324 scf_strerror(err)); 2325 exit_status = UU_EXIT_FATAL; 2326 } 2327 2328 } else if (strcmp(argv[optind], "clear") == 0) { 2329 ++optind; 2330 2331 if (optind >= argc) 2332 usage(); 2333 2334 if ((err = scf_walk_fmri(h, argc - optind, argv + optind, 0, 2335 clear_instance, NULL, &exit_status, uu_warn)) != 0) { 2336 uu_warn(gettext("failed to iterate over " 2337 "instances: %s\n"), scf_strerror(err)); 2338 exit_status = UU_EXIT_FATAL; 2339 } 2340 2341 } else if (strcmp(argv[optind], "milestone") == 0) { 2342 boolean_t temporary = B_TRUE; 2343 const char *milestone; 2344 2345 ++optind; 2346 2347 while ((o = getopt(argc, argv, "d")) != -1) { 2348 if (o == 'd') 2349 temporary = B_FALSE; 2350 else if (o == '?') 2351 usage_milestone(); 2352 else { 2353 assert(0); 2354 abort(); 2355 } 2356 } 2357 2358 if (optind >= argc) 2359 usage_milestone(); 2360 2361 milestone = validate_milestone(argv[optind]); 2362 2363 set_milestone(milestone, temporary); 2364 } else if (strcmp(argv[optind], "_smf_backup") == 0) { 2365 const char *reason = NULL; 2366 2367 ++optind; 2368 2369 if (optind != argc - 1) 2370 usage(); 2371 2372 if ((err = _scf_request_backup(h, argv[optind])) != 2373 SCF_SUCCESS) { 2374 switch (scf_error()) { 2375 case SCF_ERROR_NOT_BOUND: 2376 case SCF_ERROR_CONNECTION_BROKEN: 2377 case SCF_ERROR_BACKEND_READONLY: 2378 scfdie(); 2379 break; 2380 2381 case SCF_ERROR_PERMISSION_DENIED: 2382 case SCF_ERROR_INVALID_ARGUMENT: 2383 reason = scf_strerror(scf_error()); 2384 break; 2385 2386 case SCF_ERROR_INTERNAL: 2387 reason = 2388 "unknown error (see console for details)"; 2389 break; 2390 } 2391 2392 uu_warn("failed to backup repository: %s\n", reason); 2393 exit_status = UU_EXIT_FATAL; 2394 } 2395 } else if (strcmp(argv[optind], "_smf_repository_switch") == 0) { 2396 const char *reason = NULL; 2397 2398 ++optind; 2399 2400 /* 2401 * Check argument and setup scf_switch structure 2402 */ 2403 if (optind != argc - 1) 2404 exit(1); 2405 2406 if (strcmp(argv[optind], "fast") == 0) { 2407 sw_back = 0; 2408 } else if (strcmp(argv[optind], "perm") == 0) { 2409 sw_back = 1; 2410 } else { 2411 exit(UU_EXIT_USAGE); 2412 } 2413 2414 /* 2415 * Call into switch primitive 2416 */ 2417 if ((err = _scf_repository_switch(h, sw_back)) != 2418 SCF_SUCCESS) { 2419 /* 2420 * Retrieve per thread SCF error code 2421 */ 2422 switch (scf_error()) { 2423 case SCF_ERROR_NOT_BOUND: 2424 abort(); 2425 /* NOTREACHED */ 2426 2427 case SCF_ERROR_CONNECTION_BROKEN: 2428 case SCF_ERROR_BACKEND_READONLY: 2429 scfdie(); 2430 /* NOTREACHED */ 2431 2432 case SCF_ERROR_PERMISSION_DENIED: 2433 case SCF_ERROR_INVALID_ARGUMENT: 2434 reason = scf_strerror(scf_error()); 2435 break; 2436 2437 case SCF_ERROR_INTERNAL: 2438 reason = "File operation error: (see console)"; 2439 break; 2440 2441 default: 2442 abort(); 2443 /* NOTREACHED */ 2444 } 2445 2446 uu_warn("failed to switch repository: %s\n", reason); 2447 exit_status = UU_EXIT_FATAL; 2448 } 2449 } else { 2450 usage(); 2451 } 2452 2453 if (scf_handle_unbind(h) == -1) 2454 scfdie(); 2455 scf_handle_destroy(h); 2456 2457 return (exit_status); 2458 } 2459