xref: /illumos-gate/usr/src/cmd/svc/svcadm/svcadm.c (revision 2654012f83cec5dc15b61dfe3e4a4915f186e7a6)
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 2008 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 
75 struct ht_elt {
76 	struct ht_elt	*next;
77 	boolean_t	active;
78 	char		str[1];
79 };
80 
81 
82 scf_handle_t *h;
83 ssize_t max_scf_fmri_sz;
84 static const char *emsg_permission_denied;
85 static const char *emsg_nomem;
86 static const char *emsg_create_pg_perm_denied;
87 static const char *emsg_pg_perm_denied;
88 static const char *emsg_prop_perm_denied;
89 static const char *emsg_no_service;
90 
91 static int exit_status = 0;
92 static int verbose = 0;
93 static char *scratch_fmri;
94 
95 static struct ht_elt **visited;
96 
97 void do_scfdie(int lineno) __NORETURN;
98 static void usage_milestone(void) __NORETURN;
99 
100 /*
101  * Visitors from synch.c, needed for enable -s and disable -s.
102  */
103 extern int is_enabled(scf_instance_t *);
104 extern int has_potential(scf_instance_t *, int);
105 
106 void
107 do_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 
134 static void
135 usage()
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 
166 static uint32_t
167 hash_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  */
188 static int
189 visited_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  */
226 int
227 get_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 
291 out:
292 	scf_value_destroy(val);
293 	scf_property_destroy(prop);
294 	return (ret);
295 }
296 
297 /*
298  * Returns 0, EPERM, or EROFS.
299  */
300 static int
301 set_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 
369 out:
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  */
383 ssize_t
384 get_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  */
426 static int
427 delete_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 
527 out:
528 	scf_transaction_destroy(tx);
529 	scf_entry_destroy(ent);
530 	return (ret);
531 }
532 
533 /*
534  * Returns 0 or EPERM.
535  */
536 static int
537 pg_get_or_add(scf_instance_t *inst, const char *pgname, const char *pgtype,
538     uint32_t pgflags, scf_propertygroup_t *pg)
539 {
540 again:
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  */
569 static void
570 set_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 	/*
583 	 * An instance's configuration is incomplete if general/enabled
584 	 * doesn't exist. Create both the property group and property
585 	 * here if they don't exist.
586 	 */
587 	pgname = SCF_PG_GENERAL;
588 	if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE,
589 	    SCF_PG_GENERAL_FLAGS, pg) != 0)
590 		goto eperm;
591 
592 	if (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b) != 0) {
593 		/* Create and set state to disabled */
594 		switch (set_bool_prop(pg, SCF_PROPERTY_ENABLED, B_FALSE) != 0) {
595 		case 0:
596 			break;
597 
598 		case EPERM:
599 			goto eperm;
600 
601 		case EROFS:
602 			/* Shouldn't happen, but it can. */
603 			if (!verbose)
604 				uu_warn(gettext("%s: Repository read-only.\n"),
605 				    fmri);
606 			else
607 				uu_warn(gettext("%s: Could not set %s/%s "
608 				    "(repository read-only).\n"), fmri,
609 				    SCF_PG_GENERAL, SCF_PROPERTY_ENABLED);
610 			goto out;
611 
612 		default:
613 			assert(0);
614 			abort();
615 		}
616 	}
617 
618 	if (temp) {
619 		/* Set general_ovr/enabled */
620 		pgname = SCF_PG_GENERAL_OVR;
621 		if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_OVR_TYPE,
622 		    SCF_PG_GENERAL_OVR_FLAGS, pg) != 0)
623 			goto eperm;
624 
625 		switch (set_bool_prop(pg, SCF_PROPERTY_ENABLED, enable) != 0) {
626 		case 0:
627 			break;
628 
629 		case EPERM:
630 			goto eperm;
631 
632 		case EROFS:
633 			/* Shouldn't happen, but it can. */
634 			if (!verbose)
635 				uu_warn(gettext("%s: Repository read-only.\n"),
636 				    fmri);
637 			else
638 				uu_warn(gettext("%s: Could not set %s/%s "
639 				    "(repository read-only).\n"), fmri,
640 				    SCF_PG_GENERAL_OVR, SCF_PROPERTY_ENABLED);
641 			goto out;
642 
643 		default:
644 			assert(0);
645 			abort();
646 		}
647 
648 		if (verbose)
649 			(void) printf(enable ?
650 			    gettext("%s temporarily enabled.\n") :
651 			    gettext("%s temporarily disabled.\n"), fmri);
652 	} else {
653 again:
654 		/*
655 		 * Both pg and property should exist since we created
656 		 * them earlier. However, there's still a chance that
657 		 * someone may have deleted the property out from under
658 		 * us.
659 		 */
660 		if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE,
661 		    SCF_PG_GENERAL_FLAGS, pg) != 0)
662 			goto eperm;
663 
664 		switch (set_bool_prop(pg, SCF_PROPERTY_ENABLED, enable)) {
665 		case 0:
666 			break;
667 
668 		case EPERM:
669 			goto eperm;
670 
671 		case EROFS:
672 			/*
673 			 * If general/enabled is already set the way we want,
674 			 * proceed.
675 			 */
676 			switch (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b)) {
677 			case 0:
678 				if ((b != 0) == (enable != B_FALSE))
679 					break;
680 				/* FALLTHROUGH */
681 
682 			case ENOENT:
683 			case EINVAL:
684 			case E2BIG:
685 				if (!verbose)
686 					uu_warn(gettext("%s: Repository "
687 					    "read-only.\n"), fmri);
688 				else
689 					uu_warn(gettext("%s: Could not set "
690 					    "%s/%s (repository read-only).\n"),
691 					    fmri, SCF_PG_GENERAL,
692 					    SCF_PROPERTY_ENABLED);
693 				goto out;
694 
695 			case ECANCELED:
696 				goto again;
697 
698 			default:
699 				assert(0);
700 				abort();
701 			}
702 			break;
703 
704 		default:
705 			assert(0);
706 			abort();
707 		}
708 
709 		pgname = SCF_PG_GENERAL_OVR;
710 		if (scf_instance_get_pg(inst, pgname, pg) == 0) {
711 			r = delete_prop(pg, SCF_PROPERTY_ENABLED);
712 			switch (r) {
713 			case 0:
714 				break;
715 
716 			case ECANCELED:
717 				uu_warn(emsg_no_service, fmri);
718 				goto out;
719 
720 			case EPERM:
721 				goto eperm;
722 
723 			case EACCES:
724 				uu_warn(gettext("Could not delete %s/%s "
725 				    "property of %s: backend access denied.\n"),
726 				    pgname, SCF_PROPERTY_ENABLED, fmri);
727 				goto out;
728 
729 			case EROFS:
730 				uu_warn(gettext("Could not delete %s/%s "
731 				    "property of %s: backend is read-only.\n"),
732 				    pgname, SCF_PROPERTY_ENABLED, fmri);
733 				goto out;
734 
735 			default:
736 				bad_error("delete_prop", r);
737 			}
738 		} else {
739 			switch (scf_error()) {
740 			case SCF_ERROR_DELETED:
741 				/* Print something? */
742 
743 			case SCF_ERROR_NOT_FOUND:
744 				break;
745 
746 			case SCF_ERROR_HANDLE_MISMATCH:
747 			case SCF_ERROR_INVALID_ARGUMENT:
748 			case SCF_ERROR_NOT_SET:
749 				assert(0);
750 				abort();
751 				/* NOTREACHED */
752 
753 			case SCF_ERROR_CONNECTION_BROKEN:
754 			default:
755 				scfdie();
756 			}
757 		}
758 
759 		if (verbose)
760 			(void) printf(enable ?  gettext("%s enabled.\n") :
761 			    gettext("%s disabled.\n"), fmri);
762 	}
763 
764 	scf_pg_destroy(pg);
765 	return;
766 
767 eperm:
768 	assert(pgname != NULL);
769 	if (!verbose)
770 		uu_warn(emsg_permission_denied, fmri);
771 	else
772 		uu_warn(emsg_pg_perm_denied, fmri, pgname);
773 
774 out:
775 	scf_pg_destroy(pg);
776 	exit_status = 1;
777 }
778 
779 /*
780  * Set inst to the instance which corresponds to fmri.  If fmri identifies
781  * a service with a single instance, get that instance.
782  *
783  * Fails with
784  *   ENOTSUP - fmri has an unsupported scheme
785  *   EINVAL - fmri is invalid
786  *   ENOTDIR - fmri does not identify a service or instance
787  *   ENOENT - could not locate instance
788  *   E2BIG - fmri is a service with multiple instances (warning not printed)
789  */
790 static int
791 get_inst_mult(const char *fmri, scf_instance_t *inst)
792 {
793 	char *cfmri;
794 	const char *svc_name, *inst_name, *pg_name;
795 	scf_service_t *svc;
796 	scf_instance_t *inst2;
797 	scf_iter_t *iter;
798 	int ret;
799 
800 	if (strncmp(fmri, "lrc:", sizeof ("lrc:") - 1) == 0) {
801 		uu_warn(gettext("FMRI \"%s\" is a legacy service.\n"), fmri);
802 		exit_status = 1;
803 		return (ENOTSUP);
804 	}
805 
806 	cfmri = strdup(fmri);
807 	if (cfmri == NULL)
808 		uu_die(emsg_nomem);
809 
810 	if (scf_parse_svc_fmri(cfmri, NULL, &svc_name, &inst_name, &pg_name,
811 	    NULL) != SCF_SUCCESS) {
812 		free(cfmri);
813 		uu_warn(gettext("FMRI \"%s\" is invalid.\n"), fmri);
814 		exit_status = 1;
815 		return (EINVAL);
816 	}
817 
818 	free(cfmri);
819 
820 	if (svc_name == NULL || pg_name != NULL) {
821 		uu_warn(gettext(
822 		    "FMRI \"%s\" does not designate a service or instance.\n"),
823 		    fmri);
824 		exit_status = 1;
825 		return (ENOTDIR);
826 	}
827 
828 	if (inst_name != NULL) {
829 		if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
830 		    NULL, SCF_DECODE_FMRI_EXACT) == 0)
831 			return (0);
832 
833 		if (scf_error() != SCF_ERROR_NOT_FOUND)
834 			scfdie();
835 
836 		uu_warn(gettext("No such instance \"%s\".\n"), fmri);
837 		exit_status = 1;
838 
839 		return (ENOENT);
840 	}
841 
842 	if ((svc = scf_service_create(h)) == NULL ||
843 	    (inst2 = scf_instance_create(h)) == NULL ||
844 	    (iter = scf_iter_create(h)) == NULL)
845 		scfdie();
846 
847 	if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL,
848 	    SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
849 		if (scf_error() != SCF_ERROR_NOT_FOUND)
850 			scfdie();
851 
852 		uu_warn(emsg_no_service, fmri);
853 		exit_status = 1;
854 
855 		ret = ENOENT;
856 		goto out;
857 	}
858 
859 	/* If the service has only one child, use it. */
860 	if (scf_iter_service_instances(iter, svc) != SCF_SUCCESS)
861 		scfdie();
862 
863 	ret = scf_iter_next_instance(iter, inst);
864 	if (ret < 0)
865 		scfdie();
866 	if (ret != 1) {
867 		uu_warn(gettext("Service \"%s\" has no instances.\n"),
868 		    fmri);
869 		exit_status = 1;
870 		ret = ENOENT;
871 		goto out;
872 	}
873 
874 	ret = scf_iter_next_instance(iter, inst2);
875 	if (ret < 0)
876 		scfdie();
877 
878 	if (ret != 0) {
879 		ret = E2BIG;
880 		goto out;
881 	}
882 
883 	ret = 0;
884 
885 out:
886 	scf_iter_destroy(iter);
887 	scf_instance_destroy(inst2);
888 	scf_service_destroy(svc);
889 	return (ret);
890 }
891 
892 /*
893  * Same as get_inst_mult(), but on E2BIG prints a warning and returns ENOENT.
894  */
895 static int
896 get_inst(const char *fmri, scf_instance_t *inst)
897 {
898 	int r;
899 
900 	r = get_inst_mult(fmri, inst);
901 	if (r != E2BIG)
902 		return (r);
903 
904 	uu_warn(gettext("operation on service %s is ambiguous; "
905 	    "instance specification needed.\n"), fmri);
906 	return (ENOENT);
907 }
908 
909 static char *
910 inst_get_fmri(const scf_instance_t *inst)
911 {
912 	ssize_t sz;
913 
914 	sz = scf_instance_to_fmri(inst, scratch_fmri, max_scf_fmri_sz);
915 	if (sz < 0)
916 		scfdie();
917 	if (sz >= max_scf_fmri_sz)
918 		uu_die(gettext("scf_instance_to_fmri() returned unexpectedly "
919 		    "long value.\n"));
920 
921 	return (scratch_fmri);
922 }
923 
924 static ssize_t
925 dep_get_astring(const char *fmri, const char *pgname,
926     const scf_propertygroup_t *pg, const char *propname, scf_property_t *prop,
927     scf_value_t *v, char *buf, size_t bufsz)
928 {
929 	ssize_t sz;
930 
931 	sz = get_astring_prop(pg, propname, prop, v, buf, bufsz);
932 	if (sz >= 0)
933 		return (sz);
934 
935 	switch (-sz) {
936 	case ENOENT:
937 		uu_warn(gettext("\"%s\" is misconfigured (\"%s\" dependency "
938 		    "lacks \"%s\" property.)\n"), fmri, pgname, propname);
939 		return (-1);
940 
941 	case E2BIG:
942 		uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" property "
943 		    "is not single-valued.)\n"), fmri, pgname, propname);
944 		return (-1);
945 
946 	case EINVAL:
947 		uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" property "
948 		    "is not of astring type.)\n"), fmri, pgname, propname);
949 		return (-1);
950 
951 	default:
952 		assert(0);
953 		abort();
954 		/* NOTREACHED */
955 	}
956 }
957 
958 static boolean_t
959 multiple_instances(scf_iter_t *iter, scf_value_t *v, char *buf)
960 {
961 	int count = 0, r;
962 	boolean_t ret;
963 	scf_instance_t *inst;
964 
965 	inst = scf_instance_create(h);
966 	if (inst == NULL)
967 		scfdie();
968 
969 	for (;;) {
970 		r = scf_iter_next_value(iter, v);
971 		if (r == 0) {
972 			ret = B_FALSE;
973 			goto out;
974 		}
975 		if (r != 1)
976 			scfdie();
977 
978 		if (scf_value_get_astring(v, buf, max_scf_fmri_sz) < 0)
979 			scfdie();
980 
981 		switch (get_inst_mult(buf, inst)) {
982 		case 0:
983 			++count;
984 			if (count > 1) {
985 				ret = B_TRUE;
986 				goto out;
987 			}
988 			break;
989 
990 		case ENOTSUP:
991 		case EINVAL:
992 		case ENOTDIR:
993 		case ENOENT:
994 			continue;
995 
996 		case E2BIG:
997 			ret = B_TRUE;
998 			goto out;
999 
1000 		default:
1001 			assert(0);
1002 			abort();
1003 		}
1004 	}
1005 
1006 out:
1007 	scf_instance_destroy(inst);
1008 	return (ret);
1009 }
1010 
1011 /*
1012  * Enable the service or instance identified by fmri and its dependencies,
1013  * recursively.  Specifically, call get_inst(fmri), enable the result, and
1014  * recurse on its restarter and the dependencies.  To avoid duplication of
1015  * effort or looping around a dependency cycle, each FMRI is entered into the
1016  * "visited" hash table.  While recursing, the hash table entry is marked
1017  * "active", so that if we come upon it again, we know we've hit a cycle.
1018  * exclude_all and optional_all dependencies are ignored.  require_any
1019  * dependencies are followed only if they comprise a single service; otherwise
1020  * the user is warned.
1021  *
1022  * fmri must point to a writable max_scf_fmri_sz buffer.  Returns EINVAL if fmri
1023  * is invalid, E2BIG if fmri identifies a service with multiple instances, ELOOP
1024  * on cycle detection, or 0 on success.
1025  */
1026 static int
1027 enable_fmri_rec(char *fmri, boolean_t temp)
1028 {
1029 	scf_instance_t *inst;
1030 	scf_snapshot_t *snap;
1031 	scf_propertygroup_t *pg;
1032 	scf_property_t *prop;
1033 	scf_value_t *v;
1034 	scf_iter_t *pg_iter, *val_iter;
1035 	scf_type_t ty;
1036 	char *buf, *pgname;
1037 	ssize_t name_sz, len, sz;
1038 	int ret;
1039 	struct ht_elt *he;
1040 
1041 	len = scf_canonify_fmri(fmri, fmri, max_scf_fmri_sz);
1042 	if (len < 0) {
1043 		assert(scf_error() == SCF_ERROR_INVALID_ARGUMENT);
1044 		return (EINVAL);
1045 	}
1046 	assert(len < max_scf_fmri_sz);
1047 
1048 	switch (visited_find_or_add(fmri, &he)) {
1049 	case 0:
1050 		he->active = B_TRUE;
1051 		break;
1052 
1053 	case 1:
1054 		return (he->active ? ELOOP : 0);
1055 
1056 	case -1:
1057 		uu_die(emsg_nomem);
1058 
1059 	default:
1060 		assert(0);
1061 		abort();
1062 	}
1063 
1064 	inst = scf_instance_create(h);
1065 	if (inst == NULL)
1066 		scfdie();
1067 
1068 	switch (get_inst_mult(fmri, inst)) {
1069 	case 0:
1070 		break;
1071 
1072 	case E2BIG:
1073 		he->active = B_FALSE;
1074 		return (E2BIG);
1075 
1076 	default:
1077 		he->active = B_FALSE;
1078 		return (0);
1079 	}
1080 
1081 	set_inst_enabled(fmri, inst, temp, B_TRUE);
1082 
1083 	if ((snap = scf_snapshot_create(h)) == NULL ||
1084 	    (pg = scf_pg_create(h)) == NULL ||
1085 	    (prop = scf_property_create(h)) == NULL ||
1086 	    (v = scf_value_create(h)) == NULL ||
1087 	    (pg_iter = scf_iter_create(h)) == NULL ||
1088 	    (val_iter = scf_iter_create(h)) == NULL)
1089 		scfdie();
1090 
1091 	buf = malloc(max_scf_fmri_sz);
1092 	if (buf == NULL)
1093 		uu_die(emsg_nomem);
1094 
1095 	name_sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
1096 	if (name_sz < 0)
1097 		scfdie();
1098 	++name_sz;
1099 	pgname = malloc(name_sz);
1100 	if (pgname == NULL)
1101 		uu_die(emsg_nomem);
1102 
1103 	if (scf_instance_get_snapshot(inst, "running", snap) != 0) {
1104 		if (scf_error() != SCF_ERROR_NOT_FOUND)
1105 			scfdie();
1106 
1107 		scf_snapshot_destroy(snap);
1108 		snap = NULL;
1109 	}
1110 
1111 	/* Enable restarter */
1112 	if (scf_instance_get_pg_composed(inst, snap, SCF_PG_GENERAL, pg) != 0) {
1113 		if (scf_error() != SCF_ERROR_NOT_FOUND)
1114 			scfdie();
1115 
1116 		uu_warn(gettext("\"%s\" is misconfigured (lacks \"%s\" "
1117 		    "property group).\n"), fmri, SCF_PG_GENERAL);
1118 		ret = 0;
1119 		goto out;
1120 	}
1121 
1122 	sz = get_astring_prop(pg, SCF_PROPERTY_RESTARTER, prop, v, buf,
1123 	    max_scf_fmri_sz);
1124 	if (sz > max_scf_fmri_sz) {
1125 		uu_warn(gettext("\"%s\" is misconfigured (the value of "
1126 		    "\"%s/%s\" is too long).\n"), fmri, SCF_PG_GENERAL,
1127 		    SCF_PROPERTY_RESTARTER);
1128 		ret = 0;
1129 		goto out;
1130 	} else if (sz >= 0) {
1131 		switch (enable_fmri_rec(buf, temp)) {
1132 		case 0:
1133 			break;
1134 
1135 		case EINVAL:
1136 			uu_warn(gettext("Restarter FMRI for \"%s\" is "
1137 			    "invalid.\n"), fmri);
1138 			break;
1139 
1140 		case E2BIG:
1141 			uu_warn(gettext("Restarter FMRI for \"%s\" identifies "
1142 			    "a service with multiple instances.\n"), fmri);
1143 			break;
1144 
1145 		case ELOOP:
1146 			ret = ELOOP;
1147 			goto out;
1148 
1149 		default:
1150 			assert(0);
1151 			abort();
1152 		}
1153 	} else if (sz < 0) {
1154 		switch (-sz) {
1155 		case ENOENT:
1156 			break;
1157 
1158 		case E2BIG:
1159 			uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" "
1160 			    "property is not single-valued).\n"), fmri,
1161 			    SCF_PG_GENERAL, SCF_PROPERTY_RESTARTER);
1162 			ret = 0;
1163 			goto out;
1164 
1165 		case EINVAL:
1166 			uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" "
1167 			    "property is not of astring type).\n"), fmri,
1168 			    SCF_PG_GENERAL, SCF_PROPERTY_RESTARTER);
1169 			ret = 0;
1170 			goto out;
1171 
1172 		default:
1173 			assert(0);
1174 			abort();
1175 		}
1176 	}
1177 
1178 	if (scf_iter_instance_pgs_typed_composed(pg_iter, inst, snap,
1179 	    SCF_GROUP_DEPENDENCY) == -1)
1180 		scfdie();
1181 
1182 	while (scf_iter_next_pg(pg_iter, pg) > 0) {
1183 		len = scf_pg_get_name(pg, pgname, name_sz);
1184 		if (len < 0)
1185 			scfdie();
1186 		assert(len < name_sz);
1187 
1188 		if (dep_get_astring(fmri, pgname, pg, SCF_PROPERTY_TYPE, prop,
1189 		    v, buf, max_scf_fmri_sz) < 0)
1190 			continue;
1191 
1192 		if (strcmp(buf, "service") != 0)
1193 			continue;
1194 
1195 		if (dep_get_astring(fmri, pgname, pg, SCF_PROPERTY_GROUPING,
1196 		    prop, v, buf, max_scf_fmri_sz) < 0)
1197 			continue;
1198 
1199 		if (strcmp(buf, SCF_DEP_EXCLUDE_ALL) == 0 ||
1200 		    strcmp(buf, SCF_DEP_OPTIONAL_ALL) == 0)
1201 			continue;
1202 
1203 		if (strcmp(buf, SCF_DEP_REQUIRE_ALL) != 0 &&
1204 		    strcmp(buf, SCF_DEP_REQUIRE_ANY) != 0) {
1205 			uu_warn(gettext("Dependency \"%s\" of \"%s\" has "
1206 			    "unknown type \"%s\".\n"), pgname, fmri, buf);
1207 			continue;
1208 		}
1209 
1210 		if (scf_pg_get_property(pg, SCF_PROPERTY_ENTITIES, prop) ==
1211 		    -1) {
1212 			if (scf_error() != SCF_ERROR_NOT_FOUND)
1213 				scfdie();
1214 
1215 			uu_warn(gettext("\"%s\" is misconfigured (\"%s\" "
1216 			    "dependency lacks \"%s\" property.)\n"), fmri,
1217 			    pgname, SCF_PROPERTY_ENTITIES);
1218 			continue;
1219 		}
1220 
1221 		if (scf_property_type(prop, &ty) != SCF_SUCCESS)
1222 			scfdie();
1223 
1224 		if (ty != SCF_TYPE_FMRI) {
1225 			uu_warn(gettext("\"%s\" is misconfigured (property "
1226 			    "\"%s/%s\" is not of fmri type).\n"), fmri, pgname,
1227 			    SCF_PROPERTY_ENTITIES);
1228 			continue;
1229 		}
1230 
1231 		if (scf_iter_property_values(val_iter, prop) == -1)
1232 			scfdie();
1233 
1234 		if (strcmp(buf, SCF_DEP_REQUIRE_ANY) == 0) {
1235 			if (multiple_instances(val_iter, v, buf)) {
1236 				(void) printf(gettext("%s requires one of:\n"),
1237 				    fmri);
1238 
1239 				if (scf_iter_property_values(val_iter, prop) !=
1240 				    0)
1241 					scfdie();
1242 
1243 				for (;;) {
1244 					int r;
1245 
1246 					r = scf_iter_next_value(val_iter, v);
1247 					if (r == 0)
1248 						break;
1249 					if (r != 1)
1250 						scfdie();
1251 
1252 					if (scf_value_get_astring(v, buf,
1253 					    max_scf_fmri_sz) < 0)
1254 						scfdie();
1255 
1256 					(void) fputs("  ", stdout);
1257 					(void) puts(buf);
1258 				}
1259 
1260 				continue;
1261 			}
1262 
1263 			/*
1264 			 * Since there's only one instance, we can enable it.
1265 			 * Reset val_iter and continue.
1266 			 */
1267 			if (scf_iter_property_values(val_iter, prop) != 0)
1268 				scfdie();
1269 		}
1270 
1271 		for (;;) {
1272 			ret = scf_iter_next_value(val_iter, v);
1273 			if (ret == 0)
1274 				break;
1275 			if (ret != 1)
1276 				scfdie();
1277 
1278 			if (scf_value_get_astring(v, buf, max_scf_fmri_sz) ==
1279 			    -1)
1280 				scfdie();
1281 
1282 			switch (enable_fmri_rec(buf, temp)) {
1283 			case 0:
1284 				break;
1285 
1286 			case EINVAL:
1287 				uu_warn(gettext("\"%s\" dependency of \"%s\" "
1288 				    "has invalid FMRI \"%s\".\n"), pgname,
1289 				    fmri, buf);
1290 				break;
1291 
1292 			case E2BIG:
1293 				uu_warn(gettext("%s depends on %s, which has "
1294 				    "multiple instances.\n"), fmri, buf);
1295 				break;
1296 
1297 			case ELOOP:
1298 				ret = ELOOP;
1299 				goto out;
1300 
1301 			default:
1302 				assert(0);
1303 				abort();
1304 			}
1305 		}
1306 	}
1307 
1308 	ret = 0;
1309 
1310 out:
1311 	he->active = B_FALSE;
1312 
1313 	free(buf);
1314 	free(pgname);
1315 
1316 	(void) scf_value_destroy(v);
1317 	scf_property_destroy(prop);
1318 	scf_pg_destroy(pg);
1319 	scf_snapshot_destroy(snap);
1320 	scf_iter_destroy(pg_iter);
1321 	scf_iter_destroy(val_iter);
1322 
1323 	return (ret);
1324 }
1325 
1326 /*
1327  * fmri here is only used for verbose messages.
1328  */
1329 static void
1330 set_inst_action(const char *fmri, const scf_instance_t *inst,
1331     const char *action)
1332 {
1333 	scf_transaction_t *tx;
1334 	scf_transaction_entry_t *ent;
1335 	scf_propertygroup_t *pg;
1336 	scf_property_t *prop;
1337 	scf_value_t *v;
1338 	int ret;
1339 	int64_t t;
1340 	hrtime_t timestamp;
1341 
1342 	const char * const scf_pg_restarter_actions = SCF_PG_RESTARTER_ACTIONS;
1343 
1344 	if ((pg = scf_pg_create(h)) == NULL ||
1345 	    (prop = scf_property_create(h)) == NULL ||
1346 	    (v = scf_value_create(h)) == NULL ||
1347 	    (tx = scf_transaction_create(h)) == NULL ||
1348 	    (ent = scf_entry_create(h)) == NULL)
1349 		scfdie();
1350 
1351 	if (scf_instance_get_pg(inst, scf_pg_restarter_actions, pg) == -1) {
1352 		if (scf_error() != SCF_ERROR_NOT_FOUND)
1353 			scfdie();
1354 
1355 		/* Try creating the restarter_actions property group. */
1356 		if (scf_instance_add_pg(inst, scf_pg_restarter_actions,
1357 		    SCF_PG_RESTARTER_ACTIONS_TYPE,
1358 		    SCF_PG_RESTARTER_ACTIONS_FLAGS, pg) == -1) {
1359 			switch (scf_error()) {
1360 			case SCF_ERROR_EXISTS:
1361 				/* Someone must have added it. */
1362 				break;
1363 
1364 			case SCF_ERROR_PERMISSION_DENIED:
1365 				if (!verbose)
1366 					uu_warn(emsg_permission_denied, fmri);
1367 				else
1368 					uu_warn(emsg_create_pg_perm_denied,
1369 					    fmri, scf_pg_restarter_actions);
1370 				goto out;
1371 
1372 			default:
1373 				scfdie();
1374 			}
1375 		}
1376 	}
1377 
1378 	/*
1379 	 * If we lose the transaction race and need to retry, there are 2
1380 	 * potential other winners:
1381 	 *	- another process setting actions
1382 	 *	- the restarter marking the action complete
1383 	 * Therefore, re-read the property every time through the loop before
1384 	 * making any decisions based on their values.
1385 	 */
1386 	do {
1387 		timestamp = gethrtime();
1388 
1389 		if (scf_transaction_start(tx, pg) == -1) {
1390 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1391 				scfdie();
1392 
1393 			if (!verbose)
1394 				uu_warn(emsg_permission_denied, fmri);
1395 			else
1396 				uu_warn(emsg_pg_perm_denied, fmri,
1397 				    scf_pg_restarter_actions);
1398 			goto out;
1399 		}
1400 
1401 		if (scf_pg_get_property(pg, action, prop) == -1) {
1402 			if (scf_error() != SCF_ERROR_NOT_FOUND)
1403 				scfdie();
1404 			if (scf_transaction_property_new(tx, ent,
1405 			    action, SCF_TYPE_INTEGER) == -1)
1406 				scfdie();
1407 			goto action_set;
1408 		} else {
1409 			if (scf_transaction_property_change_type(tx, ent,
1410 			    action, SCF_TYPE_INTEGER) == -1)
1411 				scfdie();
1412 		}
1413 
1414 		if (scf_property_get_value(prop, v) == -1) {
1415 			switch (scf_error()) {
1416 			case SCF_ERROR_CONSTRAINT_VIOLATED:
1417 			case SCF_ERROR_NOT_FOUND:
1418 				/* Misconfigured, so set anyway. */
1419 				goto action_set;
1420 
1421 			default:
1422 				scfdie();
1423 			}
1424 		} else {
1425 			if (scf_value_get_integer(v, &t) == -1) {
1426 				assert(scf_error() == SCF_ERROR_TYPE_MISMATCH);
1427 				goto action_set;
1428 			}
1429 			if (t > timestamp)
1430 				break;
1431 		}
1432 
1433 action_set:
1434 		scf_value_set_integer(v, timestamp);
1435 		if (scf_entry_add_value(ent, v) == -1)
1436 			scfdie();
1437 
1438 		ret = scf_transaction_commit(tx);
1439 		if (ret == -1) {
1440 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1441 				scfdie();
1442 
1443 			if (!verbose)
1444 				uu_warn(emsg_permission_denied, fmri);
1445 			else
1446 				uu_warn(emsg_prop_perm_denied, fmri,
1447 				    scf_pg_restarter_actions, action);
1448 			scf_transaction_reset(tx);
1449 			goto out;
1450 		}
1451 
1452 		scf_transaction_reset(tx);
1453 
1454 		if (ret == 0) {
1455 			if (scf_pg_update(pg) == -1)
1456 				scfdie();
1457 		}
1458 	} while (ret == 0);
1459 
1460 	if (verbose)
1461 		(void) printf(gettext("Action %s set for %s.\n"), action, fmri);
1462 
1463 out:
1464 	scf_value_destroy(v);
1465 	scf_entry_destroy(ent);
1466 	scf_transaction_destroy(tx);
1467 	scf_property_destroy(prop);
1468 	scf_pg_destroy(pg);
1469 }
1470 
1471 /*
1472  * Get the state of inst.  state should point to a buffer of
1473  * MAX_SCF_STATE_STRING_SZ bytes.  Returns 0 on success or -1 if
1474  *   no restarter property group
1475  *   no state property
1476  *   state property is misconfigured (wrong type, not single-valued)
1477  *   state value is too long
1478  * In these cases, fmri is used to print a warning.
1479  *
1480  * If pgp is non-NULL, a successful call to inst_get_state will store
1481  * the SCF_PG_RESTARTER property group in *pgp, and the caller will be
1482  * responsible for calling scf_pg_destroy on the property group.
1483  */
1484 int
1485 inst_get_state(scf_instance_t *inst, char *state, const char *fmri,
1486     scf_propertygroup_t **pgp)
1487 {
1488 	scf_propertygroup_t *pg;
1489 	scf_property_t *prop;
1490 	scf_value_t *val;
1491 	int ret = -1;
1492 	ssize_t szret;
1493 
1494 	if ((pg = scf_pg_create(h)) == NULL ||
1495 	    (prop = scf_property_create(h)) == NULL ||
1496 	    (val = scf_value_create(h)) == NULL)
1497 		scfdie();
1498 
1499 	if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) != SCF_SUCCESS) {
1500 		if (scf_error() != SCF_ERROR_NOT_FOUND)
1501 			scfdie();
1502 
1503 		uu_warn(gettext("%s is misconfigured (lacks \"%s\" property "
1504 		    "group).\n"), fmri ? fmri : inst_get_fmri(inst),
1505 		    SCF_PG_RESTARTER);
1506 		goto out;
1507 	}
1508 
1509 	szret = get_astring_prop(pg, SCF_PROPERTY_STATE, prop, val, state,
1510 	    MAX_SCF_STATE_STRING_SZ);
1511 	if (szret < 0) {
1512 		switch (-szret) {
1513 		case ENOENT:
1514 			uu_warn(gettext("%s is misconfigured (\"%s\" property "
1515 			    "group lacks \"%s\" property).\n"),
1516 			    fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER,
1517 			    SCF_PROPERTY_STATE);
1518 			goto out;
1519 
1520 		case E2BIG:
1521 			uu_warn(gettext("%s is misconfigured (\"%s/%s\" "
1522 			    "property is not single-valued).\n"),
1523 			    fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER,
1524 			    SCF_PROPERTY_STATE);
1525 			goto out;
1526 
1527 		case EINVAL:
1528 			uu_warn(gettext("%s is misconfigured (\"%s/%s\" "
1529 			    "property is not of type astring).\n"),
1530 			    fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER,
1531 			    SCF_PROPERTY_STATE);
1532 			goto out;
1533 
1534 		default:
1535 			assert(0);
1536 			abort();
1537 		}
1538 	}
1539 	if (szret >= MAX_SCF_STATE_STRING_SZ) {
1540 		uu_warn(gettext("%s is misconfigured (\"%s/%s\" property value "
1541 		    "is too long).\n"), fmri ? fmri : inst_get_fmri(inst),
1542 		    SCF_PG_RESTARTER, SCF_PROPERTY_STATE);
1543 		goto out;
1544 	}
1545 
1546 	ret = 0;
1547 	if (pgp)
1548 		*pgp = pg;
1549 
1550 out:
1551 	(void) scf_value_destroy(val);
1552 	scf_property_destroy(prop);
1553 	if (ret || pgp == NULL)
1554 		scf_pg_destroy(pg);
1555 	return (ret);
1556 }
1557 
1558 static void
1559 set_astring_prop(const char *fmri, const char *pgname, const char *pgtype,
1560     uint32_t pgflags, const char *propname, const char *str)
1561 {
1562 	scf_instance_t *inst;
1563 	scf_propertygroup_t *pg;
1564 	scf_property_t *prop;
1565 	scf_value_t *val;
1566 	scf_transaction_t *tx;
1567 	scf_transaction_entry_t *txent;
1568 	int ret;
1569 
1570 	inst = scf_instance_create(h);
1571 	if (inst == NULL)
1572 		scfdie();
1573 
1574 	if (get_inst(fmri, inst) != 0)
1575 		return;
1576 
1577 	if ((pg = scf_pg_create(h)) == NULL ||
1578 	    (prop = scf_property_create(h)) == NULL ||
1579 	    (val = scf_value_create(h)) == NULL ||
1580 	    (tx = scf_transaction_create(h)) == NULL ||
1581 	    (txent = scf_entry_create(h)) == NULL)
1582 		scfdie();
1583 
1584 	if (scf_instance_get_pg(inst, pgname, pg) != SCF_SUCCESS) {
1585 		if (scf_error() != SCF_ERROR_NOT_FOUND)
1586 			scfdie();
1587 
1588 		if (scf_instance_add_pg(inst, pgname, pgtype, pgflags, pg) !=
1589 		    SCF_SUCCESS) {
1590 			switch (scf_error()) {
1591 			case SCF_ERROR_EXISTS:
1592 				if (scf_instance_get_pg(inst, pgname, pg) !=
1593 				    SCF_SUCCESS) {
1594 					if (scf_error() != SCF_ERROR_NOT_FOUND)
1595 						scfdie();
1596 
1597 					uu_warn(gettext("Repository write "
1598 					    "contention.\n"));
1599 					goto out;
1600 				}
1601 				break;
1602 
1603 			case SCF_ERROR_PERMISSION_DENIED:
1604 				if (!verbose)
1605 					uu_warn(emsg_permission_denied, fmri);
1606 				else
1607 					uu_warn(emsg_create_pg_perm_denied,
1608 					    fmri, pgname);
1609 				goto out;
1610 
1611 			default:
1612 				scfdie();
1613 			}
1614 		}
1615 	}
1616 
1617 	do {
1618 		if (scf_transaction_start(tx, pg) != SCF_SUCCESS) {
1619 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1620 				scfdie();
1621 
1622 			if (!verbose)
1623 				uu_warn(emsg_permission_denied, fmri);
1624 			else
1625 				uu_warn(emsg_pg_perm_denied, fmri, pgname);
1626 			goto out;
1627 		}
1628 
1629 		if (scf_transaction_property_change_type(tx, txent, propname,
1630 		    SCF_TYPE_ASTRING) != 0) {
1631 			if (scf_error() != SCF_ERROR_NOT_FOUND)
1632 				scfdie();
1633 
1634 			if (scf_transaction_property_new(tx, txent, propname,
1635 			    SCF_TYPE_ASTRING) != 0)
1636 				scfdie();
1637 		}
1638 
1639 		if (scf_value_set_astring(val, str) != SCF_SUCCESS)
1640 			scfdie();
1641 
1642 		if (scf_entry_add_value(txent, val) != SCF_SUCCESS)
1643 			scfdie();
1644 
1645 		ret = scf_transaction_commit(tx);
1646 		if (ret == -1) {
1647 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1648 				scfdie();
1649 
1650 			if (!verbose)
1651 				uu_warn(emsg_permission_denied, fmri);
1652 			else
1653 				uu_warn(emsg_prop_perm_denied, fmri, pgname,
1654 				    propname);
1655 			goto out;
1656 		}
1657 
1658 		if (ret == 0) {
1659 			scf_transaction_reset(tx);
1660 
1661 			if (scf_pg_update(pg) == -1)
1662 				scfdie();
1663 		}
1664 	} while (ret == 0);
1665 
1666 out:
1667 	scf_transaction_destroy(tx);
1668 	scf_entry_destroy(txent);
1669 	scf_value_destroy(val);
1670 	scf_property_destroy(prop);
1671 	scf_pg_destroy(pg);
1672 	scf_instance_destroy(inst);
1673 }
1674 
1675 
1676 /*
1677  * Flags to control enable and disable actions.
1678  */
1679 #define	SET_ENABLED	0x1
1680 #define	SET_TEMPORARY	0x2
1681 #define	SET_RECURSIVE	0x4
1682 
1683 static int
1684 set_fmri_enabled(void *data, scf_walkinfo_t *wip)
1685 {
1686 	int flags = (int)data;
1687 
1688 	assert(wip->inst != NULL);
1689 	assert(wip->pg == NULL);
1690 
1691 	if (flags & SET_RECURSIVE) {
1692 		char *fmri_buf = malloc(max_scf_fmri_sz);
1693 		if (fmri_buf == NULL)
1694 			uu_die(emsg_nomem);
1695 
1696 		visited = calloc(HT_BUCKETS, sizeof (*visited));
1697 		if (visited == NULL)
1698 			uu_die(emsg_nomem);
1699 
1700 		/* scf_walk_fmri() guarantees that fmri isn't too long */
1701 		assert(strlen(wip->fmri) <= max_scf_fmri_sz);
1702 		(void) strlcpy(fmri_buf, wip->fmri, max_scf_fmri_sz);
1703 
1704 		switch (enable_fmri_rec(fmri_buf, (flags & SET_TEMPORARY))) {
1705 		case E2BIG:
1706 			uu_warn(gettext("operation on service %s is ambiguous; "
1707 			    "instance specification needed.\n"), fmri_buf);
1708 			break;
1709 
1710 		case ELOOP:
1711 			uu_warn(gettext("%s: Dependency cycle detected.\n"),
1712 			    fmri_buf);
1713 		}
1714 
1715 		free(visited);
1716 		free(fmri_buf);
1717 
1718 	} else {
1719 		set_inst_enabled(wip->fmri, wip->inst,
1720 		    (flags & SET_TEMPORARY) != 0, (flags & SET_ENABLED) != 0);
1721 	}
1722 
1723 	return (0);
1724 }
1725 
1726 /* ARGSUSED */
1727 static int
1728 wait_fmri_enabled(void *data, scf_walkinfo_t *wip)
1729 {
1730 	scf_propertygroup_t *pg = NULL;
1731 	char state[MAX_SCF_STATE_STRING_SZ];
1732 
1733 	assert(wip->inst != NULL);
1734 	assert(wip->pg == NULL);
1735 
1736 	do {
1737 		if (pg)
1738 			scf_pg_destroy(pg);
1739 		if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) {
1740 			exit_status = EXIT_SVC_FAILURE;
1741 			return (0);
1742 		}
1743 
1744 		if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0 ||
1745 		    strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) {
1746 			/*
1747 			 * We're done.
1748 			 */
1749 			goto out;
1750 		}
1751 
1752 		if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) {
1753 			/*
1754 			 * The service is ill.
1755 			 */
1756 			uu_warn(gettext("Instance \"%s\" is in maintenance"
1757 			    " state.\n"), wip->fmri);
1758 			exit_status = EXIT_SVC_FAILURE;
1759 			goto out;
1760 		}
1761 
1762 		if (!is_enabled(wip->inst)) {
1763 			/*
1764 			 * Someone stepped in and disabled the service.
1765 			 */
1766 			uu_warn(gettext("Instance \"%s\" has been disabled"
1767 			    " by another entity.\n"), wip->fmri);
1768 			exit_status = EXIT_SVC_FAILURE;
1769 			goto out;
1770 		}
1771 
1772 		if (!has_potential(wip->inst, B_FALSE)) {
1773 			/*
1774 			 * Our dependencies aren't met.  We'll never
1775 			 * amount to anything.
1776 			 */
1777 			uu_warn(gettext("Instance \"%s\" has unsatisfied"
1778 			    " dependencies.\n"), wip->fmri);
1779 			/*
1780 			 * EXIT_SVC_FAILURE takes precedence over
1781 			 * EXIT_DEP_FAILURE
1782 			 */
1783 			if (exit_status == 0)
1784 				exit_status = EXIT_DEP_FAILURE;
1785 			goto out;
1786 		}
1787 	} while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0);
1788 	scfdie();
1789 	/* NOTREACHED */
1790 
1791 out:
1792 	scf_pg_destroy(pg);
1793 	return (0);
1794 }
1795 
1796 /* ARGSUSED */
1797 static int
1798 wait_fmri_disabled(void *data, scf_walkinfo_t *wip)
1799 {
1800 	scf_propertygroup_t *pg = NULL;
1801 	char state[MAX_SCF_STATE_STRING_SZ];
1802 
1803 	assert(wip->inst != NULL);
1804 	assert(wip->pg == NULL);
1805 
1806 	do {
1807 		if (pg)
1808 			scf_pg_destroy(pg);
1809 		if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) {
1810 			exit_status = EXIT_SVC_FAILURE;
1811 			return (0);
1812 		}
1813 
1814 		if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0) {
1815 			/*
1816 			 * We're done.
1817 			 */
1818 			goto out;
1819 		}
1820 
1821 		if (is_enabled(wip->inst)) {
1822 			/*
1823 			 * Someone stepped in and enabled the service.
1824 			 */
1825 			uu_warn(gettext("Instance \"%s\" has been enabled"
1826 			    " by another entity.\n"), wip->fmri);
1827 			exit_status = EXIT_SVC_FAILURE;
1828 			goto out;
1829 		}
1830 
1831 		if (!has_potential(wip->inst, B_TRUE)) {
1832 			/*
1833 			 * Our restarter is hopeless.
1834 			 */
1835 			uu_warn(gettext("Restarter for instance \"%s\" is"
1836 			    " unavailable.\n"), wip->fmri);
1837 			/*
1838 			 * EXIT_SVC_FAILURE takes precedence over
1839 			 * EXIT_DEP_FAILURE
1840 			 */
1841 			if (exit_status == 0)
1842 				exit_status = EXIT_DEP_FAILURE;
1843 			goto out;
1844 		}
1845 
1846 	} while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0);
1847 	scfdie();
1848 	/* NOTREACHED */
1849 
1850 out:
1851 	scf_pg_destroy(pg);
1852 	return (0);
1853 }
1854 
1855 /* ARGSUSED */
1856 static int
1857 clear_instance(void *data, scf_walkinfo_t *wip)
1858 {
1859 	char state[MAX_SCF_STATE_STRING_SZ];
1860 
1861 	assert(wip->inst != NULL);
1862 	assert(wip->pg == NULL);
1863 
1864 	if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
1865 		return (0);
1866 
1867 	if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) {
1868 		set_inst_action(wip->fmri, wip->inst, SCF_PROPERTY_MAINT_OFF);
1869 	} else if (strcmp(state, SCF_STATE_STRING_DEGRADED) ==
1870 	    0) {
1871 		set_inst_action(wip->fmri, wip->inst, SCF_PROPERTY_RESTORE);
1872 	} else {
1873 		uu_warn(gettext("Instance \"%s\" is not in a "
1874 		    "maintenance or degraded state.\n"), wip->fmri);
1875 
1876 		exit_status = 1;
1877 	}
1878 
1879 	return (0);
1880 }
1881 
1882 static int
1883 set_fmri_action(void *action, scf_walkinfo_t *wip)
1884 {
1885 	assert(wip->inst != NULL && wip->pg == NULL);
1886 
1887 	set_inst_action(wip->fmri, wip->inst, action);
1888 
1889 	return (0);
1890 }
1891 
1892 /*
1893  * Flags to control 'mark' action.
1894  */
1895 #define	MARK_IMMEDIATE	0x1
1896 #define	MARK_TEMPORARY	0x2
1897 
1898 static int
1899 force_degraded(void *data, scf_walkinfo_t *wip)
1900 {
1901 	int flags = (int)data;
1902 	char state[MAX_SCF_STATE_STRING_SZ];
1903 
1904 	if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0) {
1905 		exit_status = 1;
1906 		return (0);
1907 	}
1908 
1909 	if (strcmp(state, SCF_STATE_STRING_ONLINE) != 0) {
1910 		uu_warn(gettext("Instance \"%s\" is not online.\n"), wip->fmri);
1911 		exit_status = 1;
1912 		return (0);
1913 	}
1914 
1915 	set_inst_action(wip->fmri, wip->inst, (flags & MARK_IMMEDIATE) ?
1916 	    SCF_PROPERTY_DEGRADE_IMMEDIATE : SCF_PROPERTY_DEGRADED);
1917 
1918 	return (0);
1919 }
1920 
1921 static int
1922 force_maintenance(void *data, scf_walkinfo_t *wip)
1923 {
1924 	int flags = (int)data;
1925 	const char *prop;
1926 
1927 	if (flags & MARK_IMMEDIATE) {
1928 		prop = (flags & MARK_TEMPORARY) ?
1929 		    SCF_PROPERTY_MAINT_ON_IMMTEMP :
1930 		    SCF_PROPERTY_MAINT_ON_IMMEDIATE;
1931 	} else {
1932 		prop = (flags & MARK_TEMPORARY) ?
1933 		    SCF_PROPERTY_MAINT_ON_TEMPORARY :
1934 		    SCF_PROPERTY_MAINT_ON;
1935 	}
1936 
1937 	set_inst_action(wip->fmri, wip->inst, prop);
1938 
1939 	return (0);
1940 }
1941 
1942 static void
1943 set_milestone(const char *fmri, boolean_t temporary)
1944 {
1945 	scf_instance_t *inst;
1946 	scf_propertygroup_t *pg;
1947 	int r;
1948 
1949 	if (temporary) {
1950 		set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS_OVR,
1951 		    SCF_PG_OPTIONS_OVR_TYPE, SCF_PG_OPTIONS_OVR_FLAGS,
1952 		    SCF_PROPERTY_MILESTONE, fmri);
1953 		return;
1954 	}
1955 
1956 	if ((inst = scf_instance_create(h)) == NULL ||
1957 	    (pg = scf_pg_create(h)) == NULL)
1958 		scfdie();
1959 
1960 	if (get_inst(SCF_SERVICE_STARTD, inst) != 0) {
1961 		scf_instance_destroy(inst);
1962 		return;
1963 	}
1964 
1965 	/*
1966 	 * Set the persistent milestone before deleting the override so we don't
1967 	 * glitch.
1968 	 */
1969 	set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS,
1970 	    SCF_PG_OPTIONS_TYPE, SCF_PG_OPTIONS_FLAGS, SCF_PROPERTY_MILESTONE,
1971 	    fmri);
1972 
1973 	if (scf_instance_get_pg(inst, SCF_PG_OPTIONS_OVR, pg) == 0) {
1974 		r = delete_prop(pg, SCF_PROPERTY_MILESTONE);
1975 		switch (r) {
1976 		case 0:
1977 			break;
1978 
1979 		case ECANCELED:
1980 			uu_warn(emsg_no_service, fmri);
1981 			exit_status = 1;
1982 			goto out;
1983 
1984 		case EPERM:
1985 			uu_warn(gettext("Could not delete %s/%s property of "
1986 			    "%s: permission denied.\n"), SCF_PG_OPTIONS_OVR,
1987 			    SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD);
1988 			exit_status = 1;
1989 			goto out;
1990 
1991 		case EACCES:
1992 			uu_warn(gettext("Could not delete %s/%s property of "
1993 			    "%s: access denied.\n"), SCF_PG_OPTIONS_OVR,
1994 			    SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD);
1995 			exit_status = 1;
1996 			goto out;
1997 
1998 		case EROFS:
1999 			uu_warn(gettext("Could not delete %s/%s property of "
2000 			    "%s: backend read-only.\n"), SCF_PG_OPTIONS_OVR,
2001 			    SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD);
2002 			exit_status = 1;
2003 			goto out;
2004 
2005 		default:
2006 			bad_error("delete_prop", r);
2007 		}
2008 	} else {
2009 		switch (scf_error()) {
2010 		case SCF_ERROR_NOT_FOUND:
2011 			break;
2012 
2013 		case SCF_ERROR_DELETED:
2014 			uu_warn(emsg_no_service, fmri);
2015 			exit_status = 1;
2016 			goto out;
2017 
2018 		case SCF_ERROR_CONNECTION_BROKEN:
2019 		case SCF_ERROR_HANDLE_MISMATCH:
2020 		case SCF_ERROR_NOT_BOUND:
2021 		case SCF_ERROR_INVALID_ARGUMENT:
2022 		case SCF_ERROR_NOT_SET:
2023 		default:
2024 			scfdie();
2025 		}
2026 	}
2027 
2028 out:
2029 	scf_pg_destroy(pg);
2030 	scf_instance_destroy(inst);
2031 }
2032 
2033 static char const *milestones[] = {
2034 	SCF_MILESTONE_SINGLE_USER,
2035 	SCF_MILESTONE_MULTI_USER,
2036 	SCF_MILESTONE_MULTI_USER_SERVER,
2037 	NULL
2038 };
2039 
2040 static void
2041 usage_milestone(void)
2042 {
2043 	const char **ms;
2044 
2045 	(void) fprintf(stderr, gettext(
2046 	"Usage: svcadm milestone [-d] <milestone>\n\n"
2047 	"\t-d\tmake the specified milestone the default for system boot\n\n"
2048 	"\tMilestones can be specified using an FMRI or abbreviation.\n"
2049 	"\tThe major milestones are as follows:\n\n"
2050 	"\tall\n"
2051 	"\tnone\n"));
2052 
2053 	for (ms = milestones; *ms != NULL; ms++)
2054 		(void) fprintf(stderr, "\t%s\n", *ms);
2055 
2056 	exit(UU_EXIT_USAGE);
2057 }
2058 
2059 static const char *
2060 validate_milestone(const char *milestone)
2061 {
2062 	const char **ms;
2063 	const char *tmp;
2064 	size_t len;
2065 
2066 	if (strcmp(milestone, "all") == 0)
2067 		return (milestone);
2068 
2069 	if (strcmp(milestone, "none") == 0)
2070 		return (milestone);
2071 
2072 	/*
2073 	 * Determine if this is a full or partial milestone
2074 	 */
2075 	for (ms = milestones; *ms != NULL; ms++) {
2076 		if ((tmp = strstr(*ms, milestone)) != NULL) {
2077 			len = strlen(milestone);
2078 
2079 			/*
2080 			 * The beginning of the string must align with the start
2081 			 * of a milestone fmri, or on the boundary between
2082 			 * elements.  The end of the string must align with the
2083 			 * end of the milestone, or at the instance boundary.
2084 			 */
2085 			if ((tmp == *ms || tmp[-1] == '/') &&
2086 			    (tmp[len] == '\0' || tmp[len] == ':'))
2087 				return (*ms);
2088 		}
2089 	}
2090 
2091 	(void) fprintf(stderr,
2092 	    gettext("\"%s\" is not a valid major milestone.\n"), milestone);
2093 
2094 	usage_milestone();
2095 	/* NOTREACHED */
2096 }
2097 
2098 /*ARGSUSED*/
2099 static void
2100 quiet(const char *fmt, ...)
2101 {
2102 	/* Do nothing */
2103 }
2104 
2105 int
2106 main(int argc, char *argv[])
2107 {
2108 	int o;
2109 	int err;
2110 	int sw_back;
2111 
2112 	(void) setlocale(LC_ALL, "");
2113 	(void) textdomain(TEXT_DOMAIN);
2114 
2115 	(void) uu_setpname(argv[0]);
2116 
2117 	if (argc < 2)
2118 		usage();
2119 
2120 	max_scf_fmri_sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
2121 	if (max_scf_fmri_sz < 0)
2122 		scfdie();
2123 	++max_scf_fmri_sz;
2124 
2125 	scratch_fmri = malloc(max_scf_fmri_sz);
2126 	if (scratch_fmri == NULL)
2127 		uu_die(emsg_nomem);
2128 
2129 	h = scf_handle_create(SCF_VERSION);
2130 	if (h == NULL)
2131 		scfdie();
2132 
2133 	if (scf_handle_bind(h) == -1)
2134 		uu_die(gettext("Couldn't bind to svc.configd.\n"));
2135 
2136 	while ((o = getopt(argc, argv, "v")) != -1) {
2137 		if (o == 'v')
2138 			verbose = 1;
2139 		else
2140 			usage();
2141 	}
2142 
2143 	if (optind >= argc)
2144 		usage();
2145 
2146 	emsg_permission_denied = gettext("%s: Permission denied.\n");
2147 	emsg_nomem = gettext("Out of memory.\n");
2148 	emsg_create_pg_perm_denied = gettext("%s: Couldn't create \"%s\" "
2149 	    "property group (permission denied).\n");
2150 	emsg_pg_perm_denied = gettext("%s: Couldn't modify \"%s\" property "
2151 	    "group (permission denied).\n");
2152 	emsg_prop_perm_denied = gettext("%s: Couldn't modify \"%s/%s\" "
2153 	    "property (permission denied).\n");
2154 	emsg_no_service = gettext("No such service \"%s\".\n");
2155 
2156 	if (strcmp(argv[optind], "enable") == 0) {
2157 		int flags = SET_ENABLED;
2158 		int wait = 0;
2159 		int error = 0;
2160 
2161 		++optind;
2162 
2163 		while ((o = getopt(argc, argv, "rst")) != -1) {
2164 			if (o == 'r')
2165 				flags |= SET_RECURSIVE;
2166 			else if (o == 't')
2167 				flags |= SET_TEMPORARY;
2168 			else if (o == 's')
2169 				wait = 1;
2170 			else if (o == '?')
2171 				usage();
2172 			else {
2173 				assert(0);
2174 				abort();
2175 			}
2176 		}
2177 		argc -= optind;
2178 		argv += optind;
2179 
2180 		if (argc <= 0)
2181 			usage();
2182 
2183 		/*
2184 		 * We want to continue with -s processing if we had
2185 		 * invalid options, but not if an enable failed.  We
2186 		 * squelch output the second time we walk fmris; we saw
2187 		 * the errors the first time.
2188 		 */
2189 		if ((err = scf_walk_fmri(h, argc, argv, 0, set_fmri_enabled,
2190 		    (void *)flags, &error, uu_warn)) != 0) {
2191 
2192 			uu_warn(gettext("failed to iterate over "
2193 			    "instances: %s\n"), scf_strerror(err));
2194 			exit_status = UU_EXIT_FATAL;
2195 
2196 		} else if (wait && exit_status == 0 &&
2197 		    (err = scf_walk_fmri(h, argc, argv, 0, wait_fmri_enabled,
2198 		    (void *)flags, &error, quiet)) != 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 
2205 		if (error > 0)
2206 			exit_status = error;
2207 
2208 	} else if (strcmp(argv[optind], "disable") == 0) {
2209 		int flags = 0;
2210 		int wait = 0;
2211 		int error = 0;
2212 
2213 		++optind;
2214 
2215 		while ((o = getopt(argc, argv, "st")) != -1) {
2216 			if (o == 't')
2217 				flags |= SET_TEMPORARY;
2218 			else if (o == 's')
2219 				wait = 1;
2220 			else if (o == '?')
2221 				usage();
2222 			else {
2223 				assert(0);
2224 				abort();
2225 			}
2226 		}
2227 		argc -= optind;
2228 		argv += optind;
2229 
2230 		if (argc <= 0)
2231 			usage();
2232 
2233 		/*
2234 		 * We want to continue with -s processing if we had
2235 		 * invalid options, but not if a disable failed.  We
2236 		 * squelch output the second time we walk fmris; we saw
2237 		 * the errors the first time.
2238 		 */
2239 		if ((err = scf_walk_fmri(h, argc, argv, 0, set_fmri_enabled,
2240 		    (void *)flags, &exit_status, uu_warn)) != 0) {
2241 
2242 			uu_warn(gettext("failed to iterate over "
2243 			    "instances: %s\n"), scf_strerror(err));
2244 			exit_status = UU_EXIT_FATAL;
2245 
2246 		} else if (wait && exit_status == 0 &&
2247 		    (err = scf_walk_fmri(h, argc, argv, 0, wait_fmri_disabled,
2248 		    (void *)flags, &error, quiet)) != 0) {
2249 
2250 			uu_warn(gettext("failed to iterate over "
2251 			    "instances: %s\n"), scf_strerror(err));
2252 			exit_status = UU_EXIT_FATAL;
2253 		}
2254 
2255 		if (error > 0)
2256 			exit_status = error;
2257 
2258 	} else if (strcmp(argv[optind], "restart") == 0) {
2259 		++optind;
2260 
2261 		if (optind >= argc)
2262 			usage();
2263 
2264 		if ((err = scf_walk_fmri(h, argc - optind, argv + optind, 0,
2265 		    set_fmri_action, (void *)SCF_PROPERTY_RESTART,
2266 		    &exit_status, uu_warn)) != 0) {
2267 			uu_warn(gettext("failed to iterate over "
2268 			    "instances: %s\n"), scf_strerror(err));
2269 			exit_status = UU_EXIT_FATAL;
2270 		}
2271 
2272 	} else if (strcmp(argv[optind], "refresh") == 0) {
2273 		++optind;
2274 
2275 		if (optind >= argc)
2276 			usage();
2277 
2278 		if ((err = scf_walk_fmri(h, argc - optind, argv + optind, 0,
2279 		    set_fmri_action, (void *)SCF_PROPERTY_REFRESH,
2280 		    &exit_status, uu_warn)) != 0) {
2281 			uu_warn(gettext("failed to iterate over "
2282 			    "instances: %s\n"), scf_strerror(scf_error()));
2283 			exit_status = UU_EXIT_FATAL;
2284 		}
2285 
2286 	} else if (strcmp(argv[optind], "mark") == 0) {
2287 		int flags = 0;
2288 		scf_walk_callback callback;
2289 
2290 		++optind;
2291 
2292 		while ((o = getopt(argc, argv, "It")) != -1) {
2293 			if (o == 'I')
2294 				flags |= MARK_IMMEDIATE;
2295 			else if (o == 't')
2296 				flags |= MARK_TEMPORARY;
2297 			else if (o == '?')
2298 				usage();
2299 			else {
2300 				assert(0);
2301 				abort();
2302 			}
2303 		}
2304 
2305 		if (argc - optind < 2)
2306 			usage();
2307 
2308 		if (strcmp(argv[optind], "degraded") == 0) {
2309 			if (flags & MARK_TEMPORARY)
2310 				uu_xdie(UU_EXIT_USAGE, gettext("-t may not be "
2311 				    "used with degraded.\n"));
2312 			callback = force_degraded;
2313 
2314 		} else if (strcmp(argv[optind], "maintenance") == 0) {
2315 			callback = force_maintenance;
2316 		} else {
2317 			usage();
2318 		}
2319 
2320 		if ((err = scf_walk_fmri(h, argc - optind - 1,
2321 		    argv + optind + 1, 0, callback, NULL, &exit_status,
2322 		    uu_warn)) != 0) {
2323 			uu_warn(gettext("failed to iterate over "
2324 			    "instances: %s\n"),
2325 			    scf_strerror(err));
2326 			exit_status = UU_EXIT_FATAL;
2327 		}
2328 
2329 	} else if (strcmp(argv[optind], "clear") == 0) {
2330 		++optind;
2331 
2332 		if (optind >= argc)
2333 			usage();
2334 
2335 		if ((err = scf_walk_fmri(h, argc - optind, argv + optind, 0,
2336 		    clear_instance, NULL, &exit_status, uu_warn)) != 0) {
2337 			uu_warn(gettext("failed to iterate over "
2338 			    "instances: %s\n"), scf_strerror(err));
2339 			exit_status = UU_EXIT_FATAL;
2340 		}
2341 
2342 	} else if (strcmp(argv[optind], "milestone") == 0) {
2343 		boolean_t temporary = B_TRUE;
2344 		const char *milestone;
2345 
2346 		++optind;
2347 
2348 		while ((o = getopt(argc, argv, "d")) != -1) {
2349 			if (o == 'd')
2350 				temporary = B_FALSE;
2351 			else if (o == '?')
2352 				usage_milestone();
2353 			else {
2354 				assert(0);
2355 				abort();
2356 			}
2357 		}
2358 
2359 		if (optind >= argc)
2360 			usage_milestone();
2361 
2362 		milestone = validate_milestone(argv[optind]);
2363 
2364 		set_milestone(milestone, temporary);
2365 	} else if (strcmp(argv[optind], "_smf_backup") == 0) {
2366 		const char *reason = NULL;
2367 
2368 		++optind;
2369 
2370 		if (optind != argc - 1)
2371 			usage();
2372 
2373 		if ((err = _scf_request_backup(h, argv[optind])) !=
2374 		    SCF_SUCCESS) {
2375 			switch (scf_error()) {
2376 			case SCF_ERROR_NOT_BOUND:
2377 			case SCF_ERROR_CONNECTION_BROKEN:
2378 			case SCF_ERROR_BACKEND_READONLY:
2379 				scfdie();
2380 				break;
2381 
2382 			case SCF_ERROR_PERMISSION_DENIED:
2383 			case SCF_ERROR_INVALID_ARGUMENT:
2384 				reason = scf_strerror(scf_error());
2385 				break;
2386 
2387 			case SCF_ERROR_INTERNAL:
2388 				reason =
2389 				    "unknown error (see console for details)";
2390 				break;
2391 			}
2392 
2393 			uu_warn("failed to backup repository: %s\n", reason);
2394 			exit_status = UU_EXIT_FATAL;
2395 		}
2396 	} else if (strcmp(argv[optind], "_smf_repository_switch") == 0) {
2397 		const char *reason = NULL;
2398 
2399 		++optind;
2400 
2401 		/*
2402 		 * Check argument and setup scf_switch structure
2403 		 */
2404 		if (optind != argc - 1)
2405 			exit(1);
2406 
2407 		if (strcmp(argv[optind], "fast") == 0) {
2408 			sw_back = 0;
2409 		} else if (strcmp(argv[optind], "perm") == 0) {
2410 			sw_back = 1;
2411 		} else {
2412 			exit(UU_EXIT_USAGE);
2413 		}
2414 
2415 		/*
2416 		 * Call into switch primitive
2417 		 */
2418 		if ((err = _scf_repository_switch(h, sw_back)) !=
2419 		    SCF_SUCCESS) {
2420 			/*
2421 			 * Retrieve per thread SCF error code
2422 			 */
2423 			switch (scf_error()) {
2424 			case SCF_ERROR_NOT_BOUND:
2425 				abort();
2426 				/* NOTREACHED */
2427 
2428 			case SCF_ERROR_CONNECTION_BROKEN:
2429 			case SCF_ERROR_BACKEND_READONLY:
2430 				scfdie();
2431 				/* NOTREACHED */
2432 
2433 			case SCF_ERROR_PERMISSION_DENIED:
2434 			case SCF_ERROR_INVALID_ARGUMENT:
2435 				reason = scf_strerror(scf_error());
2436 				break;
2437 
2438 			case SCF_ERROR_INTERNAL:
2439 				reason = "File operation error: (see console)";
2440 				break;
2441 
2442 			default:
2443 				abort();
2444 				/* NOTREACHED */
2445 			}
2446 
2447 			uu_warn("failed to switch repository: %s\n", reason);
2448 			exit_status = UU_EXIT_FATAL;
2449 		}
2450 	} else {
2451 		usage();
2452 	}
2453 
2454 	if (scf_handle_unbind(h) == -1)
2455 		scfdie();
2456 	scf_handle_destroy(h);
2457 
2458 	return (exit_status);
2459 }
2460