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