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