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