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