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