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