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