xref: /illumos-gate/usr/src/cmd/svc/svcadm/svcadm.c (revision a97d4a16317db7306cf947e1fccba57d895df625)
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 		exit_status = 1;
1343 		goto out;
1344 	}
1345 
1346 	if (scf_instance_get_pg(inst, scf_pg_restarter_actions, pg) == -1) {
1347 		if (scf_error() != SCF_ERROR_NOT_FOUND)
1348 			scfdie();
1349 
1350 		/* Try creating the restarter_actions property group. */
1351 		if (scf_instance_add_pg(inst, scf_pg_restarter_actions,
1352 		    SCF_PG_RESTARTER_ACTIONS_TYPE,
1353 		    SCF_PG_RESTARTER_ACTIONS_FLAGS, pg) == -1) {
1354 			switch (scf_error()) {
1355 			case SCF_ERROR_EXISTS:
1356 				/* Someone must have added it. */
1357 				break;
1358 
1359 			case SCF_ERROR_PERMISSION_DENIED:
1360 				if (!verbose)
1361 					uu_warn(emsg_permission_denied, fmri);
1362 				else
1363 					uu_warn(emsg_create_pg_perm_denied,
1364 					    fmri, scf_pg_restarter_actions);
1365 				goto out;
1366 
1367 			default:
1368 				scfdie();
1369 			}
1370 		}
1371 	}
1372 
1373 	/*
1374 	 * If we lose the transaction race and need to retry, there are 2
1375 	 * potential other winners:
1376 	 *	- another process setting actions
1377 	 *	- the restarter marking the action complete
1378 	 * Therefore, re-read the property every time through the loop before
1379 	 * making any decisions based on their values.
1380 	 */
1381 	do {
1382 		timestamp = gethrtime();
1383 
1384 		if (scf_transaction_start(tx, pg) == -1) {
1385 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1386 				scfdie();
1387 
1388 			if (!verbose)
1389 				uu_warn(emsg_permission_denied, fmri);
1390 			else
1391 				uu_warn(emsg_pg_perm_denied, fmri,
1392 				    scf_pg_restarter_actions);
1393 			goto out;
1394 		}
1395 
1396 		if (scf_pg_get_property(pg, action, prop) == -1) {
1397 			if (scf_error() != SCF_ERROR_NOT_FOUND)
1398 				scfdie();
1399 			if (scf_transaction_property_new(tx, ent,
1400 			    action, SCF_TYPE_INTEGER) == -1)
1401 				scfdie();
1402 			goto action_set;
1403 		} else {
1404 			if (scf_transaction_property_change_type(tx, ent,
1405 			    action, SCF_TYPE_INTEGER) == -1)
1406 				scfdie();
1407 		}
1408 
1409 		if (scf_property_get_value(prop, v) == -1) {
1410 			switch (scf_error()) {
1411 			case SCF_ERROR_CONSTRAINT_VIOLATED:
1412 			case SCF_ERROR_NOT_FOUND:
1413 				/* Misconfigured, so set anyway. */
1414 				goto action_set;
1415 
1416 			default:
1417 				scfdie();
1418 			}
1419 		} else {
1420 			if (scf_value_get_integer(v, &t) == -1) {
1421 				assert(scf_error() == SCF_ERROR_TYPE_MISMATCH);
1422 				goto action_set;
1423 			}
1424 			if (t > timestamp)
1425 				break;
1426 		}
1427 
1428 action_set:
1429 		scf_value_set_integer(v, timestamp);
1430 		if (scf_entry_add_value(ent, v) == -1)
1431 			scfdie();
1432 
1433 		ret = scf_transaction_commit(tx);
1434 		if (ret == -1) {
1435 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1436 				scfdie();
1437 
1438 			if (!verbose)
1439 				uu_warn(emsg_permission_denied, fmri);
1440 			else
1441 				uu_warn(emsg_prop_perm_denied, fmri,
1442 				    scf_pg_restarter_actions, action);
1443 			scf_transaction_reset(tx);
1444 			goto out;
1445 		}
1446 
1447 		scf_transaction_reset(tx);
1448 
1449 		if (ret == 0) {
1450 			if (scf_pg_update(pg) == -1)
1451 				scfdie();
1452 		}
1453 	} while (ret == 0);
1454 
1455 	if (verbose)
1456 		(void) printf(gettext("Action %s set for %s.\n"), action, fmri);
1457 
1458 out:
1459 	scf_value_destroy(v);
1460 	scf_entry_destroy(ent);
1461 	scf_transaction_destroy(tx);
1462 	scf_property_destroy(prop);
1463 	scf_pg_destroy(pg);
1464 }
1465 
1466 /*
1467  * Get the state of inst.  state should point to a buffer of
1468  * MAX_SCF_STATE_STRING_SZ bytes.  Returns 0 on success or -1 if
1469  *   no restarter property group
1470  *   no state property
1471  *   state property is misconfigured (wrong type, not single-valued)
1472  *   state value is too long
1473  * In these cases, fmri is used to print a warning.
1474  *
1475  * If pgp is non-NULL, a successful call to inst_get_state will store
1476  * the SCF_PG_RESTARTER property group in *pgp, and the caller will be
1477  * responsible for calling scf_pg_destroy on the property group.
1478  */
1479 int
1480 inst_get_state(scf_instance_t *inst, char *state, const char *fmri,
1481     scf_propertygroup_t **pgp)
1482 {
1483 	scf_propertygroup_t *pg;
1484 	scf_property_t *prop;
1485 	scf_value_t *val;
1486 	int ret = -1;
1487 	ssize_t szret;
1488 
1489 	if ((pg = scf_pg_create(h)) == NULL ||
1490 	    (prop = scf_property_create(h)) == NULL ||
1491 	    (val = scf_value_create(h)) == NULL)
1492 		scfdie();
1493 
1494 	if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) != SCF_SUCCESS) {
1495 		if (scf_error() != SCF_ERROR_NOT_FOUND)
1496 			scfdie();
1497 
1498 		uu_warn(gettext("%s is misconfigured (lacks \"%s\" property "
1499 		    "group).\n"), fmri ? fmri : inst_get_fmri(inst),
1500 		    SCF_PG_RESTARTER);
1501 		goto out;
1502 	}
1503 
1504 	szret = get_astring_prop(pg, SCF_PROPERTY_STATE, prop, val, state,
1505 	    MAX_SCF_STATE_STRING_SZ);
1506 	if (szret < 0) {
1507 		switch (-szret) {
1508 		case ENOENT:
1509 			uu_warn(gettext("%s is misconfigured (\"%s\" property "
1510 			    "group lacks \"%s\" property).\n"),
1511 			    fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER,
1512 			    SCF_PROPERTY_STATE);
1513 			goto out;
1514 
1515 		case E2BIG:
1516 			uu_warn(gettext("%s is misconfigured (\"%s/%s\" "
1517 			    "property is not single-valued).\n"),
1518 			    fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER,
1519 			    SCF_PROPERTY_STATE);
1520 			goto out;
1521 
1522 		case EINVAL:
1523 			uu_warn(gettext("%s is misconfigured (\"%s/%s\" "
1524 			    "property is not of type astring).\n"),
1525 			    fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER,
1526 			    SCF_PROPERTY_STATE);
1527 			goto out;
1528 
1529 		default:
1530 			assert(0);
1531 			abort();
1532 		}
1533 	}
1534 	if (szret >= MAX_SCF_STATE_STRING_SZ) {
1535 		uu_warn(gettext("%s is misconfigured (\"%s/%s\" property value "
1536 		    "is too long).\n"), fmri ? fmri : inst_get_fmri(inst),
1537 		    SCF_PG_RESTARTER, SCF_PROPERTY_STATE);
1538 		goto out;
1539 	}
1540 
1541 	ret = 0;
1542 	if (pgp)
1543 		*pgp = pg;
1544 
1545 out:
1546 	(void) scf_value_destroy(val);
1547 	scf_property_destroy(prop);
1548 	if (ret || pgp == NULL)
1549 		scf_pg_destroy(pg);
1550 	return (ret);
1551 }
1552 
1553 static void
1554 set_astring_prop(const char *fmri, const char *pgname, const char *pgtype,
1555     uint32_t pgflags, const char *propname, const char *str)
1556 {
1557 	scf_instance_t *inst;
1558 	scf_propertygroup_t *pg;
1559 	scf_property_t *prop;
1560 	scf_value_t *val;
1561 	scf_transaction_t *tx;
1562 	scf_transaction_entry_t *txent;
1563 	int ret;
1564 
1565 	inst = scf_instance_create(h);
1566 	if (inst == NULL)
1567 		scfdie();
1568 
1569 	if (get_inst(fmri, inst) != 0)
1570 		return;
1571 
1572 	if ((pg = scf_pg_create(h)) == NULL ||
1573 	    (prop = scf_property_create(h)) == NULL ||
1574 	    (val = scf_value_create(h)) == NULL ||
1575 	    (tx = scf_transaction_create(h)) == NULL ||
1576 	    (txent = scf_entry_create(h)) == NULL)
1577 		scfdie();
1578 
1579 	if (scf_instance_get_pg(inst, pgname, pg) != SCF_SUCCESS) {
1580 		if (scf_error() != SCF_ERROR_NOT_FOUND)
1581 			scfdie();
1582 
1583 		if (scf_instance_add_pg(inst, pgname, pgtype, pgflags, pg) !=
1584 		    SCF_SUCCESS) {
1585 			switch (scf_error()) {
1586 			case SCF_ERROR_EXISTS:
1587 				if (scf_instance_get_pg(inst, pgname, pg) !=
1588 				    SCF_SUCCESS) {
1589 					if (scf_error() != SCF_ERROR_NOT_FOUND)
1590 						scfdie();
1591 
1592 					uu_warn(gettext("Repository write "
1593 					    "contention.\n"));
1594 					goto out;
1595 				}
1596 				break;
1597 
1598 			case SCF_ERROR_PERMISSION_DENIED:
1599 				if (!verbose)
1600 					uu_warn(emsg_permission_denied, fmri);
1601 				else
1602 					uu_warn(emsg_create_pg_perm_denied,
1603 					    fmri, pgname);
1604 				goto out;
1605 
1606 			default:
1607 				scfdie();
1608 			}
1609 		}
1610 	}
1611 
1612 	do {
1613 		if (scf_transaction_start(tx, pg) != SCF_SUCCESS) {
1614 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1615 				scfdie();
1616 
1617 			if (!verbose)
1618 				uu_warn(emsg_permission_denied, fmri);
1619 			else
1620 				uu_warn(emsg_pg_perm_denied, fmri, pgname);
1621 			goto out;
1622 		}
1623 
1624 		if (scf_transaction_property_change_type(tx, txent, propname,
1625 		    SCF_TYPE_ASTRING) != 0) {
1626 			if (scf_error() != SCF_ERROR_NOT_FOUND)
1627 				scfdie();
1628 
1629 			if (scf_transaction_property_new(tx, txent, propname,
1630 			    SCF_TYPE_ASTRING) != 0)
1631 				scfdie();
1632 		}
1633 
1634 		if (scf_value_set_astring(val, str) != SCF_SUCCESS)
1635 			scfdie();
1636 
1637 		if (scf_entry_add_value(txent, val) != SCF_SUCCESS)
1638 			scfdie();
1639 
1640 		ret = scf_transaction_commit(tx);
1641 		if (ret == -1) {
1642 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1643 				scfdie();
1644 
1645 			if (!verbose)
1646 				uu_warn(emsg_permission_denied, fmri);
1647 			else
1648 				uu_warn(emsg_prop_perm_denied, fmri, pgname,
1649 				    propname);
1650 			goto out;
1651 		}
1652 
1653 		if (ret == 0) {
1654 			scf_transaction_reset(tx);
1655 
1656 			if (scf_pg_update(pg) == -1)
1657 				scfdie();
1658 		}
1659 	} while (ret == 0);
1660 
1661 out:
1662 	scf_transaction_destroy(tx);
1663 	scf_entry_destroy(txent);
1664 	scf_value_destroy(val);
1665 	scf_property_destroy(prop);
1666 	scf_pg_destroy(pg);
1667 	scf_instance_destroy(inst);
1668 }
1669 
1670 
1671 /*
1672  * Flags to control enable and disable actions.
1673  */
1674 #define	SET_ENABLED	0x1
1675 #define	SET_TEMPORARY	0x2
1676 #define	SET_RECURSIVE	0x4
1677 
1678 static int
1679 set_fmri_enabled(void *data, scf_walkinfo_t *wip)
1680 {
1681 	int flags = (int)data;
1682 
1683 	assert(wip->inst != NULL);
1684 	assert(wip->pg == NULL);
1685 
1686 	if (flags & SET_RECURSIVE) {
1687 		char *fmri_buf = malloc(max_scf_fmri_sz);
1688 		if (fmri_buf == NULL)
1689 			uu_die(emsg_nomem);
1690 
1691 		visited = calloc(HT_BUCKETS, sizeof (*visited));
1692 		if (visited == NULL)
1693 			uu_die(emsg_nomem);
1694 
1695 		/* scf_walk_fmri() guarantees that fmri isn't too long */
1696 		assert(strlen(wip->fmri) <= max_scf_fmri_sz);
1697 		(void) strlcpy(fmri_buf, wip->fmri, max_scf_fmri_sz);
1698 
1699 		switch (enable_fmri_rec(fmri_buf, (flags & SET_TEMPORARY))) {
1700 		case E2BIG:
1701 			uu_warn(gettext("operation on service %s is ambiguous; "
1702 			    "instance specification needed.\n"), fmri_buf);
1703 			break;
1704 
1705 		case ELOOP:
1706 			uu_warn(gettext("%s: Dependency cycle detected.\n"),
1707 			    fmri_buf);
1708 		}
1709 
1710 		free(visited);
1711 		free(fmri_buf);
1712 
1713 	} else {
1714 		set_inst_enabled(wip->fmri, wip->inst,
1715 		    (flags & SET_TEMPORARY) != 0, (flags & SET_ENABLED) != 0);
1716 	}
1717 
1718 	return (0);
1719 }
1720 
1721 /* ARGSUSED */
1722 static int
1723 wait_fmri_enabled(void *data, scf_walkinfo_t *wip)
1724 {
1725 	scf_propertygroup_t *pg = NULL;
1726 	char state[MAX_SCF_STATE_STRING_SZ];
1727 
1728 	assert(wip->inst != NULL);
1729 	assert(wip->pg == NULL);
1730 
1731 	do {
1732 		if (pg)
1733 			scf_pg_destroy(pg);
1734 		if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) {
1735 			exit_status = EXIT_SVC_FAILURE;
1736 			return (0);
1737 		}
1738 
1739 		if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0 ||
1740 		    strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) {
1741 			/*
1742 			 * We're done.
1743 			 */
1744 			goto out;
1745 		}
1746 
1747 		if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) {
1748 			/*
1749 			 * The service is ill.
1750 			 */
1751 			uu_warn(gettext("Instance \"%s\" is in maintenance"
1752 			    " state.\n"), wip->fmri);
1753 			exit_status = EXIT_SVC_FAILURE;
1754 			goto out;
1755 		}
1756 
1757 		if (!is_enabled(wip->inst)) {
1758 			/*
1759 			 * Someone stepped in and disabled the service.
1760 			 */
1761 			uu_warn(gettext("Instance \"%s\" has been disabled"
1762 			    " by another entity.\n"), wip->fmri);
1763 			exit_status = EXIT_SVC_FAILURE;
1764 			goto out;
1765 		}
1766 
1767 		if (!has_potential(wip->inst, B_FALSE)) {
1768 			/*
1769 			 * Our dependencies aren't met.  We'll never
1770 			 * amount to anything.
1771 			 */
1772 			uu_warn(gettext("Instance \"%s\" has unsatisfied"
1773 			    " dependencies.\n"), wip->fmri);
1774 			/*
1775 			 * EXIT_SVC_FAILURE takes precedence over
1776 			 * EXIT_DEP_FAILURE
1777 			 */
1778 			if (exit_status == 0)
1779 				exit_status = EXIT_DEP_FAILURE;
1780 			goto out;
1781 		}
1782 	} while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0);
1783 	scfdie();
1784 	/* NOTREACHED */
1785 
1786 out:
1787 	scf_pg_destroy(pg);
1788 	return (0);
1789 }
1790 
1791 /* ARGSUSED */
1792 static int
1793 wait_fmri_disabled(void *data, scf_walkinfo_t *wip)
1794 {
1795 	scf_propertygroup_t *pg = NULL;
1796 	char state[MAX_SCF_STATE_STRING_SZ];
1797 
1798 	assert(wip->inst != NULL);
1799 	assert(wip->pg == NULL);
1800 
1801 	do {
1802 		if (pg)
1803 			scf_pg_destroy(pg);
1804 		if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) {
1805 			exit_status = EXIT_SVC_FAILURE;
1806 			return (0);
1807 		}
1808 
1809 		if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0) {
1810 			/*
1811 			 * We're done.
1812 			 */
1813 			goto out;
1814 		}
1815 
1816 		if (is_enabled(wip->inst)) {
1817 			/*
1818 			 * Someone stepped in and enabled the service.
1819 			 */
1820 			uu_warn(gettext("Instance \"%s\" has been enabled"
1821 			    " by another entity.\n"), wip->fmri);
1822 			exit_status = EXIT_SVC_FAILURE;
1823 			goto out;
1824 		}
1825 
1826 		if (!has_potential(wip->inst, B_TRUE)) {
1827 			/*
1828 			 * Our restarter is hopeless.
1829 			 */
1830 			uu_warn(gettext("Restarter for instance \"%s\" is"
1831 			    " unavailable.\n"), wip->fmri);
1832 			/*
1833 			 * EXIT_SVC_FAILURE takes precedence over
1834 			 * EXIT_DEP_FAILURE
1835 			 */
1836 			if (exit_status == 0)
1837 				exit_status = EXIT_DEP_FAILURE;
1838 			goto out;
1839 		}
1840 
1841 	} while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0);
1842 	scfdie();
1843 	/* NOTREACHED */
1844 
1845 out:
1846 	scf_pg_destroy(pg);
1847 	return (0);
1848 }
1849 
1850 /* ARGSUSED */
1851 static int
1852 clear_instance(void *data, scf_walkinfo_t *wip)
1853 {
1854 	char state[MAX_SCF_STATE_STRING_SZ];
1855 
1856 	assert(wip->inst != NULL);
1857 	assert(wip->pg == NULL);
1858 
1859 	if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
1860 		return (0);
1861 
1862 	if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) {
1863 		set_inst_action(wip->fmri, wip->inst, SCF_PROPERTY_MAINT_OFF);
1864 	} else if (strcmp(state, SCF_STATE_STRING_DEGRADED) ==
1865 	    0) {
1866 		set_inst_action(wip->fmri, wip->inst, SCF_PROPERTY_RESTORE);
1867 	} else {
1868 		uu_warn(gettext("Instance \"%s\" is not in a "
1869 		    "maintenance or degraded state.\n"), wip->fmri);
1870 
1871 		exit_status = 1;
1872 	}
1873 
1874 	return (0);
1875 }
1876 
1877 static int
1878 set_fmri_action(void *action, scf_walkinfo_t *wip)
1879 {
1880 	assert(wip->inst != NULL && wip->pg == NULL);
1881 
1882 	set_inst_action(wip->fmri, wip->inst, action);
1883 
1884 	return (0);
1885 }
1886 
1887 /*
1888  * Flags to control 'mark' action.
1889  */
1890 #define	MARK_IMMEDIATE	0x1
1891 #define	MARK_TEMPORARY	0x2
1892 
1893 static int
1894 force_degraded(void *data, scf_walkinfo_t *wip)
1895 {
1896 	int flags = (int)data;
1897 	char state[MAX_SCF_STATE_STRING_SZ];
1898 
1899 	if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0) {
1900 		exit_status = 1;
1901 		return (0);
1902 	}
1903 
1904 	if (strcmp(state, SCF_STATE_STRING_ONLINE) != 0) {
1905 		uu_warn(gettext("Instance \"%s\" is not online.\n"), wip->fmri);
1906 		exit_status = 1;
1907 		return (0);
1908 	}
1909 
1910 	set_inst_action(wip->fmri, wip->inst, (flags & MARK_IMMEDIATE) ?
1911 	    SCF_PROPERTY_DEGRADE_IMMEDIATE : SCF_PROPERTY_DEGRADED);
1912 
1913 	return (0);
1914 }
1915 
1916 static int
1917 force_maintenance(void *data, scf_walkinfo_t *wip)
1918 {
1919 	int flags = (int)data;
1920 	const char *prop;
1921 
1922 	if (flags & MARK_IMMEDIATE) {
1923 		prop = (flags & MARK_TEMPORARY) ?
1924 		    SCF_PROPERTY_MAINT_ON_IMMTEMP :
1925 		    SCF_PROPERTY_MAINT_ON_IMMEDIATE;
1926 	} else {
1927 		prop = (flags & MARK_TEMPORARY) ?
1928 		    SCF_PROPERTY_MAINT_ON_TEMPORARY :
1929 		    SCF_PROPERTY_MAINT_ON;
1930 	}
1931 
1932 	set_inst_action(wip->fmri, wip->inst, prop);
1933 
1934 	return (0);
1935 }
1936 
1937 static void
1938 set_milestone(const char *fmri, boolean_t temporary)
1939 {
1940 	scf_instance_t *inst;
1941 	scf_propertygroup_t *pg;
1942 	int r;
1943 
1944 	if (temporary) {
1945 		set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS_OVR,
1946 		    SCF_PG_OPTIONS_OVR_TYPE, SCF_PG_OPTIONS_OVR_FLAGS,
1947 		    SCF_PROPERTY_MILESTONE, fmri);
1948 		return;
1949 	}
1950 
1951 	if ((inst = scf_instance_create(h)) == NULL ||
1952 	    (pg = scf_pg_create(h)) == NULL)
1953 		scfdie();
1954 
1955 	if (get_inst(SCF_SERVICE_STARTD, inst) != 0) {
1956 		scf_instance_destroy(inst);
1957 		return;
1958 	}
1959 
1960 	/*
1961 	 * Set the persistent milestone before deleting the override so we don't
1962 	 * glitch.
1963 	 */
1964 	set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS,
1965 	    SCF_PG_OPTIONS_TYPE, SCF_PG_OPTIONS_FLAGS, SCF_PROPERTY_MILESTONE,
1966 	    fmri);
1967 
1968 	r = scf_instance_delete_prop(inst, SCF_PG_OPTIONS_OVR,
1969 	    SCF_PROPERTY_MILESTONE);
1970 	switch (r) {
1971 	case 0:
1972 		break;
1973 
1974 	case ECANCELED:
1975 		uu_warn(emsg_no_service, fmri);
1976 		exit_status = 1;
1977 		goto out;
1978 
1979 	case EPERM:
1980 		uu_warn(gettext("Could not delete %s/%s property of "
1981 		    "%s: permission denied.\n"), SCF_PG_OPTIONS_OVR,
1982 		    SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD);
1983 		exit_status = 1;
1984 		goto out;
1985 
1986 	case EACCES:
1987 		uu_warn(gettext("Could not delete %s/%s property of "
1988 		    "%s: access denied.\n"), SCF_PG_OPTIONS_OVR,
1989 		    SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD);
1990 		exit_status = 1;
1991 		goto out;
1992 
1993 	case EROFS:
1994 		uu_warn(gettext("Could not delete %s/%s property of "
1995 		    "%s: backend read-only.\n"), SCF_PG_OPTIONS_OVR,
1996 		    SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD);
1997 		exit_status = 1;
1998 		goto out;
1999 
2000 	default:
2001 		bad_error("scf_instance_delete_prop", r);
2002 	}
2003 
2004 out:
2005 	scf_pg_destroy(pg);
2006 	scf_instance_destroy(inst);
2007 }
2008 
2009 static char const *milestones[] = {
2010 	SCF_MILESTONE_SINGLE_USER,
2011 	SCF_MILESTONE_MULTI_USER,
2012 	SCF_MILESTONE_MULTI_USER_SERVER,
2013 	NULL
2014 };
2015 
2016 static void
2017 usage_milestone(void)
2018 {
2019 	const char **ms;
2020 
2021 	(void) fprintf(stderr, gettext(
2022 	"Usage: svcadm milestone [-d] <milestone>\n\n"
2023 	"\t-d\tmake the specified milestone the default for system boot\n\n"
2024 	"\tMilestones can be specified using an FMRI or abbreviation.\n"
2025 	"\tThe major milestones are as follows:\n\n"
2026 	"\tall\n"
2027 	"\tnone\n"));
2028 
2029 	for (ms = milestones; *ms != NULL; ms++)
2030 		(void) fprintf(stderr, "\t%s\n", *ms);
2031 
2032 	exit(UU_EXIT_USAGE);
2033 }
2034 
2035 static const char *
2036 validate_milestone(const char *milestone)
2037 {
2038 	const char **ms;
2039 	const char *tmp;
2040 	size_t len;
2041 
2042 	if (strcmp(milestone, "all") == 0)
2043 		return (milestone);
2044 
2045 	if (strcmp(milestone, "none") == 0)
2046 		return (milestone);
2047 
2048 	/*
2049 	 * Determine if this is a full or partial milestone
2050 	 */
2051 	for (ms = milestones; *ms != NULL; ms++) {
2052 		if ((tmp = strstr(*ms, milestone)) != NULL) {
2053 			len = strlen(milestone);
2054 
2055 			/*
2056 			 * The beginning of the string must align with the start
2057 			 * of a milestone fmri, or on the boundary between
2058 			 * elements.  The end of the string must align with the
2059 			 * end of the milestone, or at the instance boundary.
2060 			 */
2061 			if ((tmp == *ms || tmp[-1] == '/') &&
2062 			    (tmp[len] == '\0' || tmp[len] == ':'))
2063 				return (*ms);
2064 		}
2065 	}
2066 
2067 	(void) fprintf(stderr,
2068 	    gettext("\"%s\" is not a valid major milestone.\n"), milestone);
2069 
2070 	usage_milestone();
2071 	/* NOTREACHED */
2072 }
2073 
2074 /*ARGSUSED*/
2075 static void
2076 quiet(const char *fmt, ...)
2077 {
2078 	/* Do nothing */
2079 }
2080 
2081 int
2082 main(int argc, char *argv[])
2083 {
2084 	int o;
2085 	int err;
2086 	int sw_back;
2087 
2088 	(void) setlocale(LC_ALL, "");
2089 	(void) textdomain(TEXT_DOMAIN);
2090 
2091 	(void) uu_setpname(argv[0]);
2092 
2093 	if (argc < 2)
2094 		usage();
2095 
2096 	max_scf_fmri_sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
2097 	if (max_scf_fmri_sz < 0)
2098 		scfdie();
2099 	++max_scf_fmri_sz;
2100 
2101 	scratch_fmri = malloc(max_scf_fmri_sz);
2102 	if (scratch_fmri == NULL)
2103 		uu_die(emsg_nomem);
2104 
2105 	h = scf_handle_create(SCF_VERSION);
2106 	if (h == NULL)
2107 		scfdie();
2108 
2109 	while ((o = getopt(argc, argv, "vz:")) != -1) {
2110 		switch (o) {
2111 		case 'v':
2112 			verbose = 1;
2113 			break;
2114 
2115 		case 'z': {
2116 			scf_value_t *zone;
2117 
2118 			if (getzoneid() != GLOBAL_ZONEID)
2119 				uu_die(gettext("svcadm -z may only be used "
2120 				    "from the global zone\n"));
2121 
2122 			if ((zone = scf_value_create(h)) == NULL)
2123 				scfdie();
2124 
2125 			if (scf_value_set_astring(zone, optarg) != SCF_SUCCESS)
2126 				scfdie();
2127 
2128 			if (scf_handle_decorate(h, "zone", zone) != SCF_SUCCESS)
2129 				uu_die(gettext("invalid zone '%s'\n"), optarg);
2130 
2131 			scf_value_destroy(zone);
2132 			break;
2133 		}
2134 
2135 		default:
2136 			usage();
2137 		}
2138 	}
2139 
2140 	if (scf_handle_bind(h) == -1)
2141 		uu_die(gettext("Couldn't bind to configuration repository: "
2142 		    "%s.\n"), scf_strerror(scf_error()));
2143 
2144 	if (optind >= argc)
2145 		usage();
2146 
2147 	emsg_permission_denied = gettext("%s: Permission denied.\n");
2148 	emsg_nomem = gettext("Out of memory.\n");
2149 	emsg_create_pg_perm_denied = gettext("%s: Couldn't create \"%s\" "
2150 	    "property group (permission denied).\n");
2151 	emsg_pg_perm_denied = gettext("%s: Couldn't modify \"%s\" property "
2152 	    "group (permission denied).\n");
2153 	emsg_prop_perm_denied = gettext("%s: Couldn't modify \"%s/%s\" "
2154 	    "property (permission denied).\n");
2155 	emsg_no_service = gettext("No such service \"%s\".\n");
2156 
2157 	if (strcmp(argv[optind], "enable") == 0) {
2158 		int flags = SET_ENABLED;
2159 		int wait = 0;
2160 		int error = 0;
2161 
2162 		++optind;
2163 
2164 		while ((o = getopt(argc, argv, "rst")) != -1) {
2165 			if (o == 'r')
2166 				flags |= SET_RECURSIVE;
2167 			else if (o == 't')
2168 				flags |= SET_TEMPORARY;
2169 			else if (o == 's')
2170 				wait = 1;
2171 			else if (o == '?')
2172 				usage();
2173 			else {
2174 				assert(0);
2175 				abort();
2176 			}
2177 		}
2178 		argc -= optind;
2179 		argv += optind;
2180 
2181 		if (argc <= 0)
2182 			usage();
2183 
2184 		/*
2185 		 * We want to continue with -s processing if we had
2186 		 * invalid options, but not if an enable failed.  We
2187 		 * squelch output the second time we walk fmris; we saw
2188 		 * the errors the first time.
2189 		 */
2190 		if ((err = scf_walk_fmri(h, argc, argv, 0, set_fmri_enabled,
2191 		    (void *)flags, &error, uu_warn)) != 0) {
2192 
2193 			uu_warn(gettext("failed to iterate over "
2194 			    "instances: %s\n"), scf_strerror(err));
2195 			exit_status = UU_EXIT_FATAL;
2196 
2197 		} else if (wait && exit_status == 0 &&
2198 		    (err = scf_walk_fmri(h, argc, argv, 0, wait_fmri_enabled,
2199 		    (void *)flags, &error, quiet)) != 0) {
2200 
2201 			uu_warn(gettext("failed to iterate over "
2202 			    "instances: %s\n"), scf_strerror(err));
2203 			exit_status = UU_EXIT_FATAL;
2204 		}
2205 
2206 		if (error > 0)
2207 			exit_status = error;
2208 
2209 	} else if (strcmp(argv[optind], "disable") == 0) {
2210 		int flags = 0;
2211 		int wait = 0;
2212 		int error = 0;
2213 
2214 		++optind;
2215 
2216 		while ((o = getopt(argc, argv, "st")) != -1) {
2217 			if (o == 't')
2218 				flags |= SET_TEMPORARY;
2219 			else if (o == 's')
2220 				wait = 1;
2221 			else if (o == '?')
2222 				usage();
2223 			else {
2224 				assert(0);
2225 				abort();
2226 			}
2227 		}
2228 		argc -= optind;
2229 		argv += optind;
2230 
2231 		if (argc <= 0)
2232 			usage();
2233 
2234 		/*
2235 		 * We want to continue with -s processing if we had
2236 		 * invalid options, but not if a disable failed.  We
2237 		 * squelch output the second time we walk fmris; we saw
2238 		 * the errors the first time.
2239 		 */
2240 		if ((err = scf_walk_fmri(h, argc, argv, 0, set_fmri_enabled,
2241 		    (void *)flags, &exit_status, uu_warn)) != 0) {
2242 
2243 			uu_warn(gettext("failed to iterate over "
2244 			    "instances: %s\n"), scf_strerror(err));
2245 			exit_status = UU_EXIT_FATAL;
2246 
2247 		} else if (wait && exit_status == 0 &&
2248 		    (err = scf_walk_fmri(h, argc, argv, 0, wait_fmri_disabled,
2249 		    (void *)flags, &error, quiet)) != 0) {
2250 
2251 			uu_warn(gettext("failed to iterate over "
2252 			    "instances: %s\n"), scf_strerror(err));
2253 			exit_status = UU_EXIT_FATAL;
2254 		}
2255 
2256 		if (error > 0)
2257 			exit_status = error;
2258 
2259 	} else if (strcmp(argv[optind], "restart") == 0) {
2260 		++optind;
2261 
2262 		if (optind >= argc)
2263 			usage();
2264 
2265 		if ((err = scf_walk_fmri(h, argc - optind, argv + optind, 0,
2266 		    set_fmri_action, (void *)SCF_PROPERTY_RESTART,
2267 		    &exit_status, uu_warn)) != 0) {
2268 			uu_warn(gettext("failed to iterate over "
2269 			    "instances: %s\n"), scf_strerror(err));
2270 			exit_status = UU_EXIT_FATAL;
2271 		}
2272 
2273 	} else if (strcmp(argv[optind], "refresh") == 0) {
2274 		++optind;
2275 
2276 		if (optind >= argc)
2277 			usage();
2278 
2279 		if ((err = scf_walk_fmri(h, argc - optind, argv + optind, 0,
2280 		    set_fmri_action, (void *)SCF_PROPERTY_REFRESH,
2281 		    &exit_status, uu_warn)) != 0) {
2282 			uu_warn(gettext("failed to iterate over "
2283 			    "instances: %s\n"), scf_strerror(scf_error()));
2284 			exit_status = UU_EXIT_FATAL;
2285 		}
2286 
2287 	} else if (strcmp(argv[optind], "mark") == 0) {
2288 		int flags = 0;
2289 		scf_walk_callback callback;
2290 
2291 		++optind;
2292 
2293 		while ((o = getopt(argc, argv, "It")) != -1) {
2294 			if (o == 'I')
2295 				flags |= MARK_IMMEDIATE;
2296 			else if (o == 't')
2297 				flags |= MARK_TEMPORARY;
2298 			else if (o == '?')
2299 				usage();
2300 			else {
2301 				assert(0);
2302 				abort();
2303 			}
2304 		}
2305 
2306 		if (argc - optind < 2)
2307 			usage();
2308 
2309 		if (strcmp(argv[optind], "degraded") == 0) {
2310 			if (flags & MARK_TEMPORARY)
2311 				uu_xdie(UU_EXIT_USAGE, gettext("-t may not be "
2312 				    "used with degraded.\n"));
2313 			callback = force_degraded;
2314 
2315 		} else if (strcmp(argv[optind], "maintenance") == 0) {
2316 			callback = force_maintenance;
2317 		} else {
2318 			usage();
2319 		}
2320 
2321 		if ((err = scf_walk_fmri(h, argc - optind - 1,
2322 		    argv + optind + 1, 0, callback, NULL, &exit_status,
2323 		    uu_warn)) != 0) {
2324 			uu_warn(gettext("failed to iterate over "
2325 			    "instances: %s\n"),
2326 			    scf_strerror(err));
2327 			exit_status = UU_EXIT_FATAL;
2328 		}
2329 
2330 	} else if (strcmp(argv[optind], "clear") == 0) {
2331 		++optind;
2332 
2333 		if (optind >= argc)
2334 			usage();
2335 
2336 		if ((err = scf_walk_fmri(h, argc - optind, argv + optind, 0,
2337 		    clear_instance, NULL, &exit_status, uu_warn)) != 0) {
2338 			uu_warn(gettext("failed to iterate over "
2339 			    "instances: %s\n"), scf_strerror(err));
2340 			exit_status = UU_EXIT_FATAL;
2341 		}
2342 
2343 	} else if (strcmp(argv[optind], "milestone") == 0) {
2344 		boolean_t temporary = B_TRUE;
2345 		const char *milestone;
2346 
2347 		++optind;
2348 
2349 		while ((o = getopt(argc, argv, "d")) != -1) {
2350 			if (o == 'd')
2351 				temporary = B_FALSE;
2352 			else if (o == '?')
2353 				usage_milestone();
2354 			else {
2355 				assert(0);
2356 				abort();
2357 			}
2358 		}
2359 
2360 		if (optind >= argc)
2361 			usage_milestone();
2362 
2363 		milestone = validate_milestone(argv[optind]);
2364 
2365 		set_milestone(milestone, temporary);
2366 	} else if (strcmp(argv[optind], "_smf_backup") == 0) {
2367 		const char *reason = NULL;
2368 
2369 		++optind;
2370 
2371 		if (optind != argc - 1)
2372 			usage();
2373 
2374 		if ((err = _scf_request_backup(h, argv[optind])) !=
2375 		    SCF_SUCCESS) {
2376 			switch (scf_error()) {
2377 			case SCF_ERROR_NOT_BOUND:
2378 			case SCF_ERROR_CONNECTION_BROKEN:
2379 			case SCF_ERROR_BACKEND_READONLY:
2380 				scfdie();
2381 				break;
2382 
2383 			case SCF_ERROR_PERMISSION_DENIED:
2384 			case SCF_ERROR_INVALID_ARGUMENT:
2385 				reason = scf_strerror(scf_error());
2386 				break;
2387 
2388 			case SCF_ERROR_INTERNAL:
2389 				reason =
2390 				    "unknown error (see console for details)";
2391 				break;
2392 			}
2393 
2394 			uu_warn("failed to backup repository: %s\n", reason);
2395 			exit_status = UU_EXIT_FATAL;
2396 		}
2397 	} else if (strcmp(argv[optind], "_smf_repository_switch") == 0) {
2398 		const char *reason = NULL;
2399 
2400 		++optind;
2401 
2402 		/*
2403 		 * Check argument and setup scf_switch structure
2404 		 */
2405 		if (optind != argc - 1)
2406 			exit(1);
2407 
2408 		if (strcmp(argv[optind], "fast") == 0) {
2409 			sw_back = 0;
2410 		} else if (strcmp(argv[optind], "perm") == 0) {
2411 			sw_back = 1;
2412 		} else {
2413 			exit(UU_EXIT_USAGE);
2414 		}
2415 
2416 		/*
2417 		 * Call into switch primitive
2418 		 */
2419 		if ((err = _scf_repository_switch(h, sw_back)) !=
2420 		    SCF_SUCCESS) {
2421 			/*
2422 			 * Retrieve per thread SCF error code
2423 			 */
2424 			switch (scf_error()) {
2425 			case SCF_ERROR_NOT_BOUND:
2426 				abort();
2427 				/* NOTREACHED */
2428 
2429 			case SCF_ERROR_CONNECTION_BROKEN:
2430 			case SCF_ERROR_BACKEND_READONLY:
2431 				scfdie();
2432 				/* NOTREACHED */
2433 
2434 			case SCF_ERROR_PERMISSION_DENIED:
2435 			case SCF_ERROR_INVALID_ARGUMENT:
2436 				reason = scf_strerror(scf_error());
2437 				break;
2438 
2439 			case SCF_ERROR_INTERNAL:
2440 				reason = "File operation error: (see console)";
2441 				break;
2442 
2443 			default:
2444 				abort();
2445 				/* NOTREACHED */
2446 			}
2447 
2448 			uu_warn("failed to switch repository: %s\n", reason);
2449 			exit_status = UU_EXIT_FATAL;
2450 		}
2451 	} else {
2452 		usage();
2453 	}
2454 
2455 	if (scf_handle_unbind(h) == -1)
2456 		scfdie();
2457 	scf_handle_destroy(h);
2458 
2459 	return (exit_status);
2460 }
2461