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