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