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