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