Home | History | Annotate | Download | only in common
      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 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <librestart.h>
     28 #include <librestart_priv.h>
     29 #include <libscf.h>
     30 #include <libscf_priv.h>
     31 
     32 #include <assert.h>
     33 #include <ctype.h>
     34 #include <dlfcn.h>
     35 #include <errno.h>
     36 #include <exec_attr.h>
     37 #include <grp.h>
     38 #include <libsysevent.h>
     39 #include <libuutil.h>
     40 #include <limits.h>
     41 #include <link.h>
     42 #include <malloc.h>
     43 #include <pool.h>
     44 #include <priv.h>
     45 #include <project.h>
     46 #include <pthread.h>
     47 #include <pwd.h>
     48 #include <secdb.h>
     49 #include <signal.h>
     50 #include <stdlib.h>
     51 #include <string.h>
     52 #include <syslog.h>
     53 #include <sys/corectl.h>
     54 #include <sys/machelf.h>
     55 #include <sys/task.h>
     56 #include <sys/types.h>
     57 #include <time.h>
     58 #include <unistd.h>
     59 #include <ucontext.h>
     60 
     61 #define	min(a, b)		((a) > (b) ? (b) : (a))
     62 
     63 #define	MKW_TRUE	":true"
     64 #define	MKW_KILL	":kill"
     65 #define	MKW_KILL_PROC	":kill_process"
     66 
     67 #define	ALLOCFAIL	((char *)"Allocation failure.")
     68 #define	RCBROKEN	((char *)"Repository connection broken.")
     69 
     70 #define	MAX_COMMIT_RETRIES		10
     71 #define	MAX_COMMIT_RETRY_INT		(5 * 1000000)	/* 5 seconds */
     72 #define	INITIAL_COMMIT_RETRY_INT	(10000)		/* 1/100th second */
     73 
     74 /*
     75  * bad_fail() catches bugs in this and lower layers by reporting supposedly
     76  * impossible function failures.  The NDEBUG case keeps the strings out of the
     77  * library but still calls abort() so we can root-cause from the coredump.
     78  */
     79 #ifndef NDEBUG
     80 #define	bad_fail(func, err)	{					\
     81 	(void) fprintf(stderr,						\
     82 	    "At %s:%d, %s() failed with unexpected error %d.  Aborting.\n", \
     83 	    __FILE__, __LINE__, (func), (err));				\
     84 	abort();							\
     85 }
     86 #else
     87 #define	bad_fail(func, err)	abort()
     88 #endif
     89 
     90 struct restarter_event_handle {
     91 	char				*reh_restarter_name;
     92 	char				*reh_delegate_channel_name;
     93 	evchan_t			*reh_delegate_channel;
     94 	char				*reh_delegate_subscriber_id;
     95 	char				*reh_master_channel_name;
     96 	evchan_t			*reh_master_channel;
     97 	char				*reh_master_subscriber_id;
     98 	int				(*reh_handler)(restarter_event_t *);
     99 };
    100 
    101 struct restarter_event {
    102 	sysevent_t			*re_sysevent;
    103 	restarter_event_type_t		re_type;
    104 	char				*re_instance_name;
    105 	restarter_event_handle_t	*re_event_handle;
    106 	restarter_instance_state_t	re_state;
    107 	restarter_instance_state_t	re_next_state;
    108 };
    109 
    110 /*
    111  * A static no memory error message mc_error_t structure
    112  * to be used in cases when memory errors are to be returned
    113  * This avoids the need to attempt to allocate memory for the
    114  * message, therefore getting into a cycle of no memory failures.
    115  */
    116 mc_error_t mc_nomem_err = {
    117 	0, ENOMEM, sizeof ("Out of memory") - 1, "Out of memory"
    118 };
    119 
    120 static const char * const allocfail = "Allocation failure.\n";
    121 static const char * const rcbroken = "Repository connection broken.\n";
    122 
    123 static int method_context_safety = 0;	/* Can safely call pools/projects. */
    124 
    125 int ndebug = 1;
    126 
    127 /* PRINTFLIKE3 */
    128 static mc_error_t *
    129 mc_error_create(mc_error_t *e, int type, const char *format, ...)
    130 {
    131 	mc_error_t	*le;
    132 	va_list		args;
    133 	int		size;
    134 
    135 	/*
    136 	 * If the type is ENOMEM and format is NULL, then
    137 	 * go ahead and return the default nomem error.
    138 	 * Otherwise, attempt to allocate the memory and if
    139 	 * that fails then there is no reason to continue.
    140 	 */
    141 	if (type == ENOMEM && format == NULL)
    142 		return (&mc_nomem_err);
    143 
    144 	if (e == NULL && (le = malloc(sizeof (mc_error_t))) == NULL)
    145 		return (&mc_nomem_err);
    146 	else
    147 		le = e;
    148 
    149 	le->type = type;
    150 	le->destroy = 1;
    151 	va_start(args, format);
    152 	size = vsnprintf(NULL, 0, format, args) + 1;
    153 	if (size >= RESTARTER_ERRMSGSZ) {
    154 		if ((le = realloc(e, sizeof (mc_error_t) +
    155 		    (size - RESTARTER_ERRMSGSZ))) == NULL) {
    156 			size = RESTARTER_ERRMSGSZ - 1;
    157 			le = e;
    158 		}
    159 	}
    160 
    161 	le->size = size;
    162 	(void) vsnprintf(le->msg, le->size, format, args);
    163 	va_end(args);
    164 
    165 	return (le);
    166 }
    167 
    168 void
    169 restarter_mc_error_destroy(mc_error_t *mc_err)
    170 {
    171 	if (mc_err == NULL)
    172 		return;
    173 
    174 	/*
    175 	 * If the error messages was allocated then free.
    176 	 */
    177 	if (mc_err->destroy) {
    178 		free(mc_err);
    179 	}
    180 }
    181 
    182 static void
    183 free_restarter_event_handle(struct restarter_event_handle *h)
    184 {
    185 	if (h == NULL)
    186 		return;
    187 
    188 	/*
    189 	 * Just free the memory -- don't unbind the sysevent handle,
    190 	 * as otherwise events may be lost if this is just a restarter
    191 	 * restart.
    192 	 */
    193 
    194 	if (h->reh_restarter_name != NULL)
    195 		free(h->reh_restarter_name);
    196 	if (h->reh_delegate_channel_name != NULL)
    197 		free(h->reh_delegate_channel_name);
    198 	if (h->reh_delegate_subscriber_id != NULL)
    199 		free(h->reh_delegate_subscriber_id);
    200 	if (h->reh_master_channel_name != NULL)
    201 		free(h->reh_master_channel_name);
    202 	if (h->reh_master_subscriber_id != NULL)
    203 		free(h->reh_master_subscriber_id);
    204 
    205 	free(h);
    206 }
    207 
    208 char *
    209 _restarter_get_channel_name(const char *fmri, int type)
    210 {
    211 	char *name;
    212 	char *chan_name = malloc(MAX_CHNAME_LEN);
    213 	char prefix_name[3];
    214 	int i;
    215 
    216 	if (chan_name == NULL)
    217 		return (NULL);
    218 
    219 	if (type == RESTARTER_CHANNEL_DELEGATE)
    220 		(void) strcpy(prefix_name, "d_");
    221 	else if (type == RESTARTER_CHANNEL_MASTER)
    222 		(void) strcpy(prefix_name, "m_");
    223 	else {
    224 		free(chan_name);
    225 		return (NULL);
    226 	}
    227 
    228 	/*
    229 	 * Create a unique name
    230 	 *
    231 	 * Use the entire name, using a replacement of the /
    232 	 * characters to get a better name.
    233 	 *
    234 	 * Remove the svc:/ from the beginning as this really
    235 	 * isn't going to provide any uniqueness...
    236 	 *
    237 	 * An fmri name greater than MAX_CHNAME_LEN is going
    238 	 * to be rejected as too long for the chan_name below
    239 	 * in the snprintf call.
    240 	 */
    241 	if ((name = strdup(strchr(fmri, '/') + 1)) == NULL) {
    242 		free(chan_name);
    243 		return (NULL);
    244 	}
    245 	i = 0;
    246 	while (name[i]) {
    247 		if (name[i] == '/') {
    248 			name[i] = '_';
    249 		}
    250 
    251 		i++;
    252 	}
    253 
    254 	/*
    255 	 * Should check for [a-z],[A-Z],[0-9],.,_,-,:
    256 	 */
    257 
    258 	if (snprintf(chan_name, MAX_CHNAME_LEN, "com.sun:scf:%s%s",
    259 	    prefix_name, name) > MAX_CHNAME_LEN) {
    260 		free(chan_name);
    261 		chan_name = NULL;
    262 	}
    263 
    264 	free(name);
    265 	return (chan_name);
    266 }
    267 
    268 int
    269 cb(sysevent_t *syse, void *cookie)
    270 {
    271 	restarter_event_handle_t *h = (restarter_event_handle_t *)cookie;
    272 	restarter_event_t *e;
    273 	nvlist_t *attr_list = NULL;
    274 	int ret = 0;
    275 
    276 	e = uu_zalloc(sizeof (restarter_event_t));
    277 	if (e == NULL)
    278 		uu_die(allocfail);
    279 	e->re_event_handle = h;
    280 	e->re_sysevent = syse;
    281 
    282 	if (sysevent_get_attr_list(syse, &attr_list) != 0)
    283 		uu_die(allocfail);
    284 
    285 	if ((nvlist_lookup_uint32(attr_list, RESTARTER_NAME_TYPE,
    286 	    &(e->re_type)) != 0) ||
    287 	    (nvlist_lookup_string(attr_list,
    288 	    RESTARTER_NAME_INSTANCE, &(e->re_instance_name)) != 0)) {
    289 		uu_warn("%s: Can't decode nvlist for event %p\n",
    290 		    h->reh_restarter_name, (void *)syse);
    291 
    292 		ret = 0;
    293 	} else {
    294 		ret = h->reh_handler(e);
    295 	}
    296 
    297 	uu_free(e);
    298 	nvlist_free(attr_list);
    299 	return (ret);
    300 }
    301 
    302 /*
    303  * restarter_bind_handle(uint32_t, char *, int (*)(restarter_event_t *), int,
    304  *     restarter_event_handle_t **)
    305  *
    306  * Bind to a delegated restarter event channel.
    307  * Each delegated restarter gets its own channel for resource management.
    308  *
    309  * Returns 0 on success or
    310  *   ENOTSUP	version mismatch
    311  *   EINVAL	restarter_name or event_handle is NULL
    312  *   ENOMEM	out of memory, too many channels, or too many subscriptions
    313  *   EBUSY	sysevent_evc_bind() could not establish binding
    314  *   EFAULT	internal sysevent_evc_bind()/sysevent_evc_subscribe() error
    315  *   EMFILE	out of file descriptors
    316  *   EPERM	insufficient privilege for sysevent_evc_bind()
    317  *   EEXIST	already subscribed
    318  */
    319 int
    320 restarter_bind_handle(uint32_t version, const char *restarter_name,
    321     int (*event_handler)(restarter_event_t *), int flags,
    322     restarter_event_handle_t **rehp)
    323 {
    324 	restarter_event_handle_t *h;
    325 	size_t sz;
    326 	int err;
    327 
    328 	if (version != RESTARTER_EVENT_VERSION)
    329 		return (ENOTSUP);
    330 
    331 	if (restarter_name == NULL || event_handler == NULL)
    332 		return (EINVAL);
    333 
    334 	if (flags & RESTARTER_FLAG_DEBUG)
    335 		ndebug++;
    336 
    337 	if ((h = uu_zalloc(sizeof (restarter_event_handle_t))) == NULL)
    338 		return (ENOMEM);
    339 
    340 	h->reh_delegate_subscriber_id = malloc(MAX_SUBID_LEN);
    341 	h->reh_master_subscriber_id = malloc(MAX_SUBID_LEN);
    342 	h->reh_restarter_name = strdup(restarter_name);
    343 	if (h->reh_delegate_subscriber_id == NULL ||
    344 	    h->reh_master_subscriber_id == NULL ||
    345 	    h->reh_restarter_name == NULL) {
    346 		free_restarter_event_handle(h);
    347 		return (ENOMEM);
    348 	}
    349 
    350 	sz = strlcpy(h->reh_delegate_subscriber_id, "del", MAX_SUBID_LEN);
    351 	assert(sz < MAX_SUBID_LEN);
    352 	sz = strlcpy(h->reh_master_subscriber_id, "master", MAX_SUBID_LEN);
    353 	assert(sz < MAX_SUBID_LEN);
    354 
    355 	h->reh_delegate_channel_name =
    356 	    _restarter_get_channel_name(restarter_name,
    357 	    RESTARTER_CHANNEL_DELEGATE);
    358 	h->reh_master_channel_name =
    359 	    _restarter_get_channel_name(restarter_name,
    360 	    RESTARTER_CHANNEL_MASTER);
    361 
    362 	if (h->reh_delegate_channel_name == NULL ||
    363 	    h->reh_master_channel_name == NULL) {
    364 		free_restarter_event_handle(h);
    365 		return (ENOMEM);
    366 	}
    367 
    368 	if (sysevent_evc_bind(h->reh_delegate_channel_name,
    369 	    &h->reh_delegate_channel, EVCH_CREAT|EVCH_HOLD_PEND) != 0) {
    370 		err = errno;
    371 		assert(err != EINVAL);
    372 		assert(err != ENOENT);
    373 		free_restarter_event_handle(h);
    374 		return (err);
    375 	}
    376 
    377 	if (sysevent_evc_bind(h->reh_master_channel_name,
    378 	    &h->reh_master_channel, EVCH_CREAT|EVCH_HOLD_PEND) != 0) {
    379 		err = errno;
    380 		assert(err != EINVAL);
    381 		assert(err != ENOENT);
    382 		free_restarter_event_handle(h);
    383 		return (err);
    384 	}
    385 
    386 	h->reh_handler = event_handler;
    387 
    388 	assert(strlen(restarter_name) <= MAX_CLASS_LEN - 1);
    389 	assert(strlen(h->reh_delegate_subscriber_id) <= MAX_SUBID_LEN - 1);
    390 	assert(strlen(h->reh_master_subscriber_id) <= MAX_SUBID_LEN - 1);
    391 
    392 	if (sysevent_evc_subscribe(h->reh_delegate_channel,
    393 	    h->reh_delegate_subscriber_id, EC_ALL, cb, h, EVCH_SUB_KEEP) != 0) {
    394 		err = errno;
    395 		assert(err != EINVAL);
    396 		free_restarter_event_handle(h);
    397 		return (err);
    398 	}
    399 
    400 	*rehp = h;
    401 	return (0);
    402 }
    403 
    404 restarter_event_handle_t *
    405 restarter_event_get_handle(restarter_event_t *e)
    406 {
    407 	assert(e != NULL && e->re_event_handle != NULL);
    408 	return (e->re_event_handle);
    409 }
    410 
    411 restarter_event_type_t
    412 restarter_event_get_type(restarter_event_t *e)
    413 {
    414 	assert(e != NULL);
    415 	return (e->re_type);
    416 }
    417 
    418 ssize_t
    419 restarter_event_get_instance(restarter_event_t *e, char *inst, size_t sz)
    420 {
    421 	assert(e != NULL && inst != NULL);
    422 	return ((ssize_t)strlcpy(inst, e->re_instance_name, sz));
    423 }
    424 
    425 int
    426 restarter_event_get_current_states(restarter_event_t *e,
    427     restarter_instance_state_t *state, restarter_instance_state_t *next_state)
    428 {
    429 	if (e == NULL)
    430 		return (-1);
    431 	*state = e->re_state;
    432 	*next_state = e->re_next_state;
    433 	return (0);
    434 }
    435 
    436 /*
    437  * restarter_event_publish_retry() is a wrapper around sysevent_evc_publish().
    438  * In case, the event cannot be sent at the first attempt (sysevent_evc_publish
    439  * returned EAGAIN - sysevent queue full), this function retries a few time
    440  * and return ENOSPC if it reaches the retry limit.
    441  *
    442  * The arguments to this function map the arguments of sysevent_evc_publish().
    443  *
    444  * On success, return 0. On error, return
    445  *
    446  *   EFAULT - internal sysevent_evc_publish() error
    447  *   ENOMEM - internal sysevent_evc_publish() error
    448  *   EBADF - scp is invalid (sysevent_evc_publish() returned EINVAL)
    449  *   ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN)
    450  */
    451 int
    452 restarter_event_publish_retry(evchan_t *scp, const char *class,
    453     const char *subclass, const char *vendor, const char *pub_name,
    454     nvlist_t *attr_list, uint32_t flags)
    455 {
    456 	int retries, ret;
    457 	useconds_t retry_int = INITIAL_COMMIT_RETRY_INT;
    458 
    459 	for (retries = 0; retries < MAX_COMMIT_RETRIES; retries++) {
    460 		ret = sysevent_evc_publish(scp, class, subclass, vendor,
    461 		    pub_name, attr_list, flags);
    462 		if (ret == 0)
    463 			break;
    464 
    465 		switch (ret) {
    466 		case EAGAIN:
    467 			/* Queue is full */
    468 			(void) usleep(retry_int);
    469 
    470 			retry_int = min(retry_int * 2, MAX_COMMIT_RETRY_INT);
    471 			break;
    472 
    473 		case EINVAL:
    474 			ret = EBADF;
    475 			/* FALLTHROUGH */
    476 
    477 		case EFAULT:
    478 		case ENOMEM:
    479 			return (ret);
    480 
    481 		case EOVERFLOW:
    482 		default:
    483 			/* internal error - abort */
    484 			bad_fail("sysevent_evc_publish", ret);
    485 		}
    486 	}
    487 
    488 	if (retries == MAX_COMMIT_RETRIES)
    489 		ret = ENOSPC;
    490 
    491 	return (ret);
    492 }
    493 
    494 /*
    495  * Commit the state, next state, and auxiliary state into the repository.
    496  * Let the graph engine know about the state change and error.  On success,
    497  * return 0. On error, return
    498  *   EINVAL - aux has spaces
    499  *	    - inst is invalid or not an instance FMRI
    500  *   EPROTO - librestart compiled against different libscf
    501  *   ENOMEM - out of memory
    502  *	    - repository server out of resources
    503  *   ENOTACTIVE - repository server not running
    504  *   ECONNABORTED - repository connection established, but then broken
    505  *		  - unknown libscf error
    506  *   ENOENT - inst does not exist in the repository
    507  *   EPERM - insufficient permissions
    508  *   EACCESS - backend access denied
    509  *   EROFS - backend is readonly
    510  *   EFAULT - internal sysevent_evc_publish() error
    511  *   EBADF - h is invalid (sysevent_evc_publish() returned EINVAL)
    512  *   ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN)
    513  */
    514 int
    515 restarter_set_states(restarter_event_handle_t *h, const char *inst,
    516     restarter_instance_state_t cur_state,
    517     restarter_instance_state_t new_cur_state,
    518     restarter_instance_state_t next_state,
    519     restarter_instance_state_t new_next_state, restarter_error_t e,
    520     const char *aux)
    521 {
    522 	nvlist_t *attr;
    523 	scf_handle_t *scf_h;
    524 	instance_data_t id;
    525 	int ret = 0;
    526 	char *p = (char *)aux;
    527 
    528 	assert(h->reh_master_channel != NULL);
    529 	assert(h->reh_master_channel_name != NULL);
    530 	assert(h->reh_master_subscriber_id != NULL);
    531 
    532 	/* Validate format of auxiliary state: no spaces allowed */
    533 	if (p != NULL) {
    534 		while (*p != '\0') {
    535 			if (isspace(*p))
    536 				return (EINVAL);
    537 			p++;
    538 		}
    539 	}
    540 
    541 	if ((scf_h = scf_handle_create(SCF_VERSION)) == NULL) {
    542 		switch (scf_error()) {
    543 		case SCF_ERROR_VERSION_MISMATCH:
    544 			return (EPROTO);
    545 
    546 		case SCF_ERROR_NO_MEMORY:
    547 			return (ENOMEM);
    548 
    549 		default:
    550 			bad_fail("scf_handle_create", scf_error());
    551 		}
    552 	}
    553 
    554 	if (scf_handle_bind(scf_h) == -1) {
    555 		scf_handle_destroy(scf_h);
    556 		switch (scf_error()) {
    557 		case SCF_ERROR_NO_SERVER:
    558 			return (ENOTACTIVE);
    559 
    560 		case SCF_ERROR_NO_RESOURCES:
    561 			return (ENOMEM);
    562 
    563 		case SCF_ERROR_INVALID_ARGUMENT:
    564 		case SCF_ERROR_IN_USE:
    565 		default:
    566 			bad_fail("scf_handle_bind", scf_error());
    567 		}
    568 	}
    569 
    570 	if (nvlist_alloc(&attr, NV_UNIQUE_NAME, 0) != 0 ||
    571 	    nvlist_add_int32(attr, RESTARTER_NAME_STATE, new_cur_state) != 0 ||
    572 	    nvlist_add_int32(attr, RESTARTER_NAME_NEXT_STATE, new_next_state)
    573 	    != 0 ||
    574 	    nvlist_add_int32(attr, RESTARTER_NAME_ERROR, e) != 0 ||
    575 	    nvlist_add_string(attr, RESTARTER_NAME_INSTANCE, inst) != 0) {
    576 		ret = ENOMEM;
    577 	} else {
    578 		id.i_fmri = inst;
    579 		id.i_state = cur_state;
    580 		id.i_next_state = next_state;
    581 
    582 		ret = _restarter_commit_states(scf_h, &id, new_cur_state,
    583 		    new_next_state, aux);
    584 
    585 		if (ret == 0) {
    586 			ret = restarter_event_publish_retry(
    587 			    h->reh_master_channel, "master", "state_change",
    588 			    "com.sun", "librestart", attr, EVCH_NOSLEEP);
    589 		}
    590 	}
    591 
    592 	nvlist_free(attr);
    593 	(void) scf_handle_unbind(scf_h);
    594 	scf_handle_destroy(scf_h);
    595 
    596 	return (ret);
    597 }
    598 
    599 restarter_instance_state_t
    600 restarter_string_to_state(char *string)
    601 {
    602 	assert(string != NULL);
    603 
    604 	if (strcmp(string, SCF_STATE_STRING_NONE) == 0)
    605 		return (RESTARTER_STATE_NONE);
    606 	else if (strcmp(string, SCF_STATE_STRING_UNINIT) == 0)
    607 		return (RESTARTER_STATE_UNINIT);
    608 	else if (strcmp(string, SCF_STATE_STRING_MAINT) == 0)
    609 		return (RESTARTER_STATE_MAINT);
    610 	else if (strcmp(string, SCF_STATE_STRING_OFFLINE) == 0)
    611 		return (RESTARTER_STATE_OFFLINE);
    612 	else if (strcmp(string, SCF_STATE_STRING_DISABLED) == 0)
    613 		return (RESTARTER_STATE_DISABLED);
    614 	else if (strcmp(string, SCF_STATE_STRING_ONLINE) == 0)
    615 		return (RESTARTER_STATE_ONLINE);
    616 	else if (strcmp(string, SCF_STATE_STRING_DEGRADED) == 0)
    617 		return (RESTARTER_STATE_DEGRADED);
    618 	else {
    619 		return (RESTARTER_STATE_NONE);
    620 	}
    621 }
    622 
    623 ssize_t
    624 restarter_state_to_string(restarter_instance_state_t state, char *string,
    625     size_t len)
    626 {
    627 	assert(string != NULL);
    628 
    629 	if (state == RESTARTER_STATE_NONE)
    630 		return ((ssize_t)strlcpy(string, SCF_STATE_STRING_NONE, len));
    631 	else if (state == RESTARTER_STATE_UNINIT)
    632 		return ((ssize_t)strlcpy(string, SCF_STATE_STRING_UNINIT, len));
    633 	else if (state == RESTARTER_STATE_MAINT)
    634 		return ((ssize_t)strlcpy(string, SCF_STATE_STRING_MAINT, len));
    635 	else if (state == RESTARTER_STATE_OFFLINE)
    636 		return ((ssize_t)strlcpy(string, SCF_STATE_STRING_OFFLINE,
    637 		    len));
    638 	else if (state == RESTARTER_STATE_DISABLED)
    639 		return ((ssize_t)strlcpy(string, SCF_STATE_STRING_DISABLED,
    640 		    len));
    641 	else if (state == RESTARTER_STATE_ONLINE)
    642 		return ((ssize_t)strlcpy(string, SCF_STATE_STRING_ONLINE, len));
    643 	else if (state == RESTARTER_STATE_DEGRADED)
    644 		return ((ssize_t)strlcpy(string, SCF_STATE_STRING_DEGRADED,
    645 		    len));
    646 	else
    647 		return ((ssize_t)strlcpy(string, "unknown", len));
    648 }
    649 
    650 /*
    651  * Sets pg to the name property group of s_inst.  If it doesn't exist, it is
    652  * added.
    653  *
    654  * Fails with
    655  *   ECONNABORTED - repository disconnection or unknown libscf error
    656  *   EBADF - inst is not set
    657  *   ECANCELED - inst is deleted
    658  *   EPERM - permission is denied
    659  *   EACCES - backend denied access
    660  *   EROFS - backend readonly
    661  */
    662 static int
    663 instance_get_or_add_pg(scf_instance_t *inst, const char *name,
    664     const char *type, uint32_t flags, scf_propertygroup_t *pg)
    665 {
    666 again:
    667 	if (scf_instance_get_pg(inst, name, pg) == 0)
    668 		return (0);
    669 
    670 	switch (scf_error()) {
    671 	case SCF_ERROR_CONNECTION_BROKEN:
    672 	default:
    673 		return (ECONNABORTED);
    674 
    675 	case SCF_ERROR_NOT_SET:
    676 		return (EBADF);
    677 
    678 	case SCF_ERROR_DELETED:
    679 		return (ECANCELED);
    680 
    681 	case SCF_ERROR_NOT_FOUND:
    682 		break;
    683 
    684 	case SCF_ERROR_HANDLE_MISMATCH:
    685 	case SCF_ERROR_INVALID_ARGUMENT:
    686 		bad_fail("scf_instance_get_pg", scf_error());
    687 	}
    688 
    689 	if (scf_instance_add_pg(inst, name, type, flags, pg) == 0)
    690 		return (0);
    691 
    692 	switch (scf_error()) {
    693 	case SCF_ERROR_CONNECTION_BROKEN:
    694 	default:
    695 		return (ECONNABORTED);
    696 
    697 	case SCF_ERROR_DELETED:
    698 		return (ECANCELED);
    699 
    700 	case SCF_ERROR_EXISTS:
    701 		goto again;
    702 
    703 	case SCF_ERROR_PERMISSION_DENIED:
    704 		return (EPERM);
    705 
    706 	case SCF_ERROR_BACKEND_ACCESS:
    707 		return (EACCES);
    708 
    709 	case SCF_ERROR_BACKEND_READONLY:
    710 		return (EROFS);
    711 
    712 	case SCF_ERROR_HANDLE_MISMATCH:
    713 	case SCF_ERROR_INVALID_ARGUMENT:
    714 	case SCF_ERROR_NOT_SET:			/* should be caught above */
    715 		bad_fail("scf_instance_add_pg", scf_error());
    716 	}
    717 
    718 	return (0);
    719 }
    720 
    721 /*
    722  * Fails with
    723  *   ECONNABORTED
    724  *   ECANCELED - pg was deleted
    725  */
    726 static int
    727 tx_set_value(scf_transaction_t *tx, scf_transaction_entry_t *ent,
    728     const char *pname, scf_type_t ty, scf_value_t *val)
    729 {
    730 	int r;
    731 
    732 	for (;;) {
    733 		if (scf_transaction_property_change_type(tx, ent, pname,
    734 		    ty) == 0)
    735 			break;
    736 
    737 		switch (scf_error()) {
    738 		case SCF_ERROR_CONNECTION_BROKEN:
    739 		default:
    740 			return (ECONNABORTED);
    741 
    742 		case SCF_ERROR_DELETED:
    743 			return (ECANCELED);
    744 
    745 		case SCF_ERROR_NOT_FOUND:
    746 			break;
    747 
    748 		case SCF_ERROR_HANDLE_MISMATCH:
    749 		case SCF_ERROR_INVALID_ARGUMENT:
    750 		case SCF_ERROR_IN_USE:
    751 		case SCF_ERROR_NOT_SET:
    752 			bad_fail("scf_transaction_property_change_type",
    753 			    scf_error());
    754 		}
    755 
    756 		if (scf_transaction_property_new(tx, ent, pname, ty) == 0)
    757 			break;
    758 
    759 		switch (scf_error()) {
    760 		case SCF_ERROR_CONNECTION_BROKEN:
    761 		default:
    762 			return (ECONNABORTED);
    763 
    764 		case SCF_ERROR_DELETED:
    765 			return (ECANCELED);
    766 
    767 		case SCF_ERROR_EXISTS:
    768 			break;
    769 
    770 		case SCF_ERROR_HANDLE_MISMATCH:
    771 		case SCF_ERROR_INVALID_ARGUMENT:
    772 		case SCF_ERROR_IN_USE:
    773 		case SCF_ERROR_NOT_SET:
    774 			bad_fail("scf_transaction_property_new", scf_error());
    775 		}
    776 	}
    777 
    778 	r = scf_entry_add_value(ent, val);
    779 	assert(r == 0);
    780 
    781 	return (0);
    782 }
    783 
    784 /*
    785  * Commit new_state, new_next_state, and aux to the repository for id.  If
    786  * successful, also set id's state and next-state as given, and return 0.
    787  * Fails with
    788  *   ENOMEM - out of memory
    789  *   ECONNABORTED - repository connection broken
    790  *		  - unknown libscf error
    791  *   EINVAL - id->i_fmri is invalid or not an instance FMRI
    792  *   ENOENT - id->i_fmri does not exist
    793  *   EPERM - insufficient permissions
    794  *   EACCES - backend access denied
    795  *   EROFS - backend is readonly
    796  */
    797 int
    798 _restarter_commit_states(scf_handle_t *h, instance_data_t *id,
    799     restarter_instance_state_t new_state,
    800     restarter_instance_state_t new_state_next, const char *aux)
    801 {
    802 	char str_state[MAX_SCF_STATE_STRING_SZ];
    803 	char str_new_state[MAX_SCF_STATE_STRING_SZ];
    804 	char str_state_next[MAX_SCF_STATE_STRING_SZ];
    805 	char str_new_state_next[MAX_SCF_STATE_STRING_SZ];
    806 	int ret = 0, r;
    807 	struct timeval now;
    808 	ssize_t sz;
    809 
    810 	scf_transaction_t *t = NULL;
    811 	scf_transaction_entry_t *t_state = NULL, *t_state_next = NULL;
    812 	scf_transaction_entry_t *t_stime = NULL, *t_aux = NULL;
    813 	scf_value_t *v_state = NULL, *v_state_next = NULL, *v_stime = NULL;
    814 	scf_value_t *v_aux = NULL;
    815 	scf_instance_t *s_inst = NULL;
    816 	scf_propertygroup_t *pg = NULL;
    817 
    818 	assert(new_state != RESTARTER_STATE_NONE);
    819 
    820 	if ((s_inst = scf_instance_create(h)) == NULL ||
    821 	    (pg = scf_pg_create(h)) == NULL ||
    822 	    (t = scf_transaction_create(h)) == NULL ||
    823 	    (t_state = scf_entry_create(h)) == NULL ||
    824 	    (t_state_next = scf_entry_create(h)) == NULL ||
    825 	    (t_stime = scf_entry_create(h)) == NULL ||
    826 	    (t_aux = scf_entry_create(h)) == NULL ||
    827 	    (v_state = scf_value_create(h)) == NULL ||
    828 	    (v_state_next = scf_value_create(h)) == NULL ||
    829 	    (v_stime = scf_value_create(h)) == NULL ||
    830 	    (v_aux = scf_value_create(h)) == NULL) {
    831 		ret = ENOMEM;
    832 		goto out;
    833 	}
    834 
    835 	sz = restarter_state_to_string(new_state, str_new_state,
    836 	    sizeof (str_new_state));
    837 	assert(sz < sizeof (str_new_state));
    838 	sz = restarter_state_to_string(new_state_next, str_new_state_next,
    839 	    sizeof (str_new_state_next));
    840 	assert(sz < sizeof (str_new_state_next));
    841 	sz = restarter_state_to_string(id->i_state, str_state,
    842 	    sizeof (str_state));
    843 	assert(sz < sizeof (str_state));
    844 	sz = restarter_state_to_string(id->i_next_state, str_state_next,
    845 	    sizeof (str_state_next));
    846 	assert(sz < sizeof (str_state_next));
    847 
    848 	ret = gettimeofday(&now, NULL);
    849 	assert(ret != -1);
    850 
    851 	if (scf_handle_decode_fmri(h, id->i_fmri, NULL, NULL, s_inst,
    852 	    NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
    853 		switch (scf_error()) {
    854 		case SCF_ERROR_CONNECTION_BROKEN:
    855 		default:
    856 			ret = ECONNABORTED;
    857 			break;
    858 
    859 		case SCF_ERROR_INVALID_ARGUMENT:
    860 		case SCF_ERROR_CONSTRAINT_VIOLATED:
    861 			ret = EINVAL;
    862 			break;
    863 
    864 		case SCF_ERROR_NOT_FOUND:
    865 			ret = ENOENT;
    866 			break;
    867 
    868 		case SCF_ERROR_HANDLE_MISMATCH:
    869 			bad_fail("scf_handle_decode_fmri", scf_error());
    870 		}
    871 		goto out;
    872 	}
    873 
    874 
    875 	if (scf_value_set_astring(v_state, str_new_state) != 0 ||
    876 	    scf_value_set_astring(v_state_next, str_new_state_next) != 0)
    877 		bad_fail("scf_value_set_astring", scf_error());
    878 
    879 	if (aux) {
    880 		if (scf_value_set_astring(v_aux, aux) != 0)
    881 			bad_fail("scf_value_set_astring", scf_error());
    882 	}
    883 
    884 	if (scf_value_set_time(v_stime, now.tv_sec, now.tv_usec * 1000) != 0)
    885 		bad_fail("scf_value_set_time", scf_error());
    886 
    887 add_pg:
    888 	switch (r = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
    889 	    SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg)) {
    890 	case 0:
    891 		break;
    892 
    893 	case ECONNABORTED:
    894 	case EPERM:
    895 	case EACCES:
    896 	case EROFS:
    897 		ret = r;
    898 		goto out;
    899 
    900 	case ECANCELED:
    901 		ret = ENOENT;
    902 		goto out;
    903 
    904 	case EBADF:
    905 	default:
    906 		bad_fail("instance_get_or_add_pg", r);
    907 	}
    908 
    909 	for (;;) {
    910 		if (scf_transaction_start(t, pg) != 0) {
    911 			switch (scf_error()) {
    912 			case SCF_ERROR_CONNECTION_BROKEN:
    913 			default:
    914 				ret = ECONNABORTED;
    915 				goto out;
    916 
    917 			case SCF_ERROR_NOT_SET:
    918 				goto add_pg;
    919 
    920 			case SCF_ERROR_PERMISSION_DENIED:
    921 				ret = EPERM;
    922 				goto out;
    923 
    924 			case SCF_ERROR_BACKEND_ACCESS:
    925 				ret = EACCES;
    926 				goto out;
    927 
    928 			case SCF_ERROR_BACKEND_READONLY:
    929 				ret = EROFS;
    930 				goto out;
    931 
    932 			case SCF_ERROR_HANDLE_MISMATCH:
    933 			case SCF_ERROR_IN_USE:
    934 				bad_fail("scf_transaction_start", scf_error());
    935 			}
    936 		}
    937 
    938 		if ((r = tx_set_value(t, t_state, SCF_PROPERTY_STATE,
    939 		    SCF_TYPE_ASTRING, v_state)) != 0 ||
    940 		    (r = tx_set_value(t, t_state_next, SCF_PROPERTY_NEXT_STATE,
    941 		    SCF_TYPE_ASTRING, v_state_next)) != 0 ||
    942 		    (r = tx_set_value(t, t_stime, SCF_PROPERTY_STATE_TIMESTAMP,
    943 		    SCF_TYPE_TIME, v_stime)) != 0) {
    944 			switch (r) {
    945 			case ECONNABORTED:
    946 				ret = ECONNABORTED;
    947 				goto out;
    948 
    949 			case ECANCELED:
    950 				scf_transaction_reset(t);
    951 				goto add_pg;
    952 
    953 			default:
    954 				bad_fail("tx_set_value", r);
    955 			}
    956 		}
    957 
    958 		if (aux) {
    959 			if ((r = tx_set_value(t, t_aux, SCF_PROPERTY_AUX_STATE,
    960 			    SCF_TYPE_ASTRING, v_aux)) != 0) {
    961 				switch (r) {
    962 				case ECONNABORTED:
    963 					ret = ECONNABORTED;
    964 					goto out;
    965 
    966 				case ECANCELED:
    967 					scf_transaction_reset(t);
    968 					goto add_pg;
    969 
    970 				default:
    971 					bad_fail("tx_set_value", r);
    972 				}
    973 			}
    974 		}
    975 
    976 		ret = scf_transaction_commit(t);
    977 		if (ret == 1)
    978 			break;
    979 		if (ret == -1) {
    980 			switch (scf_error()) {
    981 			case SCF_ERROR_CONNECTION_BROKEN:
    982 			default:
    983 				ret = ECONNABORTED;
    984 				goto out;
    985 
    986 			case SCF_ERROR_PERMISSION_DENIED:
    987 				ret = EPERM;
    988 				goto out;
    989 
    990 			case SCF_ERROR_BACKEND_ACCESS:
    991 				ret = EACCES;
    992 				goto out;
    993 
    994 			case SCF_ERROR_BACKEND_READONLY:
    995 				ret = EROFS;
    996 				goto out;
    997 
    998 			case SCF_ERROR_NOT_SET:
    999 				bad_fail("scf_transaction_commit", scf_error());
   1000 			}
   1001 		}
   1002 
   1003 		scf_transaction_reset(t);
   1004 		if (scf_pg_update(pg) == -1) {
   1005 			switch (scf_error()) {
   1006 			case SCF_ERROR_CONNECTION_BROKEN:
   1007 			default:
   1008 				ret = ECONNABORTED;
   1009 				goto out;
   1010 
   1011 			case SCF_ERROR_NOT_SET:
   1012 				goto add_pg;
   1013 			}
   1014 		}
   1015 	}
   1016 
   1017 	id->i_state = new_state;
   1018 	id->i_next_state = new_state_next;
   1019 	ret = 0;
   1020 
   1021 out:
   1022 	scf_transaction_destroy(t);
   1023 	scf_entry_destroy(t_state);
   1024 	scf_entry_destroy(t_state_next);
   1025 	scf_entry_destroy(t_stime);
   1026 	scf_entry_destroy(t_aux);
   1027 	scf_value_destroy(v_state);
   1028 	scf_value_destroy(v_state_next);
   1029 	scf_value_destroy(v_stime);
   1030 	scf_value_destroy(v_aux);
   1031 	scf_pg_destroy(pg);
   1032 	scf_instance_destroy(s_inst);
   1033 
   1034 	return (ret);
   1035 }
   1036 
   1037 /*
   1038  * Fails with
   1039  *   EINVAL - type is invalid
   1040  *   ENOMEM
   1041  *   ECONNABORTED - repository connection broken
   1042  *   EBADF - s_inst is not set
   1043  *   ECANCELED - s_inst is deleted
   1044  *   EPERM - permission denied
   1045  *   EACCES - backend access denied
   1046  *   EROFS - backend readonly
   1047  */
   1048 int
   1049 restarter_remove_contract(scf_instance_t *s_inst, ctid_t contract_id,
   1050     restarter_contract_type_t type)
   1051 {
   1052 	scf_handle_t *h;
   1053 	scf_transaction_t *t = NULL;
   1054 	scf_transaction_entry_t *t_cid = NULL;
   1055 	scf_propertygroup_t *pg = NULL;
   1056 	scf_property_t *prop = NULL;
   1057 	scf_value_t *val;
   1058 	scf_iter_t *iter = NULL;
   1059 	const char *pname;
   1060 	int ret = 0, primary;
   1061 	uint64_t c;
   1062 
   1063 	switch (type) {
   1064 	case RESTARTER_CONTRACT_PRIMARY:
   1065 		primary = 1;
   1066 		break;
   1067 	case RESTARTER_CONTRACT_TRANSIENT:
   1068 		primary = 0;
   1069 		break;
   1070 	default:
   1071 		return (EINVAL);
   1072 	}
   1073 
   1074 	h = scf_instance_handle(s_inst);
   1075 
   1076 	pg = scf_pg_create(h);
   1077 	prop = scf_property_create(h);
   1078 	iter = scf_iter_create(h);
   1079 	t = scf_transaction_create(h);
   1080 
   1081 	if (pg == NULL || prop == NULL || iter == NULL || t == NULL) {
   1082 		ret = ENOMEM;
   1083 		goto remove_contract_cleanup;
   1084 	}
   1085 
   1086 add:
   1087 	scf_transaction_destroy_children(t);
   1088 	ret = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
   1089 	    SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
   1090 	if (ret != 0)
   1091 		goto remove_contract_cleanup;
   1092 
   1093 	pname = primary? SCF_PROPERTY_CONTRACT :
   1094 	    SCF_PROPERTY_TRANSIENT_CONTRACT;
   1095 
   1096 	for (;;) {
   1097 		if (scf_transaction_start(t, pg) != 0) {
   1098 			switch (scf_error()) {
   1099 			case SCF_ERROR_CONNECTION_BROKEN:
   1100 			default:
   1101 				ret = ECONNABORTED;
   1102 				goto remove_contract_cleanup;
   1103 
   1104 			case SCF_ERROR_DELETED:
   1105 				goto add;
   1106 
   1107 			case SCF_ERROR_PERMISSION_DENIED:
   1108 				ret = EPERM;
   1109 				goto remove_contract_cleanup;
   1110 
   1111 			case SCF_ERROR_BACKEND_ACCESS:
   1112 				ret = EACCES;
   1113 				goto remove_contract_cleanup;
   1114 
   1115 			case SCF_ERROR_BACKEND_READONLY:
   1116 				ret = EROFS;
   1117 				goto remove_contract_cleanup;
   1118 
   1119 			case SCF_ERROR_HANDLE_MISMATCH:
   1120 			case SCF_ERROR_IN_USE:
   1121 			case SCF_ERROR_NOT_SET:
   1122 				bad_fail("scf_transaction_start", scf_error());
   1123 			}
   1124 		}
   1125 
   1126 		t_cid = scf_entry_create(h);
   1127 
   1128 		if (scf_pg_get_property(pg, pname, prop) == 0) {
   1129 replace:
   1130 			if (scf_transaction_property_change_type(t, t_cid,
   1131 			    pname, SCF_TYPE_COUNT) != 0) {
   1132 				switch (scf_error()) {
   1133 				case SCF_ERROR_CONNECTION_BROKEN:
   1134 				default:
   1135 					ret = ECONNABORTED;
   1136 					goto remove_contract_cleanup;
   1137 
   1138 				case SCF_ERROR_DELETED:
   1139 					scf_entry_destroy(t_cid);
   1140 					goto add;
   1141 
   1142 				case SCF_ERROR_NOT_FOUND:
   1143 					goto new;
   1144 
   1145 				case SCF_ERROR_HANDLE_MISMATCH:
   1146 				case SCF_ERROR_INVALID_ARGUMENT:
   1147 				case SCF_ERROR_IN_USE:
   1148 				case SCF_ERROR_NOT_SET:
   1149 					bad_fail(
   1150 					"scf_transaction_property_changetype",
   1151 					    scf_error());
   1152 				}
   1153 			}
   1154 
   1155 			if (scf_property_is_type(prop, SCF_TYPE_COUNT) == 0) {
   1156 				if (scf_iter_property_values(iter, prop) != 0) {
   1157 					switch (scf_error()) {
   1158 					case SCF_ERROR_CONNECTION_BROKEN:
   1159 					default:
   1160 						ret = ECONNABORTED;
   1161 						goto remove_contract_cleanup;
   1162 
   1163 					case SCF_ERROR_NOT_SET:
   1164 					case SCF_ERROR_HANDLE_MISMATCH:
   1165 						bad_fail(
   1166 						    "scf_iter_property_values",
   1167 						    scf_error());
   1168 					}
   1169 				}
   1170 
   1171 next_val:
   1172 				val = scf_value_create(h);
   1173 				if (val == NULL) {
   1174 					assert(scf_error() ==
   1175 					    SCF_ERROR_NO_MEMORY);
   1176 					ret = ENOMEM;
   1177 					goto remove_contract_cleanup;
   1178 				}
   1179 
   1180 				ret = scf_iter_next_value(iter, val);
   1181 				if (ret == -1) {
   1182 					switch (scf_error()) {
   1183 					case SCF_ERROR_CONNECTION_BROKEN:
   1184 						ret = ECONNABORTED;
   1185 						goto remove_contract_cleanup;
   1186 
   1187 					case SCF_ERROR_DELETED:
   1188 						scf_value_destroy(val);
   1189 						goto add;
   1190 
   1191 					case SCF_ERROR_HANDLE_MISMATCH:
   1192 					case SCF_ERROR_INVALID_ARGUMENT:
   1193 					case SCF_ERROR_PERMISSION_DENIED:
   1194 					default:
   1195 						bad_fail("scf_iter_next_value",
   1196 						    scf_error());
   1197 					}
   1198 				}
   1199 
   1200 				if (ret == 1) {
   1201 					ret = scf_value_get_count(val, &c);
   1202 					assert(ret == 0);
   1203 
   1204 					if (c != contract_id) {
   1205 						ret = scf_entry_add_value(t_cid,
   1206 						    val);
   1207 						assert(ret == 0);
   1208 					} else {
   1209 						scf_value_destroy(val);
   1210 					}
   1211 
   1212 					goto next_val;
   1213 				}
   1214 
   1215 				scf_value_destroy(val);
   1216 			} else {
   1217 				switch (scf_error()) {
   1218 				case SCF_ERROR_CONNECTION_BROKEN:
   1219 				default:
   1220 					ret = ECONNABORTED;
   1221 					goto remove_contract_cleanup;
   1222 
   1223 				case SCF_ERROR_TYPE_MISMATCH:
   1224 					break;
   1225 
   1226 				case SCF_ERROR_INVALID_ARGUMENT:
   1227 				case SCF_ERROR_NOT_SET:
   1228 					bad_fail("scf_property_is_type",
   1229 					    scf_error());
   1230 				}
   1231 			}
   1232 		} else {
   1233 			switch (scf_error()) {
   1234 			case SCF_ERROR_CONNECTION_BROKEN:
   1235 			default:
   1236 				ret = ECONNABORTED;
   1237 				goto remove_contract_cleanup;
   1238 
   1239 			case SCF_ERROR_DELETED:
   1240 				scf_entry_destroy(t_cid);
   1241 				goto add;
   1242 
   1243 			case SCF_ERROR_NOT_FOUND:
   1244 				break;
   1245 
   1246 			case SCF_ERROR_HANDLE_MISMATCH:
   1247 			case SCF_ERROR_INVALID_ARGUMENT:
   1248 			case SCF_ERROR_NOT_SET:
   1249 				bad_fail("scf_pg_get_property", scf_error());
   1250 			}
   1251 
   1252 new:
   1253 			if (scf_transaction_property_new(t, t_cid, pname,
   1254 			    SCF_TYPE_COUNT) != 0) {
   1255 				switch (scf_error()) {
   1256 				case SCF_ERROR_CONNECTION_BROKEN:
   1257 				default:
   1258 					ret = ECONNABORTED;
   1259 					goto remove_contract_cleanup;
   1260 
   1261 				case SCF_ERROR_DELETED:
   1262 					scf_entry_destroy(t_cid);
   1263 					goto add;
   1264 
   1265 				case SCF_ERROR_EXISTS:
   1266 					goto replace;
   1267 
   1268 				case SCF_ERROR_HANDLE_MISMATCH:
   1269 				case SCF_ERROR_INVALID_ARGUMENT:
   1270 				case SCF_ERROR_NOT_SET:
   1271 					bad_fail("scf_transaction_property_new",
   1272 					    scf_error());
   1273 				}
   1274 			}
   1275 		}
   1276 
   1277 		ret = scf_transaction_commit(t);
   1278 		if (ret == -1) {
   1279 			switch (scf_error()) {
   1280 			case SCF_ERROR_CONNECTION_BROKEN:
   1281 			default:
   1282 				ret = ECONNABORTED;
   1283 				goto remove_contract_cleanup;
   1284 
   1285 			case SCF_ERROR_DELETED:
   1286 				goto add;
   1287 
   1288 			case SCF_ERROR_PERMISSION_DENIED:
   1289 				ret = EPERM;
   1290 				goto remove_contract_cleanup;
   1291 
   1292 			case SCF_ERROR_BACKEND_ACCESS:
   1293 				ret = EACCES;
   1294 				goto remove_contract_cleanup;
   1295 
   1296 			case SCF_ERROR_BACKEND_READONLY:
   1297 				ret = EROFS;
   1298 				goto remove_contract_cleanup;
   1299 
   1300 			case SCF_ERROR_NOT_SET:
   1301 				bad_fail("scf_transaction_commit", scf_error());
   1302 			}
   1303 		}
   1304 		if (ret == 1) {
   1305 			ret = 0;
   1306 			break;
   1307 		}
   1308 
   1309 		scf_transaction_destroy_children(t);
   1310 		if (scf_pg_update(pg) == -1) {
   1311 			switch (scf_error()) {
   1312 			case SCF_ERROR_CONNECTION_BROKEN:
   1313 			default:
   1314 				ret = ECONNABORTED;
   1315 				goto remove_contract_cleanup;
   1316 
   1317 			case SCF_ERROR_DELETED:
   1318 				goto add;
   1319 
   1320 			case SCF_ERROR_NOT_SET:
   1321 				bad_fail("scf_pg_update", scf_error());
   1322 			}
   1323 		}
   1324 	}
   1325 
   1326 remove_contract_cleanup:
   1327 	scf_transaction_destroy_children(t);
   1328 	scf_transaction_destroy(t);
   1329 	scf_iter_destroy(iter);
   1330 	scf_property_destroy(prop);
   1331 	scf_pg_destroy(pg);
   1332 
   1333 	return (ret);
   1334 }
   1335 
   1336 /*
   1337  * Fails with
   1338  *   EINVAL - type is invalid
   1339  *   ENOMEM
   1340  *   ECONNABORTED - repository disconnection
   1341  *   EBADF - s_inst is not set
   1342  *   ECANCELED - s_inst is deleted
   1343  *   EPERM
   1344  *   EACCES
   1345  *   EROFS
   1346  */
   1347 int
   1348 restarter_store_contract(scf_instance_t *s_inst, ctid_t contract_id,
   1349     restarter_contract_type_t type)
   1350 {
   1351 	scf_handle_t *h;
   1352 	scf_transaction_t *t = NULL;
   1353 	scf_transaction_entry_t *t_cid = NULL;
   1354 	scf_value_t *val;
   1355 	scf_propertygroup_t *pg = NULL;
   1356 	scf_property_t *prop = NULL;
   1357 	scf_iter_t *iter = NULL;
   1358 	const char *pname;
   1359 	int ret = 0, primary;
   1360 
   1361 	if (type == RESTARTER_CONTRACT_PRIMARY)
   1362 		primary = 1;
   1363 	else if (type == RESTARTER_CONTRACT_TRANSIENT)
   1364 		primary = 0;
   1365 	else
   1366 		return (EINVAL);
   1367 
   1368 	h = scf_instance_handle(s_inst);
   1369 
   1370 	pg = scf_pg_create(h);
   1371 	prop = scf_property_create(h);
   1372 	iter = scf_iter_create(h);
   1373 	t = scf_transaction_create(h);
   1374 
   1375 	if (pg == NULL || prop == NULL || iter == NULL || t == NULL) {
   1376 		ret = ENOMEM;
   1377 		goto out;
   1378 	}
   1379 
   1380 add:
   1381 	scf_transaction_destroy_children(t);
   1382 	ret = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
   1383 	    SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
   1384 	if (ret != 0)
   1385 		goto out;
   1386 
   1387 	pname = primary ? SCF_PROPERTY_CONTRACT :
   1388 	    SCF_PROPERTY_TRANSIENT_CONTRACT;
   1389 
   1390 	for (;;) {
   1391 		if (scf_transaction_start(t, pg) != 0) {
   1392 			switch (scf_error()) {
   1393 			case SCF_ERROR_CONNECTION_BROKEN:
   1394 			default:
   1395 				ret = ECONNABORTED;
   1396 				goto out;
   1397 
   1398 			case SCF_ERROR_DELETED:
   1399 				goto add;
   1400 
   1401 			case SCF_ERROR_PERMISSION_DENIED:
   1402 				ret = EPERM;
   1403 				goto out;
   1404 
   1405 			case SCF_ERROR_BACKEND_ACCESS:
   1406 				ret = EACCES;
   1407 				goto out;
   1408 
   1409 			case SCF_ERROR_BACKEND_READONLY:
   1410 				ret = EROFS;
   1411 				goto out;
   1412 
   1413 			case SCF_ERROR_HANDLE_MISMATCH:
   1414 			case SCF_ERROR_IN_USE:
   1415 			case SCF_ERROR_NOT_SET:
   1416 				bad_fail("scf_transaction_start", scf_error());
   1417 			}
   1418 		}
   1419 
   1420 		t_cid = scf_entry_create(h);
   1421 		if (t_cid == NULL) {
   1422 			ret = ENOMEM;
   1423 			goto out;
   1424 		}
   1425 
   1426 		if (scf_pg_get_property(pg, pname, prop) == 0) {
   1427 replace:
   1428 			if (scf_transaction_property_change_type(t, t_cid,
   1429 			    pname, SCF_TYPE_COUNT) != 0) {
   1430 				switch (scf_error()) {
   1431 				case SCF_ERROR_CONNECTION_BROKEN:
   1432 				default:
   1433 					ret = ECONNABORTED;
   1434 					goto out;
   1435 
   1436 				case SCF_ERROR_DELETED:
   1437 					scf_entry_destroy(t_cid);
   1438 					goto add;
   1439 
   1440 				case SCF_ERROR_NOT_FOUND:
   1441 					goto new;
   1442 
   1443 				case SCF_ERROR_HANDLE_MISMATCH:
   1444 				case SCF_ERROR_INVALID_ARGUMENT:
   1445 				case SCF_ERROR_IN_USE:
   1446 				case SCF_ERROR_NOT_SET:
   1447 					bad_fail(
   1448 					"scf_transaction_propert_change_type",
   1449 					    scf_error());
   1450 				}
   1451 			}
   1452 
   1453 			if (scf_property_is_type(prop, SCF_TYPE_COUNT) == 0) {
   1454 				if (scf_iter_property_values(iter, prop) != 0) {
   1455 					switch (scf_error()) {
   1456 					case SCF_ERROR_CONNECTION_BROKEN:
   1457 					default:
   1458 						ret = ECONNABORTED;
   1459 						goto out;
   1460 
   1461 					case SCF_ERROR_NOT_SET:
   1462 					case SCF_ERROR_HANDLE_MISMATCH:
   1463 						bad_fail(
   1464 						    "scf_iter_property_values",
   1465 						    scf_error());
   1466 					}
   1467 				}
   1468 
   1469 next_val:
   1470 				val = scf_value_create(h);
   1471 				if (val == NULL) {
   1472 					assert(scf_error() ==
   1473 					    SCF_ERROR_NO_MEMORY);
   1474 					ret = ENOMEM;
   1475 					goto out;
   1476 				}
   1477 
   1478 				ret = scf_iter_next_value(iter, val);
   1479 				if (ret == -1) {
   1480 					switch (scf_error()) {
   1481 					case SCF_ERROR_CONNECTION_BROKEN:
   1482 					default:
   1483 						ret = ECONNABORTED;
   1484 						goto out;
   1485 
   1486 					case SCF_ERROR_DELETED:
   1487 						scf_value_destroy(val);
   1488 						goto add;
   1489 
   1490 					case SCF_ERROR_HANDLE_MISMATCH:
   1491 					case SCF_ERROR_INVALID_ARGUMENT:
   1492 					case SCF_ERROR_PERMISSION_DENIED:
   1493 						bad_fail(
   1494 						    "scf_iter_next_value",
   1495 						    scf_error());
   1496 					}
   1497 				}
   1498 
   1499 				if (ret == 1) {
   1500 					ret = scf_entry_add_value(t_cid, val);
   1501 					assert(ret == 0);
   1502 
   1503 					goto next_val;
   1504 				}
   1505 
   1506 				scf_value_destroy(val);
   1507 			} else {
   1508 				switch (scf_error()) {
   1509 				case SCF_ERROR_CONNECTION_BROKEN:
   1510 				default:
   1511 					ret = ECONNABORTED;
   1512 					goto out;
   1513 
   1514 				case SCF_ERROR_TYPE_MISMATCH:
   1515 					break;
   1516 
   1517 				case SCF_ERROR_INVALID_ARGUMENT:
   1518 				case SCF_ERROR_NOT_SET:
   1519 					bad_fail("scf_property_is_type",
   1520 					    scf_error());
   1521 				}
   1522 			}
   1523 		} else {
   1524 			switch (scf_error()) {
   1525 			case SCF_ERROR_CONNECTION_BROKEN:
   1526 			default:
   1527 				ret = ECONNABORTED;
   1528 				goto out;
   1529 
   1530 			case SCF_ERROR_DELETED:
   1531 				scf_entry_destroy(t_cid);
   1532 				goto add;
   1533 
   1534 			case SCF_ERROR_NOT_FOUND:
   1535 				break;
   1536 
   1537 			case SCF_ERROR_HANDLE_MISMATCH:
   1538 			case SCF_ERROR_INVALID_ARGUMENT:
   1539 			case SCF_ERROR_NOT_SET:
   1540 				bad_fail("scf_pg_get_property", scf_error());
   1541 			}
   1542 
   1543 new:
   1544 			if (scf_transaction_property_new(t, t_cid, pname,
   1545 			    SCF_TYPE_COUNT) != 0) {
   1546 				switch (scf_error()) {
   1547 				case SCF_ERROR_CONNECTION_BROKEN:
   1548 				default:
   1549 					ret = ECONNABORTED;
   1550 					goto out;
   1551 
   1552 				case SCF_ERROR_DELETED:
   1553 					scf_entry_destroy(t_cid);
   1554 					goto add;
   1555 
   1556 				case SCF_ERROR_EXISTS:
   1557 					goto replace;
   1558 
   1559 				case SCF_ERROR_HANDLE_MISMATCH:
   1560 				case SCF_ERROR_INVALID_ARGUMENT:
   1561 				case SCF_ERROR_NOT_SET:
   1562 					bad_fail("scf_transaction_property_new",
   1563 					    scf_error());
   1564 				}
   1565 			}
   1566 		}
   1567 
   1568 		val = scf_value_create(h);
   1569 		if (val == NULL) {
   1570 			assert(scf_error() == SCF_ERROR_NO_MEMORY);
   1571 			ret = ENOMEM;
   1572 			goto out;
   1573 		}
   1574 
   1575 		scf_value_set_count(val, contract_id);
   1576 		ret = scf_entry_add_value(t_cid, val);
   1577 		assert(ret == 0);
   1578 
   1579 		ret = scf_transaction_commit(t);
   1580 		if (ret == -1) {
   1581 			switch (scf_error()) {
   1582 			case SCF_ERROR_CONNECTION_BROKEN:
   1583 			default:
   1584 				ret = ECONNABORTED;
   1585 				goto out;
   1586 
   1587 			case SCF_ERROR_DELETED:
   1588 				goto add;
   1589 
   1590 			case SCF_ERROR_PERMISSION_DENIED:
   1591 				ret = EPERM;
   1592 				goto out;
   1593 
   1594 			case SCF_ERROR_BACKEND_ACCESS:
   1595 				ret = EACCES;
   1596 				goto out;
   1597 
   1598 			case SCF_ERROR_BACKEND_READONLY:
   1599 				ret = EROFS;
   1600 				goto out;
   1601 
   1602 			case SCF_ERROR_NOT_SET:
   1603 				bad_fail("scf_transaction_commit", scf_error());
   1604 			}
   1605 		}
   1606 		if (ret == 1) {
   1607 			ret = 0;
   1608 			break;
   1609 		}
   1610 
   1611 		scf_transaction_destroy_children(t);
   1612 		if (scf_pg_update(pg) == -1) {
   1613 			switch (scf_error()) {
   1614 			case SCF_ERROR_CONNECTION_BROKEN:
   1615 			default:
   1616 				ret = ECONNABORTED;
   1617 				goto out;
   1618 
   1619 			case SCF_ERROR_DELETED:
   1620 				goto add;
   1621 
   1622 			case SCF_ERROR_NOT_SET:
   1623 				bad_fail("scf_pg_update", scf_error());
   1624 			}
   1625 		}
   1626 	}
   1627 
   1628 out:
   1629 	scf_transaction_destroy_children(t);
   1630 	scf_transaction_destroy(t);
   1631 	scf_iter_destroy(iter);
   1632 	scf_property_destroy(prop);
   1633 	scf_pg_destroy(pg);
   1634 
   1635 	return (ret);
   1636 }
   1637 
   1638 int
   1639 restarter_rm_libs_loadable()
   1640 {
   1641 	void *libhndl;
   1642 
   1643 	if (method_context_safety)
   1644 		return (1);
   1645 
   1646 	if ((libhndl = dlopen("libpool.so", RTLD_LAZY | RTLD_LOCAL)) == NULL)
   1647 		return (0);
   1648 
   1649 	(void) dlclose(libhndl);
   1650 
   1651 	if ((libhndl = dlopen("libproject.so", RTLD_LAZY | RTLD_LOCAL)) == NULL)
   1652 		return (0);
   1653 
   1654 	(void) dlclose(libhndl);
   1655 
   1656 	method_context_safety = 1;
   1657 
   1658 	return (1);
   1659 }
   1660 
   1661 static int
   1662 get_astring_val(scf_propertygroup_t *pg, const char *name, char *buf,
   1663     size_t bufsz, scf_property_t *prop, scf_value_t *val)
   1664 {
   1665 	ssize_t szret;
   1666 
   1667 	if (pg == NULL)
   1668 		return (-1);
   1669 
   1670 	if (scf_pg_get_property(pg, name, prop) != SCF_SUCCESS) {
   1671 		if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
   1672 			uu_die(rcbroken);
   1673 		return (-1);
   1674 	}
   1675 
   1676 	if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
   1677 		if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
   1678 			uu_die(rcbroken);
   1679 		return (-1);
   1680 	}
   1681 
   1682 	szret = scf_value_get_astring(val, buf, bufsz);
   1683 
   1684 	return (szret >= 0 ? 0 : -1);
   1685 }
   1686 
   1687 static int
   1688 get_boolean_val(scf_propertygroup_t *pg, const char *name, uint8_t *b,
   1689     scf_property_t *prop, scf_value_t *val)
   1690 {
   1691 	if (scf_pg_get_property(pg, name, prop) != SCF_SUCCESS) {
   1692 		if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
   1693 			uu_die(rcbroken);
   1694 		return (-1);
   1695 	}
   1696 
   1697 	if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
   1698 		if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
   1699 			uu_die(rcbroken);
   1700 		return (-1);
   1701 	}
   1702 
   1703 	if (scf_value_get_boolean(val, b))
   1704 		return (-1);
   1705 
   1706 	return (0);
   1707 }
   1708 
   1709 /*
   1710  * Try to load mcp->pwd, if it isn't already.
   1711  * Fails with
   1712  *   ENOMEM - malloc() failed
   1713  *   ENOENT - no entry found
   1714  *   EIO - I/O error
   1715  *   EMFILE - process out of file descriptors
   1716  *   ENFILE - system out of file handles
   1717  */
   1718 static int
   1719 lookup_pwd(struct method_context *mcp)
   1720 {
   1721 	struct passwd *pwdp;
   1722 
   1723 	if (mcp->pwbuf != NULL && mcp->pwd.pw_uid == mcp->uid)
   1724 		return (0);
   1725 
   1726 	if (mcp->pwbuf == NULL) {
   1727 		mcp->pwbufsz = sysconf(_SC_GETPW_R_SIZE_MAX);
   1728 		assert(mcp->pwbufsz >= 0);
   1729 		mcp->pwbuf = malloc(mcp->pwbufsz);
   1730 		if (mcp->pwbuf == NULL)
   1731 			return (ENOMEM);
   1732 	}
   1733 
   1734 	do {
   1735 		errno = 0;
   1736 		pwdp = getpwuid_r(mcp->uid, &mcp->pwd, mcp->pwbuf,
   1737 		    mcp->pwbufsz);
   1738 	} while (pwdp == NULL && errno == EINTR);
   1739 	if (pwdp != NULL)
   1740 		return (0);
   1741 
   1742 	free(mcp->pwbuf);
   1743 	mcp->pwbuf = NULL;
   1744 
   1745 	switch (errno) {
   1746 	case 0:
   1747 	default:
   1748 		/*
   1749 		 * Until bug 5065780 is fixed, getpwuid_r() can fail with
   1750 		 * ENOENT, particularly on the miniroot.  Since the
   1751 		 * documentation is inaccurate, we'll return ENOENT for unknown
   1752 		 * errors.
   1753 		 */
   1754 		return (ENOENT);
   1755 
   1756 	case EIO:
   1757 	case EMFILE:
   1758 	case ENFILE:
   1759 		return (errno);
   1760 
   1761 	case ERANGE:
   1762 		bad_fail("getpwuid_r", errno);
   1763 		/* NOTREACHED */
   1764 	}
   1765 }
   1766 
   1767 /*
   1768  * Get the user id for str.  Returns 0 on success or
   1769  *   ERANGE	the uid is too big
   1770  *   EINVAL	the string starts with a digit, but is not a valid uid
   1771  *   ENOMEM	out of memory
   1772  *   ENOENT	no passwd entry for str
   1773  *   EIO	an I/O error has occurred
   1774  *   EMFILE/ENFILE  out of file descriptors
   1775  */
   1776 int
   1777 get_uid(const char *str, struct method_context *ci, uid_t *uidp)
   1778 {
   1779 	if (isdigit(str[0])) {
   1780 		uid_t uid;
   1781 		char *cp;
   1782 
   1783 		errno = 0;
   1784 		uid = strtol(str, &cp, 10);
   1785 
   1786 		if (uid == 0 && errno != 0) {
   1787 			assert(errno != EINVAL);
   1788 			return (errno);
   1789 		}
   1790 
   1791 		for (; *cp != '\0'; ++cp)
   1792 			if (*cp != ' ' || *cp != '\t')
   1793 				return (EINVAL);
   1794 
   1795 		if (uid > UID_MAX)
   1796 			return (EINVAL);
   1797 
   1798 		*uidp = uid;
   1799 		return (0);
   1800 	} else {
   1801 		struct passwd *pwdp;
   1802 
   1803 		if (ci->pwbuf == NULL) {
   1804 			ci->pwbufsz = sysconf(_SC_GETPW_R_SIZE_MAX);
   1805 			ci->pwbuf = malloc(ci->pwbufsz);
   1806 			if (ci->pwbuf == NULL)
   1807 				return (ENOMEM);
   1808 		}
   1809 
   1810 		do {
   1811 			errno = 0;
   1812 			pwdp =
   1813 			    getpwnam_r(str, &ci->pwd, ci->pwbuf, ci->pwbufsz);
   1814 		} while (pwdp == NULL && errno == EINTR);
   1815 
   1816 		if (pwdp != NULL) {
   1817 			*uidp = ci->pwd.pw_uid;
   1818 			return (0);
   1819 		} else {
   1820 			free(ci->pwbuf);
   1821 			ci->pwbuf = NULL;
   1822 			switch (errno) {
   1823 			case 0:
   1824 				return (ENOENT);
   1825 
   1826 			case ENOENT:
   1827 			case EIO:
   1828 			case EMFILE:
   1829 			case ENFILE:
   1830 				return (errno);
   1831 
   1832 			case ERANGE:
   1833 			default:
   1834 				bad_fail("getpwnam_r", errno);
   1835 				/* NOTREACHED */
   1836 			}
   1837 		}
   1838 	}
   1839 }
   1840 
   1841 gid_t
   1842 get_gid(const char *str)
   1843 {
   1844 	if (isdigit(str[0])) {
   1845 		gid_t gid;
   1846 		char *cp;
   1847 
   1848 		errno = 0;
   1849 		gid = strtol(str, &cp, 10);
   1850 
   1851 		if (gid == 0 && errno != 0)
   1852 			return ((gid_t)-1);
   1853 
   1854 		for (; *cp != '\0'; ++cp)
   1855 			if (*cp != ' ' || *cp != '\t')
   1856 				return ((gid_t)-1);
   1857 
   1858 		return (gid);
   1859 	} else {
   1860 		struct group grp, *ret;
   1861 		char *buffer;
   1862 		size_t buflen;
   1863 
   1864 		buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
   1865 		buffer = malloc(buflen);
   1866 		if (buffer == NULL)
   1867 			uu_die(allocfail);
   1868 
   1869 		errno = 0;
   1870 		ret = getgrnam_r(str, &grp, buffer, buflen);
   1871 		free(buffer);
   1872 
   1873 		return (ret == NULL ? (gid_t)-1 : grp.gr_gid);
   1874 	}
   1875 }
   1876 
   1877 /*
   1878  * Fails with
   1879  *   ENOMEM - out of memory
   1880  *   ENOENT - no passwd entry
   1881  *	      no project entry
   1882  *   EIO - an I/O error occurred
   1883  *   EMFILE - the process is out of file descriptors
   1884  *   ENFILE - the system is out of file handles
   1885  *   ERANGE - the project id is out of range
   1886  *   EINVAL - str is invalid
   1887  *   E2BIG - the project entry was too big
   1888  *   -1 - the name service switch is misconfigured
   1889  */
   1890 int
   1891 get_projid(const char *str, struct method_context *cip)
   1892 {
   1893 	int ret;
   1894 	void *buf;
   1895 	const size_t bufsz = PROJECT_BUFSZ;
   1896 	struct project proj, *pp;
   1897 
   1898 	if (strcmp(str, ":default") == 0) {
   1899 		if (cip->uid == 0) {
   1900 			/* Don't change project for root services */
   1901 			cip->project = NULL;
   1902 			return (0);
   1903 		}
   1904 
   1905 		switch (ret = lookup_pwd(cip)) {
   1906 		case 0:
   1907 			break;
   1908 
   1909 		case ENOMEM:
   1910 		case ENOENT:
   1911 		case EIO:
   1912 		case EMFILE:
   1913 		case ENFILE:
   1914 			return (ret);
   1915 
   1916 		default:
   1917 			bad_fail("lookup_pwd", ret);
   1918 		}
   1919 
   1920 		buf = malloc(bufsz);
   1921 		if (buf == NULL)
   1922 			return (ENOMEM);
   1923 
   1924 		do {
   1925 			errno = 0;
   1926 			pp = getdefaultproj(cip->pwd.pw_name, &proj, buf,
   1927 			    bufsz);
   1928 		} while (pp == NULL && errno == EINTR);
   1929 
   1930 		/* to be continued ... */
   1931 	} else {
   1932 		projid_t projid;
   1933 		char *cp;
   1934 
   1935 		if (!isdigit(str[0])) {
   1936 			cip->project = strdup(str);
   1937 			return (cip->project != NULL ? 0 : ENOMEM);
   1938 		}
   1939 
   1940 		errno = 0;
   1941 		projid = strtol(str, &cp, 10);
   1942 
   1943 		if (projid == 0 && errno != 0) {
   1944 			assert(errno == ERANGE);
   1945 			return (errno);
   1946 		}
   1947 
   1948 		for (; *cp != '\0'; ++cp)
   1949 			if (*cp != ' ' || *cp != '\t')
   1950 				return (EINVAL);
   1951 
   1952 		if (projid > MAXPROJID)
   1953 			return (ERANGE);
   1954 
   1955 		buf = malloc(bufsz);
   1956 		if (buf == NULL)
   1957 			return (ENOMEM);
   1958 
   1959 		do {
   1960 			errno = 0;
   1961 			pp = getprojbyid(projid, &proj, buf, bufsz);
   1962 		} while (pp == NULL && errno == EINTR);
   1963 	}
   1964 
   1965 	if (pp) {
   1966 		cip->project = strdup(pp->pj_name);
   1967 		free(buf);
   1968 		return (cip->project != NULL ? 0 : ENOMEM);
   1969 	}
   1970 
   1971 	free(buf);
   1972 
   1973 	switch (errno) {
   1974 	case 0:
   1975 		return (ENOENT);
   1976 
   1977 	case EIO:
   1978 	case EMFILE:
   1979 	case ENFILE:
   1980 		return (errno);
   1981 
   1982 	case ERANGE:
   1983 		return (E2BIG);
   1984 
   1985 	default:
   1986 		return (-1);
   1987 	}
   1988 }
   1989 
   1990 /*
   1991  * Parse the supp_groups property value and populate ci->groups.  Returns
   1992  * EINVAL (get_gid() failed for one of the components), E2BIG (the property has
   1993  * more than NGROUPS_MAX-1 groups), or 0 on success.
   1994  */
   1995 int
   1996 get_groups(char *str, struct method_context *ci)
   1997 {
   1998 	char *cp, *end, *next;
   1999 	uint_t i;
   2000 
   2001 	const char * const whitespace = " \t";
   2002 	const char * const illegal = ", \t";
   2003 
   2004 	if (str[0] == '\0') {
   2005 		ci->ngroups = 0;
   2006 		return (0);
   2007 	}
   2008 
   2009 	for (cp = str, i = 0; *cp != '\0'; ) {
   2010 		/* skip whitespace */
   2011 		cp += strspn(cp, whitespace);
   2012 
   2013 		/* find the end */
   2014 		end = cp + strcspn(cp, illegal);
   2015 
   2016 		/* skip whitespace after end */
   2017 		next = end + strspn(end, whitespace);
   2018 
   2019 		/* if there's a comma, it separates the fields */
   2020 		if (*next == ',')
   2021 			++next;
   2022 
   2023 		*end = '\0';
   2024 
   2025 		if ((ci->groups[i] = get_gid(cp)) == (gid_t)-1) {
   2026 			ci->ngroups = 0;
   2027 			return (EINVAL);
   2028 		}
   2029 
   2030 		++i;
   2031 		if (i > NGROUPS_MAX - 1) {
   2032 			ci->ngroups = 0;
   2033 			return (E2BIG);
   2034 		}
   2035 
   2036 		cp = next;
   2037 	}
   2038 
   2039 	ci->ngroups = i;
   2040 	return (0);
   2041 }
   2042 
   2043 
   2044 /*
   2045  * Return an error message structure containing the error message
   2046  * with context, and the error so the caller can make a decision
   2047  * on what to do next.
   2048  *
   2049  * Because get_ids uses the mc_error_create() function which can
   2050  * reallocate the merr, this function must return the merr pointer
   2051  * in case it was reallocated.
   2052  */
   2053 static mc_error_t *
   2054 get_profile(scf_propertygroup_t *methpg, scf_propertygroup_t *instpg,
   2055     scf_property_t *prop, scf_value_t *val, const char *cmdline,
   2056     struct method_context *ci, mc_error_t *merr)
   2057 {
   2058 	char *buf = ci->vbuf;
   2059 	ssize_t buf_sz = ci->vbuf_sz;
   2060 	char cmd[PATH_MAX];
   2061 	char *cp, *value;
   2062 	const char *cmdp;
   2063 	execattr_t *eap;
   2064 	mc_error_t *err = merr;
   2065 	int r;
   2066 
   2067 	if (!(get_astring_val(methpg, SCF_PROPERTY_PROFILE, buf, buf_sz, prop,
   2068 	    val) == 0 || get_astring_val(instpg, SCF_PROPERTY_PROFILE, buf,
   2069 	    buf_sz, prop, val) == 0))
   2070 		return (mc_error_create(merr, scf_error(),
   2071 		    "Method context requires a profile, but the  \"%s\" "
   2072 		    "property could not be read. scf_error is %s",
   2073 		    SCF_PROPERTY_PROFILE, scf_strerror(scf_error())));
   2074 
   2075 	/* Extract the command from the command line. */
   2076 	cp = strpbrk(cmdline, " \t");
   2077 
   2078 	if (cp == NULL) {
   2079 		cmdp = cmdline;
   2080 	} else {
   2081 		(void) strncpy(cmd, cmdline, cp - cmdline);
   2082 		cmd[cp - cmdline] = '\0';
   2083 		cmdp = cmd;
   2084 	}
   2085 
   2086 	/* Require that cmdp[0] == '/'? */
   2087 
   2088 	eap = getexecprof(buf, KV_COMMAND, cmdp, GET_ONE);
   2089 	if (eap == NULL)
   2090 		return (mc_error_create(merr, ENOENT,
   2091 		    "Could not find the execution profile \"%s\", "
   2092 		    "command %s.", buf, cmdp));
   2093 
   2094 	/* Based on pfexec.c */
   2095 
   2096 	/* Get the euid first so we don't override ci->pwd for the uid. */
   2097 	if ((value = kva_match(eap->attr, EXECATTR_EUID_KW)) != NULL) {
   2098 		if ((r = get_uid(value, ci, &ci->euid)) != 0) {
   2099 			ci->euid = (uid_t)-1;
   2100 			err = mc_error_create(merr, r,
   2101 			    "Could not interpret profile euid value \"%s\", "
   2102 			    "from the execution profile \"%s\", error %d.",
   2103 			    value, buf, r);
   2104 			goto out;
   2105 		}
   2106 	}
   2107 
   2108 	if ((value = kva_match(eap->attr, EXECATTR_UID_KW)) != NULL) {
   2109 		if ((r = get_uid(value, ci, &ci->uid)) != 0) {
   2110 			ci->euid = ci->uid = (uid_t)-1;
   2111 			err = mc_error_create(merr, r,
   2112 			    "Could not interpret profile uid value \"%s\", "
   2113 			    "from the execution profile \"%s\", error %d.",
   2114 			    value, buf, r);
   2115 			goto out;
   2116 		}
   2117 		ci->euid = ci->uid;
   2118 	}
   2119 
   2120 	if ((value = kva_match(eap->attr, EXECATTR_GID_KW)) != NULL) {
   2121 		ci->egid = ci->gid = get_gid(value);
   2122 		if (ci->gid == (gid_t)-1) {
   2123 			err = mc_error_create(merr, EINVAL,
   2124 			    "Could not interpret profile gid value \"%s\", "
   2125 			    "from the execution profile \"%s\".", value, buf);
   2126 			goto out;
   2127 		}
   2128 	}
   2129 
   2130 	if ((value = kva_match(eap->attr, EXECATTR_EGID_KW)) != NULL) {
   2131 		ci->egid = get_gid(value);
   2132 		if (ci->egid == (gid_t)-1) {
   2133 			err = mc_error_create(merr, EINVAL,
   2134 			    "Could not interpret profile egid value \"%s\", "
   2135 			    "from the execution profile \"%s\".", value, buf);
   2136 			goto out;
   2137 		}
   2138 	}
   2139 
   2140 	if ((value = kva_match(eap->attr, EXECATTR_LPRIV_KW)) != NULL) {
   2141 		ci->lpriv_set = priv_str_to_set(value, ",", NULL);
   2142 		if (ci->lpriv_set == NULL) {
   2143 			if (errno != EINVAL)
   2144 				err = mc_error_create(merr, ENOMEM,
   2145 				    ALLOCFAIL);
   2146 			else
   2147 				err = mc_error_create(merr, EINVAL,
   2148 				    "Could not interpret profile "
   2149 				    "limitprivs value \"%s\", from "
   2150 				    "the execution profile \"%s\".",
   2151 				    value, buf);
   2152 			goto out;
   2153 		}
   2154 	}
   2155 
   2156 	if ((value = kva_match(eap->attr, EXECATTR_IPRIV_KW)) != NULL) {
   2157 		ci->priv_set = priv_str_to_set(value, ",", NULL);
   2158 		if (ci->priv_set == NULL) {
   2159 			if (errno != EINVAL)
   2160 				err = mc_error_create(merr, ENOMEM,
   2161 				    ALLOCFAIL);
   2162 			else
   2163 				err = mc_error_create(merr, EINVAL,
   2164 				    "Could not interpret profile privs value "
   2165 				    "\"%s\", from the execution profile "
   2166 				    "\"%s\".", value, buf);
   2167 			goto out;
   2168 		}
   2169 	}
   2170 
   2171 out:
   2172 	free_execattr(eap);
   2173 
   2174 	return (err);
   2175 }
   2176 
   2177 /*
   2178  * Return an error message structure containing the error message
   2179  * with context, and the error so the caller can make a decision
   2180  * on what to do next.
   2181  *
   2182  * Because get_ids uses the mc_error_create() function which can
   2183  * reallocate the merr, this function must return the merr pointer
   2184  * in case it was reallocated.
   2185  */
   2186 static mc_error_t *
   2187 get_ids(scf_propertygroup_t *methpg, scf_propertygroup_t *instpg,
   2188     scf_property_t *prop, scf_value_t *val, struct method_context *ci,
   2189     mc_error_t *merr)
   2190 {
   2191 	char *vbuf = ci->vbuf;
   2192 	ssize_t vbuf_sz = ci->vbuf_sz;
   2193 	int r;
   2194 
   2195 	/*
   2196 	 * This should never happen because the caller should fall through
   2197 	 * another path of just setting the ids to defaults, instead of
   2198 	 * attempting to get the ids here.
   2199 	 */
   2200 	if (methpg == NULL && instpg == NULL)
   2201 		return (mc_error_create(merr, ENOENT,
   2202 		    "No property groups to get ids from."));
   2203 
   2204 	if (!(get_astring_val(methpg, SCF_PROPERTY_USER,
   2205 	    vbuf, vbuf_sz, prop, val) == 0 || get_astring_val(instpg,
   2206 	    SCF_PROPERTY_USER, vbuf, vbuf_sz, prop,
   2207 	    val) == 0))
   2208 		return (mc_error_create(merr, ENOENT,
   2209 		    "Could not get \"%s\" property.", SCF_PROPERTY_USER));
   2210 
   2211 	if ((r = get_uid(vbuf, ci, &ci->uid)) != 0) {
   2212 		ci->uid = (uid_t)-1;
   2213 		return (mc_error_create(merr, r,
   2214 		    "Could not interpret \"%s\" property value \"%s\", "
   2215 		    "error %d.", SCF_PROPERTY_USER, vbuf, r));
   2216 	}
   2217 
   2218 	if (!(get_astring_val(methpg, SCF_PROPERTY_GROUP, vbuf, vbuf_sz, prop,
   2219 	    val) == 0 || get_astring_val(instpg, SCF_PROPERTY_GROUP, vbuf,
   2220 	    vbuf_sz, prop, val) == 0)) {
   2221 		if (scf_error() == SCF_ERROR_NOT_FOUND) {
   2222 			(void) strcpy(vbuf, ":default");
   2223 		} else {
   2224 			return (mc_error_create(merr, ENOENT,
   2225 			    "Could not get \"%s\" property.",
   2226 			    SCF_PROPERTY_GROUP));
   2227 		}
   2228 	}
   2229 
   2230 	if (strcmp(vbuf, ":default") != 0) {
   2231 		ci->gid = get_gid(vbuf);
   2232 		if (ci->gid == (gid_t)-1) {
   2233 			return (mc_error_create(merr, ENOENT,
   2234 			    "Could not interpret \"%s\" property value \"%s\".",
   2235 			    SCF_PROPERTY_GROUP, vbuf));
   2236 		}
   2237 	} else {
   2238 		switch (r = lookup_pwd(ci)) {
   2239 		case 0:
   2240 			ci->gid = ci->pwd.pw_gid;
   2241 			break;
   2242 
   2243 		case ENOENT:
   2244 			ci->gid = (gid_t)-1;
   2245 			return (mc_error_create(merr, ENOENT,
   2246 			    "No passwd entry for uid \"%d\".", ci->uid));
   2247 
   2248 		case ENOMEM:
   2249 			return (mc_error_create(merr, ENOMEM,
   2250 			    "Out of memory."));
   2251 
   2252 		case EIO:
   2253 		case EMFILE:
   2254 		case ENFILE:
   2255 			return (mc_error_create(merr, ENFILE,
   2256 			    "getpwuid_r() failed, error %d.", r));
   2257 
   2258 		default:
   2259 			bad_fail("lookup_pwd", r);
   2260 		}
   2261 	}
   2262 
   2263 	if (!(get_astring_val(methpg, SCF_PROPERTY_SUPP_GROUPS, vbuf, vbuf_sz,
   2264 	    prop, val) == 0 || get_astring_val(instpg,
   2265 	    SCF_PROPERTY_SUPP_GROUPS, vbuf, vbuf_sz, prop, val) == 0)) {
   2266 		if (scf_error() == SCF_ERROR_NOT_FOUND) {
   2267 			(void) strcpy(vbuf, ":default");
   2268 		} else {
   2269 			return (mc_error_create(merr, ENOENT,
   2270 			    "Could not get supplemental groups (\"%s\") "
   2271 			    "property.", SCF_PROPERTY_SUPP_GROUPS));
   2272 		}
   2273 	}
   2274 
   2275 	if (strcmp(vbuf, ":default") != 0) {
   2276 		switch (r = get_groups(vbuf, ci)) {
   2277 		case 0:
   2278 			break;
   2279 
   2280 		case EINVAL:
   2281 			return (mc_error_create(merr, EINVAL,
   2282 			    "Could not interpret supplemental groups (\"%s\") "
   2283 			    "property value \"%s\".", SCF_PROPERTY_SUPP_GROUPS,
   2284 			    vbuf));
   2285 
   2286 		case E2BIG:
   2287 			return (mc_error_create(merr, E2BIG,
   2288 			    "Too many supplemental groups values in \"%s\".",
   2289 			    vbuf));
   2290 
   2291 		default:
   2292 			bad_fail("get_groups", r);
   2293 		}
   2294 	} else {
   2295 		ci->ngroups = -1;
   2296 	}
   2297 
   2298 	if (!(get_astring_val(methpg, SCF_PROPERTY_PRIVILEGES, vbuf, vbuf_sz,
   2299 	    prop, val) == 0 || get_astring_val(instpg, SCF_PROPERTY_PRIVILEGES,
   2300 	    vbuf, vbuf_sz, prop, val) == 0)) {
   2301 		if (scf_error() == SCF_ERROR_NOT_FOUND) {
   2302 			(void) strcpy(vbuf, ":default");
   2303 		} else {
   2304 			return (mc_error_create(merr, ENOENT,
   2305 			    "Could not get \"%s\" property.",
   2306 			    SCF_PROPERTY_PRIVILEGES));
   2307 		}
   2308 	}
   2309 
   2310 	/*
   2311 	 * For default privs, we need to keep priv_set == NULL, as
   2312 	 * we use this test elsewhere.
   2313 	 */
   2314 	if (strcmp(vbuf, ":default") != 0) {
   2315 		ci->priv_set = priv_str_to_set(vbuf, ",", NULL);
   2316 		if (ci->priv_set == NULL) {
   2317 			if (errno != EINVAL) {
   2318 				return (mc_error_create(merr, ENOMEM,
   2319 				    ALLOCFAIL));
   2320 			} else {
   2321 				return (mc_error_create(merr, EINVAL,
   2322 				    "Could not interpret \"%s\" "
   2323 				    "property value \"%s\".",
   2324 				    SCF_PROPERTY_PRIVILEGES, vbuf));
   2325 			}
   2326 		}
   2327 	}
   2328 
   2329 	if (!(get_astring_val(methpg, SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf,
   2330 	    vbuf_sz, prop, val) == 0 || get_astring_val(instpg,
   2331 	    SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf, vbuf_sz, prop, val) == 0)) {
   2332 		if (scf_error() == SCF_ERROR_NOT_FOUND) {
   2333 			(void) strcpy(vbuf, ":default");
   2334 		} else {
   2335 			return (mc_error_create(merr, ENOENT,
   2336 			    "Could not get \"%s\" property.",
   2337 			    SCF_PROPERTY_LIMIT_PRIVILEGES));
   2338 		}
   2339 	}
   2340 
   2341 	if (strcmp(vbuf, ":default") == 0)
   2342 		/*
   2343 		 * L must default to all privileges so root NPA services see
   2344 		 * iE = all.  "zone" is all privileges available in the current
   2345 		 * zone, equivalent to "all" in the global zone.
   2346 		 */
   2347 		(void) strcpy(vbuf, "zone");
   2348 
   2349 	ci->lpriv_set = priv_str_to_set(vbuf, ",", NULL);
   2350 	if (ci->lpriv_set == NULL) {
   2351 		if (errno != EINVAL) {
   2352 			return (mc_error_create(merr, ENOMEM, ALLOCFAIL));
   2353 		} else {
   2354 			return (mc_error_create(merr, EINVAL,
   2355 			    "Could not interpret \"%s\" property value \"%s\".",
   2356 			    SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf));
   2357 		}
   2358 	}
   2359 
   2360 	return (merr);
   2361 }
   2362 
   2363 static int
   2364 get_environment(scf_handle_t *h, scf_propertygroup_t *pg,
   2365     struct method_context *mcp, scf_property_t *prop, scf_value_t *val)
   2366 {
   2367 	scf_iter_t *iter;
   2368 	scf_type_t type;
   2369 	size_t i = 0;
   2370 	int ret;
   2371 
   2372 	if (scf_pg_get_property(pg, SCF_PROPERTY_ENVIRONMENT, prop) != 0) {
   2373 		if (scf_error() == SCF_ERROR_NOT_FOUND)
   2374 			return (ENOENT);
   2375 		return (scf_error());
   2376 	}
   2377 	if (scf_property_type(prop, &type) != 0)
   2378 		return (scf_error());
   2379 	if (type != SCF_TYPE_ASTRING)
   2380 		return (EINVAL);
   2381 	if ((iter = scf_iter_create(h)) == NULL)
   2382 		return (scf_error());
   2383 
   2384 	if (scf_iter_property_values(iter, prop) != 0) {
   2385 		ret = scf_error();
   2386 		scf_iter_destroy(iter);
   2387 		return (ret);
   2388 	}
   2389 
   2390 	mcp->env_sz = 10;
   2391 
   2392 	if ((mcp->env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz)) == NULL) {
   2393 		ret = ENOMEM;
   2394 		goto out;
   2395 	}
   2396 
   2397 	while ((ret = scf_iter_next_value(iter, val)) == 1) {
   2398 		ret = scf_value_get_as_string(val, mcp->vbuf, mcp->vbuf_sz);
   2399 		if (ret == -1) {
   2400 			ret = scf_error();
   2401 			goto out;
   2402 		}
   2403 
   2404 		if ((mcp->env[i] = strdup(mcp->vbuf)) == NULL) {
   2405 			ret = ENOMEM;
   2406 			goto out;
   2407 		}
   2408 
   2409 		if (++i == mcp->env_sz) {
   2410 			char **env;
   2411 			mcp->env_sz *= 2;
   2412 			env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz);
   2413 			if (env == NULL) {
   2414 				ret = ENOMEM;
   2415 				goto out;
   2416 			}
   2417 			(void) memcpy(env, mcp->env,
   2418 			    sizeof (*mcp->env) * (mcp->env_sz / 2));
   2419 			free(mcp->env);
   2420 			mcp->env = env;
   2421 		}
   2422 	}
   2423 
   2424 	if (ret == -1)
   2425 		ret = scf_error();
   2426 
   2427 out:
   2428 	scf_iter_destroy(iter);
   2429 	return (ret);
   2430 }
   2431 
   2432 /*
   2433  * Fetch method context information from the repository, allocate and fill
   2434  * a method_context structure, return it in *mcpp, and return NULL.
   2435  *
   2436  * If no method_context is defined, original init context is provided, where
   2437  * the working directory is '/', and uid/gid are 0/0.  But if a method_context
   2438  * is defined at any level the smf_method(5) method_context defaults are used.
   2439  *
   2440  * Return an error message structure containing the error message
   2441  * with context, and the error so the caller can make a decision
   2442  * on what to do next.
   2443  *
   2444  * Error Types :
   2445  * 	E2BIG		Too many values or entry is too big
   2446  * 	EINVAL		Invalid value
   2447  * 	EIO		an I/O error has occured
   2448  * 	ENOENT		no entry for value
   2449  * 	ENOMEM		out of memory
   2450  * 	ENOTSUP		Version mismatch
   2451  * 	ERANGE		value is out of range
   2452  * 	EMFILE/ENFILE	out of file descriptors
   2453  *
   2454  * 	SCF_ERROR_BACKEND_ACCESS
   2455  * 	SCF_ERROR_CONNECTION_BROKEN
   2456  * 	SCF_ERROR_DELETED
   2457  * 	SCF_ERROR_CONSTRAINT_VIOLATED
   2458  * 	SCF_ERROR_HANDLE_DESTROYED
   2459  * 	SCF_ERROR_INTERNAL
   2460  * 	SCF_ERROR_INVALID_ARGUMENT
   2461  * 	SCF_ERROR_NO_MEMORY
   2462  * 	SCF_ERROR_NO_RESOURCES
   2463  * 	SCF_ERROR_NOT_BOUND
   2464  * 	SCF_ERROR_NOT_FOUND
   2465  * 	SCF_ERROR_NOT_SET
   2466  * 	SCF_ERROR_TYPE_MISMATCH
   2467  *
   2468  */
   2469 mc_error_t *
   2470 restarter_get_method_context(uint_t version, scf_instance_t *inst,
   2471     scf_snapshot_t *snap, const char *mname, const char *cmdline,
   2472     struct method_context **mcpp)
   2473 {
   2474 	scf_handle_t *h;
   2475 	scf_propertygroup_t *methpg = NULL;
   2476 	scf_propertygroup_t *instpg = NULL;
   2477 	scf_propertygroup_t *pg = NULL;
   2478 	scf_property_t *prop = NULL;
   2479 	scf_value_t *val = NULL;
   2480 	scf_type_t ty;
   2481 	uint8_t use_profile;
   2482 	int ret = 0;
   2483 	int mc_used = 0;
   2484 	mc_error_t *err = NULL;
   2485 	struct method_context *cip;
   2486 
   2487 	if ((err = malloc(sizeof (mc_error_t))) == NULL)
   2488 		return (mc_error_create(NULL, ENOMEM, NULL));
   2489 
   2490 	/* Set the type to zero to track if an error occured. */
   2491 	err->type = 0;
   2492 
   2493 	if (version != RESTARTER_METHOD_CONTEXT_VERSION)
   2494 		return (mc_error_create(err, ENOTSUP,
   2495 		    "Invalid client version %d. (Expected %d)",
   2496 		    version, RESTARTER_METHOD_CONTEXT_VERSION));
   2497 
   2498 	/* Get the handle before we allocate anything. */
   2499 	h = scf_instance_handle(inst);
   2500 	if (h == NULL)
   2501 		return (mc_error_create(err, scf_error(),
   2502 		    scf_strerror(scf_error())));
   2503 
   2504 	cip = malloc(sizeof (*cip));
   2505 	if (cip == NULL)
   2506 		return (mc_error_create(err, ENOMEM, ALLOCFAIL));
   2507 
   2508 	(void) memset(cip, 0, sizeof (*cip));
   2509 	cip->uid = (uid_t)-1;
   2510 	cip->euid = (uid_t)-1;
   2511 	cip->gid = (gid_t)-1;
   2512 	cip->egid = (gid_t)-1;
   2513 
   2514 	cip->vbuf_sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
   2515 	assert(cip->vbuf_sz >= 0);
   2516 	cip->vbuf = malloc(cip->vbuf_sz);
   2517 	if (cip->vbuf == NULL) {
   2518 		free(cip);
   2519 		return (mc_error_create(err, ENOMEM, ALLOCFAIL));
   2520 	}
   2521 
   2522 	if ((instpg = scf_pg_create(h)) == NULL ||
   2523 	    (methpg = scf_pg_create(h)) == NULL ||
   2524 	    (prop = scf_property_create(h)) == NULL ||
   2525 	    (val = scf_value_create(h)) == NULL) {
   2526 		err = mc_error_create(err, scf_error(),
   2527 		    "Failed to create repository object: %s\n",
   2528 		    scf_strerror(scf_error()));
   2529 		goto out;
   2530 	}
   2531 
   2532 	/*
   2533 	 * The method environment, and the credentials/profile data,
   2534 	 * may be found either in the pg for the method (methpg),
   2535 	 * or in the instance/service SCF_PG_METHOD_CONTEXT pg (named
   2536 	 * instpg below).
   2537 	 */
   2538 
   2539 	if (scf_instance_get_pg_composed(inst, snap, mname, methpg) !=
   2540 	    SCF_SUCCESS) {
   2541 		err = mc_error_create(err, scf_error(), "Unable to get the "
   2542 		    "\"%s\" method, %s", mname, scf_strerror(scf_error()));
   2543 		goto out;
   2544 	}
   2545 
   2546 	if (scf_instance_get_pg_composed(inst, snap, SCF_PG_METHOD_CONTEXT,
   2547 	    instpg) != SCF_SUCCESS) {
   2548 		if (scf_error() != SCF_ERROR_NOT_FOUND) {
   2549 			err = mc_error_create(err, scf_error(),
   2550 			    "Unable to retrieve the \"%s\" property group, %s",
   2551 			    SCF_PG_METHOD_CONTEXT, scf_strerror(scf_error()));
   2552 			goto out;
   2553 		}
   2554 		scf_pg_destroy(instpg);
   2555 		instpg = NULL;
   2556 	} else {
   2557 		mc_used++;
   2558 	}
   2559 
   2560 	ret = get_environment(h, methpg, cip, prop, val);
   2561 	if (ret == ENOENT && instpg != NULL) {
   2562 		ret = get_environment(h, instpg, cip, prop, val);
   2563 	}
   2564 
   2565 	switch (ret) {
   2566 	case 0:
   2567 		mc_used++;
   2568 		break;
   2569 	case ENOENT:
   2570 		break;
   2571 	case ENOMEM:
   2572 		err = mc_error_create(err, ret, "Out of memory.");
   2573 		goto out;
   2574 	case EINVAL:
   2575 		err = mc_error_create(err, ret, "Invalid method environment.");
   2576 		goto out;
   2577 	default:
   2578 		err = mc_error_create(err, ret,
   2579 		    "Get method environment failed : %s\n", scf_strerror(ret));
   2580 		goto out;
   2581 	}
   2582 
   2583 	pg = methpg;
   2584 
   2585 	ret = scf_pg_get_property(pg, SCF_PROPERTY_USE_PROFILE, prop);
   2586 	if (ret && scf_error() == SCF_ERROR_NOT_FOUND && instpg != NULL) {
   2587 		pg = NULL;
   2588 		ret = scf_pg_get_property(instpg, SCF_PROPERTY_USE_PROFILE,
   2589 		    prop);
   2590 	}
   2591 
   2592 	if (ret) {
   2593 		switch (scf_error()) {
   2594 		case SCF_ERROR_NOT_FOUND:
   2595 			/* No profile context: use default credentials */
   2596 			cip->uid = 0;
   2597 			cip->gid = 0;
   2598 			break;
   2599 
   2600 		case SCF_ERROR_CONNECTION_BROKEN:
   2601 			err = mc_error_create(err, SCF_ERROR_CONNECTION_BROKEN,
   2602 			    RCBROKEN);
   2603 			goto out;
   2604 
   2605 		case SCF_ERROR_DELETED:
   2606 			err = mc_error_create(err, SCF_ERROR_NOT_FOUND,
   2607 			    "Could not find property group \"%s\"",
   2608 			    pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
   2609 			goto out;
   2610 
   2611 		case SCF_ERROR_HANDLE_MISMATCH:
   2612 		case SCF_ERROR_INVALID_ARGUMENT:
   2613 		case SCF_ERROR_NOT_SET:
   2614 		default:
   2615 			bad_fail("scf_pg_get_property", scf_error());
   2616 		}
   2617 	} else {
   2618 		if (scf_property_type(prop, &ty) != SCF_SUCCESS) {
   2619 			ret = scf_error();
   2620 			switch (ret) {
   2621 			case SCF_ERROR_CONNECTION_BROKEN:
   2622 				err = mc_error_create(err,
   2623 				    SCF_ERROR_CONNECTION_BROKEN, RCBROKEN);
   2624 				break;
   2625 
   2626 			case SCF_ERROR_DELETED:
   2627 				err = mc_error_create(err,
   2628 				    SCF_ERROR_NOT_FOUND,
   2629 				    "Could not find property group \"%s\"",
   2630 				    pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
   2631 				break;
   2632 
   2633 			case SCF_ERROR_NOT_SET:
   2634 			default:
   2635 				bad_fail("scf_property_type", ret);
   2636 			}
   2637 
   2638 			goto out;
   2639 		}
   2640 
   2641 		if (ty != SCF_TYPE_BOOLEAN) {
   2642 			err = mc_error_create(err,
   2643 			    SCF_ERROR_TYPE_MISMATCH,
   2644 			    "\"%s\" property is not boolean in property group "
   2645 			    "\"%s\".", SCF_PROPERTY_USE_PROFILE,
   2646 			    pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
   2647 			goto out;
   2648 		}
   2649 
   2650 		if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
   2651 			ret = scf_error();
   2652 			switch (ret) {
   2653 			case SCF_ERROR_CONNECTION_BROKEN:
   2654 				err = mc_error_create(err,
   2655 				    SCF_ERROR_CONNECTION_BROKEN, RCBROKEN);
   2656 				break;
   2657 
   2658 			case SCF_ERROR_CONSTRAINT_VIOLATED:
   2659 				err = mc_error_create(err,
   2660 				    SCF_ERROR_CONSTRAINT_VIOLATED,
   2661 				    "\"%s\" property has multiple values.",
   2662 				    SCF_PROPERTY_USE_PROFILE);
   2663 				break;
   2664 
   2665 			case SCF_ERROR_NOT_FOUND:
   2666 				err = mc_error_create(err,
   2667 				    SCF_ERROR_NOT_FOUND,
   2668 				    "\"%s\" property has no values.",
   2669 				    SCF_PROPERTY_USE_PROFILE);
   2670 				break;
   2671 			default:
   2672 				bad_fail("scf_property_get_value", ret);
   2673 			}
   2674 
   2675 			goto out;
   2676 		}
   2677 
   2678 		mc_used++;
   2679 		ret = scf_value_get_boolean(val, &use_profile);
   2680 		assert(ret == SCF_SUCCESS);
   2681 
   2682 		/* get ids & privileges */
   2683 		if (use_profile)
   2684 			err = get_profile(pg, instpg, prop, val, cmdline,
   2685 			    cip, err);
   2686 		else
   2687 			err = get_ids(pg, instpg, prop, val, cip, err);
   2688 
   2689 		if (err->type != 0)
   2690 			goto out;
   2691 	}
   2692 
   2693 	/* get working directory */
   2694 	if ((methpg != NULL && scf_pg_get_property(methpg,
   2695 	    SCF_PROPERTY_WORKING_DIRECTORY, prop) == SCF_SUCCESS) ||
   2696 	    (instpg != NULL && scf_pg_get_property(instpg,
   2697 	    SCF_PROPERTY_WORKING_DIRECTORY, prop) == SCF_SUCCESS)) {
   2698 		if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
   2699 			ret = scf_error();
   2700 			switch (ret) {
   2701 			case SCF_ERROR_CONNECTION_BROKEN:
   2702 				err = mc_error_create(err, ret, RCBROKEN);
   2703 				break;
   2704 
   2705 			case SCF_ERROR_CONSTRAINT_VIOLATED:
   2706 				err = mc_error_create(err, ret,
   2707 				    "\"%s\" property has multiple values.",
   2708 				    SCF_PROPERTY_WORKING_DIRECTORY);
   2709 				break;
   2710 
   2711 			case SCF_ERROR_NOT_FOUND:
   2712 				err = mc_error_create(err, ret,
   2713 				    "\"%s\" property has no values.",
   2714 				    SCF_PROPERTY_WORKING_DIRECTORY);
   2715 				break;
   2716 
   2717 			default:
   2718 				bad_fail("scf_property_get_value", ret);
   2719 			}
   2720 
   2721 			goto out;
   2722 		}
   2723 
   2724 		mc_used++;
   2725 		ret = scf_value_get_astring(val, cip->vbuf, cip->vbuf_sz);
   2726 		assert(ret != -1);
   2727 	} else {
   2728 		ret = scf_error();
   2729 		switch (ret) {
   2730 		case SCF_ERROR_NOT_FOUND:
   2731 			/* okay if missing. */
   2732 			(void) strcpy(cip->vbuf, ":default");
   2733 			break;
   2734 
   2735 		case SCF_ERROR_CONNECTION_BROKEN:
   2736 			err = mc_error_create(err, ret, RCBROKEN);
   2737 			goto out;
   2738 
   2739 		case SCF_ERROR_DELETED:
   2740 			err = mc_error_create(err, ret,
   2741 			    "Property group could not be found");
   2742 			goto out;
   2743 
   2744 		case SCF_ERROR_HANDLE_MISMATCH:
   2745 		case SCF_ERROR_INVALID_ARGUMENT:
   2746 		case SCF_ERROR_NOT_SET:
   2747 		default:
   2748 			bad_fail("scf_pg_get_property", ret);
   2749 		}
   2750 	}
   2751 
   2752 	if (strcmp(cip->vbuf, ":default") == 0 ||
   2753 	    strcmp(cip->vbuf, ":home") == 0) {
   2754 		switch (ret = lookup_pwd(cip)) {
   2755 		case 0:
   2756 			break;
   2757 
   2758 		case ENOMEM:
   2759 			err = mc_error_create(err, ret, "Out of memory.");
   2760 			goto out;
   2761 
   2762 		case ENOENT:
   2763 		case EIO:
   2764 		case EMFILE:
   2765 		case ENFILE:
   2766 			err = mc_error_create(err, ret,
   2767 			    "Could not get passwd entry.");
   2768 			goto out;
   2769 
   2770 		default:
   2771 			bad_fail("lookup_pwd", ret);
   2772 		}
   2773 
   2774 		cip->working_dir = strdup(cip->pwd.pw_dir);
   2775 		if (cip->working_dir == NULL) {
   2776 			err = mc_error_create(err, ENOMEM, ALLOCFAIL);
   2777 			goto out;
   2778 		}
   2779 	} else {
   2780 		cip->working_dir = strdup(cip->vbuf);
   2781 		if (cip->working_dir == NULL) {
   2782 			err = mc_error_create(err, ENOMEM, ALLOCFAIL);
   2783 			goto out;
   2784 		}
   2785 	}
   2786 
   2787 	/* get (optional) corefile pattern */
   2788 	if ((methpg != NULL && scf_pg_get_property(methpg,
   2789 	    SCF_PROPERTY_COREFILE_PATTERN, prop) == SCF_SUCCESS) ||
   2790 	    (instpg != NULL && scf_pg_get_property(instpg,
   2791 	    SCF_PROPERTY_COREFILE_PATTERN, prop) == SCF_SUCCESS)) {
   2792 		if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
   2793 			ret = scf_error();
   2794 			switch (ret) {
   2795 			case SCF_ERROR_CONNECTION_BROKEN:
   2796 				err = mc_error_create(err, ret, RCBROKEN);
   2797 				break;
   2798 
   2799 			case SCF_ERROR_CONSTRAINT_VIOLATED:
   2800 				err = mc_error_create(err, ret,
   2801 				    "\"%s\" property has multiple values.",
   2802 				    SCF_PROPERTY_COREFILE_PATTERN);
   2803 				break;
   2804 
   2805 			case SCF_ERROR_NOT_FOUND:
   2806 				err = mc_error_create(err, ret,
   2807 				    "\"%s\" property has no values.",
   2808 				    SCF_PROPERTY_COREFILE_PATTERN);
   2809 				break;
   2810 
   2811 			default:
   2812 				bad_fail("scf_property_get_value", ret);
   2813 			}
   2814 
   2815 		} else {
   2816 
   2817 			ret = scf_value_get_astring(val, cip->vbuf,
   2818 			    cip->vbuf_sz);
   2819 			assert(ret != -1);
   2820 
   2821 			cip->corefile_pattern = strdup(cip->vbuf);
   2822 			if (cip->corefile_pattern == NULL) {
   2823 				err = mc_error_create(err, ENOMEM, ALLOCFAIL);
   2824 				goto out;
   2825 			}
   2826 		}
   2827 
   2828 		mc_used++;
   2829 	} else {
   2830 		ret = scf_error();
   2831 		switch (ret) {
   2832 		case SCF_ERROR_NOT_FOUND:
   2833 			/* okay if missing. */
   2834 			break;
   2835 
   2836 		case SCF_ERROR_CONNECTION_BROKEN:
   2837 			err = mc_error_create(err, ret, RCBROKEN);
   2838 			goto out;
   2839 
   2840 		case SCF_ERROR_DELETED:
   2841 			err = mc_error_create(err, ret,
   2842 			    "Property group could not be found");
   2843 			goto out;
   2844 
   2845 		case SCF_ERROR_HANDLE_MISMATCH:
   2846 		case SCF_ERROR_INVALID_ARGUMENT:
   2847 		case SCF_ERROR_NOT_SET:
   2848 		default:
   2849 			bad_fail("scf_pg_get_property", ret);
   2850 		}
   2851 	}
   2852 
   2853 	if (restarter_rm_libs_loadable()) {
   2854 		/* get project */
   2855 		if ((methpg != NULL && scf_pg_get_property(methpg,
   2856 		    SCF_PROPERTY_PROJECT, prop) == SCF_SUCCESS) ||
   2857 		    (instpg != NULL && scf_pg_get_property(instpg,
   2858 		    SCF_PROPERTY_PROJECT, prop) == SCF_SUCCESS)) {
   2859 			if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
   2860 				ret = scf_error();
   2861 				switch (ret) {
   2862 				case SCF_ERROR_CONNECTION_BROKEN:
   2863 					err = mc_error_create(err, ret,
   2864 					    RCBROKEN);
   2865 					break;
   2866 
   2867 				case SCF_ERROR_CONSTRAINT_VIOLATED:
   2868 					err = mc_error_create(err, ret,
   2869 					    "\"%s\" property has multiple "
   2870 					    "values.", SCF_PROPERTY_PROJECT);
   2871 					break;
   2872 
   2873 				case SCF_ERROR_NOT_FOUND:
   2874 					err = mc_error_create(err, ret,
   2875 					    "\"%s\" property has no values.",
   2876 					    SCF_PROPERTY_PROJECT);
   2877 					break;
   2878 
   2879 				default:
   2880 					bad_fail("scf_property_get_value", ret);
   2881 				}
   2882 
   2883 				(void) strcpy(cip->vbuf, ":default");
   2884 			} else {
   2885 				ret = scf_value_get_astring(val, cip->vbuf,
   2886 				    cip->vbuf_sz);
   2887 				assert(ret != -1);
   2888 			}
   2889 
   2890 			mc_used++;
   2891 		} else {
   2892 			(void) strcpy(cip->vbuf, ":default");
   2893 		}
   2894 
   2895 		switch (ret = get_projid(cip->vbuf, cip)) {
   2896 		case 0:
   2897 			break;
   2898 
   2899 		case ENOMEM:
   2900 			err = mc_error_create(err, ret, "Out of memory.");
   2901 			goto out;
   2902 
   2903 		case ENOENT:
   2904 			err = mc_error_create(err, ret,
   2905 			    "Missing passwd or project entry for \"%s\".",
   2906 			    cip->vbuf);
   2907 			goto out;
   2908 
   2909 		case EIO:
   2910 			err = mc_error_create(err, ret, "I/O error.");
   2911 			goto out;
   2912 
   2913 		case EMFILE:
   2914 		case ENFILE:
   2915 			err = mc_error_create(err, ret,
   2916 			    "Out of file descriptors.");
   2917 			goto out;
   2918 
   2919 		case -1:
   2920 			err = mc_error_create(err, ret,
   2921 			    "Name service switch is misconfigured.");
   2922 			goto out;
   2923 
   2924 		case ERANGE:
   2925 		case E2BIG:
   2926 			err = mc_error_create(err, ret,
   2927 			    "Project ID \"%s\" too big.", cip->vbuf);
   2928 			goto out;
   2929 
   2930 		case EINVAL:
   2931 			err = mc_error_create(err, ret,
   2932 			    "Project ID \"%s\" is invalid.", cip->vbuf);
   2933 			goto out;
   2934 
   2935 		default:
   2936 			bad_fail("get_projid", ret);
   2937 		}
   2938 
   2939 		/* get resource pool */
   2940 		if ((methpg != NULL && scf_pg_get_property(methpg,
   2941 		    SCF_PROPERTY_RESOURCE_POOL, prop) == SCF_SUCCESS) ||
   2942 		    (instpg != NULL && scf_pg_get_property(instpg,
   2943 		    SCF_PROPERTY_RESOURCE_POOL, prop) == SCF_SUCCESS)) {
   2944 			if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
   2945 				ret = scf_error();
   2946 				switch (ret) {
   2947 				case SCF_ERROR_CONNECTION_BROKEN:
   2948 					err = mc_error_create(err, ret,
   2949 					    RCBROKEN);
   2950 					break;
   2951 
   2952 				case SCF_ERROR_CONSTRAINT_VIOLATED:
   2953 					err = mc_error_create(err, ret,
   2954 					    "\"%s\" property has multiple "
   2955 					    "values.",
   2956 					    SCF_PROPERTY_RESOURCE_POOL);
   2957 					break;
   2958 
   2959 				case SCF_ERROR_NOT_FOUND:
   2960 					err = mc_error_create(err, ret,
   2961 					    "\"%s\" property has no "
   2962 					    "values.",
   2963 					    SCF_PROPERTY_RESOURCE_POOL);
   2964 					break;
   2965 
   2966 				default:
   2967 					bad_fail("scf_property_get_value", ret);
   2968 				}
   2969 
   2970 				(void) strcpy(cip->vbuf, ":default");
   2971 			} else {
   2972 				ret = scf_value_get_astring(val, cip->vbuf,
   2973 				    cip->vbuf_sz);
   2974 				assert(ret != -1);
   2975 			}
   2976 
   2977 			mc_used++;
   2978 		} else {
   2979 			ret = scf_error();
   2980 			switch (ret) {
   2981 			case SCF_ERROR_NOT_FOUND:
   2982 				/* okay if missing. */
   2983 				(void) strcpy(cip->vbuf, ":default");
   2984 				break;
   2985 
   2986 			case SCF_ERROR_CONNECTION_BROKEN:
   2987 				err = mc_error_create(err, ret, RCBROKEN);
   2988 				goto out;
   2989 
   2990 			case SCF_ERROR_DELETED:
   2991 				err = mc_error_create(err, ret,
   2992 				    "property group could not be found.");
   2993 				goto out;
   2994 
   2995 			case SCF_ERROR_HANDLE_MISMATCH:
   2996 			case SCF_ERROR_INVALID_ARGUMENT:
   2997 			case SCF_ERROR_NOT_SET:
   2998 			default:
   2999 				bad_fail("scf_pg_get_property", ret);
   3000 			}
   3001 		}
   3002 
   3003 		if (strcmp(cip->vbuf, ":default") != 0) {
   3004 			cip->resource_pool = strdup(cip->vbuf);
   3005 			if (cip->resource_pool == NULL) {
   3006 				err = mc_error_create(err, ENOMEM, ALLOCFAIL);
   3007 				goto out;
   3008 			}
   3009 		}
   3010 	}
   3011 
   3012 	/*
   3013 	 * A method_context was not used for any configurable
   3014 	 * elements or attributes, so reset and use the simple
   3015 	 * defaults that provide historic init behavior.
   3016 	 */
   3017 	if (mc_used == 0) {
   3018 		(void) memset(cip, 0, sizeof (*cip));
   3019 		cip->uid = 0;
   3020 		cip->gid = 0;
   3021 		cip->euid = (uid_t)-1;
   3022 		cip->egid = (gid_t)-1;
   3023 	}
   3024 
   3025 	*mcpp = cip;
   3026 
   3027 out:
   3028 	(void) scf_value_destroy(val);
   3029 	scf_property_destroy(prop);
   3030 	scf_pg_destroy(instpg);
   3031 	scf_pg_destroy(methpg);
   3032 
   3033 	if (cip->pwbuf != NULL)
   3034 		free(cip->pwbuf);
   3035 	free(cip->vbuf);
   3036 
   3037 	if (err->type != 0) {
   3038 		restarter_free_method_context(cip);
   3039 	} else {
   3040 		restarter_mc_error_destroy(err);
   3041 		err = NULL;
   3042 	}
   3043 
   3044 	return (err);
   3045 }
   3046 
   3047 /*
   3048  * Modify the current process per the given method_context.  On success, returns
   3049  * 0.  Note that the environment is not modified by this function to include the
   3050  * environment variables in cip->env.
   3051  *
   3052  * On failure, sets *fp to NULL or the name of the function which failed,
   3053  * and returns one of the following error codes.  The words in parentheses are
   3054  * the values to which *fp may be set for the error case.
   3055  *   ENOMEM - malloc() failed
   3056  *   EIO - an I/O error occurred (getpwuid_r, chdir)
   3057  *   EMFILE - process is out of file descriptors (getpwuid_r)
   3058  *   ENFILE - system is out of file handles (getpwuid_r)
   3059  *   EINVAL - gid or egid is out of range (setregid)
   3060  *	      ngroups is too big (setgroups)
   3061  *	      project's project id is bad (setproject)
   3062  *	      uid or euid is out of range (setreuid)
   3063  *	      poolname is invalid (pool_set_binding)
   3064  *   EPERM - insufficient privilege (setregid, initgroups, setgroups, setppriv,
   3065  *	         setproject, setreuid, settaskid)
   3066  *   ENOENT - uid has a passwd entry but no shadow entry
   3067  *	      working_dir does not exist (chdir)
   3068  *	      uid has no passwd entry
   3069  *	      the pool could not be found (pool_set_binding)
   3070  *   EFAULT - lpriv_set or priv_set has a bad address (setppriv)
   3071  *	      working_dir has a bad address (chdir)
   3072  *   EACCES - could not access working_dir (chdir)
   3073  *	      in a TASK_FINAL task (setproject, settaskid)
   3074  *	      no resource pool accepting default binding exists (setproject)
   3075  *   ELOOP - too many symbolic links in working_dir (chdir)
   3076  *   ENAMETOOLONG - working_dir is too long (chdir)
   3077  *   ENOLINK - working_dir is on an inaccessible remote machine (chdir)
   3078  *   ENOTDIR - working_dir is not a directory (chdir)
   3079  *   ESRCH - uid is not a user of project (setproject)
   3080  *	     project is invalid (setproject)
   3081  *	     the resource pool specified for project is unknown (setproject)
   3082  *   EBADF - the configuration for the pool is invalid (pool_set_binding)
   3083  *   -1 - core_set_process_path() failed (core_set_process_path)
   3084  *	  a resource control assignment failed (setproject)
   3085  *	  a system error occurred during pool_set_binding (pool_set_binding)
   3086  */
   3087 int
   3088 restarter_set_method_context(struct method_context *cip, const char **fp)
   3089 {
   3090 	pid_t mypid = -1;
   3091 	int r, ret;
   3092 
   3093 	cip->pwbuf = NULL;
   3094 	*fp = NULL;
   3095 
   3096 	if (cip->gid != (gid_t)-1) {
   3097 		if (setregid(cip->gid,
   3098 		    cip->egid != (gid_t)-1 ? cip->egid : cip->gid) != 0) {
   3099 			*fp = "setregid";
   3100 
   3101 			ret = errno;
   3102 			assert(ret == EINVAL || ret == EPERM);
   3103 			goto out;
   3104 		}
   3105 	} else {
   3106 		if (cip->pwbuf == NULL) {
   3107 			switch (ret = lookup_pwd(cip)) {
   3108 			case 0:
   3109 				break;
   3110 
   3111 			case ENOMEM:
   3112 			case ENOENT:
   3113 				*fp = NULL;
   3114 				goto out;
   3115 
   3116 			case EIO:
   3117 			case EMFILE:
   3118 			case ENFILE:
   3119 				*fp = "getpwuid_r";
   3120 				goto out;
   3121 
   3122 			default:
   3123 				bad_fail("lookup_pwd", ret);
   3124 			}
   3125 		}
   3126 
   3127 		if (setregid(cip->pwd.pw_gid,
   3128 		    cip->egid != (gid_t)-1 ?
   3129 		    cip->egid : cip->pwd.pw_gid) != 0) {
   3130 			*fp = "setregid";
   3131 
   3132 			ret = errno;
   3133 			assert(ret == EINVAL || ret == EPERM);
   3134 			goto out;
   3135 		}
   3136 	}
   3137 
   3138 	if (cip->ngroups == -1) {
   3139 		if (cip->pwbuf == NULL) {
   3140 			switch (ret = lookup_pwd(cip)) {
   3141 			case 0:
   3142 				break;
   3143 
   3144 			case ENOMEM:
   3145 			case ENOENT:
   3146 				*fp = NULL;
   3147 				goto out;
   3148 
   3149 			case EIO:
   3150 			case EMFILE:
   3151 			case ENFILE:
   3152 				*fp = "getpwuid_r";
   3153 				goto out;
   3154 
   3155 			default:
   3156 				bad_fail("lookup_pwd", ret);
   3157 			}
   3158 		}
   3159 
   3160 		/* Ok if cip->gid == -1 */
   3161 		if (initgroups(cip->pwd.pw_name, cip->gid) != 0) {
   3162 			*fp = "initgroups";
   3163 			ret = errno;
   3164 			assert(ret == EPERM);
   3165 			goto out;
   3166 		}
   3167 	} else if (cip->ngroups > 0 &&
   3168 	    setgroups(cip->ngroups, cip->groups) != 0) {
   3169 		*fp = "setgroups";
   3170 
   3171 		ret = errno;
   3172 		assert(ret == EINVAL || ret == EPERM);
   3173 		goto out;
   3174 	}
   3175 
   3176 	if (cip->corefile_pattern != NULL) {
   3177 		mypid = getpid();
   3178 
   3179 		if (core_set_process_path(cip->corefile_pattern,
   3180 		    strlen(cip->corefile_pattern) + 1, mypid) != 0) {
   3181 			*fp = "core_set_process_path";
   3182 			ret = -1;
   3183 			goto out;
   3184 		}
   3185 	}
   3186 
   3187 	if (restarter_rm_libs_loadable()) {
   3188 		if (cip->project == NULL) {
   3189 			if (settaskid(getprojid(), TASK_NORMAL) == -1) {
   3190 				switch (errno) {
   3191 				case EACCES:
   3192 				case EPERM:
   3193 					*fp = "settaskid";
   3194 					ret = errno;
   3195 					goto out;
   3196 
   3197 				case EINVAL:
   3198 				default:
   3199 					bad_fail("settaskid", errno);
   3200 				}
   3201 			}
   3202 		} else {
   3203 			switch (ret = lookup_pwd(cip)) {
   3204 			case 0:
   3205 				break;
   3206 
   3207 			case ENOMEM:
   3208 			case ENOENT:
   3209 				*fp = NULL;
   3210 				goto out;
   3211 
   3212 			case EIO:
   3213 			case EMFILE:
   3214 			case ENFILE:
   3215 				*fp = "getpwuid_r";
   3216 				goto out;
   3217 
   3218 			default:
   3219 				bad_fail("lookup_pwd", ret);
   3220 			}
   3221 
   3222 			*fp = "setproject";
   3223 
   3224 			switch (setproject(cip->project, cip->pwd.pw_name,
   3225 			    TASK_NORMAL)) {
   3226 			case 0:
   3227 				break;
   3228 
   3229 			case SETPROJ_ERR_TASK:
   3230 			case SETPROJ_ERR_POOL:
   3231 				ret = errno;
   3232 				goto out;
   3233 
   3234 			default:
   3235 				ret = -1;
   3236 				goto out;
   3237 			}
   3238 		}
   3239 
   3240 		if (cip->resource_pool != NULL) {
   3241 			if (mypid == -1)
   3242 				mypid = getpid();
   3243 
   3244 			*fp = "pool_set_binding";
   3245 
   3246 			if (pool_set_binding(cip->resource_pool, P_PID,
   3247 			    mypid) != PO_SUCCESS) {
   3248 				switch (pool_error()) {
   3249 				case POE_INVALID_SEARCH:
   3250 					ret = ENOENT;
   3251 					break;
   3252 
   3253 				case POE_BADPARAM:
   3254 					ret = EINVAL;
   3255 					break;
   3256 
   3257 				case POE_INVALID_CONF:
   3258 					ret = EBADF;
   3259 					break;
   3260 
   3261 				case POE_SYSTEM:
   3262 					ret = -1;
   3263 					break;
   3264 
   3265 				default:
   3266 					bad_fail("pool_set_binding",
   3267 					    pool_error());
   3268 				}
   3269 
   3270 				goto out;
   3271 			}
   3272 		}
   3273 	}
   3274 
   3275 	/*
   3276 	 * Now, we have to assume our ID. If the UID is 0, we want it to be
   3277 	 * privilege-aware, otherwise the limit set gets used instead of E/P.
   3278 	 * We can do this by setting P as well, which keeps
   3279 	 * PA status (see priv_can_clear_PA()).
   3280 	 */
   3281 
   3282 	*fp = "setppriv";
   3283 
   3284 	if (cip->lpriv_set != NULL) {
   3285 		if (setppriv(PRIV_SET, PRIV_LIMIT, cip->lpriv_set) != 0) {
   3286 			ret = errno;
   3287 			assert(ret == EFAULT || ret == EPERM);
   3288 			goto out;
   3289 		}
   3290 	}
   3291 	if (cip->priv_set != NULL) {
   3292 		if (setppriv(PRIV_SET, PRIV_INHERITABLE, cip->priv_set) != 0) {
   3293 			ret = errno;
   3294 			assert(ret == EFAULT || ret == EPERM);
   3295 			goto out;
   3296 		}
   3297 	}
   3298 
   3299 	/*
   3300 	 * If the limit privset is already set, then must be privilege
   3301 	 * aware.  Otherwise, don't assume anything, and force privilege
   3302 	 * aware status.
   3303 	 */
   3304 
   3305 	if (cip->lpriv_set == NULL && cip->priv_set != NULL) {
   3306 		ret = setpflags(PRIV_AWARE, 1);
   3307 		assert(ret == 0);
   3308 	}
   3309 
   3310 	*fp = "setreuid";
   3311 	if (setreuid(cip->uid,
   3312 	    cip->euid != (uid_t)-1 ? cip->euid : cip->uid) != 0) {
   3313 		ret = errno;
   3314 		assert(ret == EINVAL || ret == EPERM);
   3315 		goto out;
   3316 	}
   3317 
   3318 	*fp = "setppriv";
   3319 	if (cip->priv_set != NULL) {
   3320 		if (setppriv(PRIV_SET, PRIV_PERMITTED, cip->priv_set) != 0) {
   3321 			ret = errno;
   3322 			assert(ret == EFAULT || ret == EPERM);
   3323 			goto out;
   3324 		}
   3325 	}
   3326 
   3327 	/*
   3328 	 * The last thing to do is chdir to the specified working directory.
   3329 	 * This should come after the uid switching as only the user might
   3330 	 * have access to the specified directory.
   3331 	 */
   3332 	if (cip->working_dir != NULL) {
   3333 		do {
   3334 			r = chdir(cip->working_dir);
   3335 		} while (r != 0 && errno == EINTR);
   3336 		if (r != 0) {
   3337 			*fp = "chdir";
   3338 			ret = errno;
   3339 			goto out;
   3340 		}
   3341 	}
   3342 
   3343 	ret = 0;
   3344 out:
   3345 	free(cip->pwbuf);
   3346 	cip->pwbuf = NULL;
   3347 	return (ret);
   3348 }
   3349 
   3350 void
   3351 restarter_free_method_context(struct method_context *mcp)
   3352 {
   3353 	size_t i;
   3354 
   3355 	if (mcp->lpriv_set != NULL)
   3356 		priv_freeset(mcp->lpriv_set);
   3357 	if (mcp->priv_set != NULL)
   3358 		priv_freeset(mcp->priv_set);
   3359 
   3360 	if (mcp->env != NULL) {
   3361 		for (i = 0; i < mcp->env_sz; i++)
   3362 			free(mcp->env[i]);
   3363 		free(mcp->env);
   3364 	}
   3365 
   3366 	free(mcp->working_dir);
   3367 	free(mcp->corefile_pattern);
   3368 	free(mcp->project);
   3369 	free(mcp->resource_pool);
   3370 	free(mcp);
   3371 }
   3372 
   3373 /*
   3374  * Method keyword functions
   3375  */
   3376 
   3377 int
   3378 restarter_is_null_method(const char *meth)
   3379 {
   3380 	return (strcmp(meth, MKW_TRUE) == 0);
   3381 }
   3382 
   3383 static int
   3384 is_kill_method(const char *method, const char *kill_str,
   3385     size_t kill_str_len)
   3386 {
   3387 	const char *cp;
   3388 	int sig;
   3389 
   3390 	if (strncmp(method, kill_str, kill_str_len) != 0 ||
   3391 	    (method[kill_str_len] != '\0' &&
   3392 	    !isspace(method[kill_str_len])))
   3393 		return (-1);
   3394 
   3395 	cp = method + kill_str_len;
   3396 	while (*cp != '\0' && isspace(*cp))
   3397 		++cp;
   3398 
   3399 	if (*cp == '\0')
   3400 		return (SIGTERM);
   3401 
   3402 	if (*cp != '-')
   3403 		return (-1);
   3404 
   3405 	return (str2sig(cp + 1, &sig) == 0 ? sig : -1);
   3406 }
   3407 
   3408 int
   3409 restarter_is_kill_proc_method(const char *method)
   3410 {
   3411 	return (is_kill_method(method, MKW_KILL_PROC,
   3412 	    sizeof (MKW_KILL_PROC) - 1));
   3413 }
   3414 
   3415 int
   3416 restarter_is_kill_method(const char *method)
   3417 {
   3418 	return (is_kill_method(method, MKW_KILL, sizeof (MKW_KILL) - 1));
   3419 }
   3420 
   3421 /*
   3422  * Stubs for now.
   3423  */
   3424 
   3425 /* ARGSUSED */
   3426 int
   3427 restarter_event_get_enabled(restarter_event_t *e)
   3428 {
   3429 	return (-1);
   3430 }
   3431 
   3432 /* ARGSUSED */
   3433 uint64_t
   3434 restarter_event_get_seq(restarter_event_t *e)
   3435 {
   3436 	return (-1);
   3437 }
   3438 
   3439 /* ARGSUSED */
   3440 void
   3441 restarter_event_get_time(restarter_event_t *e, hrtime_t *time)
   3442 {
   3443 }
   3444 
   3445 /*
   3446  * Check for and validate fmri specified in restarter_actions/auxiliary_fmri
   3447  * 0 - Success
   3448  * 1 - Failure
   3449  */
   3450 int
   3451 restarter_inst_validate_ractions_aux_fmri(scf_instance_t *inst)
   3452 {
   3453 	scf_handle_t *h;
   3454 	scf_propertygroup_t *pg;
   3455 	scf_property_t *prop;
   3456 	scf_value_t *val;
   3457 	char *aux_fmri;
   3458 	size_t size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
   3459 	int ret = 1;
   3460 
   3461 	if ((aux_fmri = malloc(size)) == NULL)
   3462 		return (1);
   3463 
   3464 	h = scf_instance_handle(inst);
   3465 
   3466 	pg = scf_pg_create(h);
   3467 	prop = scf_property_create(h);
   3468 	val = scf_value_create(h);
   3469 	if (pg == NULL || prop == NULL || val == NULL)
   3470 		goto out;
   3471 
   3472 	if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
   3473 	    SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
   3474 	    pg) != SCF_SUCCESS)
   3475 		goto out;
   3476 
   3477 	if (get_astring_val(pg, SCF_PROPERTY_AUX_FMRI, aux_fmri, size,
   3478 	    prop, val) != SCF_SUCCESS)
   3479 		goto out;
   3480 
   3481 	if (scf_parse_fmri(aux_fmri, NULL, NULL, NULL, NULL, NULL,
   3482 	    NULL) != SCF_SUCCESS)
   3483 		goto out;
   3484 
   3485 	ret = 0;
   3486 
   3487 out:
   3488 	free(aux_fmri);
   3489 	scf_value_destroy(val);
   3490 	scf_property_destroy(prop);
   3491 	scf_pg_destroy(pg);
   3492 	return (ret);
   3493 }
   3494 
   3495 /*
   3496  * Get instance's boolean value in restarter_actions/auxiliary_tty
   3497  * Return -1 on failure
   3498  */
   3499 int
   3500 restarter_inst_ractions_from_tty(scf_instance_t *inst)
   3501 {
   3502 	scf_handle_t *h;
   3503 	scf_propertygroup_t *pg;
   3504 	scf_property_t *prop;
   3505 	scf_value_t *val;
   3506 	uint8_t	has_tty;
   3507 	int ret = -1;
   3508 
   3509 	h = scf_instance_handle(inst);
   3510 	pg = scf_pg_create(h);
   3511 	prop = scf_property_create(h);
   3512 	val = scf_value_create(h);
   3513 	if (pg == NULL || prop == NULL || val == NULL)
   3514 		goto out;
   3515 
   3516 	if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
   3517 	    SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
   3518 	    pg) != SCF_SUCCESS)
   3519 		goto out;
   3520 
   3521 	if (get_boolean_val(pg, SCF_PROPERTY_AUX_TTY, &has_tty, prop,
   3522 	    val) != SCF_SUCCESS)
   3523 		goto out;
   3524 
   3525 	ret = has_tty;
   3526 
   3527 out:
   3528 	scf_value_destroy(val);
   3529 	scf_property_destroy(prop);
   3530 	scf_pg_destroy(pg);
   3531 	return (ret);
   3532 }
   3533 
   3534 static int
   3535 restarter_inst_set_astring_prop(scf_instance_t *inst, const char *pgname,
   3536     const char *pgtype, uint32_t pgflags, const char *pname, const char *str)
   3537 {
   3538 	scf_handle_t *h;
   3539 	scf_propertygroup_t *pg;
   3540 	scf_transaction_t *t;
   3541 	scf_transaction_entry_t *e;
   3542 	scf_value_t *v;
   3543 	int ret = 1, r;
   3544 
   3545 	h = scf_instance_handle(inst);
   3546 
   3547 	pg = scf_pg_create(h);
   3548 	t = scf_transaction_create(h);
   3549 	e = scf_entry_create(h);
   3550 	v = scf_value_create(h);
   3551 	if (pg == NULL || t == NULL || e == NULL || v == NULL)
   3552 		goto out;
   3553 
   3554 	if (instance_get_or_add_pg(inst, pgname, pgtype, pgflags, pg))
   3555 		goto out;
   3556 
   3557 	if (scf_value_set_astring(v, str) != SCF_SUCCESS)
   3558 		goto out;
   3559 
   3560 	for (;;) {
   3561 		if (scf_transaction_start(t, pg) != 0)
   3562 			goto out;
   3563 
   3564 		if (tx_set_value(t, e, pname, SCF_TYPE_ASTRING, v) != 0)
   3565 			goto out;
   3566 
   3567 		if ((r = scf_transaction_commit(t)) == 1)
   3568 			break;
   3569 
   3570 		if (r == -1)
   3571 			goto out;
   3572 
   3573 		scf_transaction_reset(t);
   3574 		if (scf_pg_update(pg) == -1)
   3575 			goto out;
   3576 	}
   3577 	ret = 0;
   3578 
   3579 out:
   3580 	scf_transaction_destroy(t);
   3581 	scf_entry_destroy(e);
   3582 	scf_value_destroy(v);
   3583 	scf_pg_destroy(pg);
   3584 
   3585 	return (ret);
   3586 }
   3587 
   3588 int
   3589 restarter_inst_set_aux_fmri(scf_instance_t *inst)
   3590 {
   3591 	scf_handle_t *h;
   3592 	scf_propertygroup_t *pg;
   3593 	scf_property_t *prop;
   3594 	scf_value_t *val;
   3595 	char *aux_fmri;
   3596 	size_t size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
   3597 	int ret = 1;
   3598 
   3599 	if ((aux_fmri = malloc(size)) == NULL)
   3600 		return (1);
   3601 
   3602 	h = scf_instance_handle(inst);
   3603 
   3604 	pg = scf_pg_create(h);
   3605 	prop = scf_property_create(h);
   3606 	val = scf_value_create(h);
   3607 	if (pg == NULL || prop == NULL || val == NULL)
   3608 		goto out;
   3609 
   3610 	/*
   3611 	 * Get auxiliary_fmri value from restarter_actions pg
   3612 	 */
   3613 	if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
   3614 	    SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
   3615 	    pg) != SCF_SUCCESS)
   3616 		goto out;
   3617 
   3618 	if (get_astring_val(pg, SCF_PROPERTY_AUX_FMRI, aux_fmri, size,
   3619 	    prop, val) != SCF_SUCCESS)
   3620 		goto out;
   3621 
   3622 	/*
   3623 	 * Populate restarter/auxiliary_fmri with the obtained fmri.
   3624 	 */
   3625 	ret = restarter_inst_set_astring_prop(inst, SCF_PG_RESTARTER,
   3626 	    SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS,
   3627 	    SCF_PROPERTY_AUX_FMRI, aux_fmri);
   3628 
   3629 out:
   3630 	free(aux_fmri);
   3631 	scf_value_destroy(val);
   3632 	scf_property_destroy(prop);
   3633 	scf_pg_destroy(pg);
   3634 	return (ret);
   3635 }
   3636 
   3637 int
   3638 restarter_inst_reset_aux_fmri(scf_instance_t *inst)
   3639 {
   3640 	return (scf_instance_delete_prop(inst,
   3641 	    SCF_PG_RESTARTER, SCF_PROPERTY_AUX_FMRI));
   3642 }
   3643 
   3644 int
   3645 restarter_inst_reset_ractions_aux_fmri(scf_instance_t *inst)
   3646 {
   3647 	return (scf_instance_delete_prop(inst,
   3648 	    SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_FMRI));
   3649 }
   3650