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