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