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