8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / svc / svcadm / svcadm.c
blobb51d218f451d5bf1c882c42760463386f2f81c4d
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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
34 #include <locale.h>
35 #include <libintl.h>
36 #include <libscf.h>
37 #include <libscf_priv.h>
38 #include <libcontract.h>
39 #include <libcontract_priv.h>
40 #include <sys/contract/process.h>
41 #include <libuutil.h>
42 #include <stddef.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <fcntl.h>
48 #include <procfs.h>
49 #include <assert.h>
50 #include <errno.h>
51 #include <zone.h>
53 #ifndef TEXT_DOMAIN
54 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
55 #endif /* TEXT_DOMAIN */
57 /* Must be a power of two */
58 #define HT_BUCKETS 64
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));
78 struct ht_elt {
79 struct ht_elt *next;
80 boolean_t active;
81 char str[1];
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;
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);
115 void
116 do_scfdie(int lineno)
118 scf_error_t err;
120 switch (err = scf_error()) {
121 case SCF_ERROR_CONNECTION_BROKEN:
122 uu_die(gettext("Connection to repository server broken. "
123 "Exiting.\n"));
124 /* NOTREACHED */
126 case SCF_ERROR_BACKEND_READONLY:
127 uu_die(gettext("Repository is read-only. Exiting.\n"));
128 /* NOTREACHED */
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
141 #define scfdie() do_scfdie(__LINE__)
143 static void
144 usage()
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());
168 exit(UU_EXIT_USAGE);
173 * FMRI hash table for recursive enable.
176 static uint32_t
177 hash_fmri(const char *str)
179 uint32_t h = 0, g;
180 const char *p;
182 /* Generic hash function from uts/common/os/modhash.c . */
183 for (p = str; *p != '\0'; ++p) {
184 h = (h << 4) + *p;
185 if ((g = (h & 0xf0000000)) != 0) {
186 h ^= (g >> 24);
187 h ^= g;
191 return (h);
195 * Return 1 if str has been visited, 0 if it has not, and -1 if memory could not
196 * be allocated.
198 static int
199 visited_find_or_add(const char *str, struct ht_elt **hep)
201 uint32_t h;
202 uint_t i;
203 struct ht_elt *he;
205 h = hash_fmri(str);
206 i = h & (HT_BUCKETS - 1);
208 for (he = visited[i]; he != NULL; he = he->next) {
209 if (strcmp(he->str, str) == 0) {
210 if (hep)
211 *hep = he;
212 return (1);
216 he = malloc(offsetof(struct ht_elt, str) + strlen(str) + 1);
217 if (he == NULL)
218 return (-1);
220 (void) strcpy(he->str, str);
222 he->next = visited[i];
223 visited[i] = he;
225 if (hep)
226 *hep = he;
227 return (0);
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;
240 scf_value_t *val;
241 int ret;
243 if ((prop = scf_property_create(h)) == NULL ||
244 (val = scf_value_create(h)) == NULL)
245 scfdie();
247 if (scf_pg_get_property(pg, propname, prop) != 0) {
248 switch (scf_error()) {
249 case SCF_ERROR_DELETED:
250 ret = ECANCELED;
251 goto out;
253 case SCF_ERROR_NOT_FOUND:
254 ret = ENOENT;
255 goto out;
257 case SCF_ERROR_NOT_SET:
258 assert(0);
259 abort();
260 /* NOTREACHED */
262 default:
263 scfdie();
267 if (scf_property_get_value(prop, val) == 0) {
268 ret = 0;
269 } else {
270 switch (scf_error()) {
271 case SCF_ERROR_DELETED:
272 ret = ENOENT;
273 goto out;
275 case SCF_ERROR_NOT_FOUND:
276 ret = EINVAL;
277 goto out;
279 case SCF_ERROR_CONSTRAINT_VIOLATED:
280 ret = E2BIG;
281 break;
283 case SCF_ERROR_NOT_SET:
284 assert(0);
285 abort();
286 /* NOTREACHED */
288 default:
289 scfdie();
293 if (scf_value_get_boolean(val, bp) != 0) {
294 if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
295 scfdie();
297 ret = EINVAL;
298 goto out;
301 out:
302 scf_value_destroy(val);
303 scf_property_destroy(prop);
304 return (ret);
308 * Returns 0, EPERM, or EROFS.
310 static int
311 set_bool_prop(scf_propertygroup_t *pg, const char *propname, boolean_t b)
313 scf_value_t *v;
314 scf_transaction_t *tx;
315 scf_transaction_entry_t *ent;
316 int ret = 0, r;
318 if ((tx = scf_transaction_create(h)) == NULL ||
319 (ent = scf_entry_create(h)) == NULL ||
320 (v = scf_value_create(h)) == NULL)
321 scfdie();
323 scf_value_set_boolean(v, b);
325 for (;;) {
326 if (scf_transaction_start(tx, pg) == -1) {
327 switch (scf_error()) {
328 case SCF_ERROR_PERMISSION_DENIED:
329 ret = EPERM;
330 goto out;
332 case SCF_ERROR_BACKEND_READONLY:
333 ret = EROFS;
334 goto out;
336 default:
337 scfdie();
341 if (scf_transaction_property_change_type(tx, ent, propname,
342 SCF_TYPE_BOOLEAN) != 0) {
343 if (scf_error() != SCF_ERROR_NOT_FOUND)
344 scfdie();
346 if (scf_transaction_property_new(tx, ent, propname,
347 SCF_TYPE_BOOLEAN) != 0)
348 scfdie();
351 r = scf_entry_add_value(ent, v);
352 assert(r == 0);
354 r = scf_transaction_commit(tx);
355 if (r == 1)
356 break;
358 scf_transaction_reset(tx);
360 if (r != 0) {
361 switch (scf_error()) {
362 case SCF_ERROR_PERMISSION_DENIED:
363 ret = EPERM;
364 goto out;
366 case SCF_ERROR_BACKEND_READONLY:
367 ret = EROFS;
368 goto out;
370 default:
371 scfdie();
375 if (scf_pg_update(pg) == -1)
376 scfdie();
379 out:
380 scf_transaction_destroy(tx);
381 scf_entry_destroy(ent);
382 scf_value_destroy(v);
383 return (ret);
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
393 ssize_t
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)
397 ssize_t sz;
399 if (scf_pg_get_property(pg, propname, prop) != 0) {
400 if (scf_error() != SCF_ERROR_NOT_FOUND)
401 scfdie();
403 return (-ENOENT);
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:
410 return (-E2BIG);
412 default:
413 scfdie();
417 sz = scf_value_get_astring(v, buf, bufsz);
418 if (sz < 0) {
419 if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
420 scfdie();
422 return (-EINVAL);
425 return (sz);
429 * Returns 0 or EPERM.
431 static int
432 pg_get_or_add(const scf_instance_t *inst, const char *pgname,
433 const char *pgtype, uint32_t pgflags, scf_propertygroup_t *pg)
435 again:
436 if (scf_instance_get_pg(inst, pgname, pg) == 0)
437 return (0);
439 if (scf_error() != SCF_ERROR_NOT_FOUND)
440 scfdie();
442 if (scf_instance_add_pg(inst, pgname, pgtype, pgflags, pg) == 0)
443 return (0);
445 switch (scf_error()) {
446 case SCF_ERROR_EXISTS:
447 goto again;
449 case SCF_ERROR_PERMISSION_DENIED:
450 return (EPERM);
452 default:
453 scfdie();
454 /* NOTREACHED */
458 static int
459 my_ct_name(char *out, size_t len)
461 ct_stathdl_t st;
462 char *ct_fmri;
463 ctid_t ct;
464 int fd, errno, ret;
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);
481 ct_status_free(st);
482 (void) close(fd);
484 return (ret);
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
494 static int
495 restarter_setup(const char *fmri, const scf_instance_t *inst)
497 boolean_t b = B_FALSE;
498 scf_propertygroup_t *pg = NULL;
499 int ret = 0;
501 if ((pg = scf_pg_create(h)) == NULL)
502 scfdie();
504 if (pg_get_or_add(inst, SCF_PG_RESTARTER_ACTIONS,
505 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
506 pg) == EPERM) {
507 if (!verbose)
508 uu_warn(emsg_permission_denied, fmri);
509 else
510 uu_warn(emsg_create_pg_perm_denied, fmri,
511 SCF_PG_RESTARTER_ACTIONS);
513 ret = EPERM;
514 goto out;
517 /* Set auxiliary_tty property */
518 if (isatty(STDIN_FILENO))
519 b = B_TRUE;
521 /* Create and set state to disabled */
522 switch (set_bool_prop(pg, SCF_PROPERTY_AUX_TTY, b)) {
523 case 0:
524 break;
526 case EPERM:
527 if (!verbose)
528 uu_warn(emsg_permission_denied, fmri);
529 else
530 uu_warn(emsg_prop_perm_denied, fmri,
531 SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_TTY);
533 ret = EPERM;
534 goto out;
535 /* NOTREACHED */
537 case EROFS:
538 /* Shouldn't happen, but it can. */
539 if (!verbose)
540 uu_warn(gettext("%s: Repository read-only.\n"), fmri);
541 else
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);
546 ret = EROFS;
547 goto out;
548 /* NOTREACHED */
550 default:
551 scfdie();
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);
565 out:
566 scf_pg_destroy(pg);
567 return (ret);
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).
576 static void
577 set_inst_enabled(const char *fmri, scf_instance_t *inst, boolean_t temp,
578 boolean_t enable)
580 scf_propertygroup_t *pg;
581 uint8_t b;
582 const char *pgname = NULL; /* For emsg_pg_perm_denied */
583 int r;
585 pg = scf_pg_create(h);
586 if (pg == NULL)
587 scfdie();
589 if (restarter_setup(fmri, inst))
590 goto out;
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)
600 goto eperm;
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)) {
605 case 0:
606 break;
608 case EPERM:
609 goto eperm;
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;
622 default:
623 assert(0);
624 abort();
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;
635 switch (set_bool_prop(pg, SCF_PROPERTY_ENABLED, enable)) {
636 case 0:
637 break;
639 case EPERM:
640 goto eperm;
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;
653 default:
654 assert(0);
655 abort();
658 if (verbose)
659 (void) printf(enable ?
660 gettext("%s temporarily enabled.\n") :
661 gettext("%s temporarily disabled.\n"), fmri);
662 } else {
663 again:
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.
670 if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE,
671 SCF_PG_GENERAL_FLAGS, pg) != 0)
672 goto eperm;
674 switch (set_bool_prop(pg, SCF_PROPERTY_ENABLED, enable)) {
675 case 0:
676 break;
678 case EPERM:
679 goto eperm;
681 case EROFS:
683 * If general/enabled is already set the way we want,
684 * proceed.
686 switch (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b)) {
687 case 0:
688 if ((b != 0) == (enable != B_FALSE))
689 break;
690 /* FALLTHROUGH */
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;
705 case ECANCELED:
706 goto again;
708 default:
709 assert(0);
710 abort();
712 break;
714 default:
715 assert(0);
716 abort();
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;
726 case ECANCELED:
727 uu_warn(emsg_no_service, fmri);
728 goto out;
730 case EPERM:
731 goto eperm;
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;
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;
745 default:
746 bad_error("scf_instance_delete_prop", r);
749 if (verbose)
750 (void) printf(enable ? gettext("%s enabled.\n") :
751 gettext("%s disabled.\n"), fmri);
754 scf_pg_destroy(pg);
755 return;
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);
764 out:
765 scf_pg_destroy(pg);
766 exit_status = 1;
770 * Set inst to the instance which corresponds to fmri. If fmri identifies
771 * a service with a single instance, get that instance.
773 * Fails with
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)
780 static int
781 get_inst_mult(const char *fmri, scf_instance_t *inst)
783 char *cfmri;
784 const char *svc_name, *inst_name, *pg_name;
785 scf_service_t *svc;
786 scf_instance_t *inst2;
787 scf_iter_t *iter;
788 int ret;
790 if (strncmp(fmri, "lrc:", sizeof ("lrc:") - 1) == 0) {
791 uu_warn(gettext("FMRI \"%s\" is a legacy service.\n"), fmri);
792 exit_status = 1;
793 return (ENOTSUP);
796 cfmri = strdup(fmri);
797 if (cfmri == NULL)
798 uu_die(emsg_nomem);
800 if (scf_parse_svc_fmri(cfmri, NULL, &svc_name, &inst_name, &pg_name,
801 NULL) != SCF_SUCCESS) {
802 free(cfmri);
803 uu_warn(gettext("FMRI \"%s\" is invalid.\n"), fmri);
804 exit_status = 1;
805 return (EINVAL);
808 free(cfmri);
810 if (svc_name == NULL || pg_name != NULL) {
811 uu_warn(gettext(
812 "FMRI \"%s\" does not designate a service or instance.\n"),
813 fmri);
814 exit_status = 1;
815 return (ENOTDIR);
818 if (inst_name != NULL) {
819 if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
820 NULL, SCF_DECODE_FMRI_EXACT) == 0)
821 return (0);
823 if (scf_error() != SCF_ERROR_NOT_FOUND)
824 scfdie();
826 uu_warn(gettext("No such instance \"%s\".\n"), fmri);
827 exit_status = 1;
829 return (ENOENT);
832 if ((svc = scf_service_create(h)) == NULL ||
833 (inst2 = scf_instance_create(h)) == NULL ||
834 (iter = scf_iter_create(h)) == NULL)
835 scfdie();
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)
840 scfdie();
842 uu_warn(emsg_no_service, fmri);
843 exit_status = 1;
845 ret = ENOENT;
846 goto out;
849 /* If the service has only one child, use it. */
850 if (scf_iter_service_instances(iter, svc) != SCF_SUCCESS)
851 scfdie();
853 ret = scf_iter_next_instance(iter, inst);
854 if (ret < 0)
855 scfdie();
856 if (ret != 1) {
857 uu_warn(gettext("Service \"%s\" has no instances.\n"),
858 fmri);
859 exit_status = 1;
860 ret = ENOENT;
861 goto out;
864 ret = scf_iter_next_instance(iter, inst2);
865 if (ret < 0)
866 scfdie();
868 if (ret != 0) {
869 ret = E2BIG;
870 goto out;
873 ret = 0;
875 out:
876 scf_iter_destroy(iter);
877 scf_instance_destroy(inst2);
878 scf_service_destroy(svc);
879 return (ret);
883 * Same as get_inst_mult(), but on E2BIG prints a warning and returns ENOENT.
885 static int
886 get_inst(const char *fmri, scf_instance_t *inst)
888 int r;
890 r = get_inst_mult(fmri, inst);
891 if (r != E2BIG)
892 return (r);
894 uu_warn(gettext("operation on service %s is ambiguous; "
895 "instance specification needed.\n"), fmri);
896 return (ENOENT);
899 static char *
900 inst_get_fmri(const scf_instance_t *inst)
902 ssize_t sz;
904 sz = scf_instance_to_fmri(inst, scratch_fmri, max_scf_fmri_sz);
905 if (sz < 0)
906 scfdie();
907 if (sz >= max_scf_fmri_sz)
908 uu_die(gettext("scf_instance_to_fmri() returned unexpectedly "
909 "long value.\n"));
911 return (scratch_fmri);
914 static ssize_t
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)
919 ssize_t sz;
921 sz = get_astring_prop(pg, propname, prop, v, buf, bufsz);
922 if (sz >= 0)
923 return (sz);
925 switch (-sz) {
926 case ENOENT:
927 uu_warn(gettext("\"%s\" is misconfigured (\"%s\" dependency "
928 "lacks \"%s\" property.)\n"), fmri, pgname, propname);
929 return (-1);
931 case E2BIG:
932 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" property "
933 "is not single-valued.)\n"), fmri, pgname, propname);
934 return (-1);
936 case EINVAL:
937 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" property "
938 "is not of astring type.)\n"), fmri, pgname, propname);
939 return (-1);
941 default:
942 assert(0);
943 abort();
944 /* NOTREACHED */
948 static boolean_t
949 multiple_instances(scf_iter_t *iter, scf_value_t *v, char *buf)
951 int count = 0, r;
952 boolean_t ret;
953 scf_instance_t *inst;
955 inst = scf_instance_create(h);
956 if (inst == NULL)
957 scfdie();
959 for (;;) {
960 r = scf_iter_next_value(iter, v);
961 if (r == 0) {
962 ret = B_FALSE;
963 goto out;
965 if (r != 1)
966 scfdie();
968 if (scf_value_get_astring(v, buf, max_scf_fmri_sz) < 0)
969 scfdie();
971 switch (get_inst_mult(buf, inst)) {
972 case 0:
973 ++count;
974 if (count > 1) {
975 ret = B_TRUE;
976 goto out;
978 break;
980 case ENOTSUP:
981 case EINVAL:
982 case ENOTDIR:
983 case ENOENT:
984 continue;
986 case E2BIG:
987 ret = B_TRUE;
988 goto out;
990 default:
991 assert(0);
992 abort();
996 out:
997 scf_instance_destroy(inst);
998 return (ret);
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.
1016 static int
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;
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;
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);
1036 assert(len < max_scf_fmri_sz);
1038 switch (visited_find_or_add(fmri, &he)) {
1039 case 0:
1040 he->active = B_TRUE;
1041 break;
1043 case 1:
1044 return (he->active ? ELOOP : 0);
1046 case -1:
1047 uu_die(emsg_nomem);
1049 default:
1050 assert(0);
1051 abort();
1054 inst = scf_instance_create(h);
1055 if (inst == NULL)
1056 scfdie();
1058 switch (get_inst_mult(fmri, inst)) {
1059 case 0:
1060 break;
1062 case E2BIG:
1063 he->active = B_FALSE;
1064 return (E2BIG);
1066 default:
1067 he->active = B_FALSE;
1068 return (0);
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)
1079 scfdie();
1081 buf = malloc(max_scf_fmri_sz);
1082 if (buf == NULL)
1083 uu_die(emsg_nomem);
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);
1093 if (scf_instance_get_snapshot(inst, "running", snap) != 0) {
1094 if (scf_error() != SCF_ERROR_NOT_FOUND)
1095 scfdie();
1097 scf_snapshot_destroy(snap);
1098 snap = NULL;
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();
1106 uu_warn(gettext("\"%s\" is misconfigured (lacks \"%s\" "
1107 "property group).\n"), fmri, SCF_PG_GENERAL);
1108 ret = 0;
1109 goto out;
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;
1125 case EINVAL:
1126 uu_warn(gettext("Restarter FMRI for \"%s\" is "
1127 "invalid.\n"), fmri);
1128 break;
1130 case E2BIG:
1131 uu_warn(gettext("Restarter FMRI for \"%s\" identifies "
1132 "a service with multiple instances.\n"), fmri);
1133 break;
1135 case ELOOP:
1136 ret = ELOOP;
1137 goto out;
1139 default:
1140 assert(0);
1141 abort();
1143 } else if (sz < 0) {
1144 switch (-sz) {
1145 case ENOENT:
1146 break;
1148 case E2BIG:
1149 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" "
1150 "property is not single-valued).\n"), fmri,
1151 SCF_PG_GENERAL, SCF_PROPERTY_RESTARTER);
1152 ret = 0;
1153 goto out;
1155 case EINVAL:
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);
1159 ret = 0;
1160 goto out;
1162 default:
1163 assert(0);
1164 abort();
1168 if (scf_iter_instance_pgs_typed_composed(pg_iter, inst, snap,
1169 SCF_GROUP_DEPENDENCY) == -1)
1170 scfdie();
1172 while (scf_iter_next_pg(pg_iter, pg) > 0) {
1173 len = scf_pg_get_name(pg, pgname, name_sz);
1174 if (len < 0)
1175 scfdie();
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)
1180 continue;
1182 if (strcmp(buf, "service") != 0)
1183 continue;
1185 if (dep_get_astring(fmri, pgname, pg, SCF_PROPERTY_GROUPING,
1186 prop, v, buf, max_scf_fmri_sz) < 0)
1187 continue;
1189 if (strcmp(buf, SCF_DEP_EXCLUDE_ALL) == 0 ||
1190 strcmp(buf, SCF_DEP_OPTIONAL_ALL) == 0)
1191 continue;
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);
1197 continue;
1200 if (scf_pg_get_property(pg, SCF_PROPERTY_ENTITIES, prop) ==
1201 -1) {
1202 if (scf_error() != SCF_ERROR_NOT_FOUND)
1203 scfdie();
1205 uu_warn(gettext("\"%s\" is misconfigured (\"%s\" "
1206 "dependency lacks \"%s\" property.)\n"), fmri,
1207 pgname, SCF_PROPERTY_ENTITIES);
1208 continue;
1211 if (scf_property_type(prop, &ty) != SCF_SUCCESS)
1212 scfdie();
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);
1218 continue;
1221 if (scf_iter_property_values(val_iter, prop) == -1)
1222 scfdie();
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"),
1227 fmri);
1229 if (scf_iter_property_values(val_iter, prop) !=
1231 scfdie();
1233 for (;;) {
1234 int r;
1236 r = scf_iter_next_value(val_iter, v);
1237 if (r == 0)
1238 break;
1239 if (r != 1)
1240 scfdie();
1242 if (scf_value_get_astring(v, buf,
1243 max_scf_fmri_sz) < 0)
1244 scfdie();
1246 (void) fputs(" ", stdout);
1247 (void) puts(buf);
1250 continue;
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)
1258 scfdie();
1261 for (;;) {
1262 ret = scf_iter_next_value(val_iter, v);
1263 if (ret == 0)
1264 break;
1265 if (ret != 1)
1266 scfdie();
1268 if (scf_value_get_astring(v, buf, max_scf_fmri_sz) ==
1270 scfdie();
1272 switch (enable_fmri_rec(buf, temp)) {
1273 case 0:
1274 break;
1276 case EINVAL:
1277 uu_warn(gettext("\"%s\" dependency of \"%s\" "
1278 "has invalid FMRI \"%s\".\n"), pgname,
1279 fmri, buf);
1280 break;
1282 case E2BIG:
1283 uu_warn(gettext("%s depends on %s, which has "
1284 "multiple instances.\n"), fmri, buf);
1285 break;
1287 case ELOOP:
1288 ret = ELOOP;
1289 goto out;
1291 default:
1292 assert(0);
1293 abort();
1298 ret = 0;
1300 out:
1301 he->active = B_FALSE;
1303 free(buf);
1304 free(pgname);
1306 (void) scf_value_destroy(v);
1307 scf_property_destroy(prop);
1308 scf_pg_destroy(pg);
1309 scf_snapshot_destroy(snap);
1310 scf_iter_destroy(pg_iter);
1311 scf_iter_destroy(val_iter);
1313 return (ret);
1317 * fmri here is only used for verbose messages.
1319 static void
1320 set_inst_action(const char *fmri, const scf_instance_t *inst,
1321 const char *action)
1323 scf_transaction_t *tx;
1324 scf_transaction_entry_t *ent;
1325 scf_propertygroup_t *pg;
1326 scf_property_t *prop;
1327 scf_value_t *v;
1328 int ret;
1329 int64_t t;
1330 hrtime_t timestamp;
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)
1339 scfdie();
1341 if (restarter_setup(fmri, inst)) {
1342 exit_status = 1;
1343 goto out;
1346 if (scf_instance_get_pg(inst, scf_pg_restarter_actions, pg) == -1) {
1347 if (scf_error() != SCF_ERROR_NOT_FOUND)
1348 scfdie();
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. */
1357 break;
1359 case SCF_ERROR_PERMISSION_DENIED:
1360 if (!verbose)
1361 uu_warn(emsg_permission_denied, fmri);
1362 else
1363 uu_warn(emsg_create_pg_perm_denied,
1364 fmri, scf_pg_restarter_actions);
1365 goto out;
1367 default:
1368 scfdie();
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.
1381 do {
1382 timestamp = gethrtime();
1384 if (scf_transaction_start(tx, pg) == -1) {
1385 if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1386 scfdie();
1388 if (!verbose)
1389 uu_warn(emsg_permission_denied, fmri);
1390 else
1391 uu_warn(emsg_pg_perm_denied, fmri,
1392 scf_pg_restarter_actions);
1393 goto out;
1396 if (scf_pg_get_property(pg, action, prop) == -1) {
1397 if (scf_error() != SCF_ERROR_NOT_FOUND)
1398 scfdie();
1399 if (scf_transaction_property_new(tx, ent,
1400 action, SCF_TYPE_INTEGER) == -1)
1401 scfdie();
1402 goto action_set;
1403 } else {
1404 if (scf_transaction_property_change_type(tx, ent,
1405 action, SCF_TYPE_INTEGER) == -1)
1406 scfdie();
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. */
1414 goto action_set;
1416 default:
1417 scfdie();
1419 } else {
1420 if (scf_value_get_integer(v, &t) == -1) {
1421 assert(scf_error() == SCF_ERROR_TYPE_MISMATCH);
1422 goto action_set;
1424 if (t > timestamp)
1425 break;
1428 action_set:
1429 scf_value_set_integer(v, timestamp);
1430 if (scf_entry_add_value(ent, v) == -1)
1431 scfdie();
1433 ret = scf_transaction_commit(tx);
1434 if (ret == -1) {
1435 if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1436 scfdie();
1438 if (!verbose)
1439 uu_warn(emsg_permission_denied, fmri);
1440 else
1441 uu_warn(emsg_prop_perm_denied, fmri,
1442 scf_pg_restarter_actions, action);
1443 scf_transaction_reset(tx);
1444 goto out;
1447 scf_transaction_reset(tx);
1449 if (ret == 0) {
1450 if (scf_pg_update(pg) == -1)
1451 scfdie();
1453 } while (ret == 0);
1455 if (verbose)
1456 (void) printf(gettext("Action %s set for %s.\n"), action, fmri);
1458 out:
1459 scf_value_destroy(v);
1460 scf_entry_destroy(ent);
1461 scf_transaction_destroy(tx);
1462 scf_property_destroy(prop);
1463 scf_pg_destroy(pg);
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
1470 * no state property
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;
1485 scf_value_t *val;
1486 int ret = -1;
1487 ssize_t szret;
1489 if ((pg = scf_pg_create(h)) == NULL ||
1490 (prop = scf_property_create(h)) == NULL ||
1491 (val = scf_value_create(h)) == NULL)
1492 scfdie();
1494 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) != SCF_SUCCESS) {
1495 if (scf_error() != SCF_ERROR_NOT_FOUND)
1496 scfdie();
1498 uu_warn(gettext("%s is misconfigured (lacks \"%s\" property "
1499 "group).\n"), fmri ? fmri : inst_get_fmri(inst),
1500 SCF_PG_RESTARTER);
1501 goto out;
1504 szret = get_astring_prop(pg, SCF_PROPERTY_STATE, prop, val, state,
1505 MAX_SCF_STATE_STRING_SZ);
1506 if (szret < 0) {
1507 switch (-szret) {
1508 case ENOENT:
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);
1513 goto out;
1515 case E2BIG:
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);
1520 goto out;
1522 case EINVAL:
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);
1527 goto out;
1529 default:
1530 assert(0);
1531 abort();
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);
1538 goto out;
1541 ret = 0;
1542 if (pgp)
1543 *pgp = pg;
1545 out:
1546 (void) scf_value_destroy(val);
1547 scf_property_destroy(prop);
1548 if (ret || pgp == NULL)
1549 scf_pg_destroy(pg);
1550 return (ret);
1553 static void
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;
1560 scf_value_t *val;
1561 scf_transaction_t *tx;
1562 scf_transaction_entry_t *txent;
1563 int ret;
1565 inst = scf_instance_create(h);
1566 if (inst == NULL)
1567 scfdie();
1569 if (get_inst(fmri, inst) != 0)
1570 return;
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)
1577 scfdie();
1579 if (scf_instance_get_pg(inst, pgname, pg) != SCF_SUCCESS) {
1580 if (scf_error() != SCF_ERROR_NOT_FOUND)
1581 scfdie();
1583 if (scf_instance_add_pg(inst, pgname, pgtype, pgflags, pg) !=
1584 SCF_SUCCESS) {
1585 switch (scf_error()) {
1586 case SCF_ERROR_EXISTS:
1587 if (scf_instance_get_pg(inst, pgname, pg) !=
1588 SCF_SUCCESS) {
1589 if (scf_error() != SCF_ERROR_NOT_FOUND)
1590 scfdie();
1592 uu_warn(gettext("Repository write "
1593 "contention.\n"));
1594 goto out;
1596 break;
1598 case SCF_ERROR_PERMISSION_DENIED:
1599 if (!verbose)
1600 uu_warn(emsg_permission_denied, fmri);
1601 else
1602 uu_warn(emsg_create_pg_perm_denied,
1603 fmri, pgname);
1604 goto out;
1606 default:
1607 scfdie();
1612 do {
1613 if (scf_transaction_start(tx, pg) != SCF_SUCCESS) {
1614 if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1615 scfdie();
1617 if (!verbose)
1618 uu_warn(emsg_permission_denied, fmri);
1619 else
1620 uu_warn(emsg_pg_perm_denied, fmri, pgname);
1621 goto out;
1624 if (scf_transaction_property_change_type(tx, txent, propname,
1625 SCF_TYPE_ASTRING) != 0) {
1626 if (scf_error() != SCF_ERROR_NOT_FOUND)
1627 scfdie();
1629 if (scf_transaction_property_new(tx, txent, propname,
1630 SCF_TYPE_ASTRING) != 0)
1631 scfdie();
1634 if (scf_value_set_astring(val, str) != SCF_SUCCESS)
1635 scfdie();
1637 if (scf_entry_add_value(txent, val) != SCF_SUCCESS)
1638 scfdie();
1640 ret = scf_transaction_commit(tx);
1641 if (ret == -1) {
1642 if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1643 scfdie();
1645 if (!verbose)
1646 uu_warn(emsg_permission_denied, fmri);
1647 else
1648 uu_warn(emsg_prop_perm_denied, fmri, pgname,
1649 propname);
1650 goto out;
1653 if (ret == 0) {
1654 scf_transaction_reset(tx);
1656 if (scf_pg_update(pg) == -1)
1657 scfdie();
1659 } while (ret == 0);
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);
1672 * Flags to control enable and disable actions.
1674 #define SET_ENABLED 0x1
1675 #define SET_TEMPORARY 0x2
1676 #define SET_RECURSIVE 0x4
1678 static int
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);
1686 if (svcsearch) {
1687 char state[MAX_SCF_STATE_STRING_SZ];
1689 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
1690 return (0);
1691 if (strcmp(state, svcstate) != 0)
1692 return (0);
1695 if (flags & SET_RECURSIVE) {
1696 char *fmri_buf = malloc(max_scf_fmri_sz);
1697 if (fmri_buf == NULL)
1698 uu_die(emsg_nomem);
1700 visited = calloc(HT_BUCKETS, sizeof (*visited));
1701 if (visited == NULL)
1702 uu_die(emsg_nomem);
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))) {
1709 case E2BIG:
1710 uu_warn(gettext("operation on service %s is ambiguous; "
1711 "instance specification needed.\n"), fmri_buf);
1712 break;
1714 case ELOOP:
1715 uu_warn(gettext("%s: Dependency cycle detected.\n"),
1716 fmri_buf);
1719 free(visited);
1720 free(fmri_buf);
1722 } else {
1723 set_inst_enabled(wip->fmri, wip->inst,
1724 (flags & SET_TEMPORARY) != 0, (flags & SET_ENABLED) != 0);
1727 return (0);
1730 /* ARGSUSED */
1731 static int
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);
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;
1745 return (0);
1748 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0 ||
1749 strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) {
1751 * We're done.
1753 goto out;
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;
1763 goto out;
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;
1773 goto out;
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
1785 * EXIT_DEP_FAILURE
1787 if (exit_status == 0)
1788 exit_status = EXIT_DEP_FAILURE;
1789 goto out;
1791 } while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0);
1792 scfdie();
1793 /* NOTREACHED */
1795 out:
1796 scf_pg_destroy(pg);
1797 return (0);
1800 /* ARGSUSED */
1801 static int
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);
1810 do {
1811 if (pg)
1812 scf_pg_destroy(pg);
1813 if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) {
1814 exit_status = EXIT_SVC_FAILURE;
1815 return (0);
1818 if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0) {
1820 * We're done.
1822 goto out;
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;
1832 goto out;
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
1843 * EXIT_DEP_FAILURE
1845 if (exit_status == 0)
1846 exit_status = EXIT_DEP_FAILURE;
1847 goto out;
1850 } while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0);
1851 scfdie();
1852 /* NOTREACHED */
1854 out:
1855 scf_pg_destroy(pg);
1856 return (0);
1859 /* ARGSUSED */
1860 static int
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)
1869 return (0);
1871 if (svcsearch && strcmp(state, svcstate) != 0)
1872 return (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) ==
1877 0) {
1878 set_inst_action(wip->fmri, wip->inst, SCF_PROPERTY_RESTORE);
1879 } else {
1880 uu_warn(gettext("Instance \"%s\" is not in a "
1881 "maintenance or degraded state.\n"), wip->fmri);
1883 exit_status = 1;
1886 return (0);
1889 static int
1890 set_fmri_action(void *action, scf_walkinfo_t *wip)
1892 assert(wip->inst != NULL && wip->pg == NULL);
1894 if (svcsearch) {
1895 char state[MAX_SCF_STATE_STRING_SZ];
1897 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
1898 return (0);
1899 if (strcmp(state, svcstate) != 0)
1900 return (0);
1903 set_inst_action(wip->fmri, wip->inst, action);
1905 return (0);
1909 * Flags to control 'mark' action.
1911 #define MARK_IMMEDIATE 0x1
1912 #define MARK_TEMPORARY 0x2
1914 static int
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) {
1921 exit_status = 1;
1922 return (0);
1925 if (svcsearch && strcmp(state, svcstate) != 0)
1926 return (0);
1928 if (strcmp(state, SCF_STATE_STRING_ONLINE) != 0) {
1929 uu_warn(gettext("Instance \"%s\" is not online.\n"), wip->fmri);
1930 exit_status = 1;
1931 return (0);
1934 set_inst_action(wip->fmri, wip->inst, (flags & MARK_IMMEDIATE) ?
1935 SCF_PROPERTY_DEGRADE_IMMEDIATE : SCF_PROPERTY_DEGRADED);
1937 return (0);
1940 static int
1941 force_maintenance(void *data, scf_walkinfo_t *wip)
1943 int flags = (int)data;
1944 const char *prop;
1946 if (svcsearch) {
1947 char state[MAX_SCF_STATE_STRING_SZ];
1949 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
1950 return (0);
1951 if (strcmp(state, svcstate) != 0)
1952 return (0);
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;
1965 set_inst_action(wip->fmri, wip->inst, prop);
1967 return (0);
1970 static void
1971 set_milestone(const char *fmri, boolean_t temporary)
1973 scf_instance_t *inst;
1974 scf_propertygroup_t *pg;
1975 int r;
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;
1984 if ((inst = scf_instance_create(h)) == NULL ||
1985 (pg = scf_pg_create(h)) == NULL)
1986 scfdie();
1988 if (get_inst(SCF_SERVICE_STARTD, inst) != 0) {
1989 scf_instance_destroy(inst);
1990 return;
1994 * Set the persistent milestone before deleting the override so we don't
1995 * glitch.
1997 set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS,
1998 SCF_PG_OPTIONS_TYPE, SCF_PG_OPTIONS_FLAGS, SCF_PROPERTY_MILESTONE,
1999 fmri);
2001 r = scf_instance_delete_prop(inst, SCF_PG_OPTIONS_OVR,
2002 SCF_PROPERTY_MILESTONE);
2003 switch (r) {
2004 case 0:
2005 break;
2007 case ECANCELED:
2008 uu_warn(emsg_no_service, fmri);
2009 exit_status = 1;
2010 goto out;
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;
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;
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;
2033 default:
2034 bad_error("scf_instance_delete_prop", r);
2037 out:
2038 scf_pg_destroy(pg);
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,
2046 NULL
2049 static void
2050 usage_milestone(void)
2052 const char **ms;
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"
2059 "\tall\n"
2060 "\tnone\n"));
2062 for (ms = milestones; *ms != NULL; ms++)
2063 (void) fprintf(stderr, "\t%s\n", *ms);
2065 exit(UU_EXIT_USAGE);
2068 static const char *
2069 validate_milestone(const char *milestone)
2071 const char **ms;
2072 const char *tmp;
2073 size_t len;
2075 if (strcmp(milestone, "all") == 0)
2076 return (milestone);
2078 if (strcmp(milestone, "none") == 0)
2079 return (milestone);
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] == ':'))
2096 return (*ms);
2100 (void) fprintf(stderr,
2101 gettext("\"%s\" is not a valid major milestone.\n"), milestone);
2103 usage_milestone();
2104 /* NOTREACHED */
2107 /*PRINTFLIKE1*/
2108 static void
2109 pr_warn(const char *format, ...)
2111 const char *pname = uu_getpname();
2112 va_list alist;
2114 va_start(alist, format);
2116 if (pname != NULL)
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));
2129 va_end(alist);
2132 /*ARGSUSED*/
2133 static void
2134 quiet(const char *fmt, ...)
2136 /* Do nothing */
2140 main(int argc, char *argv[])
2142 int o;
2143 int err;
2144 int sw_back;
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;
2151 char **orig_argv;
2153 (void) setlocale(LC_ALL, "");
2154 (void) textdomain(TEXT_DOMAIN);
2156 (void) uu_setpname(argv[0]);
2158 if (argc < 2)
2159 usage();
2161 max_scf_fmri_sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
2162 if (max_scf_fmri_sz < 0)
2163 scfdie();
2164 ++max_scf_fmri_sz;
2166 scratch_fmri = malloc(max_scf_fmri_sz);
2167 if (scratch_fmri == NULL)
2168 uu_die(emsg_nomem);
2170 while ((o = getopt(argc, argv, "S:vZz:")) != -1) {
2171 switch (o) {
2172 case 'S':
2173 (void) strlcpy(svcstate, optarg, sizeof (svcstate));
2174 svcsearch = B_TRUE;
2175 break;
2177 case 'v':
2178 verbose = 1;
2179 break;
2181 case 'z':
2182 if (getzoneid() != GLOBAL_ZONEID)
2183 uu_die(gettext("svcadm -z may only be used "
2184 "from the global zone\n"));
2185 if (do_zones)
2186 usage();
2188 (void) strlcpy(zonename, optarg, sizeof (zonename));
2189 do_a_zone = B_TRUE;
2190 break;
2192 case 'Z':
2193 if (getzoneid() != GLOBAL_ZONEID)
2194 uu_die(gettext("svcadm -Z may only be used "
2195 "from the global zone\n"));
2196 if (do_a_zone)
2197 usage();
2199 do_zones = B_TRUE;
2200 break;
2202 default:
2203 usage();
2207 while (do_zones) {
2208 uint_t found;
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);
2218 found = 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)
2229 break;
2231 free(zids);
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;
2245 orig_argc = argc;
2246 orig_argv = argv;
2248 again:
2249 h = scf_handle_create(SCF_VERSION);
2250 if (h == NULL)
2251 scfdie();
2253 if (do_zones) {
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).
2267 zent++;
2268 goto nextzone;
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]);
2275 goto nextzone;
2278 g_zonename = zonename;
2281 if (do_a_zone || do_zones) {
2282 scf_value_t *zone;
2284 if ((zone = scf_value_create(h)) == NULL)
2285 scfdie();
2287 if (scf_value_set_astring(zone, zonename) != SCF_SUCCESS)
2288 scfdie();
2290 if (scf_handle_decorate(h, "zone", zone) != SCF_SUCCESS) {
2291 if (do_a_zone) {
2292 uu_die(gettext("invalid zone '%s'\n"), optarg);
2293 } else {
2294 scf_value_destroy(zone);
2295 goto nextzone;
2299 scf_value_destroy(zone);
2302 if (scf_handle_bind(h) == -1) {
2303 if (do_zones)
2304 goto nextzone;
2306 uu_die(gettext("Couldn't bind to configuration repository: "
2307 "%s.\n"), scf_strerror(scf_error()));
2310 optind = orig_optind;
2311 argc = orig_argc;
2312 argv = orig_argv;
2314 if (optind >= argc)
2315 usage();
2317 if (strcmp(argv[optind], "enable") == 0) {
2318 int flags = SET_ENABLED;
2319 int wait = 0;
2320 int error = 0;
2322 ++optind;
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();
2338 argc -= optind;
2339 argv += optind;
2341 if (argc == 0 && !svcsearch)
2342 usage();
2344 if (argc > 0 && svcsearch)
2345 usage();
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;
2369 if (error > 0)
2370 exit_status = error;
2372 } else if (strcmp(argv[optind], "disable") == 0) {
2373 int flags = 0;
2374 int wait = 0;
2375 int error = 0;
2377 ++optind;
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();
2391 argc -= optind;
2392 argv += optind;
2394 if (argc == 0 && !svcsearch)
2395 usage();
2397 if (argc > 0 && svcsearch)
2398 usage();
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,
2408 pr_warn)) != 0) {
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;
2423 if (error > 0)
2424 exit_status = error;
2426 } else if (strcmp(argv[optind], "restart") == 0) {
2427 boolean_t do_dump = B_FALSE;
2429 ++optind;
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 {
2437 assert(0);
2438 abort();
2441 argc -= optind;
2442 argv += optind;
2444 if (argc == 0 && !svcsearch)
2445 usage();
2447 if (argc > 0 && svcsearch)
2448 usage();
2450 if (do_dump) {
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,
2462 pr_warn)) != 0) {
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) {
2469 ++optind;
2470 argc -= optind;
2471 argv += optind;
2473 if (argc == 0 && !svcsearch)
2474 usage();
2476 if (argc > 0 && svcsearch)
2477 usage();
2479 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2480 set_fmri_action, (void *)SCF_PROPERTY_REFRESH, &exit_status,
2481 pr_warn)) != 0) {
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) {
2488 int flags = 0;
2489 scf_walk_callback callback;
2491 ++optind;
2493 while ((o = getopt(argc, argv, "It")) != -1) {
2494 if (o == 'I')
2495 flags |= MARK_IMMEDIATE;
2496 else if (o == 't')
2497 flags |= MARK_TEMPORARY;
2498 else if (o == '?')
2499 usage();
2500 else {
2501 assert(0);
2502 abort();
2506 if (argc - optind < 2)
2507 usage();
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;
2517 } else {
2518 usage();
2521 optind++;
2522 argc -= optind;
2523 argv += optind;
2525 if (argc == 0 && !svcsearch)
2526 usage();
2528 if (argc > 0 && svcsearch)
2529 usage();
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 "
2534 "instances: %s\n"),
2535 scf_strerror(err));
2536 exit_status = UU_EXIT_FATAL;
2539 } else if (strcmp(argv[optind], "clear") == 0) {
2540 ++optind;
2541 argc -= optind;
2542 argv += optind;
2544 if (argc == 0 && !svcsearch)
2545 usage();
2547 if (svcsearch) {
2548 if (argc > 0)
2549 usage();
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;
2568 ++optind;
2570 while ((o = getopt(argc, argv, "d")) != -1) {
2571 if (o == 'd')
2572 temporary = B_FALSE;
2573 else if (o == '?')
2574 usage_milestone();
2575 else {
2576 assert(0);
2577 abort();
2581 if (optind >= argc)
2582 usage_milestone();
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;
2590 ++optind;
2592 if (optind != argc - 1)
2593 usage();
2595 if ((err = _scf_request_backup(h, argv[optind])) !=
2596 SCF_SUCCESS) {
2597 switch (scf_error()) {
2598 case SCF_ERROR_NOT_BOUND:
2599 case SCF_ERROR_CONNECTION_BROKEN:
2600 case SCF_ERROR_BACKEND_READONLY:
2601 scfdie();
2602 break;
2604 case SCF_ERROR_PERMISSION_DENIED:
2605 case SCF_ERROR_INVALID_ARGUMENT:
2606 reason = scf_strerror(scf_error());
2607 break;
2609 case SCF_ERROR_INTERNAL:
2610 reason =
2611 "unknown error (see console for details)";
2612 break;
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;
2621 ++optind;
2624 * Check argument and setup scf_switch structure
2626 if (optind != argc - 1)
2627 exit(1);
2629 if (strcmp(argv[optind], "fast") == 0) {
2630 sw_back = 0;
2631 } else if (strcmp(argv[optind], "perm") == 0) {
2632 sw_back = 1;
2633 } else {
2634 exit(UU_EXIT_USAGE);
2638 * Call into switch primitive
2640 if ((err = _scf_repository_switch(h, sw_back)) !=
2641 SCF_SUCCESS) {
2643 * Retrieve per thread SCF error code
2645 switch (scf_error()) {
2646 case SCF_ERROR_NOT_BOUND:
2647 abort();
2648 /* NOTREACHED */
2650 case SCF_ERROR_CONNECTION_BROKEN:
2651 case SCF_ERROR_BACKEND_READONLY:
2652 scfdie();
2653 /* NOTREACHED */
2655 case SCF_ERROR_PERMISSION_DENIED:
2656 case SCF_ERROR_INVALID_ARGUMENT:
2657 reason = scf_strerror(scf_error());
2658 break;
2660 case SCF_ERROR_INTERNAL:
2661 reason = "File operation error: (see console)";
2662 break;
2664 default:
2665 abort();
2666 /* NOTREACHED */
2669 pr_warn("failed to switch repository: %s\n", reason);
2670 exit_status = UU_EXIT_FATAL;
2672 } else {
2673 usage();
2676 if (scf_handle_unbind(h) == -1)
2677 scfdie();
2678 nextzone:
2679 scf_handle_destroy(h);
2680 if (do_zones && zent < nzents)
2681 goto again;
2683 return (exit_status);