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