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