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