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