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