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