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