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