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