dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / librestart / common / librestart.c
blobc09e1483c08efe1d41da1522a535f29383e5e1b3
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
27 #include <libintl.h>
28 #include <librestart.h>
29 #include <librestart_priv.h>
30 #include <libscf.h>
31 #include <libscf_priv.h>
33 #include <assert.h>
34 #include <ctype.h>
35 #include <dlfcn.h>
36 #include <errno.h>
37 #include <exec_attr.h>
38 #include <grp.h>
39 #include <libsysevent.h>
40 #include <libuutil.h>
41 #include <limits.h>
42 #include <link.h>
43 #include <malloc.h>
44 #include <pool.h>
45 #include <priv.h>
46 #include <project.h>
47 #include <pthread.h>
48 #include <pwd.h>
49 #include <secdb.h>
50 #include <signal.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <syslog.h>
54 #include <sys/corectl.h>
55 #include <sys/machelf.h>
56 #include <sys/secflags.h>
57 #include <sys/task.h>
58 #include <sys/types.h>
59 #include <time.h>
60 #include <unistd.h>
61 #include <ucontext.h>
63 #define min(a, b) ((a) > (b) ? (b) : (a))
65 #define MKW_TRUE ":true"
66 #define MKW_KILL ":kill"
67 #define MKW_KILL_PROC ":kill_process"
69 #define ALLOCFAIL ((char *)"Allocation failure.")
70 #define RCBROKEN ((char *)"Repository connection broken.")
72 #define MAX_COMMIT_RETRIES 10
73 #define MAX_COMMIT_RETRY_INT (5 * 1000000) /* 5 seconds */
74 #define INITIAL_COMMIT_RETRY_INT (10000) /* 1/100th second */
77 * bad_fail() catches bugs in this and lower layers by reporting supposedly
78 * impossible function failures. The NDEBUG case keeps the strings out of the
79 * library but still calls abort() so we can root-cause from the coredump.
81 #ifndef NDEBUG
82 #define bad_fail(func, err) { \
83 (void) fprintf(stderr, \
84 "At %s:%d, %s() failed with unexpected error %d. Aborting.\n", \
85 __FILE__, __LINE__, (func), (err)); \
86 abort(); \
88 #else
89 #define bad_fail(func, err) abort()
90 #endif
92 struct restarter_event_handle {
93 char *reh_restarter_name;
94 char *reh_delegate_channel_name;
95 evchan_t *reh_delegate_channel;
96 char *reh_delegate_subscriber_id;
97 char *reh_master_channel_name;
98 evchan_t *reh_master_channel;
99 char *reh_master_subscriber_id;
100 int (*reh_handler)(restarter_event_t *);
103 struct restarter_event {
104 sysevent_t *re_sysevent;
105 restarter_event_type_t re_type;
106 char *re_instance_name;
107 restarter_event_handle_t *re_event_handle;
108 restarter_instance_state_t re_state;
109 restarter_instance_state_t re_next_state;
113 * Long reasons must all parse/read correctly in the following contexts:
115 * "A service instance transitioned state: %s."
116 * "A service failed: %s."
117 * "Reason: %s."
118 * "The service transitioned state (%s) and ..."
120 * With the exception of restart_str_none they must also fit the following
121 * moulds:
123 * "An instance transitioned because %s, and ..."
124 * "An instance transitioned to <new-state> because %s, and ..."
126 * Note that whoever is rendering the long message must provide the
127 * terminal punctuation - don't include it here. Similarly, do not
128 * provide an initial capital letter in reason-long.
130 * The long reason strings are Volatile - within the grammatical constraints
131 * above we may improve them as need be. The intention is that a consumer
132 * may blindly render the string along the lines of the above examples,
133 * but has no other guarantees as to the exact wording. Long reasons
134 * are localized.
136 * We define revisions of the set of short reason strings in use. Within
137 * a given revision, all short reasons are Committed. Consumers must check
138 * the revision in use before relying on the semantics of the short reason
139 * codes - if the version exceeds that which they are familiar with they should
140 * fail gracefully. Having checked for version compatability, a consumer
141 * is assured that
143 * "short_reason_A iff semantic_A", provided:
145 * . the restarter uses this short reason code at all,
146 * . the short reason is not "none" (which a restarter could
147 * specifiy for any transition semantics)
149 * To split/refine such a Committed semantic_A into further cases,
150 * we are required to bump the revision number. This should be an
151 * infrequent occurence. If you bump the revision number you may
152 * need to make corresponding changes in any source that calls
153 * restarter_str_version (e.g., FMA event generation).
155 * To add additional reasons to the set you must also bump the version
156 * number.
160 * The following describes revision 0 of the set of transition reasons.
161 * Read the preceding block comment before making any changes.
163 static const struct restarter_state_transition_reason restarter_str[] = {
165 * Any transition for which the restarter has not provided a reason.
168 restarter_str_none,
169 "none",
170 "the restarter gave no reason"
174 * A transition to maintenance state due to a
175 * 'svcadm mark maintenance <fmri>'. *Not* used if the libscf
176 * interface smf_maintain_instance(3SCF) is used to request maintenance.
179 restarter_str_administrative_request,
180 "administrative_request",
181 "maintenance was requested by an administrator"
185 * A transition to maintenance state if a repository inconsistency
186 * exists when the service/instance state is first read by startd
187 * into the graph engine (this can also happen during startd restart).
190 restarter_str_bad_repo_state,
191 "bad_repo_state",
192 "an SMF repository inconsistecy exists"
196 * A transition 'maintenance -> uninitialized' resulting always
197 * from 'svcadm clear <fmri>'. *Not* used if the libscf interface
198 * smf_restore_instance(3SCF) is used.
201 restarter_str_clear_request,
202 "clear_request",
203 "maintenance clear was requested by an administrator"
207 * A transition 'online -> offline' due to a process core dump.
210 restarter_str_ct_ev_core,
211 "ct_ev_core",
212 "a process dumped core"
216 * A transition 'online -> offline' due to an empty process contract,
217 * i.e., the last process in a contract type service has exited.
220 restarter_str_ct_ev_exit,
221 "ct_ev_exit",
222 "all processes in the service have exited"
226 * A transition 'online -> offline' due to a hardware error.
229 restarter_str_ct_ev_hwerr,
230 "ct_ev_hwerr",
231 "a process was killed due to uncorrectable hardware error"
235 * A transition 'online -> offline' due to a process in the service
236 * having received a fatal signal originating from outside the
237 * service process contract.
240 restarter_str_ct_ev_signal,
241 "ct_ev_signal",
242 "a process received a fatal signal from outside the service"
246 * A transition 'offline -> online' when all dependencies for the
247 * service have been met.
250 restarter_str_dependencies_satisfied,
251 "dependencies_satisfied",
252 "all dependencies have been satisfied"
256 * A transition 'online -> offline' because some dependency for the
257 * service is no-longer met.
260 restarter_str_dependency_activity,
261 "dependency_activity",
262 "a dependency activity required a stop"
266 * A transition to maintenance state due to a cycle in the
267 * service dependencies.
270 restarter_str_dependency_cycle,
271 "dependency_cycle",
272 "a dependency cycle exists"
276 * A transition 'online -> offline -> disabled' due to a
277 * 'svcadm disable [-t] <fmri>' or smf_disable_instance(3SCF) call.
280 restarter_str_disable_request,
281 "disable_request",
282 "a disable was requested"
286 * A transition 'disabled -> offline' due to a
287 * 'svcadm enable [-t] <fmri>' or smf_enable_instance(3SCF) call.
290 restarter_str_enable_request,
291 "enable_request",
292 "an enable was requested"
296 * A transition to maintenance state when a method fails
297 * repeatedly for a retryable reason.
300 restarter_str_fault_threshold_reached,
301 "fault_threshold_reached",
302 "a method is failing in a retryable manner but too often"
306 * A transition to uninitialized state when startd reads the service
307 * configuration and inserts it into the graph engine.
310 restarter_str_insert_in_graph,
311 "insert_in_graph",
312 "the instance was inserted in the graph"
316 * A transition to maintenance state due to an invalid dependency
317 * declared for the service.
320 restarter_str_invalid_dependency,
321 "invalid_dependency",
322 "a service has an invalid dependency"
326 * A transition to maintenance state because the service-declared
327 * restarter is invalid.
330 restarter_str_invalid_restarter,
331 "invalid_restarter",
332 "the service restarter is invalid"
336 * A transition to maintenance state because a restarter method
337 * exited with one of SMF_EXIT_ERR_CONFIG, SMF_EXIT_ERR_NOSMF,
338 * SMF_EXIT_ERR_PERM, or SMF_EXIT_ERR_FATAL.
341 restarter_str_method_failed,
342 "method_failed",
343 "a start, stop or refresh method failed"
347 * A transition 'uninitialized -> {disabled|offline}' after
348 * "insert_in_graph" to match the state configured in the
349 * repository.
352 restarter_str_per_configuration,
353 "per_configuration",
354 "the SMF repository configuration specifies this state"
358 * Refresh requested - no state change.
361 restarter_str_refresh,
362 NULL,
363 "a refresh was requested (no change of state)"
367 * A transition 'online -> offline -> online' due to a
368 * 'svcadm restart <fmri> or equivlaent libscf API call.
369 * Both the 'online -> offline' and 'offline -> online' transtions
370 * specify this reason.
373 restarter_str_restart_request,
374 "restart_request",
375 "a restart was requested"
379 * A transition to maintenance state because the start method is
380 * being executed successfully but too frequently.
383 restarter_str_restarting_too_quickly,
384 "restarting_too_quickly",
385 "the instance is restarting too quickly"
389 * A transition to maintenance state due a service requesting
390 * 'svcadm mark maintenance <fmri>' or equivalent libscf API call.
391 * A command line 'svcadm mark maintenance <fmri>' does not produce
392 * this reason - it produces administrative_request instead.
395 restarter_str_service_request,
396 "service_request",
397 "maintenance was requested by another service"
401 * An instanced inserted into the graph at its existing state
402 * during a startd restart - no state change.
405 restarter_str_startd_restart,
406 NULL,
407 "the instance was inserted in the graph due to startd restart"
411 uint32_t
412 restarter_str_version(void)
414 return (RESTARTER_STRING_VERSION);
417 const char *
418 restarter_get_str_short(restarter_str_t key)
420 int i;
421 for (i = 0; i < sizeof (restarter_str) /
422 sizeof (struct restarter_state_transition_reason); i++)
423 if (key == restarter_str[i].str_key)
424 return (restarter_str[i].str_short);
425 return (NULL);
428 const char *
429 restarter_get_str_long(restarter_str_t key)
431 int i;
432 for (i = 0; i < sizeof (restarter_str) /
433 sizeof (struct restarter_state_transition_reason); i++)
434 if (key == restarter_str[i].str_key)
435 return (dgettext(TEXT_DOMAIN,
436 restarter_str[i].str_long));
437 return (NULL);
441 * A static no memory error message mc_error_t structure
442 * to be used in cases when memory errors are to be returned
443 * This avoids the need to attempt to allocate memory for the
444 * message, therefore getting into a cycle of no memory failures.
446 mc_error_t mc_nomem_err = {
447 0, ENOMEM, sizeof ("Out of memory") - 1, "Out of memory"
450 static const char * const allocfail = "Allocation failure.\n";
451 static const char * const rcbroken = "Repository connection broken.\n";
453 static int method_context_safety = 0; /* Can safely call pools/projects. */
455 int ndebug = 1;
457 /* PRINTFLIKE3 */
458 static mc_error_t *
459 mc_error_create(mc_error_t *e, int type, const char *format, ...)
461 mc_error_t *le;
462 va_list args;
463 int size;
466 * If the type is ENOMEM and format is NULL, then
467 * go ahead and return the default nomem error.
468 * Otherwise, attempt to allocate the memory and if
469 * that fails then there is no reason to continue.
471 if (type == ENOMEM && format == NULL)
472 return (&mc_nomem_err);
474 if (e == NULL && (le = malloc(sizeof (mc_error_t))) == NULL)
475 return (&mc_nomem_err);
476 else
477 le = e;
479 le->type = type;
480 le->destroy = 1;
481 va_start(args, format);
482 size = vsnprintf(NULL, 0, format, args) + 1;
483 if (size >= RESTARTER_ERRMSGSZ) {
484 if ((le = realloc(e, sizeof (mc_error_t) +
485 (size - RESTARTER_ERRMSGSZ))) == NULL) {
486 size = RESTARTER_ERRMSGSZ - 1;
487 le = e;
491 le->size = size;
492 (void) vsnprintf(le->msg, le->size, format, args);
493 va_end(args);
495 return (le);
498 void
499 restarter_mc_error_destroy(mc_error_t *mc_err)
501 if (mc_err == NULL)
502 return;
505 * If the error messages was allocated then free.
507 if (mc_err->destroy) {
508 free(mc_err);
512 static void
513 free_restarter_event_handle(struct restarter_event_handle *h)
515 if (h == NULL)
516 return;
519 * Just free the memory -- don't unbind the sysevent handle,
520 * as otherwise events may be lost if this is just a restarter
521 * restart.
524 free(h->reh_restarter_name);
525 free(h->reh_delegate_channel_name);
526 free(h->reh_delegate_subscriber_id);
527 free(h->reh_master_channel_name);
528 free(h->reh_master_subscriber_id);
530 free(h);
533 char *
534 _restarter_get_channel_name(const char *fmri, int type)
536 char *name;
537 char *chan_name = malloc(MAX_CHNAME_LEN);
538 char prefix_name[3];
539 int i;
541 if (chan_name == NULL)
542 return (NULL);
544 if (type == RESTARTER_CHANNEL_DELEGATE)
545 (void) strcpy(prefix_name, "d_");
546 else if (type == RESTARTER_CHANNEL_MASTER)
547 (void) strcpy(prefix_name, "m_");
548 else {
549 free(chan_name);
550 return (NULL);
554 * Create a unique name
556 * Use the entire name, using a replacement of the /
557 * characters to get a better name.
559 * Remove the svc:/ from the beginning as this really
560 * isn't going to provide any uniqueness...
562 * An fmri name greater than MAX_CHNAME_LEN is going
563 * to be rejected as too long for the chan_name below
564 * in the snprintf call.
566 if ((name = strdup(strchr(fmri, '/') + 1)) == NULL) {
567 free(chan_name);
568 return (NULL);
570 i = 0;
571 while (name[i]) {
572 if (name[i] == '/') {
573 name[i] = '_';
576 i++;
580 * Should check for [a-z],[A-Z],[0-9],.,_,-,:
583 if (snprintf(chan_name, MAX_CHNAME_LEN, "com.sun:scf:%s%s",
584 prefix_name, name) > MAX_CHNAME_LEN) {
585 free(chan_name);
586 chan_name = NULL;
589 free(name);
590 return (chan_name);
594 cb(sysevent_t *syse, void *cookie)
596 restarter_event_handle_t *h = (restarter_event_handle_t *)cookie;
597 restarter_event_t *e;
598 nvlist_t *attr_list = NULL;
599 int ret = 0;
601 e = uu_zalloc(sizeof (restarter_event_t));
602 if (e == NULL)
603 uu_die(allocfail);
604 e->re_event_handle = h;
605 e->re_sysevent = syse;
607 if (sysevent_get_attr_list(syse, &attr_list) != 0)
608 uu_die(allocfail);
610 if ((nvlist_lookup_uint32(attr_list, RESTARTER_NAME_TYPE,
611 &(e->re_type)) != 0) ||
612 (nvlist_lookup_string(attr_list,
613 RESTARTER_NAME_INSTANCE, &(e->re_instance_name)) != 0)) {
614 uu_warn("%s: Can't decode nvlist for event %p\n",
615 h->reh_restarter_name, (void *)syse);
617 ret = 0;
618 } else {
619 ret = h->reh_handler(e);
622 uu_free(e);
623 nvlist_free(attr_list);
624 return (ret);
628 * restarter_bind_handle(uint32_t, char *, int (*)(restarter_event_t *), int,
629 * restarter_event_handle_t **)
631 * Bind to a delegated restarter event channel.
632 * Each delegated restarter gets its own channel for resource management.
634 * Returns 0 on success or
635 * ENOTSUP version mismatch
636 * EINVAL restarter_name or event_handle is NULL
637 * ENOMEM out of memory, too many channels, or too many subscriptions
638 * EBUSY sysevent_evc_bind() could not establish binding
639 * EFAULT internal sysevent_evc_bind()/sysevent_evc_subscribe() error
640 * EMFILE out of file descriptors
641 * EPERM insufficient privilege for sysevent_evc_bind()
642 * EEXIST already subscribed
645 restarter_bind_handle(uint32_t version, const char *restarter_name,
646 int (*event_handler)(restarter_event_t *), int flags,
647 restarter_event_handle_t **rehp)
649 restarter_event_handle_t *h;
650 size_t sz;
651 int err;
653 if (version != RESTARTER_EVENT_VERSION)
654 return (ENOTSUP);
656 if (restarter_name == NULL || event_handler == NULL)
657 return (EINVAL);
659 if (flags & RESTARTER_FLAG_DEBUG)
660 ndebug++;
662 if ((h = uu_zalloc(sizeof (restarter_event_handle_t))) == NULL)
663 return (ENOMEM);
665 h->reh_delegate_subscriber_id = malloc(MAX_SUBID_LEN);
666 h->reh_master_subscriber_id = malloc(MAX_SUBID_LEN);
667 h->reh_restarter_name = strdup(restarter_name);
668 if (h->reh_delegate_subscriber_id == NULL ||
669 h->reh_master_subscriber_id == NULL ||
670 h->reh_restarter_name == NULL) {
671 free_restarter_event_handle(h);
672 return (ENOMEM);
675 sz = strlcpy(h->reh_delegate_subscriber_id, "del", MAX_SUBID_LEN);
676 assert(sz < MAX_SUBID_LEN);
677 sz = strlcpy(h->reh_master_subscriber_id, "master", MAX_SUBID_LEN);
678 assert(sz < MAX_SUBID_LEN);
680 h->reh_delegate_channel_name =
681 _restarter_get_channel_name(restarter_name,
682 RESTARTER_CHANNEL_DELEGATE);
683 h->reh_master_channel_name =
684 _restarter_get_channel_name(restarter_name,
685 RESTARTER_CHANNEL_MASTER);
687 if (h->reh_delegate_channel_name == NULL ||
688 h->reh_master_channel_name == NULL) {
689 free_restarter_event_handle(h);
690 return (ENOMEM);
693 if (sysevent_evc_bind(h->reh_delegate_channel_name,
694 &h->reh_delegate_channel, EVCH_CREAT|EVCH_HOLD_PEND) != 0) {
695 err = errno;
696 assert(err != EINVAL);
697 assert(err != ENOENT);
698 free_restarter_event_handle(h);
699 return (err);
702 if (sysevent_evc_bind(h->reh_master_channel_name,
703 &h->reh_master_channel, EVCH_CREAT|EVCH_HOLD_PEND) != 0) {
704 err = errno;
705 assert(err != EINVAL);
706 assert(err != ENOENT);
707 free_restarter_event_handle(h);
708 return (err);
711 h->reh_handler = event_handler;
713 assert(strlen(restarter_name) <= MAX_CLASS_LEN - 1);
714 assert(strlen(h->reh_delegate_subscriber_id) <= MAX_SUBID_LEN - 1);
715 assert(strlen(h->reh_master_subscriber_id) <= MAX_SUBID_LEN - 1);
717 if (sysevent_evc_subscribe(h->reh_delegate_channel,
718 h->reh_delegate_subscriber_id, EC_ALL, cb, h, EVCH_SUB_KEEP) != 0) {
719 err = errno;
720 assert(err != EINVAL);
721 free_restarter_event_handle(h);
722 return (err);
725 *rehp = h;
726 return (0);
729 restarter_event_handle_t *
730 restarter_event_get_handle(restarter_event_t *e)
732 assert(e != NULL && e->re_event_handle != NULL);
733 return (e->re_event_handle);
736 restarter_event_type_t
737 restarter_event_get_type(restarter_event_t *e)
739 assert(e != NULL);
740 return (e->re_type);
743 ssize_t
744 restarter_event_get_instance(restarter_event_t *e, char *inst, size_t sz)
746 assert(e != NULL && inst != NULL);
747 return ((ssize_t)strlcpy(inst, e->re_instance_name, sz));
751 restarter_event_get_current_states(restarter_event_t *e,
752 restarter_instance_state_t *state, restarter_instance_state_t *next_state)
754 if (e == NULL)
755 return (-1);
756 *state = e->re_state;
757 *next_state = e->re_next_state;
758 return (0);
762 * restarter_event_publish_retry() is a wrapper around sysevent_evc_publish().
763 * In case, the event cannot be sent at the first attempt (sysevent_evc_publish
764 * returned EAGAIN - sysevent queue full), this function retries a few time
765 * and return ENOSPC if it reaches the retry limit.
767 * The arguments to this function map the arguments of sysevent_evc_publish().
769 * On success, return 0. On error, return
771 * EFAULT - internal sysevent_evc_publish() error
772 * ENOMEM - internal sysevent_evc_publish() error
773 * EBADF - scp is invalid (sysevent_evc_publish() returned EINVAL)
774 * ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN)
777 restarter_event_publish_retry(evchan_t *scp, const char *class,
778 const char *subclass, const char *vendor, const char *pub_name,
779 nvlist_t *attr_list, uint32_t flags)
781 int retries, ret;
782 useconds_t retry_int = INITIAL_COMMIT_RETRY_INT;
784 for (retries = 0; retries < MAX_COMMIT_RETRIES; retries++) {
785 ret = sysevent_evc_publish(scp, class, subclass, vendor,
786 pub_name, attr_list, flags);
787 if (ret == 0)
788 break;
790 switch (ret) {
791 case EAGAIN:
792 /* Queue is full */
793 (void) usleep(retry_int);
795 retry_int = min(retry_int * 2, MAX_COMMIT_RETRY_INT);
796 break;
798 case EINVAL:
799 ret = EBADF;
800 /* FALLTHROUGH */
802 case EFAULT:
803 case ENOMEM:
804 return (ret);
806 case EOVERFLOW:
807 default:
808 /* internal error - abort */
809 bad_fail("sysevent_evc_publish", ret);
813 if (retries == MAX_COMMIT_RETRIES)
814 ret = ENOSPC;
816 return (ret);
820 * Commit the state, next state, and auxiliary state into the repository.
821 * Let the graph engine know about the state change and error. On success,
822 * return 0. On error, return
823 * EPROTO - librestart compiled against different libscf
824 * ENOMEM - out of memory
825 * - repository server out of resources
826 * ENOTACTIVE - repository server not running
827 * ECONNABORTED - repository connection established, but then broken
828 * - unknown libscf error
829 * ENOENT - inst does not exist in the repository
830 * EPERM - insufficient permissions
831 * EACCESS - backend access denied
832 * EROFS - backend is readonly
833 * EFAULT - internal sysevent_evc_publish() error
834 * EBADF - h is invalid (sysevent_evc_publish() returned EINVAL)
835 * ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN)
838 restarter_set_states(restarter_event_handle_t *h, const char *inst,
839 restarter_instance_state_t cur_state,
840 restarter_instance_state_t new_cur_state,
841 restarter_instance_state_t next_state,
842 restarter_instance_state_t new_next_state, restarter_error_t e,
843 restarter_str_t aux)
845 nvlist_t *attr;
846 scf_handle_t *scf_h;
847 instance_data_t id;
848 int ret = 0;
849 const char *p = restarter_get_str_short(aux);
851 assert(h->reh_master_channel != NULL);
852 assert(h->reh_master_channel_name != NULL);
853 assert(h->reh_master_subscriber_id != NULL);
855 if ((scf_h = scf_handle_create(SCF_VERSION)) == NULL) {
856 switch (scf_error()) {
857 case SCF_ERROR_VERSION_MISMATCH:
858 return (EPROTO);
860 case SCF_ERROR_NO_MEMORY:
861 return (ENOMEM);
863 default:
864 bad_fail("scf_handle_create", scf_error());
868 if (scf_handle_bind(scf_h) == -1) {
869 scf_handle_destroy(scf_h);
870 switch (scf_error()) {
871 case SCF_ERROR_NO_SERVER:
872 return (ENOTACTIVE);
874 case SCF_ERROR_NO_RESOURCES:
875 return (ENOMEM);
877 case SCF_ERROR_INVALID_ARGUMENT:
878 case SCF_ERROR_IN_USE:
879 default:
880 bad_fail("scf_handle_bind", scf_error());
884 if (nvlist_alloc(&attr, NV_UNIQUE_NAME, 0) != 0 ||
885 nvlist_add_int32(attr, RESTARTER_NAME_STATE, new_cur_state) != 0 ||
886 nvlist_add_int32(attr, RESTARTER_NAME_NEXT_STATE, new_next_state)
887 != 0 ||
888 nvlist_add_int32(attr, RESTARTER_NAME_ERROR, e) != 0 ||
889 nvlist_add_string(attr, RESTARTER_NAME_INSTANCE, inst) != 0 ||
890 nvlist_add_int32(attr, RESTARTER_NAME_REASON, aux) != 0) {
891 ret = ENOMEM;
892 } else {
893 id.i_fmri = inst;
894 id.i_state = cur_state;
895 id.i_next_state = next_state;
897 ret = _restarter_commit_states(scf_h, &id, new_cur_state,
898 new_next_state, p);
900 if (ret == 0) {
901 ret = restarter_event_publish_retry(
902 h->reh_master_channel, "master", "state_change",
903 "com.sun", "librestart", attr, EVCH_NOSLEEP);
907 nvlist_free(attr);
908 (void) scf_handle_unbind(scf_h);
909 scf_handle_destroy(scf_h);
911 return (ret);
914 restarter_instance_state_t
915 restarter_string_to_state(char *string)
917 assert(string != NULL);
919 if (strcmp(string, SCF_STATE_STRING_NONE) == 0)
920 return (RESTARTER_STATE_NONE);
921 else if (strcmp(string, SCF_STATE_STRING_UNINIT) == 0)
922 return (RESTARTER_STATE_UNINIT);
923 else if (strcmp(string, SCF_STATE_STRING_MAINT) == 0)
924 return (RESTARTER_STATE_MAINT);
925 else if (strcmp(string, SCF_STATE_STRING_OFFLINE) == 0)
926 return (RESTARTER_STATE_OFFLINE);
927 else if (strcmp(string, SCF_STATE_STRING_DISABLED) == 0)
928 return (RESTARTER_STATE_DISABLED);
929 else if (strcmp(string, SCF_STATE_STRING_ONLINE) == 0)
930 return (RESTARTER_STATE_ONLINE);
931 else if (strcmp(string, SCF_STATE_STRING_DEGRADED) == 0)
932 return (RESTARTER_STATE_DEGRADED);
933 else {
934 return (RESTARTER_STATE_NONE);
938 ssize_t
939 restarter_state_to_string(restarter_instance_state_t state, char *string,
940 size_t len)
942 assert(string != NULL);
944 if (state == RESTARTER_STATE_NONE)
945 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_NONE, len));
946 else if (state == RESTARTER_STATE_UNINIT)
947 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_UNINIT, len));
948 else if (state == RESTARTER_STATE_MAINT)
949 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_MAINT, len));
950 else if (state == RESTARTER_STATE_OFFLINE)
951 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_OFFLINE,
952 len));
953 else if (state == RESTARTER_STATE_DISABLED)
954 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_DISABLED,
955 len));
956 else if (state == RESTARTER_STATE_ONLINE)
957 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_ONLINE, len));
958 else if (state == RESTARTER_STATE_DEGRADED)
959 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_DEGRADED,
960 len));
961 else
962 return ((ssize_t)strlcpy(string, "unknown", len));
966 * Sets pg to the name property group of s_inst. If it doesn't exist, it is
967 * added.
969 * Fails with
970 * ECONNABORTED - repository disconnection or unknown libscf error
971 * EBADF - inst is not set
972 * ECANCELED - inst is deleted
973 * EPERM - permission is denied
974 * EACCES - backend denied access
975 * EROFS - backend readonly
977 static int
978 instance_get_or_add_pg(scf_instance_t *inst, const char *name,
979 const char *type, uint32_t flags, scf_propertygroup_t *pg)
981 again:
982 if (scf_instance_get_pg(inst, name, pg) == 0)
983 return (0);
985 switch (scf_error()) {
986 case SCF_ERROR_CONNECTION_BROKEN:
987 default:
988 return (ECONNABORTED);
990 case SCF_ERROR_NOT_SET:
991 return (EBADF);
993 case SCF_ERROR_DELETED:
994 return (ECANCELED);
996 case SCF_ERROR_NOT_FOUND:
997 break;
999 case SCF_ERROR_HANDLE_MISMATCH:
1000 case SCF_ERROR_INVALID_ARGUMENT:
1001 bad_fail("scf_instance_get_pg", scf_error());
1004 if (scf_instance_add_pg(inst, name, type, flags, pg) == 0)
1005 return (0);
1007 switch (scf_error()) {
1008 case SCF_ERROR_CONNECTION_BROKEN:
1009 default:
1010 return (ECONNABORTED);
1012 case SCF_ERROR_DELETED:
1013 return (ECANCELED);
1015 case SCF_ERROR_EXISTS:
1016 goto again;
1018 case SCF_ERROR_PERMISSION_DENIED:
1019 return (EPERM);
1021 case SCF_ERROR_BACKEND_ACCESS:
1022 return (EACCES);
1024 case SCF_ERROR_BACKEND_READONLY:
1025 return (EROFS);
1027 case SCF_ERROR_HANDLE_MISMATCH:
1028 case SCF_ERROR_INVALID_ARGUMENT:
1029 case SCF_ERROR_NOT_SET: /* should be caught above */
1030 bad_fail("scf_instance_add_pg", scf_error());
1033 return (0);
1037 * Fails with
1038 * ECONNABORTED
1039 * ECANCELED - pg was deleted
1041 static int
1042 tx_set_value(scf_transaction_t *tx, scf_transaction_entry_t *ent,
1043 const char *pname, scf_type_t ty, scf_value_t *val)
1045 int r;
1047 for (;;) {
1048 if (scf_transaction_property_change_type(tx, ent, pname,
1049 ty) == 0)
1050 break;
1052 switch (scf_error()) {
1053 case SCF_ERROR_CONNECTION_BROKEN:
1054 default:
1055 return (ECONNABORTED);
1057 case SCF_ERROR_DELETED:
1058 return (ECANCELED);
1060 case SCF_ERROR_NOT_FOUND:
1061 break;
1063 case SCF_ERROR_HANDLE_MISMATCH:
1064 case SCF_ERROR_INVALID_ARGUMENT:
1065 case SCF_ERROR_IN_USE:
1066 case SCF_ERROR_NOT_SET:
1067 bad_fail("scf_transaction_property_change_type",
1068 scf_error());
1071 if (scf_transaction_property_new(tx, ent, pname, ty) == 0)
1072 break;
1074 switch (scf_error()) {
1075 case SCF_ERROR_CONNECTION_BROKEN:
1076 default:
1077 return (ECONNABORTED);
1079 case SCF_ERROR_DELETED:
1080 return (ECANCELED);
1082 case SCF_ERROR_EXISTS:
1083 break;
1085 case SCF_ERROR_HANDLE_MISMATCH:
1086 case SCF_ERROR_INVALID_ARGUMENT:
1087 case SCF_ERROR_IN_USE:
1088 case SCF_ERROR_NOT_SET:
1089 bad_fail("scf_transaction_property_new", scf_error());
1093 r = scf_entry_add_value(ent, val);
1094 assert(r == 0);
1096 return (0);
1100 * Commit new_state, new_next_state, and aux to the repository for id. If
1101 * successful, also set id's state and next-state as given, and return 0.
1102 * Fails with
1103 * ENOMEM - out of memory
1104 * ECONNABORTED - repository connection broken
1105 * - unknown libscf error
1106 * EINVAL - id->i_fmri is invalid or not an instance FMRI
1107 * ENOENT - id->i_fmri does not exist
1108 * EPERM - insufficient permissions
1109 * EACCES - backend access denied
1110 * EROFS - backend is readonly
1113 _restarter_commit_states(scf_handle_t *h, instance_data_t *id,
1114 restarter_instance_state_t new_state,
1115 restarter_instance_state_t new_state_next, const char *aux)
1117 char str_state[MAX_SCF_STATE_STRING_SZ];
1118 char str_new_state[MAX_SCF_STATE_STRING_SZ];
1119 char str_state_next[MAX_SCF_STATE_STRING_SZ];
1120 char str_new_state_next[MAX_SCF_STATE_STRING_SZ];
1121 int ret = 0, r;
1122 struct timeval now;
1123 ssize_t sz;
1125 scf_transaction_t *t = NULL;
1126 scf_transaction_entry_t *t_state = NULL, *t_state_next = NULL;
1127 scf_transaction_entry_t *t_stime = NULL, *t_aux = NULL;
1128 scf_value_t *v_state = NULL, *v_state_next = NULL, *v_stime = NULL;
1129 scf_value_t *v_aux = NULL;
1130 scf_instance_t *s_inst = NULL;
1131 scf_propertygroup_t *pg = NULL;
1133 assert(new_state != RESTARTER_STATE_NONE);
1135 if ((s_inst = scf_instance_create(h)) == NULL ||
1136 (pg = scf_pg_create(h)) == NULL ||
1137 (t = scf_transaction_create(h)) == NULL ||
1138 (t_state = scf_entry_create(h)) == NULL ||
1139 (t_state_next = scf_entry_create(h)) == NULL ||
1140 (t_stime = scf_entry_create(h)) == NULL ||
1141 (t_aux = scf_entry_create(h)) == NULL ||
1142 (v_state = scf_value_create(h)) == NULL ||
1143 (v_state_next = scf_value_create(h)) == NULL ||
1144 (v_stime = scf_value_create(h)) == NULL ||
1145 (v_aux = scf_value_create(h)) == NULL) {
1146 ret = ENOMEM;
1147 goto out;
1150 sz = restarter_state_to_string(new_state, str_new_state,
1151 sizeof (str_new_state));
1152 assert(sz < sizeof (str_new_state));
1153 sz = restarter_state_to_string(new_state_next, str_new_state_next,
1154 sizeof (str_new_state_next));
1155 assert(sz < sizeof (str_new_state_next));
1156 sz = restarter_state_to_string(id->i_state, str_state,
1157 sizeof (str_state));
1158 assert(sz < sizeof (str_state));
1159 sz = restarter_state_to_string(id->i_next_state, str_state_next,
1160 sizeof (str_state_next));
1161 assert(sz < sizeof (str_state_next));
1163 ret = gettimeofday(&now, NULL);
1164 assert(ret != -1);
1166 if (scf_handle_decode_fmri(h, id->i_fmri, NULL, NULL, s_inst,
1167 NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
1168 switch (scf_error()) {
1169 case SCF_ERROR_CONNECTION_BROKEN:
1170 default:
1171 ret = ECONNABORTED;
1172 break;
1174 case SCF_ERROR_INVALID_ARGUMENT:
1175 case SCF_ERROR_CONSTRAINT_VIOLATED:
1176 ret = EINVAL;
1177 break;
1179 case SCF_ERROR_NOT_FOUND:
1180 ret = ENOENT;
1181 break;
1183 case SCF_ERROR_HANDLE_MISMATCH:
1184 bad_fail("scf_handle_decode_fmri", scf_error());
1186 goto out;
1190 if (scf_value_set_astring(v_state, str_new_state) != 0 ||
1191 scf_value_set_astring(v_state_next, str_new_state_next) != 0)
1192 bad_fail("scf_value_set_astring", scf_error());
1194 if (aux) {
1195 if (scf_value_set_astring(v_aux, aux) != 0)
1196 bad_fail("scf_value_set_astring", scf_error());
1199 if (scf_value_set_time(v_stime, now.tv_sec, now.tv_usec * 1000) != 0)
1200 bad_fail("scf_value_set_time", scf_error());
1202 add_pg:
1203 switch (r = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
1204 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg)) {
1205 case 0:
1206 break;
1208 case ECONNABORTED:
1209 case EPERM:
1210 case EACCES:
1211 case EROFS:
1212 ret = r;
1213 goto out;
1215 case ECANCELED:
1216 ret = ENOENT;
1217 goto out;
1219 case EBADF:
1220 default:
1221 bad_fail("instance_get_or_add_pg", r);
1224 for (;;) {
1225 if (scf_transaction_start(t, pg) != 0) {
1226 switch (scf_error()) {
1227 case SCF_ERROR_CONNECTION_BROKEN:
1228 default:
1229 ret = ECONNABORTED;
1230 goto out;
1232 case SCF_ERROR_NOT_SET:
1233 goto add_pg;
1235 case SCF_ERROR_PERMISSION_DENIED:
1236 ret = EPERM;
1237 goto out;
1239 case SCF_ERROR_BACKEND_ACCESS:
1240 ret = EACCES;
1241 goto out;
1243 case SCF_ERROR_BACKEND_READONLY:
1244 ret = EROFS;
1245 goto out;
1247 case SCF_ERROR_HANDLE_MISMATCH:
1248 case SCF_ERROR_IN_USE:
1249 bad_fail("scf_transaction_start", scf_error());
1253 if ((r = tx_set_value(t, t_state, SCF_PROPERTY_STATE,
1254 SCF_TYPE_ASTRING, v_state)) != 0 ||
1255 (r = tx_set_value(t, t_state_next, SCF_PROPERTY_NEXT_STATE,
1256 SCF_TYPE_ASTRING, v_state_next)) != 0 ||
1257 (r = tx_set_value(t, t_stime, SCF_PROPERTY_STATE_TIMESTAMP,
1258 SCF_TYPE_TIME, v_stime)) != 0) {
1259 switch (r) {
1260 case ECONNABORTED:
1261 ret = ECONNABORTED;
1262 goto out;
1264 case ECANCELED:
1265 scf_transaction_reset(t);
1266 goto add_pg;
1268 default:
1269 bad_fail("tx_set_value", r);
1273 if (aux) {
1274 if ((r = tx_set_value(t, t_aux, SCF_PROPERTY_AUX_STATE,
1275 SCF_TYPE_ASTRING, v_aux)) != 0) {
1276 switch (r) {
1277 case ECONNABORTED:
1278 ret = ECONNABORTED;
1279 goto out;
1281 case ECANCELED:
1282 scf_transaction_reset(t);
1283 goto add_pg;
1285 default:
1286 bad_fail("tx_set_value", r);
1291 ret = scf_transaction_commit(t);
1292 if (ret == 1)
1293 break;
1294 if (ret == -1) {
1295 switch (scf_error()) {
1296 case SCF_ERROR_CONNECTION_BROKEN:
1297 default:
1298 ret = ECONNABORTED;
1299 goto out;
1301 case SCF_ERROR_PERMISSION_DENIED:
1302 ret = EPERM;
1303 goto out;
1305 case SCF_ERROR_BACKEND_ACCESS:
1306 ret = EACCES;
1307 goto out;
1309 case SCF_ERROR_BACKEND_READONLY:
1310 ret = EROFS;
1311 goto out;
1313 case SCF_ERROR_NOT_SET:
1314 bad_fail("scf_transaction_commit", scf_error());
1318 scf_transaction_reset(t);
1319 if (scf_pg_update(pg) == -1) {
1320 switch (scf_error()) {
1321 case SCF_ERROR_CONNECTION_BROKEN:
1322 default:
1323 ret = ECONNABORTED;
1324 goto out;
1326 case SCF_ERROR_NOT_SET:
1327 goto add_pg;
1332 id->i_state = new_state;
1333 id->i_next_state = new_state_next;
1334 ret = 0;
1336 out:
1337 scf_transaction_destroy(t);
1338 scf_entry_destroy(t_state);
1339 scf_entry_destroy(t_state_next);
1340 scf_entry_destroy(t_stime);
1341 scf_entry_destroy(t_aux);
1342 scf_value_destroy(v_state);
1343 scf_value_destroy(v_state_next);
1344 scf_value_destroy(v_stime);
1345 scf_value_destroy(v_aux);
1346 scf_pg_destroy(pg);
1347 scf_instance_destroy(s_inst);
1349 return (ret);
1353 * Fails with
1354 * EINVAL - type is invalid
1355 * ENOMEM
1356 * ECONNABORTED - repository connection broken
1357 * EBADF - s_inst is not set
1358 * ECANCELED - s_inst is deleted
1359 * EPERM - permission denied
1360 * EACCES - backend access denied
1361 * EROFS - backend readonly
1364 restarter_remove_contract(scf_instance_t *s_inst, ctid_t contract_id,
1365 restarter_contract_type_t type)
1367 scf_handle_t *h;
1368 scf_transaction_t *t = NULL;
1369 scf_transaction_entry_t *t_cid = NULL;
1370 scf_propertygroup_t *pg = NULL;
1371 scf_property_t *prop = NULL;
1372 scf_value_t *val;
1373 scf_iter_t *iter = NULL;
1374 const char *pname;
1375 int ret = 0, primary;
1376 uint64_t c;
1378 switch (type) {
1379 case RESTARTER_CONTRACT_PRIMARY:
1380 primary = 1;
1381 break;
1382 case RESTARTER_CONTRACT_TRANSIENT:
1383 primary = 0;
1384 break;
1385 default:
1386 return (EINVAL);
1389 h = scf_instance_handle(s_inst);
1391 pg = scf_pg_create(h);
1392 prop = scf_property_create(h);
1393 iter = scf_iter_create(h);
1394 t = scf_transaction_create(h);
1396 if (pg == NULL || prop == NULL || iter == NULL || t == NULL) {
1397 ret = ENOMEM;
1398 goto remove_contract_cleanup;
1401 add:
1402 scf_transaction_destroy_children(t);
1403 ret = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
1404 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
1405 if (ret != 0)
1406 goto remove_contract_cleanup;
1408 pname = primary? SCF_PROPERTY_CONTRACT :
1409 SCF_PROPERTY_TRANSIENT_CONTRACT;
1411 for (;;) {
1412 if (scf_transaction_start(t, pg) != 0) {
1413 switch (scf_error()) {
1414 case SCF_ERROR_CONNECTION_BROKEN:
1415 default:
1416 ret = ECONNABORTED;
1417 goto remove_contract_cleanup;
1419 case SCF_ERROR_DELETED:
1420 goto add;
1422 case SCF_ERROR_PERMISSION_DENIED:
1423 ret = EPERM;
1424 goto remove_contract_cleanup;
1426 case SCF_ERROR_BACKEND_ACCESS:
1427 ret = EACCES;
1428 goto remove_contract_cleanup;
1430 case SCF_ERROR_BACKEND_READONLY:
1431 ret = EROFS;
1432 goto remove_contract_cleanup;
1434 case SCF_ERROR_HANDLE_MISMATCH:
1435 case SCF_ERROR_IN_USE:
1436 case SCF_ERROR_NOT_SET:
1437 bad_fail("scf_transaction_start", scf_error());
1441 t_cid = scf_entry_create(h);
1443 if (scf_pg_get_property(pg, pname, prop) == 0) {
1444 replace:
1445 if (scf_transaction_property_change_type(t, t_cid,
1446 pname, SCF_TYPE_COUNT) != 0) {
1447 switch (scf_error()) {
1448 case SCF_ERROR_CONNECTION_BROKEN:
1449 default:
1450 ret = ECONNABORTED;
1451 goto remove_contract_cleanup;
1453 case SCF_ERROR_DELETED:
1454 scf_entry_destroy(t_cid);
1455 goto add;
1457 case SCF_ERROR_NOT_FOUND:
1458 goto new;
1460 case SCF_ERROR_HANDLE_MISMATCH:
1461 case SCF_ERROR_INVALID_ARGUMENT:
1462 case SCF_ERROR_IN_USE:
1463 case SCF_ERROR_NOT_SET:
1464 bad_fail(
1465 "scf_transaction_property_changetype",
1466 scf_error());
1470 if (scf_property_is_type(prop, SCF_TYPE_COUNT) == 0) {
1471 if (scf_iter_property_values(iter, prop) != 0) {
1472 switch (scf_error()) {
1473 case SCF_ERROR_CONNECTION_BROKEN:
1474 default:
1475 ret = ECONNABORTED;
1476 goto remove_contract_cleanup;
1478 case SCF_ERROR_NOT_SET:
1479 case SCF_ERROR_HANDLE_MISMATCH:
1480 bad_fail(
1481 "scf_iter_property_values",
1482 scf_error());
1486 next_val:
1487 val = scf_value_create(h);
1488 if (val == NULL) {
1489 assert(scf_error() ==
1490 SCF_ERROR_NO_MEMORY);
1491 ret = ENOMEM;
1492 goto remove_contract_cleanup;
1495 ret = scf_iter_next_value(iter, val);
1496 if (ret == -1) {
1497 switch (scf_error()) {
1498 case SCF_ERROR_CONNECTION_BROKEN:
1499 ret = ECONNABORTED;
1500 goto remove_contract_cleanup;
1502 case SCF_ERROR_DELETED:
1503 scf_value_destroy(val);
1504 goto add;
1506 case SCF_ERROR_HANDLE_MISMATCH:
1507 case SCF_ERROR_INVALID_ARGUMENT:
1508 case SCF_ERROR_PERMISSION_DENIED:
1509 default:
1510 bad_fail("scf_iter_next_value",
1511 scf_error());
1515 if (ret == 1) {
1516 ret = scf_value_get_count(val, &c);
1517 assert(ret == 0);
1519 if (c != contract_id) {
1520 ret = scf_entry_add_value(t_cid,
1521 val);
1522 assert(ret == 0);
1523 } else {
1524 scf_value_destroy(val);
1527 goto next_val;
1530 scf_value_destroy(val);
1531 } else {
1532 switch (scf_error()) {
1533 case SCF_ERROR_CONNECTION_BROKEN:
1534 default:
1535 ret = ECONNABORTED;
1536 goto remove_contract_cleanup;
1538 case SCF_ERROR_TYPE_MISMATCH:
1539 break;
1541 case SCF_ERROR_INVALID_ARGUMENT:
1542 case SCF_ERROR_NOT_SET:
1543 bad_fail("scf_property_is_type",
1544 scf_error());
1547 } else {
1548 switch (scf_error()) {
1549 case SCF_ERROR_CONNECTION_BROKEN:
1550 default:
1551 ret = ECONNABORTED;
1552 goto remove_contract_cleanup;
1554 case SCF_ERROR_DELETED:
1555 scf_entry_destroy(t_cid);
1556 goto add;
1558 case SCF_ERROR_NOT_FOUND:
1559 break;
1561 case SCF_ERROR_HANDLE_MISMATCH:
1562 case SCF_ERROR_INVALID_ARGUMENT:
1563 case SCF_ERROR_NOT_SET:
1564 bad_fail("scf_pg_get_property", scf_error());
1567 new:
1568 if (scf_transaction_property_new(t, t_cid, pname,
1569 SCF_TYPE_COUNT) != 0) {
1570 switch (scf_error()) {
1571 case SCF_ERROR_CONNECTION_BROKEN:
1572 default:
1573 ret = ECONNABORTED;
1574 goto remove_contract_cleanup;
1576 case SCF_ERROR_DELETED:
1577 scf_entry_destroy(t_cid);
1578 goto add;
1580 case SCF_ERROR_EXISTS:
1581 goto replace;
1583 case SCF_ERROR_HANDLE_MISMATCH:
1584 case SCF_ERROR_INVALID_ARGUMENT:
1585 case SCF_ERROR_NOT_SET:
1586 bad_fail("scf_transaction_property_new",
1587 scf_error());
1592 ret = scf_transaction_commit(t);
1593 if (ret == -1) {
1594 switch (scf_error()) {
1595 case SCF_ERROR_CONNECTION_BROKEN:
1596 default:
1597 ret = ECONNABORTED;
1598 goto remove_contract_cleanup;
1600 case SCF_ERROR_DELETED:
1601 goto add;
1603 case SCF_ERROR_PERMISSION_DENIED:
1604 ret = EPERM;
1605 goto remove_contract_cleanup;
1607 case SCF_ERROR_BACKEND_ACCESS:
1608 ret = EACCES;
1609 goto remove_contract_cleanup;
1611 case SCF_ERROR_BACKEND_READONLY:
1612 ret = EROFS;
1613 goto remove_contract_cleanup;
1615 case SCF_ERROR_NOT_SET:
1616 bad_fail("scf_transaction_commit", scf_error());
1619 if (ret == 1) {
1620 ret = 0;
1621 break;
1624 scf_transaction_destroy_children(t);
1625 if (scf_pg_update(pg) == -1) {
1626 switch (scf_error()) {
1627 case SCF_ERROR_CONNECTION_BROKEN:
1628 default:
1629 ret = ECONNABORTED;
1630 goto remove_contract_cleanup;
1632 case SCF_ERROR_DELETED:
1633 goto add;
1635 case SCF_ERROR_NOT_SET:
1636 bad_fail("scf_pg_update", scf_error());
1641 remove_contract_cleanup:
1642 scf_transaction_destroy_children(t);
1643 scf_transaction_destroy(t);
1644 scf_iter_destroy(iter);
1645 scf_property_destroy(prop);
1646 scf_pg_destroy(pg);
1648 return (ret);
1652 * Fails with
1653 * EINVAL - type is invalid
1654 * ENOMEM
1655 * ECONNABORTED - repository disconnection
1656 * EBADF - s_inst is not set
1657 * ECANCELED - s_inst is deleted
1658 * EPERM
1659 * EACCES
1660 * EROFS
1663 restarter_store_contract(scf_instance_t *s_inst, ctid_t contract_id,
1664 restarter_contract_type_t type)
1666 scf_handle_t *h;
1667 scf_transaction_t *t = NULL;
1668 scf_transaction_entry_t *t_cid = NULL;
1669 scf_value_t *val;
1670 scf_propertygroup_t *pg = NULL;
1671 scf_property_t *prop = NULL;
1672 scf_iter_t *iter = NULL;
1673 const char *pname;
1674 int ret = 0, primary;
1676 if (type == RESTARTER_CONTRACT_PRIMARY)
1677 primary = 1;
1678 else if (type == RESTARTER_CONTRACT_TRANSIENT)
1679 primary = 0;
1680 else
1681 return (EINVAL);
1683 h = scf_instance_handle(s_inst);
1685 pg = scf_pg_create(h);
1686 prop = scf_property_create(h);
1687 iter = scf_iter_create(h);
1688 t = scf_transaction_create(h);
1690 if (pg == NULL || prop == NULL || iter == NULL || t == NULL) {
1691 ret = ENOMEM;
1692 goto out;
1695 add:
1696 scf_transaction_destroy_children(t);
1697 ret = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
1698 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
1699 if (ret != 0)
1700 goto out;
1702 pname = primary ? SCF_PROPERTY_CONTRACT :
1703 SCF_PROPERTY_TRANSIENT_CONTRACT;
1705 for (;;) {
1706 if (scf_transaction_start(t, pg) != 0) {
1707 switch (scf_error()) {
1708 case SCF_ERROR_CONNECTION_BROKEN:
1709 default:
1710 ret = ECONNABORTED;
1711 goto out;
1713 case SCF_ERROR_DELETED:
1714 goto add;
1716 case SCF_ERROR_PERMISSION_DENIED:
1717 ret = EPERM;
1718 goto out;
1720 case SCF_ERROR_BACKEND_ACCESS:
1721 ret = EACCES;
1722 goto out;
1724 case SCF_ERROR_BACKEND_READONLY:
1725 ret = EROFS;
1726 goto out;
1728 case SCF_ERROR_HANDLE_MISMATCH:
1729 case SCF_ERROR_IN_USE:
1730 case SCF_ERROR_NOT_SET:
1731 bad_fail("scf_transaction_start", scf_error());
1735 t_cid = scf_entry_create(h);
1736 if (t_cid == NULL) {
1737 ret = ENOMEM;
1738 goto out;
1741 if (scf_pg_get_property(pg, pname, prop) == 0) {
1742 replace:
1743 if (scf_transaction_property_change_type(t, t_cid,
1744 pname, SCF_TYPE_COUNT) != 0) {
1745 switch (scf_error()) {
1746 case SCF_ERROR_CONNECTION_BROKEN:
1747 default:
1748 ret = ECONNABORTED;
1749 goto out;
1751 case SCF_ERROR_DELETED:
1752 scf_entry_destroy(t_cid);
1753 goto add;
1755 case SCF_ERROR_NOT_FOUND:
1756 goto new;
1758 case SCF_ERROR_HANDLE_MISMATCH:
1759 case SCF_ERROR_INVALID_ARGUMENT:
1760 case SCF_ERROR_IN_USE:
1761 case SCF_ERROR_NOT_SET:
1762 bad_fail(
1763 "scf_transaction_propert_change_type",
1764 scf_error());
1768 if (scf_property_is_type(prop, SCF_TYPE_COUNT) == 0) {
1769 if (scf_iter_property_values(iter, prop) != 0) {
1770 switch (scf_error()) {
1771 case SCF_ERROR_CONNECTION_BROKEN:
1772 default:
1773 ret = ECONNABORTED;
1774 goto out;
1776 case SCF_ERROR_NOT_SET:
1777 case SCF_ERROR_HANDLE_MISMATCH:
1778 bad_fail(
1779 "scf_iter_property_values",
1780 scf_error());
1784 next_val:
1785 val = scf_value_create(h);
1786 if (val == NULL) {
1787 assert(scf_error() ==
1788 SCF_ERROR_NO_MEMORY);
1789 ret = ENOMEM;
1790 goto out;
1793 ret = scf_iter_next_value(iter, val);
1794 if (ret == -1) {
1795 switch (scf_error()) {
1796 case SCF_ERROR_CONNECTION_BROKEN:
1797 default:
1798 ret = ECONNABORTED;
1799 goto out;
1801 case SCF_ERROR_DELETED:
1802 scf_value_destroy(val);
1803 goto add;
1805 case SCF_ERROR_HANDLE_MISMATCH:
1806 case SCF_ERROR_INVALID_ARGUMENT:
1807 case SCF_ERROR_PERMISSION_DENIED:
1808 bad_fail(
1809 "scf_iter_next_value",
1810 scf_error());
1814 if (ret == 1) {
1815 ret = scf_entry_add_value(t_cid, val);
1816 assert(ret == 0);
1818 goto next_val;
1821 scf_value_destroy(val);
1822 } else {
1823 switch (scf_error()) {
1824 case SCF_ERROR_CONNECTION_BROKEN:
1825 default:
1826 ret = ECONNABORTED;
1827 goto out;
1829 case SCF_ERROR_TYPE_MISMATCH:
1830 break;
1832 case SCF_ERROR_INVALID_ARGUMENT:
1833 case SCF_ERROR_NOT_SET:
1834 bad_fail("scf_property_is_type",
1835 scf_error());
1838 } else {
1839 switch (scf_error()) {
1840 case SCF_ERROR_CONNECTION_BROKEN:
1841 default:
1842 ret = ECONNABORTED;
1843 goto out;
1845 case SCF_ERROR_DELETED:
1846 scf_entry_destroy(t_cid);
1847 goto add;
1849 case SCF_ERROR_NOT_FOUND:
1850 break;
1852 case SCF_ERROR_HANDLE_MISMATCH:
1853 case SCF_ERROR_INVALID_ARGUMENT:
1854 case SCF_ERROR_NOT_SET:
1855 bad_fail("scf_pg_get_property", scf_error());
1858 new:
1859 if (scf_transaction_property_new(t, t_cid, pname,
1860 SCF_TYPE_COUNT) != 0) {
1861 switch (scf_error()) {
1862 case SCF_ERROR_CONNECTION_BROKEN:
1863 default:
1864 ret = ECONNABORTED;
1865 goto out;
1867 case SCF_ERROR_DELETED:
1868 scf_entry_destroy(t_cid);
1869 goto add;
1871 case SCF_ERROR_EXISTS:
1872 goto replace;
1874 case SCF_ERROR_HANDLE_MISMATCH:
1875 case SCF_ERROR_INVALID_ARGUMENT:
1876 case SCF_ERROR_NOT_SET:
1877 bad_fail("scf_transaction_property_new",
1878 scf_error());
1883 val = scf_value_create(h);
1884 if (val == NULL) {
1885 assert(scf_error() == SCF_ERROR_NO_MEMORY);
1886 ret = ENOMEM;
1887 goto out;
1890 scf_value_set_count(val, contract_id);
1891 ret = scf_entry_add_value(t_cid, val);
1892 assert(ret == 0);
1894 ret = scf_transaction_commit(t);
1895 if (ret == -1) {
1896 switch (scf_error()) {
1897 case SCF_ERROR_CONNECTION_BROKEN:
1898 default:
1899 ret = ECONNABORTED;
1900 goto out;
1902 case SCF_ERROR_DELETED:
1903 goto add;
1905 case SCF_ERROR_PERMISSION_DENIED:
1906 ret = EPERM;
1907 goto out;
1909 case SCF_ERROR_BACKEND_ACCESS:
1910 ret = EACCES;
1911 goto out;
1913 case SCF_ERROR_BACKEND_READONLY:
1914 ret = EROFS;
1915 goto out;
1917 case SCF_ERROR_NOT_SET:
1918 bad_fail("scf_transaction_commit", scf_error());
1921 if (ret == 1) {
1922 ret = 0;
1923 break;
1926 scf_transaction_destroy_children(t);
1927 if (scf_pg_update(pg) == -1) {
1928 switch (scf_error()) {
1929 case SCF_ERROR_CONNECTION_BROKEN:
1930 default:
1931 ret = ECONNABORTED;
1932 goto out;
1934 case SCF_ERROR_DELETED:
1935 goto add;
1937 case SCF_ERROR_NOT_SET:
1938 bad_fail("scf_pg_update", scf_error());
1943 out:
1944 scf_transaction_destroy_children(t);
1945 scf_transaction_destroy(t);
1946 scf_iter_destroy(iter);
1947 scf_property_destroy(prop);
1948 scf_pg_destroy(pg);
1950 return (ret);
1954 restarter_rm_libs_loadable()
1956 void *libhndl;
1958 if (method_context_safety)
1959 return (1);
1961 if ((libhndl = dlopen("libpool.so", RTLD_LAZY | RTLD_LOCAL)) == NULL)
1962 return (0);
1964 (void) dlclose(libhndl);
1966 if ((libhndl = dlopen("libproject.so", RTLD_LAZY | RTLD_LOCAL)) == NULL)
1967 return (0);
1969 (void) dlclose(libhndl);
1971 method_context_safety = 1;
1973 return (1);
1976 static int
1977 get_astring_val(scf_propertygroup_t *pg, const char *name, char *buf,
1978 size_t bufsz, scf_property_t *prop, scf_value_t *val)
1980 ssize_t szret;
1982 if (pg == NULL)
1983 return (-1);
1985 if (scf_pg_get_property(pg, name, prop) != SCF_SUCCESS) {
1986 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
1987 uu_die(rcbroken);
1988 return (-1);
1991 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
1992 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
1993 uu_die(rcbroken);
1994 return (-1);
1997 szret = scf_value_get_astring(val, buf, bufsz);
1999 return (szret >= 0 ? 0 : -1);
2002 static int
2003 get_boolean_val(scf_propertygroup_t *pg, const char *name, uint8_t *b,
2004 scf_property_t *prop, scf_value_t *val)
2006 if (scf_pg_get_property(pg, name, prop) != SCF_SUCCESS) {
2007 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
2008 uu_die(rcbroken);
2009 return (-1);
2012 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
2013 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
2014 uu_die(rcbroken);
2015 return (-1);
2018 if (scf_value_get_boolean(val, b))
2019 return (-1);
2021 return (0);
2025 * Try to load mcp->pwd, if it isn't already.
2026 * Fails with
2027 * ENOMEM - malloc() failed
2028 * ENOENT - no entry found
2029 * EIO - I/O error
2030 * EMFILE - process out of file descriptors
2031 * ENFILE - system out of file handles
2033 static int
2034 lookup_pwd(struct method_context *mcp)
2036 struct passwd *pwdp;
2037 int ret;
2039 if (mcp->pwbuf != NULL && mcp->pwd.pw_uid == mcp->uid)
2040 return (0);
2042 if (mcp->pwbuf == NULL) {
2043 mcp->pwbufsz = sysconf(_SC_GETPW_R_SIZE_MAX);
2044 assert(mcp->pwbufsz >= 0);
2045 mcp->pwbuf = malloc(mcp->pwbufsz);
2046 if (mcp->pwbuf == NULL)
2047 return (ENOMEM);
2050 do {
2051 ret = getpwuid_r(mcp->uid, &mcp->pwd, mcp->pwbuf, mcp->pwbufsz,
2052 &pwdp);
2053 } while (pwdp == NULL && ret == EINTR);
2054 if (pwdp != NULL)
2055 return (0);
2057 free(mcp->pwbuf);
2058 mcp->pwbuf = NULL;
2060 switch (errno) {
2061 case 0:
2062 default:
2064 * Until bug 5065780 is fixed, getpwuid_r() can fail with
2065 * ENOENT, particularly on the miniroot. Since the
2066 * documentation is inaccurate, we'll return ENOENT for unknown
2067 * errors.
2069 return (ENOENT);
2071 case EIO:
2072 case EMFILE:
2073 case ENFILE:
2074 return (errno);
2076 case ERANGE:
2077 bad_fail("getpwuid_r", errno);
2078 /* NOTREACHED */
2083 * Get the user id for str. Returns 0 on success or
2084 * ERANGE the uid is too big
2085 * EINVAL the string starts with a digit, but is not a valid uid
2086 * ENOMEM out of memory
2087 * ENOENT no passwd entry for str
2088 * EIO an I/O error has occurred
2089 * EMFILE/ENFILE out of file descriptors
2092 get_uid(const char *str, struct method_context *ci, uid_t *uidp)
2094 if (isdigit(str[0])) {
2095 uid_t uid;
2096 char *cp;
2098 errno = 0;
2099 uid = strtol(str, &cp, 10);
2101 if (uid == 0 && errno != 0) {
2102 assert(errno != EINVAL);
2103 return (errno);
2106 for (; *cp != '\0'; ++cp)
2107 if (*cp != ' ' || *cp != '\t')
2108 return (EINVAL);
2110 if (uid > UID_MAX)
2111 return (EINVAL);
2113 *uidp = uid;
2114 return (0);
2115 } else {
2116 struct passwd *pwdp;
2117 int ret;
2119 if (ci->pwbuf == NULL) {
2120 ci->pwbufsz = sysconf(_SC_GETPW_R_SIZE_MAX);
2121 ci->pwbuf = malloc(ci->pwbufsz);
2122 if (ci->pwbuf == NULL)
2123 return (ENOMEM);
2126 do {
2127 ret = getpwnam_r(str, &ci->pwd, ci->pwbuf,
2128 ci->pwbufsz, &pwdp);
2129 } while (pwdp == NULL && ret == EINTR);
2131 if (pwdp != NULL) {
2132 *uidp = ci->pwd.pw_uid;
2133 return (0);
2134 } else {
2135 free(ci->pwbuf);
2136 ci->pwbuf = NULL;
2137 switch (errno) {
2138 case 0:
2139 return (ENOENT);
2141 case ENOENT:
2142 case EIO:
2143 case EMFILE:
2144 case ENFILE:
2145 return (errno);
2147 case ERANGE:
2148 default:
2149 bad_fail("getpwnam_r", errno);
2150 /* NOTREACHED */
2156 gid_t
2157 get_gid(const char *str)
2159 if (isdigit(str[0])) {
2160 gid_t gid;
2161 char *cp;
2163 errno = 0;
2164 gid = strtol(str, &cp, 10);
2166 if (gid == 0 && errno != 0)
2167 return ((gid_t)-1);
2169 for (; *cp != '\0'; ++cp)
2170 if (*cp != ' ' || *cp != '\t')
2171 return ((gid_t)-1);
2173 return (gid);
2174 } else {
2175 struct group grp, *ret;
2176 char *buffer;
2177 size_t buflen;
2179 buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
2180 buffer = malloc(buflen);
2181 if (buffer == NULL)
2182 uu_die(allocfail);
2184 errno = 0;
2185 ret = getgrnam_r(str, &grp, buffer, buflen);
2186 free(buffer);
2188 return (ret == NULL ? (gid_t)-1 : grp.gr_gid);
2193 * Fails with
2194 * ENOMEM - out of memory
2195 * ENOENT - no passwd entry
2196 * no project entry
2197 * EIO - an I/O error occurred
2198 * EMFILE - the process is out of file descriptors
2199 * ENFILE - the system is out of file handles
2200 * ERANGE - the project id is out of range
2201 * EINVAL - str is invalid
2202 * E2BIG - the project entry was too big
2203 * -1 - the name service switch is misconfigured
2206 get_projid(const char *str, struct method_context *cip)
2208 int ret;
2209 void *buf;
2210 const size_t bufsz = PROJECT_BUFSZ;
2211 struct project proj, *pp;
2213 if (strcmp(str, ":default") == 0) {
2214 if (cip->uid == 0) {
2215 /* Don't change project for root services */
2216 cip->project = NULL;
2217 return (0);
2220 switch (ret = lookup_pwd(cip)) {
2221 case 0:
2222 break;
2224 case ENOMEM:
2225 case ENOENT:
2226 case EIO:
2227 case EMFILE:
2228 case ENFILE:
2229 return (ret);
2231 default:
2232 bad_fail("lookup_pwd", ret);
2235 buf = malloc(bufsz);
2236 if (buf == NULL)
2237 return (ENOMEM);
2239 do {
2240 errno = 0;
2241 pp = getdefaultproj(cip->pwd.pw_name, &proj, buf,
2242 bufsz);
2243 } while (pp == NULL && errno == EINTR);
2245 /* to be continued ... */
2246 } else {
2247 projid_t projid;
2248 char *cp;
2250 if (!isdigit(str[0])) {
2251 cip->project = strdup(str);
2252 return (cip->project != NULL ? 0 : ENOMEM);
2255 errno = 0;
2256 projid = strtol(str, &cp, 10);
2258 if (projid == 0 && errno != 0) {
2259 assert(errno == ERANGE);
2260 return (errno);
2263 for (; *cp != '\0'; ++cp)
2264 if (*cp != ' ' || *cp != '\t')
2265 return (EINVAL);
2267 if (projid > MAXPROJID)
2268 return (ERANGE);
2270 buf = malloc(bufsz);
2271 if (buf == NULL)
2272 return (ENOMEM);
2274 do {
2275 errno = 0;
2276 pp = getprojbyid(projid, &proj, buf, bufsz);
2277 } while (pp == NULL && errno == EINTR);
2280 if (pp) {
2281 cip->project = strdup(pp->pj_name);
2282 free(buf);
2283 return (cip->project != NULL ? 0 : ENOMEM);
2286 free(buf);
2288 switch (errno) {
2289 case 0:
2290 return (ENOENT);
2292 case EIO:
2293 case EMFILE:
2294 case ENFILE:
2295 return (errno);
2297 case ERANGE:
2298 return (E2BIG);
2300 default:
2301 return (-1);
2306 * Parse the supp_groups property value and populate ci->groups. Returns
2307 * EINVAL (get_gid() failed for one of the components), E2BIG (the property has
2308 * more than NGROUPS_MAX-1 groups), or 0 on success.
2311 get_groups(char *str, struct method_context *ci)
2313 char *cp, *end, *next;
2314 uint_t i;
2316 const char * const whitespace = " \t";
2317 const char * const illegal = ", \t";
2319 if (str[0] == '\0') {
2320 ci->ngroups = 0;
2321 return (0);
2324 for (cp = str, i = 0; *cp != '\0'; ) {
2325 /* skip whitespace */
2326 cp += strspn(cp, whitespace);
2328 /* find the end */
2329 end = cp + strcspn(cp, illegal);
2331 /* skip whitespace after end */
2332 next = end + strspn(end, whitespace);
2334 /* if there's a comma, it separates the fields */
2335 if (*next == ',')
2336 ++next;
2338 *end = '\0';
2340 if ((ci->groups[i] = get_gid(cp)) == (gid_t)-1) {
2341 ci->ngroups = 0;
2342 return (EINVAL);
2345 ++i;
2346 if (i > NGROUPS_MAX - 1) {
2347 ci->ngroups = 0;
2348 return (E2BIG);
2351 cp = next;
2354 ci->ngroups = i;
2355 return (0);
2360 * Return an error message structure containing the error message
2361 * with context, and the error so the caller can make a decision
2362 * on what to do next.
2364 * Because get_ids uses the mc_error_create() function which can
2365 * reallocate the merr, this function must return the merr pointer
2366 * in case it was reallocated.
2368 static mc_error_t *
2369 get_profile(scf_propertygroup_t *methpg, scf_propertygroup_t *instpg,
2370 scf_property_t *prop, scf_value_t *val, const char *cmdline,
2371 struct method_context *ci, mc_error_t *merr)
2373 char *buf = ci->vbuf;
2374 ssize_t buf_sz = ci->vbuf_sz;
2375 char cmd[PATH_MAX];
2376 char *cp, *value;
2377 const char *cmdp;
2378 execattr_t *eap;
2379 mc_error_t *err = merr;
2380 int r;
2382 if (!(get_astring_val(methpg, SCF_PROPERTY_PROFILE, buf, buf_sz, prop,
2383 val) == 0 || get_astring_val(instpg, SCF_PROPERTY_PROFILE, buf,
2384 buf_sz, prop, val) == 0))
2385 return (mc_error_create(merr, scf_error(),
2386 "Method context requires a profile, but the \"%s\" "
2387 "property could not be read. scf_error is %s",
2388 SCF_PROPERTY_PROFILE, scf_strerror(scf_error())));
2390 /* Extract the command from the command line. */
2391 cp = strpbrk(cmdline, " \t");
2393 if (cp == NULL) {
2394 cmdp = cmdline;
2395 } else {
2396 (void) strncpy(cmd, cmdline, cp - cmdline);
2397 cmd[cp - cmdline] = '\0';
2398 cmdp = cmd;
2401 /* Require that cmdp[0] == '/'? */
2403 eap = getexecprof(buf, KV_COMMAND, cmdp, GET_ONE);
2404 if (eap == NULL)
2405 return (mc_error_create(merr, ENOENT,
2406 "Could not find the execution profile \"%s\", "
2407 "command %s.", buf, cmdp));
2409 /* Based on pfexec.c */
2411 /* Get the euid first so we don't override ci->pwd for the uid. */
2412 if ((value = kva_match(eap->attr, EXECATTR_EUID_KW)) != NULL) {
2413 if ((r = get_uid(value, ci, &ci->euid)) != 0) {
2414 ci->euid = (uid_t)-1;
2415 err = mc_error_create(merr, r,
2416 "Could not interpret profile euid value \"%s\", "
2417 "from the execution profile \"%s\", error %d.",
2418 value, buf, r);
2419 goto out;
2423 if ((value = kva_match(eap->attr, EXECATTR_UID_KW)) != NULL) {
2424 if ((r = get_uid(value, ci, &ci->uid)) != 0) {
2425 ci->euid = ci->uid = (uid_t)-1;
2426 err = mc_error_create(merr, r,
2427 "Could not interpret profile uid value \"%s\", "
2428 "from the execution profile \"%s\", error %d.",
2429 value, buf, r);
2430 goto out;
2432 ci->euid = ci->uid;
2435 if ((value = kva_match(eap->attr, EXECATTR_GID_KW)) != NULL) {
2436 ci->egid = ci->gid = get_gid(value);
2437 if (ci->gid == (gid_t)-1) {
2438 err = mc_error_create(merr, EINVAL,
2439 "Could not interpret profile gid value \"%s\", "
2440 "from the execution profile \"%s\".", value, buf);
2441 goto out;
2445 if ((value = kva_match(eap->attr, EXECATTR_EGID_KW)) != NULL) {
2446 ci->egid = get_gid(value);
2447 if (ci->egid == (gid_t)-1) {
2448 err = mc_error_create(merr, EINVAL,
2449 "Could not interpret profile egid value \"%s\", "
2450 "from the execution profile \"%s\".", value, buf);
2451 goto out;
2455 if ((value = kva_match(eap->attr, EXECATTR_LPRIV_KW)) != NULL) {
2456 ci->lpriv_set = priv_str_to_set(value, ",", NULL);
2457 if (ci->lpriv_set == NULL) {
2458 if (errno != EINVAL)
2459 err = mc_error_create(merr, ENOMEM,
2460 ALLOCFAIL);
2461 else
2462 err = mc_error_create(merr, EINVAL,
2463 "Could not interpret profile "
2464 "limitprivs value \"%s\", from "
2465 "the execution profile \"%s\".",
2466 value, buf);
2467 goto out;
2471 if ((value = kva_match(eap->attr, EXECATTR_IPRIV_KW)) != NULL) {
2472 ci->priv_set = priv_str_to_set(value, ",", NULL);
2473 if (ci->priv_set == NULL) {
2474 if (errno != EINVAL)
2475 err = mc_error_create(merr, ENOMEM,
2476 ALLOCFAIL);
2477 else
2478 err = mc_error_create(merr, EINVAL,
2479 "Could not interpret profile privs value "
2480 "\"%s\", from the execution profile "
2481 "\"%s\".", value, buf);
2482 goto out;
2486 out:
2487 free_execattr(eap);
2489 return (err);
2493 * Return an error message structure containing the error message
2494 * with context, and the error so the caller can make a decision
2495 * on what to do next.
2497 * Because get_ids uses the mc_error_create() function which can
2498 * reallocate the merr, this function must return the merr pointer
2499 * in case it was reallocated.
2501 static mc_error_t *
2502 get_ids(scf_propertygroup_t *methpg, scf_propertygroup_t *instpg,
2503 scf_property_t *prop, scf_value_t *val, struct method_context *ci,
2504 mc_error_t *merr)
2506 char *vbuf = ci->vbuf;
2507 ssize_t vbuf_sz = ci->vbuf_sz;
2508 int r;
2511 * This should never happen because the caller should fall through
2512 * another path of just setting the ids to defaults, instead of
2513 * attempting to get the ids here.
2515 if (methpg == NULL && instpg == NULL)
2516 return (mc_error_create(merr, ENOENT,
2517 "No property groups to get ids from."));
2519 if (!(get_astring_val(methpg, SCF_PROPERTY_USER,
2520 vbuf, vbuf_sz, prop, val) == 0 || get_astring_val(instpg,
2521 SCF_PROPERTY_USER, vbuf, vbuf_sz, prop,
2522 val) == 0))
2523 return (mc_error_create(merr, ENOENT,
2524 "Could not get \"%s\" property.", SCF_PROPERTY_USER));
2526 if ((r = get_uid(vbuf, ci, &ci->uid)) != 0) {
2527 ci->uid = (uid_t)-1;
2528 return (mc_error_create(merr, r,
2529 "Could not interpret \"%s\" property value \"%s\", "
2530 "error %d.", SCF_PROPERTY_USER, vbuf, r));
2533 if (!(get_astring_val(methpg, SCF_PROPERTY_GROUP, vbuf, vbuf_sz, prop,
2534 val) == 0 || get_astring_val(instpg, SCF_PROPERTY_GROUP, vbuf,
2535 vbuf_sz, prop, val) == 0)) {
2536 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2537 (void) strcpy(vbuf, ":default");
2538 } else {
2539 return (mc_error_create(merr, ENOENT,
2540 "Could not get \"%s\" property.",
2541 SCF_PROPERTY_GROUP));
2545 if (strcmp(vbuf, ":default") != 0) {
2546 ci->gid = get_gid(vbuf);
2547 if (ci->gid == (gid_t)-1) {
2548 return (mc_error_create(merr, ENOENT,
2549 "Could not interpret \"%s\" property value \"%s\".",
2550 SCF_PROPERTY_GROUP, vbuf));
2552 } else {
2553 switch (r = lookup_pwd(ci)) {
2554 case 0:
2555 ci->gid = ci->pwd.pw_gid;
2556 break;
2558 case ENOENT:
2559 ci->gid = (gid_t)-1;
2560 return (mc_error_create(merr, ENOENT,
2561 "No passwd entry for uid \"%d\".", ci->uid));
2563 case ENOMEM:
2564 return (mc_error_create(merr, ENOMEM,
2565 "Out of memory."));
2567 case EIO:
2568 case EMFILE:
2569 case ENFILE:
2570 return (mc_error_create(merr, ENFILE,
2571 "getpwuid_r() failed, error %d.", r));
2573 default:
2574 bad_fail("lookup_pwd", r);
2578 if (!(get_astring_val(methpg, SCF_PROPERTY_SUPP_GROUPS, vbuf, vbuf_sz,
2579 prop, val) == 0 || get_astring_val(instpg,
2580 SCF_PROPERTY_SUPP_GROUPS, vbuf, vbuf_sz, prop, val) == 0)) {
2581 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2582 (void) strcpy(vbuf, ":default");
2583 } else {
2584 return (mc_error_create(merr, ENOENT,
2585 "Could not get supplemental groups (\"%s\") "
2586 "property.", SCF_PROPERTY_SUPP_GROUPS));
2590 if (strcmp(vbuf, ":default") != 0) {
2591 switch (r = get_groups(vbuf, ci)) {
2592 case 0:
2593 break;
2595 case EINVAL:
2596 return (mc_error_create(merr, EINVAL,
2597 "Could not interpret supplemental groups (\"%s\") "
2598 "property value \"%s\".", SCF_PROPERTY_SUPP_GROUPS,
2599 vbuf));
2601 case E2BIG:
2602 return (mc_error_create(merr, E2BIG,
2603 "Too many supplemental groups values in \"%s\".",
2604 vbuf));
2606 default:
2607 bad_fail("get_groups", r);
2609 } else {
2610 ci->ngroups = -1;
2613 if (!(get_astring_val(methpg, SCF_PROPERTY_PRIVILEGES, vbuf, vbuf_sz,
2614 prop, val) == 0 || get_astring_val(instpg, SCF_PROPERTY_PRIVILEGES,
2615 vbuf, vbuf_sz, prop, val) == 0)) {
2616 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2617 (void) strcpy(vbuf, ":default");
2618 } else {
2619 return (mc_error_create(merr, ENOENT,
2620 "Could not get \"%s\" property.",
2621 SCF_PROPERTY_PRIVILEGES));
2626 * For default privs, we need to keep priv_set == NULL, as
2627 * we use this test elsewhere.
2629 if (strcmp(vbuf, ":default") != 0) {
2630 ci->priv_set = priv_str_to_set(vbuf, ",", NULL);
2631 if (ci->priv_set == NULL) {
2632 if (errno != EINVAL) {
2633 return (mc_error_create(merr, ENOMEM,
2634 ALLOCFAIL));
2635 } else {
2636 return (mc_error_create(merr, EINVAL,
2637 "Could not interpret \"%s\" "
2638 "property value \"%s\".",
2639 SCF_PROPERTY_PRIVILEGES, vbuf));
2644 if (!(get_astring_val(methpg, SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf,
2645 vbuf_sz, prop, val) == 0 || get_astring_val(instpg,
2646 SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf, vbuf_sz, prop, val) == 0)) {
2647 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2648 (void) strcpy(vbuf, ":default");
2649 } else {
2650 return (mc_error_create(merr, ENOENT,
2651 "Could not get \"%s\" property.",
2652 SCF_PROPERTY_LIMIT_PRIVILEGES));
2656 if (strcmp(vbuf, ":default") == 0)
2658 * L must default to all privileges so root NPA services see
2659 * iE = all. "zone" is all privileges available in the current
2660 * zone, equivalent to "all" in the global zone.
2662 (void) strcpy(vbuf, "zone");
2664 ci->lpriv_set = priv_str_to_set(vbuf, ",", NULL);
2665 if (ci->lpriv_set == NULL) {
2666 if (errno != EINVAL) {
2667 return (mc_error_create(merr, ENOMEM, ALLOCFAIL));
2668 } else {
2669 return (mc_error_create(merr, EINVAL,
2670 "Could not interpret \"%s\" property value \"%s\".",
2671 SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf));
2675 return (merr);
2678 static int
2679 get_environment(scf_handle_t *h, scf_propertygroup_t *pg,
2680 struct method_context *mcp, scf_property_t *prop, scf_value_t *val)
2682 scf_iter_t *iter;
2683 scf_type_t type;
2684 size_t i = 0;
2685 int ret;
2687 if (scf_pg_get_property(pg, SCF_PROPERTY_ENVIRONMENT, prop) != 0) {
2688 if (scf_error() == SCF_ERROR_NOT_FOUND)
2689 return (ENOENT);
2690 return (scf_error());
2692 if (scf_property_type(prop, &type) != 0)
2693 return (scf_error());
2694 if (type != SCF_TYPE_ASTRING)
2695 return (EINVAL);
2696 if ((iter = scf_iter_create(h)) == NULL)
2697 return (scf_error());
2699 if (scf_iter_property_values(iter, prop) != 0) {
2700 ret = scf_error();
2701 scf_iter_destroy(iter);
2702 return (ret);
2705 mcp->env_sz = 10;
2707 if ((mcp->env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz)) == NULL) {
2708 ret = ENOMEM;
2709 goto out;
2712 while ((ret = scf_iter_next_value(iter, val)) == 1) {
2713 ret = scf_value_get_as_string(val, mcp->vbuf, mcp->vbuf_sz);
2714 if (ret == -1) {
2715 ret = scf_error();
2716 goto out;
2719 if ((mcp->env[i] = strdup(mcp->vbuf)) == NULL) {
2720 ret = ENOMEM;
2721 goto out;
2724 if (++i == mcp->env_sz) {
2725 char **env;
2726 mcp->env_sz *= 2;
2727 env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz);
2728 if (env == NULL) {
2729 ret = ENOMEM;
2730 goto out;
2732 (void) memcpy(env, mcp->env,
2733 sizeof (*mcp->env) * (mcp->env_sz / 2));
2734 free(mcp->env);
2735 mcp->env = env;
2739 if (ret == -1)
2740 ret = scf_error();
2742 out:
2743 scf_iter_destroy(iter);
2744 return (ret);
2748 * Fetch method context information from the repository, allocate and fill
2749 * a method_context structure, return it in *mcpp, and return NULL.
2751 * If no method_context is defined, original init context is provided, where
2752 * the working directory is '/', and uid/gid are 0/0. But if a method_context
2753 * is defined at any level the smf_method(5) method_context defaults are used.
2755 * Return an error message structure containing the error message
2756 * with context, and the error so the caller can make a decision
2757 * on what to do next.
2759 * Error Types :
2760 * E2BIG Too many values or entry is too big
2761 * EINVAL Invalid value
2762 * EIO an I/O error has occured
2763 * ENOENT no entry for value
2764 * ENOMEM out of memory
2765 * ENOTSUP Version mismatch
2766 * ERANGE value is out of range
2767 * EMFILE/ENFILE out of file descriptors
2769 * SCF_ERROR_BACKEND_ACCESS
2770 * SCF_ERROR_CONNECTION_BROKEN
2771 * SCF_ERROR_DELETED
2772 * SCF_ERROR_CONSTRAINT_VIOLATED
2773 * SCF_ERROR_HANDLE_DESTROYED
2774 * SCF_ERROR_INTERNAL
2775 * SCF_ERROR_INVALID_ARGUMENT
2776 * SCF_ERROR_NO_MEMORY
2777 * SCF_ERROR_NO_RESOURCES
2778 * SCF_ERROR_NOT_BOUND
2779 * SCF_ERROR_NOT_FOUND
2780 * SCF_ERROR_NOT_SET
2781 * SCF_ERROR_TYPE_MISMATCH
2784 mc_error_t *
2785 restarter_get_method_context(uint_t version, scf_instance_t *inst,
2786 scf_snapshot_t *snap, const char *mname, const char *cmdline,
2787 struct method_context **mcpp)
2789 scf_handle_t *h;
2790 scf_propertygroup_t *methpg = NULL;
2791 scf_propertygroup_t *instpg = NULL;
2792 scf_propertygroup_t *pg = NULL;
2793 scf_property_t *prop = NULL;
2794 scf_value_t *val = NULL;
2795 scf_type_t ty;
2796 uint8_t use_profile;
2797 int ret = 0;
2798 int mc_used = 0;
2799 mc_error_t *err = NULL;
2800 struct method_context *cip;
2802 if ((err = malloc(sizeof (mc_error_t))) == NULL)
2803 return (mc_error_create(NULL, ENOMEM, NULL));
2805 /* Set the type to zero to track if an error occured. */
2806 err->type = 0;
2808 if (version != RESTARTER_METHOD_CONTEXT_VERSION)
2809 return (mc_error_create(err, ENOTSUP,
2810 "Invalid client version %d. (Expected %d)",
2811 version, RESTARTER_METHOD_CONTEXT_VERSION));
2813 /* Get the handle before we allocate anything. */
2814 h = scf_instance_handle(inst);
2815 if (h == NULL)
2816 return (mc_error_create(err, scf_error(),
2817 scf_strerror(scf_error())));
2819 cip = malloc(sizeof (*cip));
2820 if (cip == NULL)
2821 return (mc_error_create(err, ENOMEM, ALLOCFAIL));
2823 (void) memset(cip, 0, sizeof (*cip));
2824 cip->uid = (uid_t)-1;
2825 cip->euid = (uid_t)-1;
2826 cip->gid = (gid_t)-1;
2827 cip->egid = (gid_t)-1;
2829 cip->vbuf_sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
2830 assert(cip->vbuf_sz >= 0);
2831 cip->vbuf = malloc(cip->vbuf_sz);
2832 if (cip->vbuf == NULL) {
2833 free(cip);
2834 return (mc_error_create(err, ENOMEM, ALLOCFAIL));
2837 if ((instpg = scf_pg_create(h)) == NULL ||
2838 (methpg = scf_pg_create(h)) == NULL ||
2839 (prop = scf_property_create(h)) == NULL ||
2840 (val = scf_value_create(h)) == NULL) {
2841 err = mc_error_create(err, scf_error(),
2842 "Failed to create repository object: %s",
2843 scf_strerror(scf_error()));
2844 goto out;
2848 * The method environment, and the credentials/profile data,
2849 * may be found either in the pg for the method (methpg),
2850 * or in the instance/service SCF_PG_METHOD_CONTEXT pg (named
2851 * instpg below).
2854 if (scf_instance_get_pg_composed(inst, snap, mname, methpg) !=
2855 SCF_SUCCESS) {
2856 err = mc_error_create(err, scf_error(), "Unable to get the "
2857 "\"%s\" method, %s", mname, scf_strerror(scf_error()));
2858 goto out;
2861 if (scf_instance_get_pg_composed(inst, snap, SCF_PG_METHOD_CONTEXT,
2862 instpg) != SCF_SUCCESS) {
2863 if (scf_error() != SCF_ERROR_NOT_FOUND) {
2864 err = mc_error_create(err, scf_error(),
2865 "Unable to retrieve the \"%s\" property group, %s",
2866 SCF_PG_METHOD_CONTEXT, scf_strerror(scf_error()));
2867 goto out;
2869 scf_pg_destroy(instpg);
2870 instpg = NULL;
2871 } else {
2872 mc_used++;
2875 ret = get_environment(h, methpg, cip, prop, val);
2876 if (ret == ENOENT && instpg != NULL) {
2877 ret = get_environment(h, instpg, cip, prop, val);
2880 switch (ret) {
2881 case 0:
2882 mc_used++;
2883 break;
2884 case ENOENT:
2885 break;
2886 case ENOMEM:
2887 err = mc_error_create(err, ret, "Out of memory.");
2888 goto out;
2889 case EINVAL:
2890 err = mc_error_create(err, ret, "Invalid method environment.");
2891 goto out;
2892 default:
2893 err = mc_error_create(err, ret,
2894 "Get method environment failed: %s", scf_strerror(ret));
2895 goto out;
2898 pg = methpg;
2900 ret = scf_pg_get_property(pg, SCF_PROPERTY_USE_PROFILE, prop);
2901 if (ret && scf_error() == SCF_ERROR_NOT_FOUND && instpg != NULL) {
2902 pg = NULL;
2903 ret = scf_pg_get_property(instpg, SCF_PROPERTY_USE_PROFILE,
2904 prop);
2907 if (ret) {
2908 switch (scf_error()) {
2909 case SCF_ERROR_NOT_FOUND:
2910 /* No profile context: use default credentials */
2911 cip->uid = 0;
2912 cip->gid = 0;
2913 break;
2915 case SCF_ERROR_CONNECTION_BROKEN:
2916 err = mc_error_create(err, SCF_ERROR_CONNECTION_BROKEN,
2917 RCBROKEN);
2918 goto out;
2920 case SCF_ERROR_DELETED:
2921 err = mc_error_create(err, SCF_ERROR_NOT_FOUND,
2922 "Could not find property group \"%s\"",
2923 pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
2924 goto out;
2926 case SCF_ERROR_HANDLE_MISMATCH:
2927 case SCF_ERROR_INVALID_ARGUMENT:
2928 case SCF_ERROR_NOT_SET:
2929 default:
2930 bad_fail("scf_pg_get_property", scf_error());
2932 } else {
2933 if (scf_property_type(prop, &ty) != SCF_SUCCESS) {
2934 ret = scf_error();
2935 switch (ret) {
2936 case SCF_ERROR_CONNECTION_BROKEN:
2937 err = mc_error_create(err,
2938 SCF_ERROR_CONNECTION_BROKEN, RCBROKEN);
2939 break;
2941 case SCF_ERROR_DELETED:
2942 err = mc_error_create(err,
2943 SCF_ERROR_NOT_FOUND,
2944 "Could not find property group \"%s\"",
2945 pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
2946 break;
2948 case SCF_ERROR_NOT_SET:
2949 default:
2950 bad_fail("scf_property_type", ret);
2953 goto out;
2956 if (ty != SCF_TYPE_BOOLEAN) {
2957 err = mc_error_create(err,
2958 SCF_ERROR_TYPE_MISMATCH,
2959 "\"%s\" property is not boolean in property group "
2960 "\"%s\".", SCF_PROPERTY_USE_PROFILE,
2961 pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
2962 goto out;
2965 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
2966 ret = scf_error();
2967 switch (ret) {
2968 case SCF_ERROR_CONNECTION_BROKEN:
2969 err = mc_error_create(err,
2970 SCF_ERROR_CONNECTION_BROKEN, RCBROKEN);
2971 break;
2973 case SCF_ERROR_CONSTRAINT_VIOLATED:
2974 err = mc_error_create(err,
2975 SCF_ERROR_CONSTRAINT_VIOLATED,
2976 "\"%s\" property has multiple values.",
2977 SCF_PROPERTY_USE_PROFILE);
2978 break;
2980 case SCF_ERROR_NOT_FOUND:
2981 err = mc_error_create(err,
2982 SCF_ERROR_NOT_FOUND,
2983 "\"%s\" property has no values.",
2984 SCF_PROPERTY_USE_PROFILE);
2985 break;
2986 default:
2987 bad_fail("scf_property_get_value", ret);
2990 goto out;
2993 mc_used++;
2994 ret = scf_value_get_boolean(val, &use_profile);
2995 assert(ret == SCF_SUCCESS);
2997 /* get ids & privileges */
2998 if (use_profile)
2999 err = get_profile(pg, instpg, prop, val, cmdline,
3000 cip, err);
3001 else
3002 err = get_ids(pg, instpg, prop, val, cip, err);
3004 if (err->type != 0)
3005 goto out;
3008 /* get working directory */
3009 if ((methpg != NULL && scf_pg_get_property(methpg,
3010 SCF_PROPERTY_WORKING_DIRECTORY, prop) == SCF_SUCCESS) ||
3011 (instpg != NULL && scf_pg_get_property(instpg,
3012 SCF_PROPERTY_WORKING_DIRECTORY, prop) == SCF_SUCCESS)) {
3013 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3014 ret = scf_error();
3015 switch (ret) {
3016 case SCF_ERROR_CONNECTION_BROKEN:
3017 err = mc_error_create(err, ret, RCBROKEN);
3018 break;
3020 case SCF_ERROR_CONSTRAINT_VIOLATED:
3021 err = mc_error_create(err, ret,
3022 "\"%s\" property has multiple values.",
3023 SCF_PROPERTY_WORKING_DIRECTORY);
3024 break;
3026 case SCF_ERROR_NOT_FOUND:
3027 err = mc_error_create(err, ret,
3028 "\"%s\" property has no values.",
3029 SCF_PROPERTY_WORKING_DIRECTORY);
3030 break;
3032 default:
3033 bad_fail("scf_property_get_value", ret);
3036 goto out;
3039 mc_used++;
3040 ret = scf_value_get_astring(val, cip->vbuf, cip->vbuf_sz);
3041 assert(ret != -1);
3042 } else {
3043 ret = scf_error();
3044 switch (ret) {
3045 case SCF_ERROR_NOT_FOUND:
3046 /* okay if missing. */
3047 (void) strcpy(cip->vbuf, ":default");
3048 break;
3050 case SCF_ERROR_CONNECTION_BROKEN:
3051 err = mc_error_create(err, ret, RCBROKEN);
3052 goto out;
3054 case SCF_ERROR_DELETED:
3055 err = mc_error_create(err, ret,
3056 "Property group could not be found");
3057 goto out;
3059 case SCF_ERROR_HANDLE_MISMATCH:
3060 case SCF_ERROR_INVALID_ARGUMENT:
3061 case SCF_ERROR_NOT_SET:
3062 default:
3063 bad_fail("scf_pg_get_property", ret);
3067 if (strcmp(cip->vbuf, ":default") == 0 ||
3068 strcmp(cip->vbuf, ":home") == 0) {
3069 switch (ret = lookup_pwd(cip)) {
3070 case 0:
3071 break;
3073 case ENOMEM:
3074 err = mc_error_create(err, ret, "Out of memory.");
3075 goto out;
3077 case ENOENT:
3078 case EIO:
3079 case EMFILE:
3080 case ENFILE:
3081 err = mc_error_create(err, ret,
3082 "Could not get passwd entry.");
3083 goto out;
3085 default:
3086 bad_fail("lookup_pwd", ret);
3089 cip->working_dir = strdup(cip->pwd.pw_dir);
3090 if (cip->working_dir == NULL) {
3091 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3092 goto out;
3094 } else {
3095 cip->working_dir = strdup(cip->vbuf);
3096 if (cip->working_dir == NULL) {
3097 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3098 goto out;
3102 /* get security flags */
3103 if ((methpg != NULL && scf_pg_get_property(methpg,
3104 SCF_PROPERTY_SECFLAGS, prop) == SCF_SUCCESS) ||
3105 (instpg != NULL && scf_pg_get_property(instpg,
3106 SCF_PROPERTY_SECFLAGS, prop) == SCF_SUCCESS)) {
3107 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3108 ret = scf_error();
3109 switch (ret) {
3110 case SCF_ERROR_CONNECTION_BROKEN:
3111 err = mc_error_create(err, ret, RCBROKEN);
3112 break;
3114 case SCF_ERROR_CONSTRAINT_VIOLATED:
3115 err = mc_error_create(err, ret,
3116 "\"%s\" property has multiple values.",
3117 SCF_PROPERTY_SECFLAGS);
3118 break;
3120 case SCF_ERROR_NOT_FOUND:
3121 err = mc_error_create(err, ret,
3122 "\"%s\" property has no values.",
3123 SCF_PROPERTY_SECFLAGS);
3124 break;
3126 default:
3127 bad_fail("scf_property_get_value", ret);
3130 (void) strlcpy(cip->vbuf, ":default", cip->vbuf_sz);
3131 } else {
3132 ret = scf_value_get_astring(val, cip->vbuf,
3133 cip->vbuf_sz);
3134 assert(ret != -1);
3136 mc_used++;
3137 } else {
3138 ret = scf_error();
3139 switch (ret) {
3140 case SCF_ERROR_NOT_FOUND:
3141 /* okay if missing. */
3142 (void) strlcpy(cip->vbuf, ":default", cip->vbuf_sz);
3143 break;
3145 case SCF_ERROR_CONNECTION_BROKEN:
3146 err = mc_error_create(err, ret, RCBROKEN);
3147 goto out;
3149 case SCF_ERROR_DELETED:
3150 err = mc_error_create(err, ret,
3151 "Property group could not be found");
3152 goto out;
3154 case SCF_ERROR_HANDLE_MISMATCH:
3155 case SCF_ERROR_INVALID_ARGUMENT:
3156 case SCF_ERROR_NOT_SET:
3157 default:
3158 bad_fail("scf_pg_get_property", ret);
3163 if (scf_default_secflags(h, &cip->def_secflags) != 0) {
3164 err = mc_error_create(err, EINVAL, "couldn't fetch "
3165 "default security-flags");
3166 goto out;
3169 if (strcmp(cip->vbuf, ":default") != 0) {
3170 if (secflags_parse(NULL, cip->vbuf,
3171 &cip->secflag_delta) != 0) {
3172 err = mc_error_create(err, EINVAL, "couldn't parse "
3173 "security flags: %s", cip->vbuf);
3174 goto out;
3178 /* get (optional) corefile pattern */
3179 if ((methpg != NULL && scf_pg_get_property(methpg,
3180 SCF_PROPERTY_COREFILE_PATTERN, prop) == SCF_SUCCESS) ||
3181 (instpg != NULL && scf_pg_get_property(instpg,
3182 SCF_PROPERTY_COREFILE_PATTERN, prop) == SCF_SUCCESS)) {
3183 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3184 ret = scf_error();
3185 switch (ret) {
3186 case SCF_ERROR_CONNECTION_BROKEN:
3187 err = mc_error_create(err, ret, RCBROKEN);
3188 break;
3190 case SCF_ERROR_CONSTRAINT_VIOLATED:
3191 err = mc_error_create(err, ret,
3192 "\"%s\" property has multiple values.",
3193 SCF_PROPERTY_COREFILE_PATTERN);
3194 break;
3196 case SCF_ERROR_NOT_FOUND:
3197 err = mc_error_create(err, ret,
3198 "\"%s\" property has no values.",
3199 SCF_PROPERTY_COREFILE_PATTERN);
3200 break;
3202 default:
3203 bad_fail("scf_property_get_value", ret);
3206 } else {
3208 ret = scf_value_get_astring(val, cip->vbuf,
3209 cip->vbuf_sz);
3210 assert(ret != -1);
3212 cip->corefile_pattern = strdup(cip->vbuf);
3213 if (cip->corefile_pattern == NULL) {
3214 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3215 goto out;
3219 mc_used++;
3220 } else {
3221 ret = scf_error();
3222 switch (ret) {
3223 case SCF_ERROR_NOT_FOUND:
3224 /* okay if missing. */
3225 break;
3227 case SCF_ERROR_CONNECTION_BROKEN:
3228 err = mc_error_create(err, ret, RCBROKEN);
3229 goto out;
3231 case SCF_ERROR_DELETED:
3232 err = mc_error_create(err, ret,
3233 "Property group could not be found");
3234 goto out;
3236 case SCF_ERROR_HANDLE_MISMATCH:
3237 case SCF_ERROR_INVALID_ARGUMENT:
3238 case SCF_ERROR_NOT_SET:
3239 default:
3240 bad_fail("scf_pg_get_property", ret);
3244 if (restarter_rm_libs_loadable()) {
3245 /* get project */
3246 if ((methpg != NULL && scf_pg_get_property(methpg,
3247 SCF_PROPERTY_PROJECT, prop) == SCF_SUCCESS) ||
3248 (instpg != NULL && scf_pg_get_property(instpg,
3249 SCF_PROPERTY_PROJECT, prop) == SCF_SUCCESS)) {
3250 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3251 ret = scf_error();
3252 switch (ret) {
3253 case SCF_ERROR_CONNECTION_BROKEN:
3254 err = mc_error_create(err, ret,
3255 RCBROKEN);
3256 break;
3258 case SCF_ERROR_CONSTRAINT_VIOLATED:
3259 err = mc_error_create(err, ret,
3260 "\"%s\" property has multiple "
3261 "values.", SCF_PROPERTY_PROJECT);
3262 break;
3264 case SCF_ERROR_NOT_FOUND:
3265 err = mc_error_create(err, ret,
3266 "\"%s\" property has no values.",
3267 SCF_PROPERTY_PROJECT);
3268 break;
3270 default:
3271 bad_fail("scf_property_get_value", ret);
3274 (void) strcpy(cip->vbuf, ":default");
3275 } else {
3276 ret = scf_value_get_astring(val, cip->vbuf,
3277 cip->vbuf_sz);
3278 assert(ret != -1);
3281 mc_used++;
3282 } else {
3283 (void) strcpy(cip->vbuf, ":default");
3286 switch (ret = get_projid(cip->vbuf, cip)) {
3287 case 0:
3288 break;
3290 case ENOMEM:
3291 err = mc_error_create(err, ret, "Out of memory.");
3292 goto out;
3294 case ENOENT:
3295 err = mc_error_create(err, ret,
3296 "Missing passwd or project entry for \"%s\".",
3297 cip->vbuf);
3298 goto out;
3300 case EIO:
3301 err = mc_error_create(err, ret, "I/O error.");
3302 goto out;
3304 case EMFILE:
3305 case ENFILE:
3306 err = mc_error_create(err, ret,
3307 "Out of file descriptors.");
3308 goto out;
3310 case -1:
3311 err = mc_error_create(err, ret,
3312 "Name service switch is misconfigured.");
3313 goto out;
3315 case ERANGE:
3316 case E2BIG:
3317 err = mc_error_create(err, ret,
3318 "Project ID \"%s\" too big.", cip->vbuf);
3319 goto out;
3321 case EINVAL:
3322 err = mc_error_create(err, ret,
3323 "Project ID \"%s\" is invalid.", cip->vbuf);
3324 goto out;
3326 default:
3327 bad_fail("get_projid", ret);
3330 /* get resource pool */
3331 if ((methpg != NULL && scf_pg_get_property(methpg,
3332 SCF_PROPERTY_RESOURCE_POOL, prop) == SCF_SUCCESS) ||
3333 (instpg != NULL && scf_pg_get_property(instpg,
3334 SCF_PROPERTY_RESOURCE_POOL, prop) == SCF_SUCCESS)) {
3335 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3336 ret = scf_error();
3337 switch (ret) {
3338 case SCF_ERROR_CONNECTION_BROKEN:
3339 err = mc_error_create(err, ret,
3340 RCBROKEN);
3341 break;
3343 case SCF_ERROR_CONSTRAINT_VIOLATED:
3344 err = mc_error_create(err, ret,
3345 "\"%s\" property has multiple "
3346 "values.",
3347 SCF_PROPERTY_RESOURCE_POOL);
3348 break;
3350 case SCF_ERROR_NOT_FOUND:
3351 err = mc_error_create(err, ret,
3352 "\"%s\" property has no "
3353 "values.",
3354 SCF_PROPERTY_RESOURCE_POOL);
3355 break;
3357 default:
3358 bad_fail("scf_property_get_value", ret);
3361 (void) strcpy(cip->vbuf, ":default");
3362 } else {
3363 ret = scf_value_get_astring(val, cip->vbuf,
3364 cip->vbuf_sz);
3365 assert(ret != -1);
3368 mc_used++;
3369 } else {
3370 ret = scf_error();
3371 switch (ret) {
3372 case SCF_ERROR_NOT_FOUND:
3373 /* okay if missing. */
3374 (void) strcpy(cip->vbuf, ":default");
3375 break;
3377 case SCF_ERROR_CONNECTION_BROKEN:
3378 err = mc_error_create(err, ret, RCBROKEN);
3379 goto out;
3381 case SCF_ERROR_DELETED:
3382 err = mc_error_create(err, ret,
3383 "property group could not be found.");
3384 goto out;
3386 case SCF_ERROR_HANDLE_MISMATCH:
3387 case SCF_ERROR_INVALID_ARGUMENT:
3388 case SCF_ERROR_NOT_SET:
3389 default:
3390 bad_fail("scf_pg_get_property", ret);
3394 if (strcmp(cip->vbuf, ":default") != 0) {
3395 cip->resource_pool = strdup(cip->vbuf);
3396 if (cip->resource_pool == NULL) {
3397 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3398 goto out;
3404 * A method_context was not used for any configurable
3405 * elements or attributes, so reset and use the simple
3406 * defaults that provide historic init behavior.
3408 if (mc_used == 0) {
3409 free(cip->pwbuf);
3410 free(cip->vbuf);
3411 free(cip->working_dir);
3413 (void) memset(cip, 0, sizeof (*cip));
3414 cip->uid = 0;
3415 cip->gid = 0;
3416 cip->euid = (uid_t)-1;
3417 cip->egid = (gid_t)-1;
3419 if (scf_default_secflags(h, &cip->def_secflags) != 0) {
3420 err = mc_error_create(err, EINVAL, "couldn't fetch "
3421 "default security-flags");
3422 goto out;
3426 *mcpp = cip;
3428 out:
3429 (void) scf_value_destroy(val);
3430 scf_property_destroy(prop);
3431 scf_pg_destroy(instpg);
3432 scf_pg_destroy(methpg);
3434 if (cip->pwbuf != NULL) {
3435 free(cip->pwbuf);
3436 cip->pwbuf = NULL;
3439 free(cip->vbuf);
3441 if (err->type != 0) {
3442 restarter_free_method_context(cip);
3443 } else {
3444 restarter_mc_error_destroy(err);
3445 err = NULL;
3448 return (err);
3452 * Modify the current process per the given method_context. On success, returns
3453 * 0. Note that the environment is not modified by this function to include the
3454 * environment variables in cip->env.
3456 * On failure, sets *fp to NULL or the name of the function which failed,
3457 * and returns one of the following error codes. The words in parentheses are
3458 * the values to which *fp may be set for the error case.
3459 * ENOMEM - malloc() failed
3460 * EIO - an I/O error occurred (getpwuid_r, chdir)
3461 * EMFILE - process is out of file descriptors (getpwuid_r)
3462 * ENFILE - system is out of file handles (getpwuid_r)
3463 * EINVAL - gid or egid is out of range (setregid)
3464 * ngroups is too big (setgroups)
3465 * project's project id is bad (setproject)
3466 * uid or euid is out of range (setreuid)
3467 * poolname is invalid (pool_set_binding)
3468 * EPERM - insufficient privilege (setregid, initgroups, setgroups, setppriv,
3469 * setproject, setreuid, settaskid)
3470 * ENOENT - uid has a passwd entry but no shadow entry
3471 * working_dir does not exist (chdir)
3472 * uid has no passwd entry
3473 * the pool could not be found (pool_set_binding)
3474 * EFAULT - lpriv_set or priv_set has a bad address (setppriv)
3475 * working_dir has a bad address (chdir)
3476 * EACCES - could not access working_dir (chdir)
3477 * in a TASK_FINAL task (setproject, settaskid)
3478 * no resource pool accepting default binding exists (setproject)
3479 * ELOOP - too many symbolic links in working_dir (chdir)
3480 * ENAMETOOLONG - working_dir is too long (chdir)
3481 * ENOLINK - working_dir is on an inaccessible remote machine (chdir)
3482 * ENOTDIR - working_dir is not a directory (chdir)
3483 * ESRCH - uid is not a user of project (setproject)
3484 * project is invalid (setproject)
3485 * the resource pool specified for project is unknown (setproject)
3486 * EBADF - the configuration for the pool is invalid (pool_set_binding)
3487 * -1 - core_set_process_path() failed (core_set_process_path)
3488 * a resource control assignment failed (setproject)
3489 * a system error occurred during pool_set_binding (pool_set_binding)
3492 restarter_set_method_context(struct method_context *cip, const char **fp)
3494 pid_t mypid = -1;
3495 int r, ret;
3497 cip->pwbuf = NULL;
3498 *fp = NULL;
3500 if (cip->gid != (gid_t)-1) {
3501 if (setregid(cip->gid,
3502 cip->egid != (gid_t)-1 ? cip->egid : cip->gid) != 0) {
3503 *fp = "setregid";
3505 ret = errno;
3506 assert(ret == EINVAL || ret == EPERM);
3507 goto out;
3509 } else {
3510 if (cip->pwbuf == NULL) {
3511 switch (ret = lookup_pwd(cip)) {
3512 case 0:
3513 break;
3515 case ENOMEM:
3516 case ENOENT:
3517 *fp = NULL;
3518 goto out;
3520 case EIO:
3521 case EMFILE:
3522 case ENFILE:
3523 *fp = "getpwuid_r";
3524 goto out;
3526 default:
3527 bad_fail("lookup_pwd", ret);
3531 if (setregid(cip->pwd.pw_gid,
3532 cip->egid != (gid_t)-1 ?
3533 cip->egid : cip->pwd.pw_gid) != 0) {
3534 *fp = "setregid";
3536 ret = errno;
3537 assert(ret == EINVAL || ret == EPERM);
3538 goto out;
3542 if (cip->ngroups == -1) {
3543 if (cip->pwbuf == NULL) {
3544 switch (ret = lookup_pwd(cip)) {
3545 case 0:
3546 break;
3548 case ENOMEM:
3549 case ENOENT:
3550 *fp = NULL;
3551 goto out;
3553 case EIO:
3554 case EMFILE:
3555 case ENFILE:
3556 *fp = "getpwuid_r";
3557 goto out;
3559 default:
3560 bad_fail("lookup_pwd", ret);
3564 /* Ok if cip->gid == -1 */
3565 if (initgroups(cip->pwd.pw_name, cip->gid) != 0) {
3566 *fp = "initgroups";
3567 ret = errno;
3568 assert(ret == EPERM);
3569 goto out;
3571 } else if (cip->ngroups > 0 &&
3572 setgroups(cip->ngroups, cip->groups) != 0) {
3573 *fp = "setgroups";
3575 ret = errno;
3576 assert(ret == EINVAL || ret == EPERM);
3577 goto out;
3580 if (cip->corefile_pattern != NULL) {
3581 mypid = getpid();
3583 if (core_set_process_path(cip->corefile_pattern,
3584 strlen(cip->corefile_pattern) + 1, mypid) != 0) {
3585 *fp = "core_set_process_path";
3586 ret = -1;
3587 goto out;
3592 if (psecflags(P_PID, P_MYID, PSF_INHERIT,
3593 &cip->def_secflags.ss_default) != 0) {
3594 *fp = "psecflags (default inherit)";
3595 ret = errno;
3596 goto out;
3599 if (psecflags(P_PID, P_MYID, PSF_LOWER,
3600 &cip->def_secflags.ss_lower) != 0) {
3601 *fp = "psecflags (default lower)";
3602 ret = errno;
3603 goto out;
3606 if (psecflags(P_PID, P_MYID, PSF_UPPER,
3607 &cip->def_secflags.ss_upper) != 0) {
3608 *fp = "psecflags (default upper)";
3609 ret = errno;
3610 goto out;
3613 if (psecflags(P_PID, P_MYID, PSF_INHERIT,
3614 &cip->secflag_delta) != 0) {
3615 *fp = "psecflags (from manifest)";
3616 ret = errno;
3617 goto out;
3620 if (restarter_rm_libs_loadable()) {
3621 if (cip->project == NULL) {
3622 if (settaskid(getprojid(), TASK_NORMAL) == -1) {
3623 switch (errno) {
3624 case EACCES:
3625 case EPERM:
3626 *fp = "settaskid";
3627 ret = errno;
3628 goto out;
3630 case EINVAL:
3631 default:
3632 bad_fail("settaskid", errno);
3635 } else {
3636 switch (ret = lookup_pwd(cip)) {
3637 case 0:
3638 break;
3640 case ENOMEM:
3641 case ENOENT:
3642 *fp = NULL;
3643 goto out;
3645 case EIO:
3646 case EMFILE:
3647 case ENFILE:
3648 *fp = "getpwuid_r";
3649 goto out;
3651 default:
3652 bad_fail("lookup_pwd", ret);
3655 *fp = "setproject";
3657 switch (setproject(cip->project, cip->pwd.pw_name,
3658 TASK_NORMAL)) {
3659 case 0:
3660 break;
3662 case SETPROJ_ERR_TASK:
3663 case SETPROJ_ERR_POOL:
3664 ret = errno;
3665 goto out;
3667 default:
3668 ret = -1;
3669 goto out;
3673 if (cip->resource_pool != NULL) {
3674 if (mypid == -1)
3675 mypid = getpid();
3677 *fp = "pool_set_binding";
3679 if (pool_set_binding(cip->resource_pool, P_PID,
3680 mypid) != PO_SUCCESS) {
3681 switch (pool_error()) {
3682 case POE_INVALID_SEARCH:
3683 ret = ENOENT;
3684 break;
3686 case POE_BADPARAM:
3687 ret = EINVAL;
3688 break;
3690 case POE_INVALID_CONF:
3691 ret = EBADF;
3692 break;
3694 case POE_SYSTEM:
3695 ret = -1;
3696 break;
3698 default:
3699 bad_fail("pool_set_binding",
3700 pool_error());
3703 goto out;
3709 * Now, we have to assume our ID. If the UID is 0, we want it to be
3710 * privilege-aware, otherwise the limit set gets used instead of E/P.
3711 * We can do this by setting P as well, which keeps
3712 * PA status (see priv_can_clear_PA()).
3715 *fp = "setppriv";
3717 if (cip->lpriv_set != NULL) {
3718 if (setppriv(PRIV_SET, PRIV_LIMIT, cip->lpriv_set) != 0) {
3719 ret = errno;
3720 assert(ret == EFAULT || ret == EPERM);
3721 goto out;
3724 if (cip->priv_set != NULL) {
3725 if (setppriv(PRIV_SET, PRIV_INHERITABLE, cip->priv_set) != 0) {
3726 ret = errno;
3727 assert(ret == EFAULT || ret == EPERM);
3728 goto out;
3733 * If the limit privset is already set, then must be privilege
3734 * aware. Otherwise, don't assume anything, and force privilege
3735 * aware status.
3738 if (cip->lpriv_set == NULL && cip->priv_set != NULL) {
3739 ret = setpflags(PRIV_AWARE, 1);
3740 assert(ret == 0);
3743 *fp = "setreuid";
3744 if (setreuid(cip->uid,
3745 cip->euid != (uid_t)-1 ? cip->euid : cip->uid) != 0) {
3746 ret = errno;
3747 assert(ret == EINVAL || ret == EPERM);
3748 goto out;
3751 *fp = "setppriv";
3752 if (cip->priv_set != NULL) {
3753 if (setppriv(PRIV_SET, PRIV_PERMITTED, cip->priv_set) != 0) {
3754 ret = errno;
3755 assert(ret == EFAULT || ret == EPERM);
3756 goto out;
3761 * The last thing to do is chdir to the specified working directory.
3762 * This should come after the uid switching as only the user might
3763 * have access to the specified directory.
3765 if (cip->working_dir != NULL) {
3766 do {
3767 r = chdir(cip->working_dir);
3768 } while (r != 0 && errno == EINTR);
3769 if (r != 0) {
3770 *fp = "chdir";
3771 ret = errno;
3772 goto out;
3776 ret = 0;
3777 out:
3778 free(cip->pwbuf);
3779 cip->pwbuf = NULL;
3780 return (ret);
3783 void
3784 restarter_free_method_context(struct method_context *mcp)
3786 size_t i;
3788 if (mcp->lpriv_set != NULL)
3789 priv_freeset(mcp->lpriv_set);
3790 if (mcp->priv_set != NULL)
3791 priv_freeset(mcp->priv_set);
3793 if (mcp->env != NULL) {
3794 for (i = 0; i < mcp->env_sz; i++)
3795 free(mcp->env[i]);
3796 free(mcp->env);
3799 free(mcp->working_dir);
3800 free(mcp->corefile_pattern);
3801 free(mcp->project);
3802 free(mcp->resource_pool);
3803 free(mcp);
3807 * Method keyword functions
3811 restarter_is_null_method(const char *meth)
3813 return (strcmp(meth, MKW_TRUE) == 0);
3816 static int
3817 is_kill_method(const char *method, const char *kill_str,
3818 size_t kill_str_len)
3820 const char *cp;
3821 int sig;
3823 if (strncmp(method, kill_str, kill_str_len) != 0 ||
3824 (method[kill_str_len] != '\0' &&
3825 !isspace(method[kill_str_len])))
3826 return (-1);
3828 cp = method + kill_str_len;
3829 while (*cp != '\0' && isspace(*cp))
3830 ++cp;
3832 if (*cp == '\0')
3833 return (SIGTERM);
3835 if (*cp != '-')
3836 return (-1);
3838 return (str2sig(cp + 1, &sig) == 0 ? sig : -1);
3842 restarter_is_kill_proc_method(const char *method)
3844 return (is_kill_method(method, MKW_KILL_PROC,
3845 sizeof (MKW_KILL_PROC) - 1));
3849 restarter_is_kill_method(const char *method)
3851 return (is_kill_method(method, MKW_KILL, sizeof (MKW_KILL) - 1));
3855 * Stubs for now.
3858 /* ARGSUSED */
3860 restarter_event_get_enabled(restarter_event_t *e)
3862 return (-1);
3865 /* ARGSUSED */
3866 uint64_t
3867 restarter_event_get_seq(restarter_event_t *e)
3869 return (-1);
3872 /* ARGSUSED */
3873 void
3874 restarter_event_get_time(restarter_event_t *e, hrtime_t *time)
3879 * Check for and validate fmri specified in restarter_actions/auxiliary_fmri
3880 * 0 - Success
3881 * 1 - Failure
3884 restarter_inst_validate_ractions_aux_fmri(scf_instance_t *inst)
3886 scf_handle_t *h;
3887 scf_propertygroup_t *pg;
3888 scf_property_t *prop;
3889 scf_value_t *val;
3890 char *aux_fmri;
3891 size_t size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
3892 int ret = 1;
3894 if ((aux_fmri = malloc(size)) == NULL)
3895 return (1);
3897 h = scf_instance_handle(inst);
3899 pg = scf_pg_create(h);
3900 prop = scf_property_create(h);
3901 val = scf_value_create(h);
3902 if (pg == NULL || prop == NULL || val == NULL)
3903 goto out;
3905 if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
3906 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
3907 pg) != SCF_SUCCESS)
3908 goto out;
3910 if (get_astring_val(pg, SCF_PROPERTY_AUX_FMRI, aux_fmri, size,
3911 prop, val) != SCF_SUCCESS)
3912 goto out;
3914 if (scf_parse_fmri(aux_fmri, NULL, NULL, NULL, NULL, NULL,
3915 NULL) != SCF_SUCCESS)
3916 goto out;
3918 ret = 0;
3920 out:
3921 free(aux_fmri);
3922 scf_value_destroy(val);
3923 scf_property_destroy(prop);
3924 scf_pg_destroy(pg);
3925 return (ret);
3929 * Get instance's boolean value in restarter_actions/auxiliary_tty
3930 * Return -1 on failure
3933 restarter_inst_ractions_from_tty(scf_instance_t *inst)
3935 scf_handle_t *h;
3936 scf_propertygroup_t *pg;
3937 scf_property_t *prop;
3938 scf_value_t *val;
3939 uint8_t has_tty;
3940 int ret = -1;
3942 h = scf_instance_handle(inst);
3943 pg = scf_pg_create(h);
3944 prop = scf_property_create(h);
3945 val = scf_value_create(h);
3946 if (pg == NULL || prop == NULL || val == NULL)
3947 goto out;
3949 if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
3950 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
3951 pg) != SCF_SUCCESS)
3952 goto out;
3954 if (get_boolean_val(pg, SCF_PROPERTY_AUX_TTY, &has_tty, prop,
3955 val) != SCF_SUCCESS)
3956 goto out;
3958 ret = has_tty;
3960 out:
3961 scf_value_destroy(val);
3962 scf_property_destroy(prop);
3963 scf_pg_destroy(pg);
3964 return (ret);
3968 * If the instance's dump-on-restart property exists, remove it and return true,
3969 * otherwise return false.
3972 restarter_inst_dump(scf_instance_t *inst)
3974 scf_handle_t *h;
3975 scf_propertygroup_t *pg;
3976 scf_property_t *prop;
3977 scf_value_t *val;
3978 int ret = 0;
3980 h = scf_instance_handle(inst);
3981 pg = scf_pg_create(h);
3982 prop = scf_property_create(h);
3983 val = scf_value_create(h);
3984 if (pg == NULL || prop == NULL || val == NULL)
3985 goto out;
3987 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER_ACTIONS, pg) !=
3988 SCF_SUCCESS) {
3989 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
3990 uu_die(rcbroken);
3991 goto out;
3994 if (scf_pg_get_property(pg, SCF_PROPERTY_DODUMP, prop) != SCF_SUCCESS) {
3995 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
3996 uu_die(rcbroken);
3997 goto out;
4000 ret = 1;
4002 if (scf_instance_delete_prop(inst, SCF_PG_RESTARTER_ACTIONS,
4003 SCF_PROPERTY_DODUMP) != SCF_SUCCESS) {
4004 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
4005 uu_die(rcbroken);
4006 goto out;
4009 out:
4010 scf_value_destroy(val);
4011 scf_property_destroy(prop);
4012 scf_pg_destroy(pg);
4013 return (ret);
4016 static int
4017 restarter_inst_set_astring_prop(scf_instance_t *inst, const char *pgname,
4018 const char *pgtype, uint32_t pgflags, const char *pname, const char *str)
4020 scf_handle_t *h;
4021 scf_propertygroup_t *pg;
4022 scf_transaction_t *t;
4023 scf_transaction_entry_t *e;
4024 scf_value_t *v;
4025 int ret = 1, r;
4027 h = scf_instance_handle(inst);
4029 pg = scf_pg_create(h);
4030 t = scf_transaction_create(h);
4031 e = scf_entry_create(h);
4032 v = scf_value_create(h);
4033 if (pg == NULL || t == NULL || e == NULL || v == NULL)
4034 goto out;
4036 if (instance_get_or_add_pg(inst, pgname, pgtype, pgflags, pg))
4037 goto out;
4039 if (scf_value_set_astring(v, str) != SCF_SUCCESS)
4040 goto out;
4042 for (;;) {
4043 if (scf_transaction_start(t, pg) != 0)
4044 goto out;
4046 if (tx_set_value(t, e, pname, SCF_TYPE_ASTRING, v) != 0)
4047 goto out;
4049 if ((r = scf_transaction_commit(t)) == 1)
4050 break;
4052 if (r == -1)
4053 goto out;
4055 scf_transaction_reset(t);
4056 if (scf_pg_update(pg) == -1)
4057 goto out;
4059 ret = 0;
4061 out:
4062 scf_transaction_destroy(t);
4063 scf_entry_destroy(e);
4064 scf_value_destroy(v);
4065 scf_pg_destroy(pg);
4067 return (ret);
4071 restarter_inst_set_aux_fmri(scf_instance_t *inst)
4073 scf_handle_t *h;
4074 scf_propertygroup_t *pg;
4075 scf_property_t *prop;
4076 scf_value_t *val;
4077 char *aux_fmri;
4078 size_t size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
4079 int ret = 1;
4081 if ((aux_fmri = malloc(size)) == NULL)
4082 return (1);
4084 h = scf_instance_handle(inst);
4086 pg = scf_pg_create(h);
4087 prop = scf_property_create(h);
4088 val = scf_value_create(h);
4089 if (pg == NULL || prop == NULL || val == NULL)
4090 goto out;
4093 * Get auxiliary_fmri value from restarter_actions pg
4095 if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
4096 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
4097 pg) != SCF_SUCCESS)
4098 goto out;
4100 if (get_astring_val(pg, SCF_PROPERTY_AUX_FMRI, aux_fmri, size,
4101 prop, val) != SCF_SUCCESS)
4102 goto out;
4105 * Populate restarter/auxiliary_fmri with the obtained fmri.
4107 ret = restarter_inst_set_astring_prop(inst, SCF_PG_RESTARTER,
4108 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS,
4109 SCF_PROPERTY_AUX_FMRI, aux_fmri);
4111 out:
4112 free(aux_fmri);
4113 scf_value_destroy(val);
4114 scf_property_destroy(prop);
4115 scf_pg_destroy(pg);
4116 return (ret);
4120 restarter_inst_reset_aux_fmri(scf_instance_t *inst)
4122 return (scf_instance_delete_prop(inst,
4123 SCF_PG_RESTARTER, SCF_PROPERTY_AUX_FMRI));
4127 restarter_inst_reset_ractions_aux_fmri(scf_instance_t *inst)
4129 return (scf_instance_delete_prop(inst,
4130 SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_FMRI));