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