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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Copyright 2015, Joyent, Inc. All rights reserved.
31 * svcadm - request adminstrative actions for service instances
37 #include <libscf_priv.h>
38 #include <libcontract.h>
39 #include <libcontract_priv.h>
40 #include <sys/contract/process.h>
54 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
55 #endif /* TEXT_DOMAIN */
57 /* Must be a power of two */
61 * Exit codes for enable and disable -s.
63 #define EXIT_SVC_FAILURE 3
64 #define EXIT_DEP_FAILURE 4
66 #define WALK_FLAGS (SCF_WALK_UNIPARTIAL | SCF_WALK_MULTIPLE)
69 * How long we will wait (in seconds) for a service to change state
70 * before re-checking its dependencies.
72 #define WAIT_INTERVAL 3
74 #define bad_error(func, err) \
75 uu_panic("%s:%d: %s() failed with unexpected error %d.\n", \
76 __FILE__, __LINE__, (func), (err));
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
;
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
;
101 static struct ht_elt
**visited
;
103 void do_scfdie(int lineno
) __NORETURN
;
104 static void usage_milestone(void) __NORETURN
;
105 static void set_astring_prop(const char *, const char *, const char *,
106 uint32_t, const char *, const char *);
107 static void pr_warn(const char *format
, ...);
110 * Visitors from synch.c, needed for enable -s and disable -s.
112 extern int is_enabled(scf_instance_t
*);
113 extern int has_potential(scf_instance_t
*, int);
116 do_scfdie(int lineno
)
120 switch (err
= scf_error()) {
121 case SCF_ERROR_CONNECTION_BROKEN
:
122 uu_die(gettext("Connection to repository server broken. "
126 case SCF_ERROR_BACKEND_READONLY
:
127 uu_die(gettext("Repository is read-only. Exiting.\n"));
132 uu_die(gettext("Unexpected libscf error: %s. Exiting.\n"),
135 uu_die("Unexpected libscf error on line %d: %s.\n", lineno
,
141 #define scfdie() do_scfdie(__LINE__)
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 "
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"
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"
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());
173 * FMRI hash table for recursive enable.
177 hash_fmri(const char *str
)
182 /* Generic hash function from uts/common/os/modhash.c . */
183 for (p
= str
; *p
!= '\0'; ++p
) {
185 if ((g
= (h
& 0xf0000000)) != 0) {
195 * Return 1 if str has been visited, 0 if it has not, and -1 if memory could not
199 visited_find_or_add(const char *str
, struct ht_elt
**hep
)
206 i
= h
& (HT_BUCKETS
- 1);
208 for (he
= visited
[i
]; he
!= NULL
; he
= he
->next
) {
209 if (strcmp(he
->str
, str
) == 0) {
216 he
= malloc(offsetof(struct ht_elt
, str
) + strlen(str
) + 1);
220 (void) strcpy(he
->str
, str
);
222 he
->next
= visited
[i
];
232 * Returns 0, ECANCELED if pg is deleted, ENOENT if propname doesn't exist,
233 * EINVAL if the property is not of boolean type or has no values, and E2BIG
234 * if it has more than one value. *bp is set if 0 or E2BIG is returned.
237 get_bool_prop(scf_propertygroup_t
*pg
, const char *propname
, uint8_t *bp
)
239 scf_property_t
*prop
;
243 if ((prop
= scf_property_create(h
)) == NULL
||
244 (val
= scf_value_create(h
)) == NULL
)
247 if (scf_pg_get_property(pg
, propname
, prop
) != 0) {
248 switch (scf_error()) {
249 case SCF_ERROR_DELETED
:
253 case SCF_ERROR_NOT_FOUND
:
257 case SCF_ERROR_NOT_SET
:
267 if (scf_property_get_value(prop
, val
) == 0) {
270 switch (scf_error()) {
271 case SCF_ERROR_DELETED
:
275 case SCF_ERROR_NOT_FOUND
:
279 case SCF_ERROR_CONSTRAINT_VIOLATED
:
283 case SCF_ERROR_NOT_SET
:
293 if (scf_value_get_boolean(val
, bp
) != 0) {
294 if (scf_error() != SCF_ERROR_TYPE_MISMATCH
)
302 scf_value_destroy(val
);
303 scf_property_destroy(prop
);
308 * Returns 0, EPERM, or EROFS.
311 set_bool_prop(scf_propertygroup_t
*pg
, const char *propname
, boolean_t b
)
314 scf_transaction_t
*tx
;
315 scf_transaction_entry_t
*ent
;
318 if ((tx
= scf_transaction_create(h
)) == NULL
||
319 (ent
= scf_entry_create(h
)) == NULL
||
320 (v
= scf_value_create(h
)) == NULL
)
323 scf_value_set_boolean(v
, b
);
326 if (scf_transaction_start(tx
, pg
) == -1) {
327 switch (scf_error()) {
328 case SCF_ERROR_PERMISSION_DENIED
:
332 case SCF_ERROR_BACKEND_READONLY
:
341 if (scf_transaction_property_change_type(tx
, ent
, propname
,
342 SCF_TYPE_BOOLEAN
) != 0) {
343 if (scf_error() != SCF_ERROR_NOT_FOUND
)
346 if (scf_transaction_property_new(tx
, ent
, propname
,
347 SCF_TYPE_BOOLEAN
) != 0)
351 r
= scf_entry_add_value(ent
, v
);
354 r
= scf_transaction_commit(tx
);
358 scf_transaction_reset(tx
);
361 switch (scf_error()) {
362 case SCF_ERROR_PERMISSION_DENIED
:
366 case SCF_ERROR_BACKEND_READONLY
:
375 if (scf_pg_update(pg
) == -1)
380 scf_transaction_destroy(tx
);
381 scf_entry_destroy(ent
);
382 scf_value_destroy(v
);
387 * Gets the single astring value of the propname property of pg. prop & v are
388 * scratch space. Returns the length of the string on success or
389 * -ENOENT - pg has no property named propname
390 * -E2BIG - property has no values or multiple values
391 * -EINVAL - property type is not compatible with astring
394 get_astring_prop(const scf_propertygroup_t
*pg
, const char *propname
,
395 scf_property_t
*prop
, scf_value_t
*v
, char *buf
, size_t bufsz
)
399 if (scf_pg_get_property(pg
, propname
, prop
) != 0) {
400 if (scf_error() != SCF_ERROR_NOT_FOUND
)
406 if (scf_property_get_value(prop
, v
) != 0) {
407 switch (scf_error()) {
408 case SCF_ERROR_NOT_FOUND
:
409 case SCF_ERROR_CONSTRAINT_VIOLATED
:
417 sz
= scf_value_get_astring(v
, buf
, bufsz
);
419 if (scf_error() != SCF_ERROR_TYPE_MISMATCH
)
429 * Returns 0 or EPERM.
432 pg_get_or_add(const scf_instance_t
*inst
, const char *pgname
,
433 const char *pgtype
, uint32_t pgflags
, scf_propertygroup_t
*pg
)
436 if (scf_instance_get_pg(inst
, pgname
, pg
) == 0)
439 if (scf_error() != SCF_ERROR_NOT_FOUND
)
442 if (scf_instance_add_pg(inst
, pgname
, pgtype
, pgflags
, pg
) == 0)
445 switch (scf_error()) {
446 case SCF_ERROR_EXISTS
:
449 case SCF_ERROR_PERMISSION_DENIED
:
459 my_ct_name(char *out
, size_t len
)
466 if ((ct
= getctid()) == -1)
467 uu_die(gettext("Could not get contract id for process"));
469 fd
= contract_open(ct
, "process", "status", O_RDONLY
);
471 if ((errno
= ct_status_read(fd
, CTD_ALL
, &st
)) != 0)
472 uu_warn(gettext("Could not read status of contract "
473 "%ld: %s.\n"), ct
, strerror(errno
));
475 if ((errno
= ct_pr_status_get_svc_fmri(st
, &ct_fmri
)) != 0)
476 uu_warn(gettext("Could not get svc_fmri for contract "
477 "%ld: %s.\n"), ct
, strerror(errno
));
479 ret
= strlcpy(out
, ct_fmri
, len
);
488 * Set auxiliary_tty and auxiliary_fmri properties in restarter_actions pg to
489 * communicate whether the action is requested from a tty and the fmri of the
490 * responsible process.
492 * Returns 0, EPERM, or EROFS
495 restarter_setup(const char *fmri
, const scf_instance_t
*inst
)
497 boolean_t b
= B_FALSE
;
498 scf_propertygroup_t
*pg
= NULL
;
501 if ((pg
= scf_pg_create(h
)) == NULL
)
504 if (pg_get_or_add(inst
, SCF_PG_RESTARTER_ACTIONS
,
505 SCF_PG_RESTARTER_ACTIONS_TYPE
, SCF_PG_RESTARTER_ACTIONS_FLAGS
,
508 uu_warn(emsg_permission_denied
, fmri
);
510 uu_warn(emsg_create_pg_perm_denied
, fmri
,
511 SCF_PG_RESTARTER_ACTIONS
);
517 /* Set auxiliary_tty property */
518 if (isatty(STDIN_FILENO
))
521 /* Create and set state to disabled */
522 switch (set_bool_prop(pg
, SCF_PROPERTY_AUX_TTY
, b
)) {
528 uu_warn(emsg_permission_denied
, fmri
);
530 uu_warn(emsg_prop_perm_denied
, fmri
,
531 SCF_PG_RESTARTER_ACTIONS
, SCF_PROPERTY_AUX_TTY
);
538 /* Shouldn't happen, but it can. */
540 uu_warn(gettext("%s: Repository read-only.\n"), fmri
);
542 uu_warn(gettext("%s: Could not set %s/%s "
543 "(repository read-only).\n"), fmri
,
544 SCF_PG_RESTARTER_ACTIONS
, SCF_PROPERTY_AUX_TTY
);
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
);
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
);
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).
577 set_inst_enabled(const char *fmri
, scf_instance_t
*inst
, boolean_t temp
,
580 scf_propertygroup_t
*pg
;
582 const char *pgname
= NULL
; /* For emsg_pg_perm_denied */
585 pg
= scf_pg_create(h
);
589 if (restarter_setup(fmri
, inst
))
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.
597 pgname
= SCF_PG_GENERAL
;
598 if (pg_get_or_add(inst
, pgname
, SCF_PG_GENERAL_TYPE
,
599 SCF_PG_GENERAL_FLAGS
, pg
) != 0)
602 if (get_bool_prop(pg
, SCF_PROPERTY_ENABLED
, &b
) != 0) {
603 /* Create and set state to disabled */
604 switch (set_bool_prop(pg
, SCF_PROPERTY_ENABLED
, B_FALSE
)) {
612 /* Shouldn't happen, but it can. */
614 uu_warn(gettext("%s: Repository read-only.\n"),
617 uu_warn(gettext("%s: Could not set %s/%s "
618 "(repository read-only).\n"), fmri
,
619 SCF_PG_GENERAL
, SCF_PROPERTY_ENABLED
);
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)
635 switch (set_bool_prop(pg
, SCF_PROPERTY_ENABLED
, enable
)) {
643 /* Shouldn't happen, but it can. */
645 uu_warn(gettext("%s: Repository read-only.\n"),
648 uu_warn(gettext("%s: Could not set %s/%s "
649 "(repository read-only).\n"), fmri
,
650 SCF_PG_GENERAL_OVR
, SCF_PROPERTY_ENABLED
);
659 (void) printf(enable
?
660 gettext("%s temporarily enabled.\n") :
661 gettext("%s temporarily disabled.\n"), fmri
);
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
670 if (pg_get_or_add(inst
, pgname
, SCF_PG_GENERAL_TYPE
,
671 SCF_PG_GENERAL_FLAGS
, pg
) != 0)
674 switch (set_bool_prop(pg
, SCF_PROPERTY_ENABLED
, enable
)) {
683 * If general/enabled is already set the way we want,
686 switch (get_bool_prop(pg
, SCF_PROPERTY_ENABLED
, &b
)) {
688 if ((b
!= 0) == (enable
!= B_FALSE
))
696 uu_warn(gettext("%s: Repository "
697 "read-only.\n"), fmri
);
699 uu_warn(gettext("%s: Could not set "
700 "%s/%s (repository read-only).\n"),
701 fmri
, SCF_PG_GENERAL
,
702 SCF_PROPERTY_ENABLED
);
719 pgname
= SCF_PG_GENERAL_OVR
;
720 r
= scf_instance_delete_prop(inst
, pgname
,
721 SCF_PROPERTY_ENABLED
);
727 uu_warn(emsg_no_service
, fmri
);
734 uu_warn(gettext("Could not delete %s/%s "
735 "property of %s: backend access denied.\n"),
736 pgname
, SCF_PROPERTY_ENABLED
, fmri
);
740 uu_warn(gettext("Could not delete %s/%s "
741 "property of %s: backend is read-only.\n"),
742 pgname
, SCF_PROPERTY_ENABLED
, fmri
);
746 bad_error("scf_instance_delete_prop", r
);
750 (void) printf(enable
? gettext("%s enabled.\n") :
751 gettext("%s disabled.\n"), fmri
);
758 assert(pgname
!= NULL
);
760 uu_warn(emsg_permission_denied
, fmri
);
762 uu_warn(emsg_pg_perm_denied
, fmri
, pgname
);
770 * Set inst to the instance which corresponds to fmri. If fmri identifies
771 * a service with a single instance, get that instance.
774 * ENOTSUP - fmri has an unsupported scheme
775 * EINVAL - fmri is invalid
776 * ENOTDIR - fmri does not identify a service or instance
777 * ENOENT - could not locate instance
778 * E2BIG - fmri is a service with multiple instances (warning not printed)
781 get_inst_mult(const char *fmri
, scf_instance_t
*inst
)
784 const char *svc_name
, *inst_name
, *pg_name
;
786 scf_instance_t
*inst2
;
790 if (strncmp(fmri
, "lrc:", sizeof ("lrc:") - 1) == 0) {
791 uu_warn(gettext("FMRI \"%s\" is a legacy service.\n"), fmri
);
796 cfmri
= strdup(fmri
);
800 if (scf_parse_svc_fmri(cfmri
, NULL
, &svc_name
, &inst_name
, &pg_name
,
801 NULL
) != SCF_SUCCESS
) {
803 uu_warn(gettext("FMRI \"%s\" is invalid.\n"), fmri
);
810 if (svc_name
== NULL
|| pg_name
!= NULL
) {
812 "FMRI \"%s\" does not designate a service or instance.\n"),
818 if (inst_name
!= NULL
) {
819 if (scf_handle_decode_fmri(h
, fmri
, NULL
, NULL
, inst
, NULL
,
820 NULL
, SCF_DECODE_FMRI_EXACT
) == 0)
823 if (scf_error() != SCF_ERROR_NOT_FOUND
)
826 uu_warn(gettext("No such instance \"%s\".\n"), fmri
);
832 if ((svc
= scf_service_create(h
)) == NULL
||
833 (inst2
= scf_instance_create(h
)) == NULL
||
834 (iter
= scf_iter_create(h
)) == NULL
)
837 if (scf_handle_decode_fmri(h
, fmri
, NULL
, svc
, NULL
, NULL
, NULL
,
838 SCF_DECODE_FMRI_EXACT
) != SCF_SUCCESS
) {
839 if (scf_error() != SCF_ERROR_NOT_FOUND
)
842 uu_warn(emsg_no_service
, fmri
);
849 /* If the service has only one child, use it. */
850 if (scf_iter_service_instances(iter
, svc
) != SCF_SUCCESS
)
853 ret
= scf_iter_next_instance(iter
, inst
);
857 uu_warn(gettext("Service \"%s\" has no instances.\n"),
864 ret
= scf_iter_next_instance(iter
, inst2
);
876 scf_iter_destroy(iter
);
877 scf_instance_destroy(inst2
);
878 scf_service_destroy(svc
);
883 * Same as get_inst_mult(), but on E2BIG prints a warning and returns ENOENT.
886 get_inst(const char *fmri
, scf_instance_t
*inst
)
890 r
= get_inst_mult(fmri
, inst
);
894 uu_warn(gettext("operation on service %s is ambiguous; "
895 "instance specification needed.\n"), fmri
);
900 inst_get_fmri(const scf_instance_t
*inst
)
904 sz
= scf_instance_to_fmri(inst
, scratch_fmri
, max_scf_fmri_sz
);
907 if (sz
>= max_scf_fmri_sz
)
908 uu_die(gettext("scf_instance_to_fmri() returned unexpectedly "
911 return (scratch_fmri
);
915 dep_get_astring(const char *fmri
, const char *pgname
,
916 const scf_propertygroup_t
*pg
, const char *propname
, scf_property_t
*prop
,
917 scf_value_t
*v
, char *buf
, size_t bufsz
)
921 sz
= get_astring_prop(pg
, propname
, prop
, v
, buf
, bufsz
);
927 uu_warn(gettext("\"%s\" is misconfigured (\"%s\" dependency "
928 "lacks \"%s\" property.)\n"), fmri
, pgname
, propname
);
932 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" property "
933 "is not single-valued.)\n"), fmri
, pgname
, propname
);
937 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" property "
938 "is not of astring type.)\n"), fmri
, pgname
, propname
);
949 multiple_instances(scf_iter_t
*iter
, scf_value_t
*v
, char *buf
)
953 scf_instance_t
*inst
;
955 inst
= scf_instance_create(h
);
960 r
= scf_iter_next_value(iter
, v
);
968 if (scf_value_get_astring(v
, buf
, max_scf_fmri_sz
) < 0)
971 switch (get_inst_mult(buf
, inst
)) {
997 scf_instance_destroy(inst
);
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.
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.
1017 enable_fmri_rec(char *fmri
, boolean_t temp
)
1019 scf_instance_t
*inst
;
1020 scf_snapshot_t
*snap
;
1021 scf_propertygroup_t
*pg
;
1022 scf_property_t
*prop
;
1024 scf_iter_t
*pg_iter
, *val_iter
;
1027 ssize_t name_sz
, len
, sz
;
1031 len
= scf_canonify_fmri(fmri
, fmri
, max_scf_fmri_sz
);
1033 assert(scf_error() == SCF_ERROR_INVALID_ARGUMENT
);
1036 assert(len
< max_scf_fmri_sz
);
1038 switch (visited_find_or_add(fmri
, &he
)) {
1040 he
->active
= B_TRUE
;
1044 return (he
->active
? ELOOP
: 0);
1054 inst
= scf_instance_create(h
);
1058 switch (get_inst_mult(fmri
, inst
)) {
1063 he
->active
= B_FALSE
;
1067 he
->active
= B_FALSE
;
1071 set_inst_enabled(fmri
, inst
, temp
, B_TRUE
);
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
)
1081 buf
= malloc(max_scf_fmri_sz
);
1085 name_sz
= scf_limit(SCF_LIMIT_MAX_NAME_LENGTH
);
1089 pgname
= malloc(name_sz
);
1093 if (scf_instance_get_snapshot(inst
, "running", snap
) != 0) {
1094 if (scf_error() != SCF_ERROR_NOT_FOUND
)
1097 scf_snapshot_destroy(snap
);
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
)
1106 uu_warn(gettext("\"%s\" is misconfigured (lacks \"%s\" "
1107 "property group).\n"), fmri
, SCF_PG_GENERAL
);
1112 sz
= get_astring_prop(pg
, SCF_PROPERTY_RESTARTER
, prop
, v
, buf
,
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
);
1120 } else if (sz
>= 0) {
1121 switch (enable_fmri_rec(buf
, temp
)) {
1126 uu_warn(gettext("Restarter FMRI for \"%s\" is "
1127 "invalid.\n"), fmri
);
1131 uu_warn(gettext("Restarter FMRI for \"%s\" identifies "
1132 "a service with multiple instances.\n"), fmri
);
1143 } else if (sz
< 0) {
1149 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" "
1150 "property is not single-valued).\n"), fmri
,
1151 SCF_PG_GENERAL
, SCF_PROPERTY_RESTARTER
);
1156 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" "
1157 "property is not of astring type).\n"), fmri
,
1158 SCF_PG_GENERAL
, SCF_PROPERTY_RESTARTER
);
1168 if (scf_iter_instance_pgs_typed_composed(pg_iter
, inst
, snap
,
1169 SCF_GROUP_DEPENDENCY
) == -1)
1172 while (scf_iter_next_pg(pg_iter
, pg
) > 0) {
1173 len
= scf_pg_get_name(pg
, pgname
, name_sz
);
1176 assert(len
< name_sz
);
1178 if (dep_get_astring(fmri
, pgname
, pg
, SCF_PROPERTY_TYPE
, prop
,
1179 v
, buf
, max_scf_fmri_sz
) < 0)
1182 if (strcmp(buf
, "service") != 0)
1185 if (dep_get_astring(fmri
, pgname
, pg
, SCF_PROPERTY_GROUPING
,
1186 prop
, v
, buf
, max_scf_fmri_sz
) < 0)
1189 if (strcmp(buf
, SCF_DEP_EXCLUDE_ALL
) == 0 ||
1190 strcmp(buf
, SCF_DEP_OPTIONAL_ALL
) == 0)
1193 if (strcmp(buf
, SCF_DEP_REQUIRE_ALL
) != 0 &&
1194 strcmp(buf
, SCF_DEP_REQUIRE_ANY
) != 0) {
1195 uu_warn(gettext("Dependency \"%s\" of \"%s\" has "
1196 "unknown type \"%s\".\n"), pgname
, fmri
, buf
);
1200 if (scf_pg_get_property(pg
, SCF_PROPERTY_ENTITIES
, prop
) ==
1202 if (scf_error() != SCF_ERROR_NOT_FOUND
)
1205 uu_warn(gettext("\"%s\" is misconfigured (\"%s\" "
1206 "dependency lacks \"%s\" property.)\n"), fmri
,
1207 pgname
, SCF_PROPERTY_ENTITIES
);
1211 if (scf_property_type(prop
, &ty
) != SCF_SUCCESS
)
1214 if (ty
!= SCF_TYPE_FMRI
) {
1215 uu_warn(gettext("\"%s\" is misconfigured (property "
1216 "\"%s/%s\" is not of fmri type).\n"), fmri
, pgname
,
1217 SCF_PROPERTY_ENTITIES
);
1221 if (scf_iter_property_values(val_iter
, prop
) == -1)
1224 if (strcmp(buf
, SCF_DEP_REQUIRE_ANY
) == 0) {
1225 if (multiple_instances(val_iter
, v
, buf
)) {
1226 (void) printf(gettext("%s requires one of:\n"),
1229 if (scf_iter_property_values(val_iter
, prop
) !=
1236 r
= scf_iter_next_value(val_iter
, v
);
1242 if (scf_value_get_astring(v
, buf
,
1243 max_scf_fmri_sz
) < 0)
1246 (void) fputs(" ", stdout
);
1254 * Since there's only one instance, we can enable it.
1255 * Reset val_iter and continue.
1257 if (scf_iter_property_values(val_iter
, prop
) != 0)
1262 ret
= scf_iter_next_value(val_iter
, v
);
1268 if (scf_value_get_astring(v
, buf
, max_scf_fmri_sz
) ==
1272 switch (enable_fmri_rec(buf
, temp
)) {
1277 uu_warn(gettext("\"%s\" dependency of \"%s\" "
1278 "has invalid FMRI \"%s\".\n"), pgname
,
1283 uu_warn(gettext("%s depends on %s, which has "
1284 "multiple instances.\n"), fmri
, buf
);
1301 he
->active
= B_FALSE
;
1306 (void) scf_value_destroy(v
);
1307 scf_property_destroy(prop
);
1309 scf_snapshot_destroy(snap
);
1310 scf_iter_destroy(pg_iter
);
1311 scf_iter_destroy(val_iter
);
1317 * fmri here is only used for verbose messages.
1320 set_inst_action(const char *fmri
, const scf_instance_t
*inst
,
1323 scf_transaction_t
*tx
;
1324 scf_transaction_entry_t
*ent
;
1325 scf_propertygroup_t
*pg
;
1326 scf_property_t
*prop
;
1332 const char * const scf_pg_restarter_actions
= SCF_PG_RESTARTER_ACTIONS
;
1334 if ((pg
= scf_pg_create(h
)) == NULL
||
1335 (prop
= scf_property_create(h
)) == NULL
||
1336 (v
= scf_value_create(h
)) == NULL
||
1337 (tx
= scf_transaction_create(h
)) == NULL
||
1338 (ent
= scf_entry_create(h
)) == NULL
)
1341 if (restarter_setup(fmri
, inst
)) {
1346 if (scf_instance_get_pg(inst
, scf_pg_restarter_actions
, pg
) == -1) {
1347 if (scf_error() != SCF_ERROR_NOT_FOUND
)
1350 /* Try creating the restarter_actions property group. */
1351 if (scf_instance_add_pg(inst
, scf_pg_restarter_actions
,
1352 SCF_PG_RESTARTER_ACTIONS_TYPE
,
1353 SCF_PG_RESTARTER_ACTIONS_FLAGS
, pg
) == -1) {
1354 switch (scf_error()) {
1355 case SCF_ERROR_EXISTS
:
1356 /* Someone must have added it. */
1359 case SCF_ERROR_PERMISSION_DENIED
:
1361 uu_warn(emsg_permission_denied
, fmri
);
1363 uu_warn(emsg_create_pg_perm_denied
,
1364 fmri
, scf_pg_restarter_actions
);
1374 * If we lose the transaction race and need to retry, there are 2
1375 * potential other winners:
1376 * - another process setting actions
1377 * - the restarter marking the action complete
1378 * Therefore, re-read the property every time through the loop before
1379 * making any decisions based on their values.
1382 timestamp
= gethrtime();
1384 if (scf_transaction_start(tx
, pg
) == -1) {
1385 if (scf_error() != SCF_ERROR_PERMISSION_DENIED
)
1389 uu_warn(emsg_permission_denied
, fmri
);
1391 uu_warn(emsg_pg_perm_denied
, fmri
,
1392 scf_pg_restarter_actions
);
1396 if (scf_pg_get_property(pg
, action
, prop
) == -1) {
1397 if (scf_error() != SCF_ERROR_NOT_FOUND
)
1399 if (scf_transaction_property_new(tx
, ent
,
1400 action
, SCF_TYPE_INTEGER
) == -1)
1404 if (scf_transaction_property_change_type(tx
, ent
,
1405 action
, SCF_TYPE_INTEGER
) == -1)
1409 if (scf_property_get_value(prop
, v
) == -1) {
1410 switch (scf_error()) {
1411 case SCF_ERROR_CONSTRAINT_VIOLATED
:
1412 case SCF_ERROR_NOT_FOUND
:
1413 /* Misconfigured, so set anyway. */
1420 if (scf_value_get_integer(v
, &t
) == -1) {
1421 assert(scf_error() == SCF_ERROR_TYPE_MISMATCH
);
1429 scf_value_set_integer(v
, timestamp
);
1430 if (scf_entry_add_value(ent
, v
) == -1)
1433 ret
= scf_transaction_commit(tx
);
1435 if (scf_error() != SCF_ERROR_PERMISSION_DENIED
)
1439 uu_warn(emsg_permission_denied
, fmri
);
1441 uu_warn(emsg_prop_perm_denied
, fmri
,
1442 scf_pg_restarter_actions
, action
);
1443 scf_transaction_reset(tx
);
1447 scf_transaction_reset(tx
);
1450 if (scf_pg_update(pg
) == -1)
1456 (void) printf(gettext("Action %s set for %s.\n"), action
, fmri
);
1459 scf_value_destroy(v
);
1460 scf_entry_destroy(ent
);
1461 scf_transaction_destroy(tx
);
1462 scf_property_destroy(prop
);
1467 * Get the state of inst. state should point to a buffer of
1468 * MAX_SCF_STATE_STRING_SZ bytes. Returns 0 on success or -1 if
1469 * no restarter property group
1471 * state property is misconfigured (wrong type, not single-valued)
1472 * state value is too long
1473 * In these cases, fmri is used to print a warning.
1475 * If pgp is non-NULL, a successful call to inst_get_state will store
1476 * the SCF_PG_RESTARTER property group in *pgp, and the caller will be
1477 * responsible for calling scf_pg_destroy on the property group.
1480 inst_get_state(scf_instance_t
*inst
, char *state
, const char *fmri
,
1481 scf_propertygroup_t
**pgp
)
1483 scf_propertygroup_t
*pg
;
1484 scf_property_t
*prop
;
1489 if ((pg
= scf_pg_create(h
)) == NULL
||
1490 (prop
= scf_property_create(h
)) == NULL
||
1491 (val
= scf_value_create(h
)) == NULL
)
1494 if (scf_instance_get_pg(inst
, SCF_PG_RESTARTER
, pg
) != SCF_SUCCESS
) {
1495 if (scf_error() != SCF_ERROR_NOT_FOUND
)
1498 uu_warn(gettext("%s is misconfigured (lacks \"%s\" property "
1499 "group).\n"), fmri
? fmri
: inst_get_fmri(inst
),
1504 szret
= get_astring_prop(pg
, SCF_PROPERTY_STATE
, prop
, val
, state
,
1505 MAX_SCF_STATE_STRING_SZ
);
1509 uu_warn(gettext("%s is misconfigured (\"%s\" property "
1510 "group lacks \"%s\" property).\n"),
1511 fmri
? fmri
: inst_get_fmri(inst
), SCF_PG_RESTARTER
,
1512 SCF_PROPERTY_STATE
);
1516 uu_warn(gettext("%s is misconfigured (\"%s/%s\" "
1517 "property is not single-valued).\n"),
1518 fmri
? fmri
: inst_get_fmri(inst
), SCF_PG_RESTARTER
,
1519 SCF_PROPERTY_STATE
);
1523 uu_warn(gettext("%s is misconfigured (\"%s/%s\" "
1524 "property is not of type astring).\n"),
1525 fmri
? fmri
: inst_get_fmri(inst
), SCF_PG_RESTARTER
,
1526 SCF_PROPERTY_STATE
);
1534 if (szret
>= MAX_SCF_STATE_STRING_SZ
) {
1535 uu_warn(gettext("%s is misconfigured (\"%s/%s\" property value "
1536 "is too long).\n"), fmri
? fmri
: inst_get_fmri(inst
),
1537 SCF_PG_RESTARTER
, SCF_PROPERTY_STATE
);
1546 (void) scf_value_destroy(val
);
1547 scf_property_destroy(prop
);
1548 if (ret
|| pgp
== NULL
)
1554 set_astring_prop(const char *fmri
, const char *pgname
, const char *pgtype
,
1555 uint32_t pgflags
, const char *propname
, const char *str
)
1557 scf_instance_t
*inst
;
1558 scf_propertygroup_t
*pg
;
1559 scf_property_t
*prop
;
1561 scf_transaction_t
*tx
;
1562 scf_transaction_entry_t
*txent
;
1565 inst
= scf_instance_create(h
);
1569 if (get_inst(fmri
, inst
) != 0)
1572 if ((pg
= scf_pg_create(h
)) == NULL
||
1573 (prop
= scf_property_create(h
)) == NULL
||
1574 (val
= scf_value_create(h
)) == NULL
||
1575 (tx
= scf_transaction_create(h
)) == NULL
||
1576 (txent
= scf_entry_create(h
)) == NULL
)
1579 if (scf_instance_get_pg(inst
, pgname
, pg
) != SCF_SUCCESS
) {
1580 if (scf_error() != SCF_ERROR_NOT_FOUND
)
1583 if (scf_instance_add_pg(inst
, pgname
, pgtype
, pgflags
, pg
) !=
1585 switch (scf_error()) {
1586 case SCF_ERROR_EXISTS
:
1587 if (scf_instance_get_pg(inst
, pgname
, pg
) !=
1589 if (scf_error() != SCF_ERROR_NOT_FOUND
)
1592 uu_warn(gettext("Repository write "
1598 case SCF_ERROR_PERMISSION_DENIED
:
1600 uu_warn(emsg_permission_denied
, fmri
);
1602 uu_warn(emsg_create_pg_perm_denied
,
1613 if (scf_transaction_start(tx
, pg
) != SCF_SUCCESS
) {
1614 if (scf_error() != SCF_ERROR_PERMISSION_DENIED
)
1618 uu_warn(emsg_permission_denied
, fmri
);
1620 uu_warn(emsg_pg_perm_denied
, fmri
, pgname
);
1624 if (scf_transaction_property_change_type(tx
, txent
, propname
,
1625 SCF_TYPE_ASTRING
) != 0) {
1626 if (scf_error() != SCF_ERROR_NOT_FOUND
)
1629 if (scf_transaction_property_new(tx
, txent
, propname
,
1630 SCF_TYPE_ASTRING
) != 0)
1634 if (scf_value_set_astring(val
, str
) != SCF_SUCCESS
)
1637 if (scf_entry_add_value(txent
, val
) != SCF_SUCCESS
)
1640 ret
= scf_transaction_commit(tx
);
1642 if (scf_error() != SCF_ERROR_PERMISSION_DENIED
)
1646 uu_warn(emsg_permission_denied
, fmri
);
1648 uu_warn(emsg_prop_perm_denied
, fmri
, pgname
,
1654 scf_transaction_reset(tx
);
1656 if (scf_pg_update(pg
) == -1)
1662 scf_transaction_destroy(tx
);
1663 scf_entry_destroy(txent
);
1664 scf_value_destroy(val
);
1665 scf_property_destroy(prop
);
1667 scf_instance_destroy(inst
);
1672 * Flags to control enable and disable actions.
1674 #define SET_ENABLED 0x1
1675 #define SET_TEMPORARY 0x2
1676 #define SET_RECURSIVE 0x4
1679 set_fmri_enabled(void *data
, scf_walkinfo_t
*wip
)
1681 int flags
= (int)data
;
1683 assert(wip
->inst
!= NULL
);
1684 assert(wip
->pg
== NULL
);
1687 char state
[MAX_SCF_STATE_STRING_SZ
];
1689 if (inst_get_state(wip
->inst
, state
, wip
->fmri
, NULL
) != 0)
1691 if (strcmp(state
, svcstate
) != 0)
1695 if (flags
& SET_RECURSIVE
) {
1696 char *fmri_buf
= malloc(max_scf_fmri_sz
);
1697 if (fmri_buf
== NULL
)
1700 visited
= calloc(HT_BUCKETS
, sizeof (*visited
));
1701 if (visited
== NULL
)
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
);
1708 switch (enable_fmri_rec(fmri_buf
, (flags
& SET_TEMPORARY
))) {
1710 uu_warn(gettext("operation on service %s is ambiguous; "
1711 "instance specification needed.\n"), fmri_buf
);
1715 uu_warn(gettext("%s: Dependency cycle detected.\n"),
1723 set_inst_enabled(wip
->fmri
, wip
->inst
,
1724 (flags
& SET_TEMPORARY
) != 0, (flags
& SET_ENABLED
) != 0);
1732 wait_fmri_enabled(void *data
, scf_walkinfo_t
*wip
)
1734 scf_propertygroup_t
*pg
= NULL
;
1735 char state
[MAX_SCF_STATE_STRING_SZ
];
1737 assert(wip
->inst
!= NULL
);
1738 assert(wip
->pg
== NULL
);
1743 if (inst_get_state(wip
->inst
, state
, wip
->fmri
, &pg
) != 0) {
1744 exit_status
= EXIT_SVC_FAILURE
;
1748 if (strcmp(state
, SCF_STATE_STRING_ONLINE
) == 0 ||
1749 strcmp(state
, SCF_STATE_STRING_DEGRADED
) == 0) {
1756 if (strcmp(state
, SCF_STATE_STRING_MAINT
) == 0) {
1758 * The service is ill.
1760 uu_warn(gettext("Instance \"%s\" is in maintenance"
1761 " state.\n"), wip
->fmri
);
1762 exit_status
= EXIT_SVC_FAILURE
;
1766 if (!is_enabled(wip
->inst
)) {
1768 * Someone stepped in and disabled the service.
1770 uu_warn(gettext("Instance \"%s\" has been disabled"
1771 " by another entity.\n"), wip
->fmri
);
1772 exit_status
= EXIT_SVC_FAILURE
;
1776 if (!has_potential(wip
->inst
, B_FALSE
)) {
1778 * Our dependencies aren't met. We'll never
1779 * amount to anything.
1781 uu_warn(gettext("Instance \"%s\" has unsatisfied"
1782 " dependencies.\n"), wip
->fmri
);
1784 * EXIT_SVC_FAILURE takes precedence over
1787 if (exit_status
== 0)
1788 exit_status
= EXIT_DEP_FAILURE
;
1791 } while (_scf_pg_wait(pg
, WAIT_INTERVAL
) >= 0);
1802 wait_fmri_disabled(void *data
, scf_walkinfo_t
*wip
)
1804 scf_propertygroup_t
*pg
= NULL
;
1805 char state
[MAX_SCF_STATE_STRING_SZ
];
1807 assert(wip
->inst
!= NULL
);
1808 assert(wip
->pg
== NULL
);
1813 if (inst_get_state(wip
->inst
, state
, wip
->fmri
, &pg
) != 0) {
1814 exit_status
= EXIT_SVC_FAILURE
;
1818 if (strcmp(state
, SCF_STATE_STRING_DISABLED
) == 0) {
1825 if (is_enabled(wip
->inst
)) {
1827 * Someone stepped in and enabled the service.
1829 uu_warn(gettext("Instance \"%s\" has been enabled"
1830 " by another entity.\n"), wip
->fmri
);
1831 exit_status
= EXIT_SVC_FAILURE
;
1835 if (!has_potential(wip
->inst
, B_TRUE
)) {
1837 * Our restarter is hopeless.
1839 uu_warn(gettext("Restarter for instance \"%s\" is"
1840 " unavailable.\n"), wip
->fmri
);
1842 * EXIT_SVC_FAILURE takes precedence over
1845 if (exit_status
== 0)
1846 exit_status
= EXIT_DEP_FAILURE
;
1850 } while (_scf_pg_wait(pg
, WAIT_INTERVAL
) >= 0);
1861 clear_instance(void *data
, scf_walkinfo_t
*wip
)
1863 char state
[MAX_SCF_STATE_STRING_SZ
];
1865 assert(wip
->inst
!= NULL
);
1866 assert(wip
->pg
== NULL
);
1868 if (inst_get_state(wip
->inst
, state
, wip
->fmri
, NULL
) != 0)
1871 if (svcsearch
&& strcmp(state
, svcstate
) != 0)
1874 if (strcmp(state
, SCF_STATE_STRING_MAINT
) == 0) {
1875 set_inst_action(wip
->fmri
, wip
->inst
, SCF_PROPERTY_MAINT_OFF
);
1876 } else if (strcmp(state
, SCF_STATE_STRING_DEGRADED
) ==
1878 set_inst_action(wip
->fmri
, wip
->inst
, SCF_PROPERTY_RESTORE
);
1880 uu_warn(gettext("Instance \"%s\" is not in a "
1881 "maintenance or degraded state.\n"), wip
->fmri
);
1890 set_fmri_action(void *action
, scf_walkinfo_t
*wip
)
1892 assert(wip
->inst
!= NULL
&& wip
->pg
== NULL
);
1895 char state
[MAX_SCF_STATE_STRING_SZ
];
1897 if (inst_get_state(wip
->inst
, state
, wip
->fmri
, NULL
) != 0)
1899 if (strcmp(state
, svcstate
) != 0)
1903 set_inst_action(wip
->fmri
, wip
->inst
, action
);
1909 * Flags to control 'mark' action.
1911 #define MARK_IMMEDIATE 0x1
1912 #define MARK_TEMPORARY 0x2
1915 force_degraded(void *data
, scf_walkinfo_t
*wip
)
1917 int flags
= (int)data
;
1918 char state
[MAX_SCF_STATE_STRING_SZ
];
1920 if (inst_get_state(wip
->inst
, state
, wip
->fmri
, NULL
) != 0) {
1925 if (svcsearch
&& strcmp(state
, svcstate
) != 0)
1928 if (strcmp(state
, SCF_STATE_STRING_ONLINE
) != 0) {
1929 uu_warn(gettext("Instance \"%s\" is not online.\n"), wip
->fmri
);
1934 set_inst_action(wip
->fmri
, wip
->inst
, (flags
& MARK_IMMEDIATE
) ?
1935 SCF_PROPERTY_DEGRADE_IMMEDIATE
: SCF_PROPERTY_DEGRADED
);
1941 force_maintenance(void *data
, scf_walkinfo_t
*wip
)
1943 int flags
= (int)data
;
1947 char state
[MAX_SCF_STATE_STRING_SZ
];
1949 if (inst_get_state(wip
->inst
, state
, wip
->fmri
, NULL
) != 0)
1951 if (strcmp(state
, svcstate
) != 0)
1955 if (flags
& MARK_IMMEDIATE
) {
1956 prop
= (flags
& MARK_TEMPORARY
) ?
1957 SCF_PROPERTY_MAINT_ON_IMMTEMP
:
1958 SCF_PROPERTY_MAINT_ON_IMMEDIATE
;
1960 prop
= (flags
& MARK_TEMPORARY
) ?
1961 SCF_PROPERTY_MAINT_ON_TEMPORARY
:
1962 SCF_PROPERTY_MAINT_ON
;
1965 set_inst_action(wip
->fmri
, wip
->inst
, prop
);
1971 set_milestone(const char *fmri
, boolean_t temporary
)
1973 scf_instance_t
*inst
;
1974 scf_propertygroup_t
*pg
;
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
);
1984 if ((inst
= scf_instance_create(h
)) == NULL
||
1985 (pg
= scf_pg_create(h
)) == NULL
)
1988 if (get_inst(SCF_SERVICE_STARTD
, inst
) != 0) {
1989 scf_instance_destroy(inst
);
1994 * Set the persistent milestone before deleting the override so we don't
1997 set_astring_prop(SCF_SERVICE_STARTD
, SCF_PG_OPTIONS
,
1998 SCF_PG_OPTIONS_TYPE
, SCF_PG_OPTIONS_FLAGS
, SCF_PROPERTY_MILESTONE
,
2001 r
= scf_instance_delete_prop(inst
, SCF_PG_OPTIONS_OVR
,
2002 SCF_PROPERTY_MILESTONE
);
2008 uu_warn(emsg_no_service
, fmri
);
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
);
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
);
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
);
2034 bad_error("scf_instance_delete_prop", r
);
2039 scf_instance_destroy(inst
);
2042 static char const *milestones
[] = {
2043 SCF_MILESTONE_SINGLE_USER
,
2044 SCF_MILESTONE_MULTI_USER
,
2045 SCF_MILESTONE_MULTI_USER_SERVER
,
2050 usage_milestone(void)
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"
2058 "\tThe major milestones are as follows:\n\n"
2062 for (ms
= milestones
; *ms
!= NULL
; ms
++)
2063 (void) fprintf(stderr
, "\t%s\n", *ms
);
2065 exit(UU_EXIT_USAGE
);
2069 validate_milestone(const char *milestone
)
2075 if (strcmp(milestone
, "all") == 0)
2078 if (strcmp(milestone
, "none") == 0)
2082 * Determine if this is a full or partial milestone
2084 for (ms
= milestones
; *ms
!= NULL
; ms
++) {
2085 if ((tmp
= strstr(*ms
, milestone
)) != NULL
) {
2086 len
= strlen(milestone
);
2089 * The beginning of the string must align with the start
2090 * of a milestone fmri, or on the boundary between
2091 * elements. The end of the string must align with the
2092 * end of the milestone, or at the instance boundary.
2094 if ((tmp
== *ms
|| tmp
[-1] == '/') &&
2095 (tmp
[len
] == '\0' || tmp
[len
] == ':'))
2100 (void) fprintf(stderr
,
2101 gettext("\"%s\" is not a valid major milestone.\n"), milestone
);
2109 pr_warn(const char *format
, ...)
2111 const char *pname
= uu_getpname();
2114 va_start(alist
, format
);
2117 (void) fprintf(stderr
, "%s", pname
);
2119 if (g_zonename
!= NULL
)
2120 (void) fprintf(stderr
, " (%s)", g_zonename
);
2122 (void) fprintf(stderr
, ": ");
2124 (void) vfprintf(stderr
, format
, alist
);
2126 if (strrchr(format
, '\n') == NULL
)
2127 (void) fprintf(stderr
, ": %s\n", strerror(errno
));
2134 quiet(const char *fmt
, ...)
2140 main(int argc
, char *argv
[])
2145 boolean_t do_zones
= B_FALSE
;
2146 boolean_t do_a_zone
= B_FALSE
;
2147 char zonename
[ZONENAME_MAX
];
2148 uint_t nzents
= 0, zent
= 0;
2149 zoneid_t
*zids
= NULL
;
2150 int orig_optind
, orig_argc
;
2153 (void) setlocale(LC_ALL
, "");
2154 (void) textdomain(TEXT_DOMAIN
);
2156 (void) uu_setpname(argv
[0]);
2161 max_scf_fmri_sz
= scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH
);
2162 if (max_scf_fmri_sz
< 0)
2166 scratch_fmri
= malloc(max_scf_fmri_sz
);
2167 if (scratch_fmri
== NULL
)
2170 while ((o
= getopt(argc
, argv
, "S:vZz:")) != -1) {
2173 (void) strlcpy(svcstate
, optarg
, sizeof (svcstate
));
2182 if (getzoneid() != GLOBAL_ZONEID
)
2183 uu_die(gettext("svcadm -z may only be used "
2184 "from the global zone\n"));
2188 (void) strlcpy(zonename
, optarg
, sizeof (zonename
));
2193 if (getzoneid() != GLOBAL_ZONEID
)
2194 uu_die(gettext("svcadm -Z may only be used "
2195 "from the global zone\n"));
2210 if (zone_list(NULL
, &nzents
) != 0)
2211 uu_die(gettext("could not get number of zones"));
2213 if ((zids
= malloc(nzents
* sizeof (zoneid_t
))) == NULL
) {
2214 uu_die(gettext("could not allocate array for "
2215 "%d zone IDs"), nzents
);
2220 if (zone_list(zids
, &found
) != 0)
2221 uu_die(gettext("could not get zone list"));
2224 * If the number of zones has not changed between our calls to
2225 * zone_list(), we're done -- otherwise, we must free our array
2226 * of zone IDs and take another lap.
2228 if (found
== nzents
)
2234 emsg_permission_denied
= gettext("%s: Permission denied.\n");
2235 emsg_nomem
= gettext("Out of memory.\n");
2236 emsg_create_pg_perm_denied
= gettext("%s: Couldn't create \"%s\" "
2237 "property group (permission denied).\n");
2238 emsg_pg_perm_denied
= gettext("%s: Couldn't modify \"%s\" property "
2239 "group (permission denied).\n");
2240 emsg_prop_perm_denied
= gettext("%s: Couldn't modify \"%s/%s\" "
2241 "property (permission denied).\n");
2242 emsg_no_service
= gettext("No such service \"%s\".\n");
2244 orig_optind
= optind
;
2249 h
= scf_handle_create(SCF_VERSION
);
2254 zone_status_t status
;
2256 if (zone_getattr(zids
[zent
], ZONE_ATTR_STATUS
, &status
,
2257 sizeof (status
)) < 0 || status
!= ZONE_IS_RUNNING
) {
2259 * If this zone is not running or we cannot
2260 * get its status, we do not want to attempt
2261 * to bind an SCF handle to it, lest we
2262 * accidentally interfere with a zone that
2263 * is not yet running by looking up a door
2264 * to its svc.configd (which could potentially
2265 * block a mount with an EBUSY).
2271 if (getzonenamebyid(zids
[zent
++], zonename
,
2272 sizeof (zonename
)) < 0) {
2273 uu_warn(gettext("could not get name for "
2274 "zone %d; ignoring"), zids
[zent
- 1]);
2278 g_zonename
= zonename
;
2281 if (do_a_zone
|| do_zones
) {
2284 if ((zone
= scf_value_create(h
)) == NULL
)
2287 if (scf_value_set_astring(zone
, zonename
) != SCF_SUCCESS
)
2290 if (scf_handle_decorate(h
, "zone", zone
) != SCF_SUCCESS
) {
2292 uu_die(gettext("invalid zone '%s'\n"), optarg
);
2294 scf_value_destroy(zone
);
2299 scf_value_destroy(zone
);
2302 if (scf_handle_bind(h
) == -1) {
2306 uu_die(gettext("Couldn't bind to configuration repository: "
2307 "%s.\n"), scf_strerror(scf_error()));
2310 optind
= orig_optind
;
2317 if (strcmp(argv
[optind
], "enable") == 0) {
2318 int flags
= SET_ENABLED
;
2324 while ((o
= getopt(argc
, argv
, "rst")) != -1) {
2326 flags
|= SET_RECURSIVE
;
2328 flags
|= SET_TEMPORARY
;
2341 if (argc
== 0 && !svcsearch
)
2344 if (argc
> 0 && svcsearch
)
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.
2353 if ((err
= scf_walk_fmri(h
, argc
, argv
, WALK_FLAGS
,
2354 set_fmri_enabled
, (void *)flags
, &error
, pr_warn
)) != 0) {
2356 pr_warn(gettext("failed to iterate over "
2357 "instances: %s\n"), scf_strerror(err
));
2358 exit_status
= UU_EXIT_FATAL
;
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) {
2364 pr_warn(gettext("failed to iterate over "
2365 "instances: %s\n"), scf_strerror(err
));
2366 exit_status
= UU_EXIT_FATAL
;
2370 exit_status
= error
;
2372 } else if (strcmp(argv
[optind
], "disable") == 0) {
2379 while ((o
= getopt(argc
, argv
, "st")) != -1) {
2381 flags
|= SET_TEMPORARY
;
2394 if (argc
== 0 && !svcsearch
)
2397 if (argc
> 0 && svcsearch
)
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.
2406 if ((err
= scf_walk_fmri(h
, argc
, argv
, WALK_FLAGS
,
2407 set_fmri_enabled
, (void *)flags
, &exit_status
,
2410 pr_warn(gettext("failed to iterate over "
2411 "instances: %s\n"), scf_strerror(err
));
2412 exit_status
= UU_EXIT_FATAL
;
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) {
2418 pr_warn(gettext("failed to iterate over "
2419 "instances: %s\n"), scf_strerror(err
));
2420 exit_status
= UU_EXIT_FATAL
;
2424 exit_status
= error
;
2426 } else if (strcmp(argv
[optind
], "restart") == 0) {
2427 boolean_t do_dump
= B_FALSE
;
2431 while ((o
= getopt(argc
, argv
, "d")) != -1) {
2444 if (argc
== 0 && !svcsearch
)
2447 if (argc
> 0 && svcsearch
)
2451 if ((err
= scf_walk_fmri(h
, argc
, argv
, WALK_FLAGS
,
2452 set_fmri_action
, (void *)SCF_PROPERTY_DODUMP
,
2453 &exit_status
, pr_warn
)) != 0) {
2454 pr_warn(gettext("failed to iterate over "
2455 "instances: %s\n"), scf_strerror(err
));
2456 exit_status
= UU_EXIT_FATAL
;
2460 if ((err
= scf_walk_fmri(h
, argc
, argv
, WALK_FLAGS
,
2461 set_fmri_action
, (void *)SCF_PROPERTY_RESTART
, &exit_status
,
2463 pr_warn(gettext("failed to iterate over "
2464 "instances: %s\n"), scf_strerror(err
));
2465 exit_status
= UU_EXIT_FATAL
;
2468 } else if (strcmp(argv
[optind
], "refresh") == 0) {
2473 if (argc
== 0 && !svcsearch
)
2476 if (argc
> 0 && svcsearch
)
2479 if ((err
= scf_walk_fmri(h
, argc
, argv
, WALK_FLAGS
,
2480 set_fmri_action
, (void *)SCF_PROPERTY_REFRESH
, &exit_status
,
2482 pr_warn(gettext("failed to iterate over "
2483 "instances: %s\n"), scf_strerror(scf_error()));
2484 exit_status
= UU_EXIT_FATAL
;
2487 } else if (strcmp(argv
[optind
], "mark") == 0) {
2489 scf_walk_callback callback
;
2493 while ((o
= getopt(argc
, argv
, "It")) != -1) {
2495 flags
|= MARK_IMMEDIATE
;
2497 flags
|= MARK_TEMPORARY
;
2506 if (argc
- optind
< 2)
2509 if (strcmp(argv
[optind
], "degraded") == 0) {
2510 if (flags
& MARK_TEMPORARY
)
2511 uu_xdie(UU_EXIT_USAGE
, gettext("-t may not be "
2512 "used with degraded.\n"));
2513 callback
= force_degraded
;
2515 } else if (strcmp(argv
[optind
], "maintenance") == 0) {
2516 callback
= force_maintenance
;
2525 if (argc
== 0 && !svcsearch
)
2528 if (argc
> 0 && svcsearch
)
2531 if ((err
= scf_walk_fmri(h
, argc
, argv
, WALK_FLAGS
, callback
,
2532 NULL
, &exit_status
, pr_warn
)) != 0) {
2533 pr_warn(gettext("failed to iterate over "
2536 exit_status
= UU_EXIT_FATAL
;
2539 } else if (strcmp(argv
[optind
], "clear") == 0) {
2544 if (argc
== 0 && !svcsearch
)
2550 if (strcmp(svcstate
, SCF_STATE_STRING_MAINT
) != 0 &&
2551 strcmp(svcstate
, SCF_STATE_STRING_DEGRADED
) != 0)
2552 uu_die(gettext("State must be '%s' or '%s'\n"),
2553 SCF_STATE_STRING_MAINT
,
2554 SCF_STATE_STRING_DEGRADED
);
2557 if ((err
= scf_walk_fmri(h
, argc
, argv
, WALK_FLAGS
,
2558 clear_instance
, NULL
, &exit_status
, pr_warn
)) != 0) {
2559 pr_warn(gettext("failed to iterate over "
2560 "instances: %s\n"), scf_strerror(err
));
2561 exit_status
= UU_EXIT_FATAL
;
2564 } else if (strcmp(argv
[optind
], "milestone") == 0) {
2565 boolean_t temporary
= B_TRUE
;
2566 const char *milestone
;
2570 while ((o
= getopt(argc
, argv
, "d")) != -1) {
2572 temporary
= B_FALSE
;
2584 milestone
= validate_milestone(argv
[optind
]);
2586 set_milestone(milestone
, temporary
);
2587 } else if (strcmp(argv
[optind
], "_smf_backup") == 0) {
2588 const char *reason
= NULL
;
2592 if (optind
!= argc
- 1)
2595 if ((err
= _scf_request_backup(h
, argv
[optind
])) !=
2597 switch (scf_error()) {
2598 case SCF_ERROR_NOT_BOUND
:
2599 case SCF_ERROR_CONNECTION_BROKEN
:
2600 case SCF_ERROR_BACKEND_READONLY
:
2604 case SCF_ERROR_PERMISSION_DENIED
:
2605 case SCF_ERROR_INVALID_ARGUMENT
:
2606 reason
= scf_strerror(scf_error());
2609 case SCF_ERROR_INTERNAL
:
2611 "unknown error (see console for details)";
2615 pr_warn("failed to backup repository: %s\n", reason
);
2616 exit_status
= UU_EXIT_FATAL
;
2618 } else if (strcmp(argv
[optind
], "_smf_repository_switch") == 0) {
2619 const char *reason
= NULL
;
2624 * Check argument and setup scf_switch structure
2626 if (optind
!= argc
- 1)
2629 if (strcmp(argv
[optind
], "fast") == 0) {
2631 } else if (strcmp(argv
[optind
], "perm") == 0) {
2634 exit(UU_EXIT_USAGE
);
2638 * Call into switch primitive
2640 if ((err
= _scf_repository_switch(h
, sw_back
)) !=
2643 * Retrieve per thread SCF error code
2645 switch (scf_error()) {
2646 case SCF_ERROR_NOT_BOUND
:
2650 case SCF_ERROR_CONNECTION_BROKEN
:
2651 case SCF_ERROR_BACKEND_READONLY
:
2655 case SCF_ERROR_PERMISSION_DENIED
:
2656 case SCF_ERROR_INVALID_ARGUMENT
:
2657 reason
= scf_strerror(scf_error());
2660 case SCF_ERROR_INTERNAL
:
2661 reason
= "File operation error: (see console)";
2669 pr_warn("failed to switch repository: %s\n", reason
);
2670 exit_status
= UU_EXIT_FATAL
;
2676 if (scf_handle_unbind(h
) == -1)
2679 scf_handle_destroy(h
);
2680 if (do_zones
&& zent
< nzents
)
2683 return (exit_status
);