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