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