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