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