xref: /titanic_44/usr/src/lib/librestart/common/librestart.c (revision 8de5c4f463386063e184a851437d58080c6c626c)
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