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