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