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 */
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>
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 scf_handle_t *h;
86 ssize_t max_scf_fmri_sz;
87 static const char *emsg_permission_denied;
88 static const char *emsg_nomem;
89 static const char *emsg_create_pg_perm_denied;
90 static const char *emsg_pg_perm_denied;
91 static const char *emsg_prop_perm_denied;
92 static const char *emsg_no_service;
93
94 static int exit_status = 0;
95 static int verbose = 0;
96 static char *scratch_fmri;
97 static char *g_zonename = NULL;
98 static char svcstate[80];
99 static boolean_t svcsearch = B_FALSE;
100
101 static struct ht_elt **visited;
102
103 void do_scfdie(int lineno) __NORETURN;
104 static void usage_milestone(void) __NORETURN;
129
130 default:
131 #ifdef NDEBUG
132 uu_die(gettext("Unexpected libscf error: %s. Exiting.\n"),
133 scf_strerror(err));
134 #else
135 uu_die("Unexpected libscf error on line %d: %s.\n", lineno,
136 scf_strerror(err));
137 #endif
138 }
139 }
140
141 #define scfdie() do_scfdie(__LINE__)
142
143 static void
144 usage()
145 {
146 (void) fprintf(stderr, gettext(
147 "Usage: %1$s [-S <state>] [-v] [-Z | -z zone] [cmd [args ... ]]\n\n"
148 "\t%1$s enable [-rst] [<service> ...]\t- enable and online service(s)\n"
149 "\t%1$s disable [-st] [<service> ...]\t- disable and offline "
150 "service(s)\n"
151 "\t%1$s restart [-d] [<service> ...]\t- restart specified service(s)\n"
152 "\t%1$s refresh [<service> ...]\t\t- re-read service configuration\n"
153 "\t%1$s mark [-It] <state> [<service> ...] - set maintenance state\n"
154 "\t%1$s clear [<service> ...]\t\t- clear maintenance state\n"
155 "\t%1$s milestone [-d] <milestone>\t- advance to a service milestone\n"
156 "\n\t"
157 "Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n"
158 "\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n"
159 "\n"
160 "\t%1$s <cmd> svc:/network/smtp:sendmail\n"
161 "\t%1$s <cmd> network/smtp:sendmail\n"
162 "\t%1$s <cmd> network/*mail\n"
163 "\t%1$s <cmd> network/smtp\n"
164 "\t%1$s <cmd> smtp:sendmail\n"
165 "\t%1$s <cmd> smtp\n"
166 "\t%1$s <cmd> sendmail\n"), uu_getpname());
167
168 exit(UU_EXIT_USAGE);
169 }
550 default:
551 scfdie();
552 }
553
554 if (my_ct_name(scratch_fmri, max_scf_fmri_sz) > 0) {
555 set_astring_prop(fmri, SCF_PG_RESTARTER_ACTIONS,
556 SCF_PG_RESTARTER_ACTIONS_TYPE,
557 SCF_PG_RESTARTER_ACTIONS_FLAGS,
558 SCF_PROPERTY_AUX_FMRI, scratch_fmri);
559 } else {
560 uu_warn(gettext("%s: Could not set %s/%s: "
561 "my_ct_name failed.\n"), fmri,
562 SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_FMRI);
563 }
564
565 out:
566 scf_pg_destroy(pg);
567 return (ret);
568 }
569
570 /*
571 * Enable or disable inst, per enable. If temp is true, set
572 * general_ovr/enabled. Otherwise set general/enabled and delete
573 * general_ovr/enabled if it exists (order is important here: we don't want the
574 * enabled status to glitch).
575 */
576 static void
577 set_inst_enabled(const char *fmri, scf_instance_t *inst, boolean_t temp,
578 boolean_t enable)
579 {
580 scf_propertygroup_t *pg;
581 uint8_t b;
582 const char *pgname = NULL; /* For emsg_pg_perm_denied */
583 int r;
584
585 pg = scf_pg_create(h);
586 if (pg == NULL)
587 scfdie();
588
589 if (restarter_setup(fmri, inst))
590 goto out;
591
592 /*
593 * An instance's configuration is incomplete if general/enabled
594 * doesn't exist. Create both the property group and property
595 * here if they don't exist.
596 */
597 pgname = SCF_PG_GENERAL;
598 if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE,
599 SCF_PG_GENERAL_FLAGS, pg) != 0)
600 goto eperm;
601
602 if (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b) != 0) {
603 /* Create and set state to disabled */
608 case EPERM:
609 goto eperm;
610
611 case EROFS:
612 /* Shouldn't happen, but it can. */
613 if (!verbose)
614 uu_warn(gettext("%s: Repository read-only.\n"),
615 fmri);
616 else
617 uu_warn(gettext("%s: Could not set %s/%s "
618 "(repository read-only).\n"), fmri,
619 SCF_PG_GENERAL, SCF_PROPERTY_ENABLED);
620 goto out;
621
622 default:
623 assert(0);
624 abort();
625 }
626 }
627
628 if (temp) {
629 /* Set general_ovr/enabled */
630 pgname = SCF_PG_GENERAL_OVR;
631 if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_OVR_TYPE,
632 SCF_PG_GENERAL_OVR_FLAGS, pg) != 0)
633 goto eperm;
634
635 switch (set_bool_prop(pg, SCF_PROPERTY_ENABLED, enable)) {
636 case 0:
637 break;
638
639 case EPERM:
640 goto eperm;
641
642 case EROFS:
643 /* Shouldn't happen, but it can. */
644 if (!verbose)
645 uu_warn(gettext("%s: Repository read-only.\n"),
646 fmri);
647 else
648 uu_warn(gettext("%s: Could not set %s/%s "
649 "(repository read-only).\n"), fmri,
650 SCF_PG_GENERAL_OVR, SCF_PROPERTY_ENABLED);
651 goto out;
652
653 default:
654 assert(0);
655 abort();
656 }
657
658 if (verbose)
659 (void) printf(enable ?
660 gettext("%s temporarily enabled.\n") :
661 gettext("%s temporarily disabled.\n"), fmri);
662 } else {
663 again:
664 /*
665 * Both pg and property should exist since we created
666 * them earlier. However, there's still a chance that
667 * someone may have deleted the property out from under
668 * us.
669 */
670 if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE,
671 SCF_PG_GENERAL_FLAGS, pg) != 0)
672 goto eperm;
673
674 switch (set_bool_prop(pg, SCF_PROPERTY_ENABLED, enable)) {
675 case 0:
676 break;
677
678 case EPERM:
679 goto eperm;
680
681 case EROFS:
682 /*
683 * If general/enabled is already set the way we want,
684 * proceed.
685 */
686 switch (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b)) {
687 case 0:
688 if ((b != 0) == (enable != B_FALSE))
689 break;
690 /* FALLTHROUGH */
691
692 case ENOENT:
693 case EINVAL:
694 case E2BIG:
695 if (!verbose)
696 uu_warn(gettext("%s: Repository "
697 "read-only.\n"), fmri);
698 else
699 uu_warn(gettext("%s: Could not set "
700 "%s/%s (repository read-only).\n"),
701 fmri, SCF_PG_GENERAL,
702 SCF_PROPERTY_ENABLED);
703 goto out;
704
705 case ECANCELED:
706 goto again;
707
708 default:
709 assert(0);
710 abort();
711 }
712 break;
713
714 default:
715 assert(0);
716 abort();
717 }
718
719 pgname = SCF_PG_GENERAL_OVR;
720 r = scf_instance_delete_prop(inst, pgname,
721 SCF_PROPERTY_ENABLED);
722 switch (r) {
723 case 0:
724 break;
725
726 case ECANCELED:
727 uu_warn(emsg_no_service, fmri);
728 goto out;
729
730 case EPERM:
731 goto eperm;
732
733 case EACCES:
734 uu_warn(gettext("Could not delete %s/%s "
735 "property of %s: backend access denied.\n"),
736 pgname, SCF_PROPERTY_ENABLED, fmri);
737 goto out;
738
739 case EROFS:
740 uu_warn(gettext("Could not delete %s/%s "
741 "property of %s: backend is read-only.\n"),
742 pgname, SCF_PROPERTY_ENABLED, fmri);
743 goto out;
744
745 default:
746 bad_error("scf_instance_delete_prop", r);
747 }
748
749 if (verbose)
750 (void) printf(enable ? gettext("%s enabled.\n") :
751 gettext("%s disabled.\n"), fmri);
752 }
753
754 scf_pg_destroy(pg);
755 return;
756
757 eperm:
758 assert(pgname != NULL);
759 if (!verbose)
760 uu_warn(emsg_permission_denied, fmri);
761 else
762 uu_warn(emsg_pg_perm_denied, fmri, pgname);
763
764 out:
765 scf_pg_destroy(pg);
766 exit_status = 1;
767 }
768
769 /*
770 * Set inst to the instance which corresponds to fmri. If fmri identifies
771 * a service with a single instance, get that instance.
772 *
997 scf_instance_destroy(inst);
998 return (ret);
999 }
1000
1001 /*
1002 * Enable the service or instance identified by fmri and its dependencies,
1003 * recursively. Specifically, call get_inst(fmri), enable the result, and
1004 * recurse on its restarter and the dependencies. To avoid duplication of
1005 * effort or looping around a dependency cycle, each FMRI is entered into the
1006 * "visited" hash table. While recursing, the hash table entry is marked
1007 * "active", so that if we come upon it again, we know we've hit a cycle.
1008 * exclude_all and optional_all dependencies are ignored. require_any
1009 * dependencies are followed only if they comprise a single service; otherwise
1010 * the user is warned.
1011 *
1012 * fmri must point to a writable max_scf_fmri_sz buffer. Returns EINVAL if fmri
1013 * is invalid, E2BIG if fmri identifies a service with multiple instances, ELOOP
1014 * on cycle detection, or 0 on success.
1015 */
1016 static int
1017 enable_fmri_rec(char *fmri, boolean_t temp)
1018 {
1019 scf_instance_t *inst;
1020 scf_snapshot_t *snap;
1021 scf_propertygroup_t *pg;
1022 scf_property_t *prop;
1023 scf_value_t *v;
1024 scf_iter_t *pg_iter, *val_iter;
1025 scf_type_t ty;
1026 char *buf, *pgname;
1027 ssize_t name_sz, len, sz;
1028 int ret;
1029 struct ht_elt *he;
1030
1031 len = scf_canonify_fmri(fmri, fmri, max_scf_fmri_sz);
1032 if (len < 0) {
1033 assert(scf_error() == SCF_ERROR_INVALID_ARGUMENT);
1034 return (EINVAL);
1035 }
1036 assert(len < max_scf_fmri_sz);
1037
1051 abort();
1052 }
1053
1054 inst = scf_instance_create(h);
1055 if (inst == NULL)
1056 scfdie();
1057
1058 switch (get_inst_mult(fmri, inst)) {
1059 case 0:
1060 break;
1061
1062 case E2BIG:
1063 he->active = B_FALSE;
1064 return (E2BIG);
1065
1066 default:
1067 he->active = B_FALSE;
1068 return (0);
1069 }
1070
1071 set_inst_enabled(fmri, inst, temp, B_TRUE);
1072
1073 if ((snap = scf_snapshot_create(h)) == NULL ||
1074 (pg = scf_pg_create(h)) == NULL ||
1075 (prop = scf_property_create(h)) == NULL ||
1076 (v = scf_value_create(h)) == NULL ||
1077 (pg_iter = scf_iter_create(h)) == NULL ||
1078 (val_iter = scf_iter_create(h)) == NULL)
1079 scfdie();
1080
1081 buf = malloc(max_scf_fmri_sz);
1082 if (buf == NULL)
1083 uu_die(emsg_nomem);
1084
1085 name_sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
1086 if (name_sz < 0)
1087 scfdie();
1088 ++name_sz;
1089 pgname = malloc(name_sz);
1090 if (pgname == NULL)
1091 uu_die(emsg_nomem);
1101 /* Enable restarter */
1102 if (scf_instance_get_pg_composed(inst, snap, SCF_PG_GENERAL, pg) != 0) {
1103 if (scf_error() != SCF_ERROR_NOT_FOUND)
1104 scfdie();
1105
1106 uu_warn(gettext("\"%s\" is misconfigured (lacks \"%s\" "
1107 "property group).\n"), fmri, SCF_PG_GENERAL);
1108 ret = 0;
1109 goto out;
1110 }
1111
1112 sz = get_astring_prop(pg, SCF_PROPERTY_RESTARTER, prop, v, buf,
1113 max_scf_fmri_sz);
1114 if (sz > max_scf_fmri_sz) {
1115 uu_warn(gettext("\"%s\" is misconfigured (the value of "
1116 "\"%s/%s\" is too long).\n"), fmri, SCF_PG_GENERAL,
1117 SCF_PROPERTY_RESTARTER);
1118 ret = 0;
1119 goto out;
1120 } else if (sz >= 0) {
1121 switch (enable_fmri_rec(buf, temp)) {
1122 case 0:
1123 break;
1124
1125 case EINVAL:
1126 uu_warn(gettext("Restarter FMRI for \"%s\" is "
1127 "invalid.\n"), fmri);
1128 break;
1129
1130 case E2BIG:
1131 uu_warn(gettext("Restarter FMRI for \"%s\" identifies "
1132 "a service with multiple instances.\n"), fmri);
1133 break;
1134
1135 case ELOOP:
1136 ret = ELOOP;
1137 goto out;
1138
1139 default:
1140 assert(0);
1141 abort();
1252
1253 /*
1254 * Since there's only one instance, we can enable it.
1255 * Reset val_iter and continue.
1256 */
1257 if (scf_iter_property_values(val_iter, prop) != 0)
1258 scfdie();
1259 }
1260
1261 for (;;) {
1262 ret = scf_iter_next_value(val_iter, v);
1263 if (ret == 0)
1264 break;
1265 if (ret != 1)
1266 scfdie();
1267
1268 if (scf_value_get_astring(v, buf, max_scf_fmri_sz) ==
1269 -1)
1270 scfdie();
1271
1272 switch (enable_fmri_rec(buf, temp)) {
1273 case 0:
1274 break;
1275
1276 case EINVAL:
1277 uu_warn(gettext("\"%s\" dependency of \"%s\" "
1278 "has invalid FMRI \"%s\".\n"), pgname,
1279 fmri, buf);
1280 break;
1281
1282 case E2BIG:
1283 uu_warn(gettext("%s depends on %s, which has "
1284 "multiple instances.\n"), fmri, buf);
1285 break;
1286
1287 case ELOOP:
1288 ret = ELOOP;
1289 goto out;
1290
1291 default:
1292 assert(0);
1651 }
1652
1653 if (ret == 0) {
1654 scf_transaction_reset(tx);
1655
1656 if (scf_pg_update(pg) == -1)
1657 scfdie();
1658 }
1659 } while (ret == 0);
1660
1661 out:
1662 scf_transaction_destroy(tx);
1663 scf_entry_destroy(txent);
1664 scf_value_destroy(val);
1665 scf_property_destroy(prop);
1666 scf_pg_destroy(pg);
1667 scf_instance_destroy(inst);
1668 }
1669
1670
1671 /*
1672 * Flags to control enable and disable actions.
1673 */
1674 #define SET_ENABLED 0x1
1675 #define SET_TEMPORARY 0x2
1676 #define SET_RECURSIVE 0x4
1677
1678 static int
1679 set_fmri_enabled(void *data, scf_walkinfo_t *wip)
1680 {
1681 int flags = (int)data;
1682
1683 assert(wip->inst != NULL);
1684 assert(wip->pg == NULL);
1685
1686 if (svcsearch) {
1687 char state[MAX_SCF_STATE_STRING_SZ];
1688
1689 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
1690 return (0);
1691 if (strcmp(state, svcstate) != 0)
1692 return (0);
1693 }
1694
1695 if (flags & SET_RECURSIVE) {
1696 char *fmri_buf = malloc(max_scf_fmri_sz);
1697 if (fmri_buf == NULL)
1698 uu_die(emsg_nomem);
1699
1700 visited = calloc(HT_BUCKETS, sizeof (*visited));
1701 if (visited == NULL)
1702 uu_die(emsg_nomem);
1703
1704 /* scf_walk_fmri() guarantees that fmri isn't too long */
1705 assert(strlen(wip->fmri) <= max_scf_fmri_sz);
1706 (void) strlcpy(fmri_buf, wip->fmri, max_scf_fmri_sz);
1707
1708 switch (enable_fmri_rec(fmri_buf, (flags & SET_TEMPORARY))) {
1709 case E2BIG:
1710 uu_warn(gettext("operation on service %s is ambiguous; "
1711 "instance specification needed.\n"), fmri_buf);
1712 break;
1713
1714 case ELOOP:
1715 uu_warn(gettext("%s: Dependency cycle detected.\n"),
1716 fmri_buf);
1717 }
1718
1719 free(visited);
1720 free(fmri_buf);
1721
1722 } else {
1723 set_inst_enabled(wip->fmri, wip->inst,
1724 (flags & SET_TEMPORARY) != 0, (flags & SET_ENABLED) != 0);
1725 }
1726
1727 return (0);
1728 }
1729
1730 /* ARGSUSED */
1731 static int
1732 wait_fmri_enabled(void *data, scf_walkinfo_t *wip)
1733 {
1734 scf_propertygroup_t *pg = NULL;
1735 char state[MAX_SCF_STATE_STRING_SZ];
1736
1737 assert(wip->inst != NULL);
1738 assert(wip->pg == NULL);
1739
1740 do {
1741 if (pg)
1742 scf_pg_destroy(pg);
1743 if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) {
1744 exit_status = EXIT_SVC_FAILURE;
1955 if (flags & MARK_IMMEDIATE) {
1956 prop = (flags & MARK_TEMPORARY) ?
1957 SCF_PROPERTY_MAINT_ON_IMMTEMP :
1958 SCF_PROPERTY_MAINT_ON_IMMEDIATE;
1959 } else {
1960 prop = (flags & MARK_TEMPORARY) ?
1961 SCF_PROPERTY_MAINT_ON_TEMPORARY :
1962 SCF_PROPERTY_MAINT_ON;
1963 }
1964
1965 set_inst_action(wip->fmri, wip->inst, prop);
1966
1967 return (0);
1968 }
1969
1970 static void
1971 set_milestone(const char *fmri, boolean_t temporary)
1972 {
1973 scf_instance_t *inst;
1974 scf_propertygroup_t *pg;
1975 int r;
1976
1977 if (temporary) {
1978 set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS_OVR,
1979 SCF_PG_OPTIONS_OVR_TYPE, SCF_PG_OPTIONS_OVR_FLAGS,
1980 SCF_PROPERTY_MILESTONE, fmri);
1981 return;
1982 }
1983
1984 if ((inst = scf_instance_create(h)) == NULL ||
1985 (pg = scf_pg_create(h)) == NULL)
1986 scfdie();
1987
1988 if (get_inst(SCF_SERVICE_STARTD, inst) != 0) {
1989 scf_instance_destroy(inst);
1990 return;
1991 }
1992
1993 /*
1994 * Set the persistent milestone before deleting the override so we don't
1995 * glitch.
1996 */
1997 set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS,
1998 SCF_PG_OPTIONS_TYPE, SCF_PG_OPTIONS_FLAGS, SCF_PROPERTY_MILESTONE,
1999 fmri);
2000
2001 r = scf_instance_delete_prop(inst, SCF_PG_OPTIONS_OVR,
2002 SCF_PROPERTY_MILESTONE);
2003 switch (r) {
2004 case 0:
2005 break;
2006
2007 case ECANCELED:
2008 uu_warn(emsg_no_service, fmri);
2009 exit_status = 1;
2010 goto out;
2011
2012 case EPERM:
2013 uu_warn(gettext("Could not delete %s/%s property of "
2014 "%s: permission denied.\n"), SCF_PG_OPTIONS_OVR,
2015 SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD);
2016 exit_status = 1;
2017 goto out;
2018
2019 case EACCES:
2020 uu_warn(gettext("Could not delete %s/%s property of "
2021 "%s: access denied.\n"), SCF_PG_OPTIONS_OVR,
2022 SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD);
2023 exit_status = 1;
2024 goto out;
2025
2026 case EROFS:
2027 uu_warn(gettext("Could not delete %s/%s property of "
2028 "%s: backend read-only.\n"), SCF_PG_OPTIONS_OVR,
2029 SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD);
2030 exit_status = 1;
2031 goto out;
2032
2033 default:
2034 bad_error("scf_instance_delete_prop", r);
2035 }
2036
2037 out:
2038 scf_pg_destroy(pg);
2039 scf_instance_destroy(inst);
2040 }
2041
2042 static char const *milestones[] = {
2043 SCF_MILESTONE_SINGLE_USER,
2044 SCF_MILESTONE_MULTI_USER,
2045 SCF_MILESTONE_MULTI_USER_SERVER,
2046 NULL
2047 };
2048
2049 static void
2050 usage_milestone(void)
2051 {
2052 const char **ms;
2053
2054 (void) fprintf(stderr, gettext(
2055 "Usage: svcadm milestone [-d] <milestone>\n\n"
2056 "\t-d\tmake the specified milestone the default for system boot\n\n"
2057 "\tMilestones can be specified using an FMRI or abbreviation.\n"
2298
2299 scf_value_destroy(zone);
2300 }
2301
2302 if (scf_handle_bind(h) == -1) {
2303 if (do_zones)
2304 goto nextzone;
2305
2306 uu_die(gettext("Couldn't bind to configuration repository: "
2307 "%s.\n"), scf_strerror(scf_error()));
2308 }
2309
2310 optind = orig_optind;
2311 argc = orig_argc;
2312 argv = orig_argv;
2313
2314 if (optind >= argc)
2315 usage();
2316
2317 if (strcmp(argv[optind], "enable") == 0) {
2318 int flags = SET_ENABLED;
2319 int wait = 0;
2320 int error = 0;
2321
2322 ++optind;
2323
2324 while ((o = getopt(argc, argv, "rst")) != -1) {
2325 if (o == 'r')
2326 flags |= SET_RECURSIVE;
2327 else if (o == 't')
2328 flags |= SET_TEMPORARY;
2329 else if (o == 's')
2330 wait = 1;
2331 else if (o == '?')
2332 usage();
2333 else {
2334 assert(0);
2335 abort();
2336 }
2337 }
2338 argc -= optind;
2339 argv += optind;
2340
2341 if (argc == 0 && !svcsearch)
2342 usage();
2343
2344 if (argc > 0 && svcsearch)
2345 usage();
2346
2347 /*
2348 * We want to continue with -s processing if we had
2349 * invalid options, but not if an enable failed. We
2350 * squelch output the second time we walk fmris; we saw
2351 * the errors the first time.
2352 */
2353 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2354 set_fmri_enabled, (void *)flags, &error, pr_warn)) != 0) {
2355
2356 pr_warn(gettext("failed to iterate over "
2357 "instances: %s\n"), scf_strerror(err));
2358 exit_status = UU_EXIT_FATAL;
2359
2360 } else if (wait && exit_status == 0 &&
2361 (err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2362 wait_fmri_enabled, (void *)flags, &error, quiet)) != 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
2369 if (error > 0)
2370 exit_status = error;
2371
2372 } else if (strcmp(argv[optind], "disable") == 0) {
2373 int flags = 0;
2374 int wait = 0;
2375 int error = 0;
2376
2377 ++optind;
2378
2379 while ((o = getopt(argc, argv, "st")) != -1) {
2380 if (o == 't')
2381 flags |= SET_TEMPORARY;
2382 else if (o == 's')
2383 wait = 1;
2384 else if (o == '?')
2385 usage();
2386 else {
2387 assert(0);
2388 abort();
2389 }
2390 }
2391 argc -= optind;
2392 argv += optind;
2393
2394 if (argc == 0 && !svcsearch)
2395 usage();
2396
2397 if (argc > 0 && svcsearch)
2398 usage();
2399
2400 /*
2401 * We want to continue with -s processing if we had
2402 * invalid options, but not if a disable failed. We
2403 * squelch output the second time we walk fmris; we saw
2404 * the errors the first time.
2405 */
2406 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2407 set_fmri_enabled, (void *)flags, &exit_status,
2408 pr_warn)) != 0) {
2409
2410 pr_warn(gettext("failed to iterate over "
2411 "instances: %s\n"), scf_strerror(err));
2412 exit_status = UU_EXIT_FATAL;
2413
2414 } else if (wait && exit_status == 0 &&
2415 (err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2416 wait_fmri_disabled, (void *)flags, &error, quiet)) != 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
2423 if (error > 0)
2424 exit_status = error;
2425
2426 } else if (strcmp(argv[optind], "restart") == 0) {
2427 boolean_t do_dump = B_FALSE;
2428
2429 ++optind;
2430
2431 while ((o = getopt(argc, argv, "d")) != -1) {
2432 if (o == 'd')
2433 do_dump = B_TRUE;
2434 else if (o == '?')
2435 usage();
2436 else {
|
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>
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;
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(5)\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 }
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 */
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 *
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
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);
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();
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);
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;
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"
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 {
|