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