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