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
78struct ht_elt {
79	struct ht_elt	*next;
80	boolean_t	active;
81	char		str[1];
82};
83
84
85scf_handle_t *h;
86ssize_t max_scf_fmri_sz;
87static const char *emsg_permission_denied;
88static const char *emsg_nomem;
89static const char *emsg_create_pg_perm_denied;
90static const char *emsg_pg_perm_denied;
91static const char *emsg_prop_perm_denied;
92static const char *emsg_no_service;
93
94static int exit_status = 0;
95static int verbose = 0;
96static char *scratch_fmri;
97static char *g_zonename = NULL;
98static char svcstate[80];
99static boolean_t svcsearch = B_FALSE;
100
101static struct ht_elt **visited;
102
103void do_scfdie(int lineno) __NORETURN;
104static void usage_milestone(void) __NORETURN;
105static void set_astring_prop(const char *, const char *, const char *,
106    uint32_t, const char *, const char *);
107static void pr_warn(const char *format, ...);
108
109/*
110 * Visitors from synch.c, needed for enable -s and disable -s.
111 */
112extern int is_enabled(scf_instance_t *);
113extern int has_potential(scf_instance_t *, int);
114
115void
116do_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
143static void
144usage()
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
176static uint32_t
177hash_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 */
198static int
199visited_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 */
236int
237get_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
301out:
302	scf_value_destroy(val);
303	scf_property_destroy(prop);
304	return (ret);
305}
306
307/*
308 * Returns 0, EPERM, or EROFS.
309 */
310static int
311set_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
379out:
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 */
393ssize_t
394get_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 */
431static int
432pg_get_or_add(const scf_instance_t *inst, const char *pgname,
433    const char *pgtype, uint32_t pgflags, scf_propertygroup_t *pg)
434{
435again:
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
458static int
459my_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 */
494static int
495restarter_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
565out:
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 */
576static void
577set_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 {
663again:
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
757eperm:
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
764out:
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 */
780static int
781get_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
875out:
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 */
885static int
886get_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
899static char *
900inst_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
914static ssize_t
915dep_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
948static boolean_t
949multiple_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
996out:
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 */
1016static int
1017enable_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
1300out:
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 */
1319static void
1320set_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
1428action_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
1458out:
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 */
1479int
1480inst_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
1545out:
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
1553static void
1554set_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
1661out:
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
1678static int
1679set_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 */
1731static int
1732wait_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
1795out:
1796	scf_pg_destroy(pg);
1797	return (0);
1798}
1799
1800/* ARGSUSED */
1801static int
1802wait_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
1854out:
1855	scf_pg_destroy(pg);
1856	return (0);
1857}
1858
1859/* ARGSUSED */
1860static int
1861clear_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
1889static int
1890set_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
1914static int
1915force_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
1940static int
1941force_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
1970static void
1971set_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
2037out:
2038	scf_pg_destroy(pg);
2039	scf_instance_destroy(inst);
2040}
2041
2042static char const *milestones[] = {
2043	SCF_MILESTONE_SINGLE_USER,
2044	SCF_MILESTONE_MULTI_USER,
2045	SCF_MILESTONE_MULTI_USER_SERVER,
2046	NULL
2047};
2048
2049static void
2050usage_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
2068static const char *
2069validate_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*/
2108static void
2109pr_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*/
2133static void
2134quiet(const char *fmt, ...)
2135{
2136	/* Do nothing */
2137}
2138
2139int
2140main(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
2248again:
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();
2678nextzone:
2679	scf_handle_destroy(h);
2680	if (do_zones && zent < nzents)
2681		goto again;
2682
2683	return (exit_status);
2684}
2685