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