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