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