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]
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
28 #include <librestart.h>
29 #include <librestart_priv.h>
31 #include <libscf_priv.h>
37 #include <exec_attr.h>
39 #include <libsysevent.h>
54 #include <sys/corectl.h>
55 #include <sys/machelf.h>
56 #include <sys/secflags.h>
58 #include <sys/types.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.
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)); \
89 #define bad_fail(func, err) abort()
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."
118 * "The service transitioned state (%s) and ..."
120 * With the exception of restart_str_none they must also fit the following
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
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
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
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.
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
,
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
,
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
,
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
,
222 "all processes in the service have exited"
226 * A transition 'online -> offline' due to a hardware error.
229 restarter_str_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
,
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
,
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
,
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
,
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
,
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
,
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
,
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
352 restarter_str_per_configuration
,
354 "the SMF repository configuration specifies this state"
358 * Refresh requested - no state change.
361 restarter_str_refresh
,
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
,
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
,
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
,
407 "the instance was inserted in the graph due to startd restart"
412 restarter_str_version(void)
414 return (RESTARTER_STRING_VERSION
);
418 restarter_get_str_short(restarter_str_t key
)
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
);
429 restarter_get_str_long(restarter_str_t key
)
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
));
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. */
459 mc_error_create(mc_error_t
*e
, int type
, const char *format
, ...)
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
);
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;
492 (void) vsnprintf(le
->msg
, le
->size
, format
, args
);
499 restarter_mc_error_destroy(mc_error_t
*mc_err
)
505 * If the error messages was allocated then free.
507 if (mc_err
->destroy
) {
513 free_restarter_event_handle(struct restarter_event_handle
*h
)
519 * Just free the memory -- don't unbind the sysevent handle,
520 * as otherwise events may be lost if this is just a restarter
524 if (h
->reh_restarter_name
!= NULL
)
525 free(h
->reh_restarter_name
);
526 if (h
->reh_delegate_channel_name
!= NULL
)
527 free(h
->reh_delegate_channel_name
);
528 if (h
->reh_delegate_subscriber_id
!= NULL
)
529 free(h
->reh_delegate_subscriber_id
);
530 if (h
->reh_master_channel_name
!= NULL
)
531 free(h
->reh_master_channel_name
);
532 if (h
->reh_master_subscriber_id
!= NULL
)
533 free(h
->reh_master_subscriber_id
);
539 _restarter_get_channel_name(const char *fmri
, int type
)
542 char *chan_name
= malloc(MAX_CHNAME_LEN
);
546 if (chan_name
== NULL
)
549 if (type
== RESTARTER_CHANNEL_DELEGATE
)
550 (void) strcpy(prefix_name
, "d_");
551 else if (type
== RESTARTER_CHANNEL_MASTER
)
552 (void) strcpy(prefix_name
, "m_");
559 * Create a unique name
561 * Use the entire name, using a replacement of the /
562 * characters to get a better name.
564 * Remove the svc:/ from the beginning as this really
565 * isn't going to provide any uniqueness...
567 * An fmri name greater than MAX_CHNAME_LEN is going
568 * to be rejected as too long for the chan_name below
569 * in the snprintf call.
571 if ((name
= strdup(strchr(fmri
, '/') + 1)) == NULL
) {
577 if (name
[i
] == '/') {
585 * Should check for [a-z],[A-Z],[0-9],.,_,-,:
588 if (snprintf(chan_name
, MAX_CHNAME_LEN
, "com.sun:scf:%s%s",
589 prefix_name
, name
) > MAX_CHNAME_LEN
) {
599 cb(sysevent_t
*syse
, void *cookie
)
601 restarter_event_handle_t
*h
= (restarter_event_handle_t
*)cookie
;
602 restarter_event_t
*e
;
603 nvlist_t
*attr_list
= NULL
;
606 e
= uu_zalloc(sizeof (restarter_event_t
));
609 e
->re_event_handle
= h
;
610 e
->re_sysevent
= syse
;
612 if (sysevent_get_attr_list(syse
, &attr_list
) != 0)
615 if ((nvlist_lookup_uint32(attr_list
, RESTARTER_NAME_TYPE
,
616 &(e
->re_type
)) != 0) ||
617 (nvlist_lookup_string(attr_list
,
618 RESTARTER_NAME_INSTANCE
, &(e
->re_instance_name
)) != 0)) {
619 uu_warn("%s: Can't decode nvlist for event %p\n",
620 h
->reh_restarter_name
, (void *)syse
);
624 ret
= h
->reh_handler(e
);
628 nvlist_free(attr_list
);
633 * restarter_bind_handle(uint32_t, char *, int (*)(restarter_event_t *), int,
634 * restarter_event_handle_t **)
636 * Bind to a delegated restarter event channel.
637 * Each delegated restarter gets its own channel for resource management.
639 * Returns 0 on success or
640 * ENOTSUP version mismatch
641 * EINVAL restarter_name or event_handle is NULL
642 * ENOMEM out of memory, too many channels, or too many subscriptions
643 * EBUSY sysevent_evc_bind() could not establish binding
644 * EFAULT internal sysevent_evc_bind()/sysevent_evc_subscribe() error
645 * EMFILE out of file descriptors
646 * EPERM insufficient privilege for sysevent_evc_bind()
647 * EEXIST already subscribed
650 restarter_bind_handle(uint32_t version
, const char *restarter_name
,
651 int (*event_handler
)(restarter_event_t
*), int flags
,
652 restarter_event_handle_t
**rehp
)
654 restarter_event_handle_t
*h
;
658 if (version
!= RESTARTER_EVENT_VERSION
)
661 if (restarter_name
== NULL
|| event_handler
== NULL
)
664 if (flags
& RESTARTER_FLAG_DEBUG
)
667 if ((h
= uu_zalloc(sizeof (restarter_event_handle_t
))) == NULL
)
670 h
->reh_delegate_subscriber_id
= malloc(MAX_SUBID_LEN
);
671 h
->reh_master_subscriber_id
= malloc(MAX_SUBID_LEN
);
672 h
->reh_restarter_name
= strdup(restarter_name
);
673 if (h
->reh_delegate_subscriber_id
== NULL
||
674 h
->reh_master_subscriber_id
== NULL
||
675 h
->reh_restarter_name
== NULL
) {
676 free_restarter_event_handle(h
);
680 sz
= strlcpy(h
->reh_delegate_subscriber_id
, "del", MAX_SUBID_LEN
);
681 assert(sz
< MAX_SUBID_LEN
);
682 sz
= strlcpy(h
->reh_master_subscriber_id
, "master", MAX_SUBID_LEN
);
683 assert(sz
< MAX_SUBID_LEN
);
685 h
->reh_delegate_channel_name
=
686 _restarter_get_channel_name(restarter_name
,
687 RESTARTER_CHANNEL_DELEGATE
);
688 h
->reh_master_channel_name
=
689 _restarter_get_channel_name(restarter_name
,
690 RESTARTER_CHANNEL_MASTER
);
692 if (h
->reh_delegate_channel_name
== NULL
||
693 h
->reh_master_channel_name
== NULL
) {
694 free_restarter_event_handle(h
);
698 if (sysevent_evc_bind(h
->reh_delegate_channel_name
,
699 &h
->reh_delegate_channel
, EVCH_CREAT
|EVCH_HOLD_PEND
) != 0) {
701 assert(err
!= EINVAL
);
702 assert(err
!= ENOENT
);
703 free_restarter_event_handle(h
);
707 if (sysevent_evc_bind(h
->reh_master_channel_name
,
708 &h
->reh_master_channel
, EVCH_CREAT
|EVCH_HOLD_PEND
) != 0) {
710 assert(err
!= EINVAL
);
711 assert(err
!= ENOENT
);
712 free_restarter_event_handle(h
);
716 h
->reh_handler
= event_handler
;
718 assert(strlen(restarter_name
) <= MAX_CLASS_LEN
- 1);
719 assert(strlen(h
->reh_delegate_subscriber_id
) <= MAX_SUBID_LEN
- 1);
720 assert(strlen(h
->reh_master_subscriber_id
) <= MAX_SUBID_LEN
- 1);
722 if (sysevent_evc_subscribe(h
->reh_delegate_channel
,
723 h
->reh_delegate_subscriber_id
, EC_ALL
, cb
, h
, EVCH_SUB_KEEP
) != 0) {
725 assert(err
!= EINVAL
);
726 free_restarter_event_handle(h
);
734 restarter_event_handle_t
*
735 restarter_event_get_handle(restarter_event_t
*e
)
737 assert(e
!= NULL
&& e
->re_event_handle
!= NULL
);
738 return (e
->re_event_handle
);
741 restarter_event_type_t
742 restarter_event_get_type(restarter_event_t
*e
)
749 restarter_event_get_instance(restarter_event_t
*e
, char *inst
, size_t sz
)
751 assert(e
!= NULL
&& inst
!= NULL
);
752 return ((ssize_t
)strlcpy(inst
, e
->re_instance_name
, sz
));
756 restarter_event_get_current_states(restarter_event_t
*e
,
757 restarter_instance_state_t
*state
, restarter_instance_state_t
*next_state
)
761 *state
= e
->re_state
;
762 *next_state
= e
->re_next_state
;
767 * restarter_event_publish_retry() is a wrapper around sysevent_evc_publish().
768 * In case, the event cannot be sent at the first attempt (sysevent_evc_publish
769 * returned EAGAIN - sysevent queue full), this function retries a few time
770 * and return ENOSPC if it reaches the retry limit.
772 * The arguments to this function map the arguments of sysevent_evc_publish().
774 * On success, return 0. On error, return
776 * EFAULT - internal sysevent_evc_publish() error
777 * ENOMEM - internal sysevent_evc_publish() error
778 * EBADF - scp is invalid (sysevent_evc_publish() returned EINVAL)
779 * ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN)
782 restarter_event_publish_retry(evchan_t
*scp
, const char *class,
783 const char *subclass
, const char *vendor
, const char *pub_name
,
784 nvlist_t
*attr_list
, uint32_t flags
)
787 useconds_t retry_int
= INITIAL_COMMIT_RETRY_INT
;
789 for (retries
= 0; retries
< MAX_COMMIT_RETRIES
; retries
++) {
790 ret
= sysevent_evc_publish(scp
, class, subclass
, vendor
,
791 pub_name
, attr_list
, flags
);
798 (void) usleep(retry_int
);
800 retry_int
= min(retry_int
* 2, MAX_COMMIT_RETRY_INT
);
813 /* internal error - abort */
814 bad_fail("sysevent_evc_publish", ret
);
818 if (retries
== MAX_COMMIT_RETRIES
)
825 * Commit the state, next state, and auxiliary state into the repository.
826 * Let the graph engine know about the state change and error. On success,
827 * return 0. On error, return
828 * EPROTO - librestart compiled against different libscf
829 * ENOMEM - out of memory
830 * - repository server out of resources
831 * ENOTACTIVE - repository server not running
832 * ECONNABORTED - repository connection established, but then broken
833 * - unknown libscf error
834 * ENOENT - inst does not exist in the repository
835 * EPERM - insufficient permissions
836 * EACCESS - backend access denied
837 * EROFS - backend is readonly
838 * EFAULT - internal sysevent_evc_publish() error
839 * EBADF - h is invalid (sysevent_evc_publish() returned EINVAL)
840 * ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN)
843 restarter_set_states(restarter_event_handle_t
*h
, const char *inst
,
844 restarter_instance_state_t cur_state
,
845 restarter_instance_state_t new_cur_state
,
846 restarter_instance_state_t next_state
,
847 restarter_instance_state_t new_next_state
, restarter_error_t e
,
854 const char *p
= restarter_get_str_short(aux
);
856 assert(h
->reh_master_channel
!= NULL
);
857 assert(h
->reh_master_channel_name
!= NULL
);
858 assert(h
->reh_master_subscriber_id
!= NULL
);
860 if ((scf_h
= scf_handle_create(SCF_VERSION
)) == NULL
) {
861 switch (scf_error()) {
862 case SCF_ERROR_VERSION_MISMATCH
:
865 case SCF_ERROR_NO_MEMORY
:
869 bad_fail("scf_handle_create", scf_error());
873 if (scf_handle_bind(scf_h
) == -1) {
874 scf_handle_destroy(scf_h
);
875 switch (scf_error()) {
876 case SCF_ERROR_NO_SERVER
:
879 case SCF_ERROR_NO_RESOURCES
:
882 case SCF_ERROR_INVALID_ARGUMENT
:
883 case SCF_ERROR_IN_USE
:
885 bad_fail("scf_handle_bind", scf_error());
889 if (nvlist_alloc(&attr
, NV_UNIQUE_NAME
, 0) != 0 ||
890 nvlist_add_int32(attr
, RESTARTER_NAME_STATE
, new_cur_state
) != 0 ||
891 nvlist_add_int32(attr
, RESTARTER_NAME_NEXT_STATE
, new_next_state
)
893 nvlist_add_int32(attr
, RESTARTER_NAME_ERROR
, e
) != 0 ||
894 nvlist_add_string(attr
, RESTARTER_NAME_INSTANCE
, inst
) != 0 ||
895 nvlist_add_int32(attr
, RESTARTER_NAME_REASON
, aux
) != 0) {
899 id
.i_state
= cur_state
;
900 id
.i_next_state
= next_state
;
902 ret
= _restarter_commit_states(scf_h
, &id
, new_cur_state
,
906 ret
= restarter_event_publish_retry(
907 h
->reh_master_channel
, "master", "state_change",
908 "com.sun", "librestart", attr
, EVCH_NOSLEEP
);
913 (void) scf_handle_unbind(scf_h
);
914 scf_handle_destroy(scf_h
);
919 restarter_instance_state_t
920 restarter_string_to_state(char *string
)
922 assert(string
!= NULL
);
924 if (strcmp(string
, SCF_STATE_STRING_NONE
) == 0)
925 return (RESTARTER_STATE_NONE
);
926 else if (strcmp(string
, SCF_STATE_STRING_UNINIT
) == 0)
927 return (RESTARTER_STATE_UNINIT
);
928 else if (strcmp(string
, SCF_STATE_STRING_MAINT
) == 0)
929 return (RESTARTER_STATE_MAINT
);
930 else if (strcmp(string
, SCF_STATE_STRING_OFFLINE
) == 0)
931 return (RESTARTER_STATE_OFFLINE
);
932 else if (strcmp(string
, SCF_STATE_STRING_DISABLED
) == 0)
933 return (RESTARTER_STATE_DISABLED
);
934 else if (strcmp(string
, SCF_STATE_STRING_ONLINE
) == 0)
935 return (RESTARTER_STATE_ONLINE
);
936 else if (strcmp(string
, SCF_STATE_STRING_DEGRADED
) == 0)
937 return (RESTARTER_STATE_DEGRADED
);
939 return (RESTARTER_STATE_NONE
);
944 restarter_state_to_string(restarter_instance_state_t state
, char *string
,
947 assert(string
!= NULL
);
949 if (state
== RESTARTER_STATE_NONE
)
950 return ((ssize_t
)strlcpy(string
, SCF_STATE_STRING_NONE
, len
));
951 else if (state
== RESTARTER_STATE_UNINIT
)
952 return ((ssize_t
)strlcpy(string
, SCF_STATE_STRING_UNINIT
, len
));
953 else if (state
== RESTARTER_STATE_MAINT
)
954 return ((ssize_t
)strlcpy(string
, SCF_STATE_STRING_MAINT
, len
));
955 else if (state
== RESTARTER_STATE_OFFLINE
)
956 return ((ssize_t
)strlcpy(string
, SCF_STATE_STRING_OFFLINE
,
958 else if (state
== RESTARTER_STATE_DISABLED
)
959 return ((ssize_t
)strlcpy(string
, SCF_STATE_STRING_DISABLED
,
961 else if (state
== RESTARTER_STATE_ONLINE
)
962 return ((ssize_t
)strlcpy(string
, SCF_STATE_STRING_ONLINE
, len
));
963 else if (state
== RESTARTER_STATE_DEGRADED
)
964 return ((ssize_t
)strlcpy(string
, SCF_STATE_STRING_DEGRADED
,
967 return ((ssize_t
)strlcpy(string
, "unknown", len
));
971 * Sets pg to the name property group of s_inst. If it doesn't exist, it is
975 * ECONNABORTED - repository disconnection or unknown libscf error
976 * EBADF - inst is not set
977 * ECANCELED - inst is deleted
978 * EPERM - permission is denied
979 * EACCES - backend denied access
980 * EROFS - backend readonly
983 instance_get_or_add_pg(scf_instance_t
*inst
, const char *name
,
984 const char *type
, uint32_t flags
, scf_propertygroup_t
*pg
)
987 if (scf_instance_get_pg(inst
, name
, pg
) == 0)
990 switch (scf_error()) {
991 case SCF_ERROR_CONNECTION_BROKEN
:
993 return (ECONNABORTED
);
995 case SCF_ERROR_NOT_SET
:
998 case SCF_ERROR_DELETED
:
1001 case SCF_ERROR_NOT_FOUND
:
1004 case SCF_ERROR_HANDLE_MISMATCH
:
1005 case SCF_ERROR_INVALID_ARGUMENT
:
1006 bad_fail("scf_instance_get_pg", scf_error());
1009 if (scf_instance_add_pg(inst
, name
, type
, flags
, pg
) == 0)
1012 switch (scf_error()) {
1013 case SCF_ERROR_CONNECTION_BROKEN
:
1015 return (ECONNABORTED
);
1017 case SCF_ERROR_DELETED
:
1020 case SCF_ERROR_EXISTS
:
1023 case SCF_ERROR_PERMISSION_DENIED
:
1026 case SCF_ERROR_BACKEND_ACCESS
:
1029 case SCF_ERROR_BACKEND_READONLY
:
1032 case SCF_ERROR_HANDLE_MISMATCH
:
1033 case SCF_ERROR_INVALID_ARGUMENT
:
1034 case SCF_ERROR_NOT_SET
: /* should be caught above */
1035 bad_fail("scf_instance_add_pg", scf_error());
1044 * ECANCELED - pg was deleted
1047 tx_set_value(scf_transaction_t
*tx
, scf_transaction_entry_t
*ent
,
1048 const char *pname
, scf_type_t ty
, scf_value_t
*val
)
1053 if (scf_transaction_property_change_type(tx
, ent
, pname
,
1057 switch (scf_error()) {
1058 case SCF_ERROR_CONNECTION_BROKEN
:
1060 return (ECONNABORTED
);
1062 case SCF_ERROR_DELETED
:
1065 case SCF_ERROR_NOT_FOUND
:
1068 case SCF_ERROR_HANDLE_MISMATCH
:
1069 case SCF_ERROR_INVALID_ARGUMENT
:
1070 case SCF_ERROR_IN_USE
:
1071 case SCF_ERROR_NOT_SET
:
1072 bad_fail("scf_transaction_property_change_type",
1076 if (scf_transaction_property_new(tx
, ent
, pname
, ty
) == 0)
1079 switch (scf_error()) {
1080 case SCF_ERROR_CONNECTION_BROKEN
:
1082 return (ECONNABORTED
);
1084 case SCF_ERROR_DELETED
:
1087 case SCF_ERROR_EXISTS
:
1090 case SCF_ERROR_HANDLE_MISMATCH
:
1091 case SCF_ERROR_INVALID_ARGUMENT
:
1092 case SCF_ERROR_IN_USE
:
1093 case SCF_ERROR_NOT_SET
:
1094 bad_fail("scf_transaction_property_new", scf_error());
1098 r
= scf_entry_add_value(ent
, val
);
1105 * Commit new_state, new_next_state, and aux to the repository for id. If
1106 * successful, also set id's state and next-state as given, and return 0.
1108 * ENOMEM - out of memory
1109 * ECONNABORTED - repository connection broken
1110 * - unknown libscf error
1111 * EINVAL - id->i_fmri is invalid or not an instance FMRI
1112 * ENOENT - id->i_fmri does not exist
1113 * EPERM - insufficient permissions
1114 * EACCES - backend access denied
1115 * EROFS - backend is readonly
1118 _restarter_commit_states(scf_handle_t
*h
, instance_data_t
*id
,
1119 restarter_instance_state_t new_state
,
1120 restarter_instance_state_t new_state_next
, const char *aux
)
1122 char str_state
[MAX_SCF_STATE_STRING_SZ
];
1123 char str_new_state
[MAX_SCF_STATE_STRING_SZ
];
1124 char str_state_next
[MAX_SCF_STATE_STRING_SZ
];
1125 char str_new_state_next
[MAX_SCF_STATE_STRING_SZ
];
1130 scf_transaction_t
*t
= NULL
;
1131 scf_transaction_entry_t
*t_state
= NULL
, *t_state_next
= NULL
;
1132 scf_transaction_entry_t
*t_stime
= NULL
, *t_aux
= NULL
;
1133 scf_value_t
*v_state
= NULL
, *v_state_next
= NULL
, *v_stime
= NULL
;
1134 scf_value_t
*v_aux
= NULL
;
1135 scf_instance_t
*s_inst
= NULL
;
1136 scf_propertygroup_t
*pg
= NULL
;
1138 assert(new_state
!= RESTARTER_STATE_NONE
);
1140 if ((s_inst
= scf_instance_create(h
)) == NULL
||
1141 (pg
= scf_pg_create(h
)) == NULL
||
1142 (t
= scf_transaction_create(h
)) == NULL
||
1143 (t_state
= scf_entry_create(h
)) == NULL
||
1144 (t_state_next
= scf_entry_create(h
)) == NULL
||
1145 (t_stime
= scf_entry_create(h
)) == NULL
||
1146 (t_aux
= scf_entry_create(h
)) == NULL
||
1147 (v_state
= scf_value_create(h
)) == NULL
||
1148 (v_state_next
= scf_value_create(h
)) == NULL
||
1149 (v_stime
= scf_value_create(h
)) == NULL
||
1150 (v_aux
= scf_value_create(h
)) == NULL
) {
1155 sz
= restarter_state_to_string(new_state
, str_new_state
,
1156 sizeof (str_new_state
));
1157 assert(sz
< sizeof (str_new_state
));
1158 sz
= restarter_state_to_string(new_state_next
, str_new_state_next
,
1159 sizeof (str_new_state_next
));
1160 assert(sz
< sizeof (str_new_state_next
));
1161 sz
= restarter_state_to_string(id
->i_state
, str_state
,
1162 sizeof (str_state
));
1163 assert(sz
< sizeof (str_state
));
1164 sz
= restarter_state_to_string(id
->i_next_state
, str_state_next
,
1165 sizeof (str_state_next
));
1166 assert(sz
< sizeof (str_state_next
));
1168 ret
= gettimeofday(&now
, NULL
);
1171 if (scf_handle_decode_fmri(h
, id
->i_fmri
, NULL
, NULL
, s_inst
,
1172 NULL
, NULL
, SCF_DECODE_FMRI_EXACT
) == -1) {
1173 switch (scf_error()) {
1174 case SCF_ERROR_CONNECTION_BROKEN
:
1179 case SCF_ERROR_INVALID_ARGUMENT
:
1180 case SCF_ERROR_CONSTRAINT_VIOLATED
:
1184 case SCF_ERROR_NOT_FOUND
:
1188 case SCF_ERROR_HANDLE_MISMATCH
:
1189 bad_fail("scf_handle_decode_fmri", scf_error());
1195 if (scf_value_set_astring(v_state
, str_new_state
) != 0 ||
1196 scf_value_set_astring(v_state_next
, str_new_state_next
) != 0)
1197 bad_fail("scf_value_set_astring", scf_error());
1200 if (scf_value_set_astring(v_aux
, aux
) != 0)
1201 bad_fail("scf_value_set_astring", scf_error());
1204 if (scf_value_set_time(v_stime
, now
.tv_sec
, now
.tv_usec
* 1000) != 0)
1205 bad_fail("scf_value_set_time", scf_error());
1208 switch (r
= instance_get_or_add_pg(s_inst
, SCF_PG_RESTARTER
,
1209 SCF_PG_RESTARTER_TYPE
, SCF_PG_RESTARTER_FLAGS
, pg
)) {
1226 bad_fail("instance_get_or_add_pg", r
);
1230 if (scf_transaction_start(t
, pg
) != 0) {
1231 switch (scf_error()) {
1232 case SCF_ERROR_CONNECTION_BROKEN
:
1237 case SCF_ERROR_NOT_SET
:
1240 case SCF_ERROR_PERMISSION_DENIED
:
1244 case SCF_ERROR_BACKEND_ACCESS
:
1248 case SCF_ERROR_BACKEND_READONLY
:
1252 case SCF_ERROR_HANDLE_MISMATCH
:
1253 case SCF_ERROR_IN_USE
:
1254 bad_fail("scf_transaction_start", scf_error());
1258 if ((r
= tx_set_value(t
, t_state
, SCF_PROPERTY_STATE
,
1259 SCF_TYPE_ASTRING
, v_state
)) != 0 ||
1260 (r
= tx_set_value(t
, t_state_next
, SCF_PROPERTY_NEXT_STATE
,
1261 SCF_TYPE_ASTRING
, v_state_next
)) != 0 ||
1262 (r
= tx_set_value(t
, t_stime
, SCF_PROPERTY_STATE_TIMESTAMP
,
1263 SCF_TYPE_TIME
, v_stime
)) != 0) {
1270 scf_transaction_reset(t
);
1274 bad_fail("tx_set_value", r
);
1279 if ((r
= tx_set_value(t
, t_aux
, SCF_PROPERTY_AUX_STATE
,
1280 SCF_TYPE_ASTRING
, v_aux
)) != 0) {
1287 scf_transaction_reset(t
);
1291 bad_fail("tx_set_value", r
);
1296 ret
= scf_transaction_commit(t
);
1300 switch (scf_error()) {
1301 case SCF_ERROR_CONNECTION_BROKEN
:
1306 case SCF_ERROR_PERMISSION_DENIED
:
1310 case SCF_ERROR_BACKEND_ACCESS
:
1314 case SCF_ERROR_BACKEND_READONLY
:
1318 case SCF_ERROR_NOT_SET
:
1319 bad_fail("scf_transaction_commit", scf_error());
1323 scf_transaction_reset(t
);
1324 if (scf_pg_update(pg
) == -1) {
1325 switch (scf_error()) {
1326 case SCF_ERROR_CONNECTION_BROKEN
:
1331 case SCF_ERROR_NOT_SET
:
1337 id
->i_state
= new_state
;
1338 id
->i_next_state
= new_state_next
;
1342 scf_transaction_destroy(t
);
1343 scf_entry_destroy(t_state
);
1344 scf_entry_destroy(t_state_next
);
1345 scf_entry_destroy(t_stime
);
1346 scf_entry_destroy(t_aux
);
1347 scf_value_destroy(v_state
);
1348 scf_value_destroy(v_state_next
);
1349 scf_value_destroy(v_stime
);
1350 scf_value_destroy(v_aux
);
1352 scf_instance_destroy(s_inst
);
1359 * EINVAL - type is invalid
1361 * ECONNABORTED - repository connection broken
1362 * EBADF - s_inst is not set
1363 * ECANCELED - s_inst is deleted
1364 * EPERM - permission denied
1365 * EACCES - backend access denied
1366 * EROFS - backend readonly
1369 restarter_remove_contract(scf_instance_t
*s_inst
, ctid_t contract_id
,
1370 restarter_contract_type_t type
)
1373 scf_transaction_t
*t
= NULL
;
1374 scf_transaction_entry_t
*t_cid
= NULL
;
1375 scf_propertygroup_t
*pg
= NULL
;
1376 scf_property_t
*prop
= NULL
;
1378 scf_iter_t
*iter
= NULL
;
1380 int ret
= 0, primary
;
1384 case RESTARTER_CONTRACT_PRIMARY
:
1387 case RESTARTER_CONTRACT_TRANSIENT
:
1394 h
= scf_instance_handle(s_inst
);
1396 pg
= scf_pg_create(h
);
1397 prop
= scf_property_create(h
);
1398 iter
= scf_iter_create(h
);
1399 t
= scf_transaction_create(h
);
1401 if (pg
== NULL
|| prop
== NULL
|| iter
== NULL
|| t
== NULL
) {
1403 goto remove_contract_cleanup
;
1407 scf_transaction_destroy_children(t
);
1408 ret
= instance_get_or_add_pg(s_inst
, SCF_PG_RESTARTER
,
1409 SCF_PG_RESTARTER_TYPE
, SCF_PG_RESTARTER_FLAGS
, pg
);
1411 goto remove_contract_cleanup
;
1413 pname
= primary
? SCF_PROPERTY_CONTRACT
:
1414 SCF_PROPERTY_TRANSIENT_CONTRACT
;
1417 if (scf_transaction_start(t
, pg
) != 0) {
1418 switch (scf_error()) {
1419 case SCF_ERROR_CONNECTION_BROKEN
:
1422 goto remove_contract_cleanup
;
1424 case SCF_ERROR_DELETED
:
1427 case SCF_ERROR_PERMISSION_DENIED
:
1429 goto remove_contract_cleanup
;
1431 case SCF_ERROR_BACKEND_ACCESS
:
1433 goto remove_contract_cleanup
;
1435 case SCF_ERROR_BACKEND_READONLY
:
1437 goto remove_contract_cleanup
;
1439 case SCF_ERROR_HANDLE_MISMATCH
:
1440 case SCF_ERROR_IN_USE
:
1441 case SCF_ERROR_NOT_SET
:
1442 bad_fail("scf_transaction_start", scf_error());
1446 t_cid
= scf_entry_create(h
);
1448 if (scf_pg_get_property(pg
, pname
, prop
) == 0) {
1450 if (scf_transaction_property_change_type(t
, t_cid
,
1451 pname
, SCF_TYPE_COUNT
) != 0) {
1452 switch (scf_error()) {
1453 case SCF_ERROR_CONNECTION_BROKEN
:
1456 goto remove_contract_cleanup
;
1458 case SCF_ERROR_DELETED
:
1459 scf_entry_destroy(t_cid
);
1462 case SCF_ERROR_NOT_FOUND
:
1465 case SCF_ERROR_HANDLE_MISMATCH
:
1466 case SCF_ERROR_INVALID_ARGUMENT
:
1467 case SCF_ERROR_IN_USE
:
1468 case SCF_ERROR_NOT_SET
:
1470 "scf_transaction_property_changetype",
1475 if (scf_property_is_type(prop
, SCF_TYPE_COUNT
) == 0) {
1476 if (scf_iter_property_values(iter
, prop
) != 0) {
1477 switch (scf_error()) {
1478 case SCF_ERROR_CONNECTION_BROKEN
:
1481 goto remove_contract_cleanup
;
1483 case SCF_ERROR_NOT_SET
:
1484 case SCF_ERROR_HANDLE_MISMATCH
:
1486 "scf_iter_property_values",
1492 val
= scf_value_create(h
);
1494 assert(scf_error() ==
1495 SCF_ERROR_NO_MEMORY
);
1497 goto remove_contract_cleanup
;
1500 ret
= scf_iter_next_value(iter
, val
);
1502 switch (scf_error()) {
1503 case SCF_ERROR_CONNECTION_BROKEN
:
1505 goto remove_contract_cleanup
;
1507 case SCF_ERROR_DELETED
:
1508 scf_value_destroy(val
);
1511 case SCF_ERROR_HANDLE_MISMATCH
:
1512 case SCF_ERROR_INVALID_ARGUMENT
:
1513 case SCF_ERROR_PERMISSION_DENIED
:
1515 bad_fail("scf_iter_next_value",
1521 ret
= scf_value_get_count(val
, &c
);
1524 if (c
!= contract_id
) {
1525 ret
= scf_entry_add_value(t_cid
,
1529 scf_value_destroy(val
);
1535 scf_value_destroy(val
);
1537 switch (scf_error()) {
1538 case SCF_ERROR_CONNECTION_BROKEN
:
1541 goto remove_contract_cleanup
;
1543 case SCF_ERROR_TYPE_MISMATCH
:
1546 case SCF_ERROR_INVALID_ARGUMENT
:
1547 case SCF_ERROR_NOT_SET
:
1548 bad_fail("scf_property_is_type",
1553 switch (scf_error()) {
1554 case SCF_ERROR_CONNECTION_BROKEN
:
1557 goto remove_contract_cleanup
;
1559 case SCF_ERROR_DELETED
:
1560 scf_entry_destroy(t_cid
);
1563 case SCF_ERROR_NOT_FOUND
:
1566 case SCF_ERROR_HANDLE_MISMATCH
:
1567 case SCF_ERROR_INVALID_ARGUMENT
:
1568 case SCF_ERROR_NOT_SET
:
1569 bad_fail("scf_pg_get_property", scf_error());
1573 if (scf_transaction_property_new(t
, t_cid
, pname
,
1574 SCF_TYPE_COUNT
) != 0) {
1575 switch (scf_error()) {
1576 case SCF_ERROR_CONNECTION_BROKEN
:
1579 goto remove_contract_cleanup
;
1581 case SCF_ERROR_DELETED
:
1582 scf_entry_destroy(t_cid
);
1585 case SCF_ERROR_EXISTS
:
1588 case SCF_ERROR_HANDLE_MISMATCH
:
1589 case SCF_ERROR_INVALID_ARGUMENT
:
1590 case SCF_ERROR_NOT_SET
:
1591 bad_fail("scf_transaction_property_new",
1597 ret
= scf_transaction_commit(t
);
1599 switch (scf_error()) {
1600 case SCF_ERROR_CONNECTION_BROKEN
:
1603 goto remove_contract_cleanup
;
1605 case SCF_ERROR_DELETED
:
1608 case SCF_ERROR_PERMISSION_DENIED
:
1610 goto remove_contract_cleanup
;
1612 case SCF_ERROR_BACKEND_ACCESS
:
1614 goto remove_contract_cleanup
;
1616 case SCF_ERROR_BACKEND_READONLY
:
1618 goto remove_contract_cleanup
;
1620 case SCF_ERROR_NOT_SET
:
1621 bad_fail("scf_transaction_commit", scf_error());
1629 scf_transaction_destroy_children(t
);
1630 if (scf_pg_update(pg
) == -1) {
1631 switch (scf_error()) {
1632 case SCF_ERROR_CONNECTION_BROKEN
:
1635 goto remove_contract_cleanup
;
1637 case SCF_ERROR_DELETED
:
1640 case SCF_ERROR_NOT_SET
:
1641 bad_fail("scf_pg_update", scf_error());
1646 remove_contract_cleanup
:
1647 scf_transaction_destroy_children(t
);
1648 scf_transaction_destroy(t
);
1649 scf_iter_destroy(iter
);
1650 scf_property_destroy(prop
);
1658 * EINVAL - type is invalid
1660 * ECONNABORTED - repository disconnection
1661 * EBADF - s_inst is not set
1662 * ECANCELED - s_inst is deleted
1668 restarter_store_contract(scf_instance_t
*s_inst
, ctid_t contract_id
,
1669 restarter_contract_type_t type
)
1672 scf_transaction_t
*t
= NULL
;
1673 scf_transaction_entry_t
*t_cid
= NULL
;
1675 scf_propertygroup_t
*pg
= NULL
;
1676 scf_property_t
*prop
= NULL
;
1677 scf_iter_t
*iter
= NULL
;
1679 int ret
= 0, primary
;
1681 if (type
== RESTARTER_CONTRACT_PRIMARY
)
1683 else if (type
== RESTARTER_CONTRACT_TRANSIENT
)
1688 h
= scf_instance_handle(s_inst
);
1690 pg
= scf_pg_create(h
);
1691 prop
= scf_property_create(h
);
1692 iter
= scf_iter_create(h
);
1693 t
= scf_transaction_create(h
);
1695 if (pg
== NULL
|| prop
== NULL
|| iter
== NULL
|| t
== NULL
) {
1701 scf_transaction_destroy_children(t
);
1702 ret
= instance_get_or_add_pg(s_inst
, SCF_PG_RESTARTER
,
1703 SCF_PG_RESTARTER_TYPE
, SCF_PG_RESTARTER_FLAGS
, pg
);
1707 pname
= primary
? SCF_PROPERTY_CONTRACT
:
1708 SCF_PROPERTY_TRANSIENT_CONTRACT
;
1711 if (scf_transaction_start(t
, pg
) != 0) {
1712 switch (scf_error()) {
1713 case SCF_ERROR_CONNECTION_BROKEN
:
1718 case SCF_ERROR_DELETED
:
1721 case SCF_ERROR_PERMISSION_DENIED
:
1725 case SCF_ERROR_BACKEND_ACCESS
:
1729 case SCF_ERROR_BACKEND_READONLY
:
1733 case SCF_ERROR_HANDLE_MISMATCH
:
1734 case SCF_ERROR_IN_USE
:
1735 case SCF_ERROR_NOT_SET
:
1736 bad_fail("scf_transaction_start", scf_error());
1740 t_cid
= scf_entry_create(h
);
1741 if (t_cid
== NULL
) {
1746 if (scf_pg_get_property(pg
, pname
, prop
) == 0) {
1748 if (scf_transaction_property_change_type(t
, t_cid
,
1749 pname
, SCF_TYPE_COUNT
) != 0) {
1750 switch (scf_error()) {
1751 case SCF_ERROR_CONNECTION_BROKEN
:
1756 case SCF_ERROR_DELETED
:
1757 scf_entry_destroy(t_cid
);
1760 case SCF_ERROR_NOT_FOUND
:
1763 case SCF_ERROR_HANDLE_MISMATCH
:
1764 case SCF_ERROR_INVALID_ARGUMENT
:
1765 case SCF_ERROR_IN_USE
:
1766 case SCF_ERROR_NOT_SET
:
1768 "scf_transaction_propert_change_type",
1773 if (scf_property_is_type(prop
, SCF_TYPE_COUNT
) == 0) {
1774 if (scf_iter_property_values(iter
, prop
) != 0) {
1775 switch (scf_error()) {
1776 case SCF_ERROR_CONNECTION_BROKEN
:
1781 case SCF_ERROR_NOT_SET
:
1782 case SCF_ERROR_HANDLE_MISMATCH
:
1784 "scf_iter_property_values",
1790 val
= scf_value_create(h
);
1792 assert(scf_error() ==
1793 SCF_ERROR_NO_MEMORY
);
1798 ret
= scf_iter_next_value(iter
, val
);
1800 switch (scf_error()) {
1801 case SCF_ERROR_CONNECTION_BROKEN
:
1806 case SCF_ERROR_DELETED
:
1807 scf_value_destroy(val
);
1810 case SCF_ERROR_HANDLE_MISMATCH
:
1811 case SCF_ERROR_INVALID_ARGUMENT
:
1812 case SCF_ERROR_PERMISSION_DENIED
:
1814 "scf_iter_next_value",
1820 ret
= scf_entry_add_value(t_cid
, val
);
1826 scf_value_destroy(val
);
1828 switch (scf_error()) {
1829 case SCF_ERROR_CONNECTION_BROKEN
:
1834 case SCF_ERROR_TYPE_MISMATCH
:
1837 case SCF_ERROR_INVALID_ARGUMENT
:
1838 case SCF_ERROR_NOT_SET
:
1839 bad_fail("scf_property_is_type",
1844 switch (scf_error()) {
1845 case SCF_ERROR_CONNECTION_BROKEN
:
1850 case SCF_ERROR_DELETED
:
1851 scf_entry_destroy(t_cid
);
1854 case SCF_ERROR_NOT_FOUND
:
1857 case SCF_ERROR_HANDLE_MISMATCH
:
1858 case SCF_ERROR_INVALID_ARGUMENT
:
1859 case SCF_ERROR_NOT_SET
:
1860 bad_fail("scf_pg_get_property", scf_error());
1864 if (scf_transaction_property_new(t
, t_cid
, pname
,
1865 SCF_TYPE_COUNT
) != 0) {
1866 switch (scf_error()) {
1867 case SCF_ERROR_CONNECTION_BROKEN
:
1872 case SCF_ERROR_DELETED
:
1873 scf_entry_destroy(t_cid
);
1876 case SCF_ERROR_EXISTS
:
1879 case SCF_ERROR_HANDLE_MISMATCH
:
1880 case SCF_ERROR_INVALID_ARGUMENT
:
1881 case SCF_ERROR_NOT_SET
:
1882 bad_fail("scf_transaction_property_new",
1888 val
= scf_value_create(h
);
1890 assert(scf_error() == SCF_ERROR_NO_MEMORY
);
1895 scf_value_set_count(val
, contract_id
);
1896 ret
= scf_entry_add_value(t_cid
, val
);
1899 ret
= scf_transaction_commit(t
);
1901 switch (scf_error()) {
1902 case SCF_ERROR_CONNECTION_BROKEN
:
1907 case SCF_ERROR_DELETED
:
1910 case SCF_ERROR_PERMISSION_DENIED
:
1914 case SCF_ERROR_BACKEND_ACCESS
:
1918 case SCF_ERROR_BACKEND_READONLY
:
1922 case SCF_ERROR_NOT_SET
:
1923 bad_fail("scf_transaction_commit", scf_error());
1931 scf_transaction_destroy_children(t
);
1932 if (scf_pg_update(pg
) == -1) {
1933 switch (scf_error()) {
1934 case SCF_ERROR_CONNECTION_BROKEN
:
1939 case SCF_ERROR_DELETED
:
1942 case SCF_ERROR_NOT_SET
:
1943 bad_fail("scf_pg_update", scf_error());
1949 scf_transaction_destroy_children(t
);
1950 scf_transaction_destroy(t
);
1951 scf_iter_destroy(iter
);
1952 scf_property_destroy(prop
);
1959 restarter_rm_libs_loadable()
1963 if (method_context_safety
)
1966 if ((libhndl
= dlopen("libpool.so", RTLD_LAZY
| RTLD_LOCAL
)) == NULL
)
1969 (void) dlclose(libhndl
);
1971 if ((libhndl
= dlopen("libproject.so", RTLD_LAZY
| RTLD_LOCAL
)) == NULL
)
1974 (void) dlclose(libhndl
);
1976 method_context_safety
= 1;
1982 get_astring_val(scf_propertygroup_t
*pg
, const char *name
, char *buf
,
1983 size_t bufsz
, scf_property_t
*prop
, scf_value_t
*val
)
1990 if (scf_pg_get_property(pg
, name
, prop
) != SCF_SUCCESS
) {
1991 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN
)
1996 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
1997 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN
)
2002 szret
= scf_value_get_astring(val
, buf
, bufsz
);
2004 return (szret
>= 0 ? 0 : -1);
2008 get_boolean_val(scf_propertygroup_t
*pg
, const char *name
, uint8_t *b
,
2009 scf_property_t
*prop
, scf_value_t
*val
)
2011 if (scf_pg_get_property(pg
, name
, prop
) != SCF_SUCCESS
) {
2012 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN
)
2017 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
2018 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN
)
2023 if (scf_value_get_boolean(val
, b
))
2030 * Try to load mcp->pwd, if it isn't already.
2032 * ENOMEM - malloc() failed
2033 * ENOENT - no entry found
2035 * EMFILE - process out of file descriptors
2036 * ENFILE - system out of file handles
2039 lookup_pwd(struct method_context
*mcp
)
2041 struct passwd
*pwdp
;
2043 if (mcp
->pwbuf
!= NULL
&& mcp
->pwd
.pw_uid
== mcp
->uid
)
2046 if (mcp
->pwbuf
== NULL
) {
2047 mcp
->pwbufsz
= sysconf(_SC_GETPW_R_SIZE_MAX
);
2048 assert(mcp
->pwbufsz
>= 0);
2049 mcp
->pwbuf
= malloc(mcp
->pwbufsz
);
2050 if (mcp
->pwbuf
== NULL
)
2056 pwdp
= getpwuid_r(mcp
->uid
, &mcp
->pwd
, mcp
->pwbuf
,
2058 } while (pwdp
== NULL
&& errno
== EINTR
);
2069 * Until bug 5065780 is fixed, getpwuid_r() can fail with
2070 * ENOENT, particularly on the miniroot. Since the
2071 * documentation is inaccurate, we'll return ENOENT for unknown
2082 bad_fail("getpwuid_r", errno
);
2088 * Get the user id for str. Returns 0 on success or
2089 * ERANGE the uid is too big
2090 * EINVAL the string starts with a digit, but is not a valid uid
2091 * ENOMEM out of memory
2092 * ENOENT no passwd entry for str
2093 * EIO an I/O error has occurred
2094 * EMFILE/ENFILE out of file descriptors
2097 get_uid(const char *str
, struct method_context
*ci
, uid_t
*uidp
)
2099 if (isdigit(str
[0])) {
2104 uid
= strtol(str
, &cp
, 10);
2106 if (uid
== 0 && errno
!= 0) {
2107 assert(errno
!= EINVAL
);
2111 for (; *cp
!= '\0'; ++cp
)
2112 if (*cp
!= ' ' || *cp
!= '\t')
2121 struct passwd
*pwdp
;
2123 if (ci
->pwbuf
== NULL
) {
2124 ci
->pwbufsz
= sysconf(_SC_GETPW_R_SIZE_MAX
);
2125 ci
->pwbuf
= malloc(ci
->pwbufsz
);
2126 if (ci
->pwbuf
== NULL
)
2133 getpwnam_r(str
, &ci
->pwd
, ci
->pwbuf
, ci
->pwbufsz
);
2134 } while (pwdp
== NULL
&& errno
== EINTR
);
2137 *uidp
= ci
->pwd
.pw_uid
;
2154 bad_fail("getpwnam_r", errno
);
2162 get_gid(const char *str
)
2164 if (isdigit(str
[0])) {
2169 gid
= strtol(str
, &cp
, 10);
2171 if (gid
== 0 && errno
!= 0)
2174 for (; *cp
!= '\0'; ++cp
)
2175 if (*cp
!= ' ' || *cp
!= '\t')
2180 struct group grp
, *ret
;
2184 buflen
= sysconf(_SC_GETGR_R_SIZE_MAX
);
2185 buffer
= malloc(buflen
);
2190 ret
= getgrnam_r(str
, &grp
, buffer
, buflen
);
2193 return (ret
== NULL
? (gid_t
)-1 : grp
.gr_gid
);
2199 * ENOMEM - out of memory
2200 * ENOENT - no passwd entry
2202 * EIO - an I/O error occurred
2203 * EMFILE - the process is out of file descriptors
2204 * ENFILE - the system is out of file handles
2205 * ERANGE - the project id is out of range
2206 * EINVAL - str is invalid
2207 * E2BIG - the project entry was too big
2208 * -1 - the name service switch is misconfigured
2211 get_projid(const char *str
, struct method_context
*cip
)
2215 const size_t bufsz
= PROJECT_BUFSZ
;
2216 struct project proj
, *pp
;
2218 if (strcmp(str
, ":default") == 0) {
2219 if (cip
->uid
== 0) {
2220 /* Don't change project for root services */
2221 cip
->project
= NULL
;
2225 switch (ret
= lookup_pwd(cip
)) {
2237 bad_fail("lookup_pwd", ret
);
2240 buf
= malloc(bufsz
);
2246 pp
= getdefaultproj(cip
->pwd
.pw_name
, &proj
, buf
,
2248 } while (pp
== NULL
&& errno
== EINTR
);
2250 /* to be continued ... */
2255 if (!isdigit(str
[0])) {
2256 cip
->project
= strdup(str
);
2257 return (cip
->project
!= NULL
? 0 : ENOMEM
);
2261 projid
= strtol(str
, &cp
, 10);
2263 if (projid
== 0 && errno
!= 0) {
2264 assert(errno
== ERANGE
);
2268 for (; *cp
!= '\0'; ++cp
)
2269 if (*cp
!= ' ' || *cp
!= '\t')
2272 if (projid
> MAXPROJID
)
2275 buf
= malloc(bufsz
);
2281 pp
= getprojbyid(projid
, &proj
, buf
, bufsz
);
2282 } while (pp
== NULL
&& errno
== EINTR
);
2286 cip
->project
= strdup(pp
->pj_name
);
2288 return (cip
->project
!= NULL
? 0 : ENOMEM
);
2311 * Parse the supp_groups property value and populate ci->groups. Returns
2312 * EINVAL (get_gid() failed for one of the components), E2BIG (the property has
2313 * more than NGROUPS_MAX-1 groups), or 0 on success.
2316 get_groups(char *str
, struct method_context
*ci
)
2318 char *cp
, *end
, *next
;
2321 const char * const whitespace
= " \t";
2322 const char * const illegal
= ", \t";
2324 if (str
[0] == '\0') {
2329 for (cp
= str
, i
= 0; *cp
!= '\0'; ) {
2330 /* skip whitespace */
2331 cp
+= strspn(cp
, whitespace
);
2334 end
= cp
+ strcspn(cp
, illegal
);
2336 /* skip whitespace after end */
2337 next
= end
+ strspn(end
, whitespace
);
2339 /* if there's a comma, it separates the fields */
2345 if ((ci
->groups
[i
] = get_gid(cp
)) == (gid_t
)-1) {
2351 if (i
> NGROUPS_MAX
- 1) {
2365 * Return an error message structure containing the error message
2366 * with context, and the error so the caller can make a decision
2367 * on what to do next.
2369 * Because get_ids uses the mc_error_create() function which can
2370 * reallocate the merr, this function must return the merr pointer
2371 * in case it was reallocated.
2374 get_profile(scf_propertygroup_t
*methpg
, scf_propertygroup_t
*instpg
,
2375 scf_property_t
*prop
, scf_value_t
*val
, const char *cmdline
,
2376 struct method_context
*ci
, mc_error_t
*merr
)
2378 char *buf
= ci
->vbuf
;
2379 ssize_t buf_sz
= ci
->vbuf_sz
;
2384 mc_error_t
*err
= merr
;
2387 if (!(get_astring_val(methpg
, SCF_PROPERTY_PROFILE
, buf
, buf_sz
, prop
,
2388 val
) == 0 || get_astring_val(instpg
, SCF_PROPERTY_PROFILE
, buf
,
2389 buf_sz
, prop
, val
) == 0))
2390 return (mc_error_create(merr
, scf_error(),
2391 "Method context requires a profile, but the \"%s\" "
2392 "property could not be read. scf_error is %s",
2393 SCF_PROPERTY_PROFILE
, scf_strerror(scf_error())));
2395 /* Extract the command from the command line. */
2396 cp
= strpbrk(cmdline
, " \t");
2401 (void) strncpy(cmd
, cmdline
, cp
- cmdline
);
2402 cmd
[cp
- cmdline
] = '\0';
2406 /* Require that cmdp[0] == '/'? */
2408 eap
= getexecprof(buf
, KV_COMMAND
, cmdp
, GET_ONE
);
2410 return (mc_error_create(merr
, ENOENT
,
2411 "Could not find the execution profile \"%s\", "
2412 "command %s.", buf
, cmdp
));
2414 /* Based on pfexec.c */
2416 /* Get the euid first so we don't override ci->pwd for the uid. */
2417 if ((value
= kva_match(eap
->attr
, EXECATTR_EUID_KW
)) != NULL
) {
2418 if ((r
= get_uid(value
, ci
, &ci
->euid
)) != 0) {
2419 ci
->euid
= (uid_t
)-1;
2420 err
= mc_error_create(merr
, r
,
2421 "Could not interpret profile euid value \"%s\", "
2422 "from the execution profile \"%s\", error %d.",
2428 if ((value
= kva_match(eap
->attr
, EXECATTR_UID_KW
)) != NULL
) {
2429 if ((r
= get_uid(value
, ci
, &ci
->uid
)) != 0) {
2430 ci
->euid
= ci
->uid
= (uid_t
)-1;
2431 err
= mc_error_create(merr
, r
,
2432 "Could not interpret profile uid value \"%s\", "
2433 "from the execution profile \"%s\", error %d.",
2440 if ((value
= kva_match(eap
->attr
, EXECATTR_GID_KW
)) != NULL
) {
2441 ci
->egid
= ci
->gid
= get_gid(value
);
2442 if (ci
->gid
== (gid_t
)-1) {
2443 err
= mc_error_create(merr
, EINVAL
,
2444 "Could not interpret profile gid value \"%s\", "
2445 "from the execution profile \"%s\".", value
, buf
);
2450 if ((value
= kva_match(eap
->attr
, EXECATTR_EGID_KW
)) != NULL
) {
2451 ci
->egid
= get_gid(value
);
2452 if (ci
->egid
== (gid_t
)-1) {
2453 err
= mc_error_create(merr
, EINVAL
,
2454 "Could not interpret profile egid value \"%s\", "
2455 "from the execution profile \"%s\".", value
, buf
);
2460 if ((value
= kva_match(eap
->attr
, EXECATTR_LPRIV_KW
)) != NULL
) {
2461 ci
->lpriv_set
= priv_str_to_set(value
, ",", NULL
);
2462 if (ci
->lpriv_set
== NULL
) {
2463 if (errno
!= EINVAL
)
2464 err
= mc_error_create(merr
, ENOMEM
,
2467 err
= mc_error_create(merr
, EINVAL
,
2468 "Could not interpret profile "
2469 "limitprivs value \"%s\", from "
2470 "the execution profile \"%s\".",
2476 if ((value
= kva_match(eap
->attr
, EXECATTR_IPRIV_KW
)) != NULL
) {
2477 ci
->priv_set
= priv_str_to_set(value
, ",", NULL
);
2478 if (ci
->priv_set
== NULL
) {
2479 if (errno
!= EINVAL
)
2480 err
= mc_error_create(merr
, ENOMEM
,
2483 err
= mc_error_create(merr
, EINVAL
,
2484 "Could not interpret profile privs value "
2485 "\"%s\", from the execution profile "
2486 "\"%s\".", value
, buf
);
2498 * Return an error message structure containing the error message
2499 * with context, and the error so the caller can make a decision
2500 * on what to do next.
2502 * Because get_ids uses the mc_error_create() function which can
2503 * reallocate the merr, this function must return the merr pointer
2504 * in case it was reallocated.
2507 get_ids(scf_propertygroup_t
*methpg
, scf_propertygroup_t
*instpg
,
2508 scf_property_t
*prop
, scf_value_t
*val
, struct method_context
*ci
,
2511 char *vbuf
= ci
->vbuf
;
2512 ssize_t vbuf_sz
= ci
->vbuf_sz
;
2516 * This should never happen because the caller should fall through
2517 * another path of just setting the ids to defaults, instead of
2518 * attempting to get the ids here.
2520 if (methpg
== NULL
&& instpg
== NULL
)
2521 return (mc_error_create(merr
, ENOENT
,
2522 "No property groups to get ids from."));
2524 if (!(get_astring_val(methpg
, SCF_PROPERTY_USER
,
2525 vbuf
, vbuf_sz
, prop
, val
) == 0 || get_astring_val(instpg
,
2526 SCF_PROPERTY_USER
, vbuf
, vbuf_sz
, prop
,
2528 return (mc_error_create(merr
, ENOENT
,
2529 "Could not get \"%s\" property.", SCF_PROPERTY_USER
));
2531 if ((r
= get_uid(vbuf
, ci
, &ci
->uid
)) != 0) {
2532 ci
->uid
= (uid_t
)-1;
2533 return (mc_error_create(merr
, r
,
2534 "Could not interpret \"%s\" property value \"%s\", "
2535 "error %d.", SCF_PROPERTY_USER
, vbuf
, r
));
2538 if (!(get_astring_val(methpg
, SCF_PROPERTY_GROUP
, vbuf
, vbuf_sz
, prop
,
2539 val
) == 0 || get_astring_val(instpg
, SCF_PROPERTY_GROUP
, vbuf
,
2540 vbuf_sz
, prop
, val
) == 0)) {
2541 if (scf_error() == SCF_ERROR_NOT_FOUND
) {
2542 (void) strcpy(vbuf
, ":default");
2544 return (mc_error_create(merr
, ENOENT
,
2545 "Could not get \"%s\" property.",
2546 SCF_PROPERTY_GROUP
));
2550 if (strcmp(vbuf
, ":default") != 0) {
2551 ci
->gid
= get_gid(vbuf
);
2552 if (ci
->gid
== (gid_t
)-1) {
2553 return (mc_error_create(merr
, ENOENT
,
2554 "Could not interpret \"%s\" property value \"%s\".",
2555 SCF_PROPERTY_GROUP
, vbuf
));
2558 switch (r
= lookup_pwd(ci
)) {
2560 ci
->gid
= ci
->pwd
.pw_gid
;
2564 ci
->gid
= (gid_t
)-1;
2565 return (mc_error_create(merr
, ENOENT
,
2566 "No passwd entry for uid \"%d\".", ci
->uid
));
2569 return (mc_error_create(merr
, ENOMEM
,
2575 return (mc_error_create(merr
, ENFILE
,
2576 "getpwuid_r() failed, error %d.", r
));
2579 bad_fail("lookup_pwd", r
);
2583 if (!(get_astring_val(methpg
, SCF_PROPERTY_SUPP_GROUPS
, vbuf
, vbuf_sz
,
2584 prop
, val
) == 0 || get_astring_val(instpg
,
2585 SCF_PROPERTY_SUPP_GROUPS
, vbuf
, vbuf_sz
, prop
, val
) == 0)) {
2586 if (scf_error() == SCF_ERROR_NOT_FOUND
) {
2587 (void) strcpy(vbuf
, ":default");
2589 return (mc_error_create(merr
, ENOENT
,
2590 "Could not get supplemental groups (\"%s\") "
2591 "property.", SCF_PROPERTY_SUPP_GROUPS
));
2595 if (strcmp(vbuf
, ":default") != 0) {
2596 switch (r
= get_groups(vbuf
, ci
)) {
2601 return (mc_error_create(merr
, EINVAL
,
2602 "Could not interpret supplemental groups (\"%s\") "
2603 "property value \"%s\".", SCF_PROPERTY_SUPP_GROUPS
,
2607 return (mc_error_create(merr
, E2BIG
,
2608 "Too many supplemental groups values in \"%s\".",
2612 bad_fail("get_groups", r
);
2618 if (!(get_astring_val(methpg
, SCF_PROPERTY_PRIVILEGES
, vbuf
, vbuf_sz
,
2619 prop
, val
) == 0 || get_astring_val(instpg
, SCF_PROPERTY_PRIVILEGES
,
2620 vbuf
, vbuf_sz
, prop
, val
) == 0)) {
2621 if (scf_error() == SCF_ERROR_NOT_FOUND
) {
2622 (void) strcpy(vbuf
, ":default");
2624 return (mc_error_create(merr
, ENOENT
,
2625 "Could not get \"%s\" property.",
2626 SCF_PROPERTY_PRIVILEGES
));
2631 * For default privs, we need to keep priv_set == NULL, as
2632 * we use this test elsewhere.
2634 if (strcmp(vbuf
, ":default") != 0) {
2635 ci
->priv_set
= priv_str_to_set(vbuf
, ",", NULL
);
2636 if (ci
->priv_set
== NULL
) {
2637 if (errno
!= EINVAL
) {
2638 return (mc_error_create(merr
, ENOMEM
,
2641 return (mc_error_create(merr
, EINVAL
,
2642 "Could not interpret \"%s\" "
2643 "property value \"%s\".",
2644 SCF_PROPERTY_PRIVILEGES
, vbuf
));
2649 if (!(get_astring_val(methpg
, SCF_PROPERTY_LIMIT_PRIVILEGES
, vbuf
,
2650 vbuf_sz
, prop
, val
) == 0 || get_astring_val(instpg
,
2651 SCF_PROPERTY_LIMIT_PRIVILEGES
, vbuf
, vbuf_sz
, prop
, val
) == 0)) {
2652 if (scf_error() == SCF_ERROR_NOT_FOUND
) {
2653 (void) strcpy(vbuf
, ":default");
2655 return (mc_error_create(merr
, ENOENT
,
2656 "Could not get \"%s\" property.",
2657 SCF_PROPERTY_LIMIT_PRIVILEGES
));
2661 if (strcmp(vbuf
, ":default") == 0)
2663 * L must default to all privileges so root NPA services see
2664 * iE = all. "zone" is all privileges available in the current
2665 * zone, equivalent to "all" in the global zone.
2667 (void) strcpy(vbuf
, "zone");
2669 ci
->lpriv_set
= priv_str_to_set(vbuf
, ",", NULL
);
2670 if (ci
->lpriv_set
== NULL
) {
2671 if (errno
!= EINVAL
) {
2672 return (mc_error_create(merr
, ENOMEM
, ALLOCFAIL
));
2674 return (mc_error_create(merr
, EINVAL
,
2675 "Could not interpret \"%s\" property value \"%s\".",
2676 SCF_PROPERTY_LIMIT_PRIVILEGES
, vbuf
));
2684 get_environment(scf_handle_t
*h
, scf_propertygroup_t
*pg
,
2685 struct method_context
*mcp
, scf_property_t
*prop
, scf_value_t
*val
)
2692 if (scf_pg_get_property(pg
, SCF_PROPERTY_ENVIRONMENT
, prop
) != 0) {
2693 if (scf_error() == SCF_ERROR_NOT_FOUND
)
2695 return (scf_error());
2697 if (scf_property_type(prop
, &type
) != 0)
2698 return (scf_error());
2699 if (type
!= SCF_TYPE_ASTRING
)
2701 if ((iter
= scf_iter_create(h
)) == NULL
)
2702 return (scf_error());
2704 if (scf_iter_property_values(iter
, prop
) != 0) {
2706 scf_iter_destroy(iter
);
2712 if ((mcp
->env
= uu_zalloc(sizeof (*mcp
->env
) * mcp
->env_sz
)) == NULL
) {
2717 while ((ret
= scf_iter_next_value(iter
, val
)) == 1) {
2718 ret
= scf_value_get_as_string(val
, mcp
->vbuf
, mcp
->vbuf_sz
);
2724 if ((mcp
->env
[i
] = strdup(mcp
->vbuf
)) == NULL
) {
2729 if (++i
== mcp
->env_sz
) {
2732 env
= uu_zalloc(sizeof (*mcp
->env
) * mcp
->env_sz
);
2737 (void) memcpy(env
, mcp
->env
,
2738 sizeof (*mcp
->env
) * (mcp
->env_sz
/ 2));
2748 scf_iter_destroy(iter
);
2753 * Fetch method context information from the repository, allocate and fill
2754 * a method_context structure, return it in *mcpp, and return NULL.
2756 * If no method_context is defined, original init context is provided, where
2757 * the working directory is '/', and uid/gid are 0/0. But if a method_context
2758 * is defined at any level the smf_method(5) method_context defaults are used.
2760 * Return an error message structure containing the error message
2761 * with context, and the error so the caller can make a decision
2762 * on what to do next.
2765 * E2BIG Too many values or entry is too big
2766 * EINVAL Invalid value
2767 * EIO an I/O error has occured
2768 * ENOENT no entry for value
2769 * ENOMEM out of memory
2770 * ENOTSUP Version mismatch
2771 * ERANGE value is out of range
2772 * EMFILE/ENFILE out of file descriptors
2774 * SCF_ERROR_BACKEND_ACCESS
2775 * SCF_ERROR_CONNECTION_BROKEN
2777 * SCF_ERROR_CONSTRAINT_VIOLATED
2778 * SCF_ERROR_HANDLE_DESTROYED
2779 * SCF_ERROR_INTERNAL
2780 * SCF_ERROR_INVALID_ARGUMENT
2781 * SCF_ERROR_NO_MEMORY
2782 * SCF_ERROR_NO_RESOURCES
2783 * SCF_ERROR_NOT_BOUND
2784 * SCF_ERROR_NOT_FOUND
2786 * SCF_ERROR_TYPE_MISMATCH
2790 restarter_get_method_context(uint_t version
, scf_instance_t
*inst
,
2791 scf_snapshot_t
*snap
, const char *mname
, const char *cmdline
,
2792 struct method_context
**mcpp
)
2795 scf_propertygroup_t
*methpg
= NULL
;
2796 scf_propertygroup_t
*instpg
= NULL
;
2797 scf_propertygroup_t
*pg
= NULL
;
2798 scf_property_t
*prop
= NULL
;
2799 scf_value_t
*val
= NULL
;
2801 uint8_t use_profile
;
2804 mc_error_t
*err
= NULL
;
2805 struct method_context
*cip
;
2807 if ((err
= malloc(sizeof (mc_error_t
))) == NULL
)
2808 return (mc_error_create(NULL
, ENOMEM
, NULL
));
2810 /* Set the type to zero to track if an error occured. */
2813 if (version
!= RESTARTER_METHOD_CONTEXT_VERSION
)
2814 return (mc_error_create(err
, ENOTSUP
,
2815 "Invalid client version %d. (Expected %d)",
2816 version
, RESTARTER_METHOD_CONTEXT_VERSION
));
2818 /* Get the handle before we allocate anything. */
2819 h
= scf_instance_handle(inst
);
2821 return (mc_error_create(err
, scf_error(),
2822 scf_strerror(scf_error())));
2824 cip
= malloc(sizeof (*cip
));
2826 return (mc_error_create(err
, ENOMEM
, ALLOCFAIL
));
2828 (void) memset(cip
, 0, sizeof (*cip
));
2829 cip
->uid
= (uid_t
)-1;
2830 cip
->euid
= (uid_t
)-1;
2831 cip
->gid
= (gid_t
)-1;
2832 cip
->egid
= (gid_t
)-1;
2834 cip
->vbuf_sz
= scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
2835 assert(cip
->vbuf_sz
>= 0);
2836 cip
->vbuf
= malloc(cip
->vbuf_sz
);
2837 if (cip
->vbuf
== NULL
) {
2839 return (mc_error_create(err
, ENOMEM
, ALLOCFAIL
));
2842 if ((instpg
= scf_pg_create(h
)) == NULL
||
2843 (methpg
= scf_pg_create(h
)) == NULL
||
2844 (prop
= scf_property_create(h
)) == NULL
||
2845 (val
= scf_value_create(h
)) == NULL
) {
2846 err
= mc_error_create(err
, scf_error(),
2847 "Failed to create repository object: %s",
2848 scf_strerror(scf_error()));
2853 * The method environment, and the credentials/profile data,
2854 * may be found either in the pg for the method (methpg),
2855 * or in the instance/service SCF_PG_METHOD_CONTEXT pg (named
2859 if (scf_instance_get_pg_composed(inst
, snap
, mname
, methpg
) !=
2861 err
= mc_error_create(err
, scf_error(), "Unable to get the "
2862 "\"%s\" method, %s", mname
, scf_strerror(scf_error()));
2866 if (scf_instance_get_pg_composed(inst
, snap
, SCF_PG_METHOD_CONTEXT
,
2867 instpg
) != SCF_SUCCESS
) {
2868 if (scf_error() != SCF_ERROR_NOT_FOUND
) {
2869 err
= mc_error_create(err
, scf_error(),
2870 "Unable to retrieve the \"%s\" property group, %s",
2871 SCF_PG_METHOD_CONTEXT
, scf_strerror(scf_error()));
2874 scf_pg_destroy(instpg
);
2880 ret
= get_environment(h
, methpg
, cip
, prop
, val
);
2881 if (ret
== ENOENT
&& instpg
!= NULL
) {
2882 ret
= get_environment(h
, instpg
, cip
, prop
, val
);
2892 err
= mc_error_create(err
, ret
, "Out of memory.");
2895 err
= mc_error_create(err
, ret
, "Invalid method environment.");
2898 err
= mc_error_create(err
, ret
,
2899 "Get method environment failed: %s", scf_strerror(ret
));
2905 ret
= scf_pg_get_property(pg
, SCF_PROPERTY_USE_PROFILE
, prop
);
2906 if (ret
&& scf_error() == SCF_ERROR_NOT_FOUND
&& instpg
!= NULL
) {
2908 ret
= scf_pg_get_property(instpg
, SCF_PROPERTY_USE_PROFILE
,
2913 switch (scf_error()) {
2914 case SCF_ERROR_NOT_FOUND
:
2915 /* No profile context: use default credentials */
2920 case SCF_ERROR_CONNECTION_BROKEN
:
2921 err
= mc_error_create(err
, SCF_ERROR_CONNECTION_BROKEN
,
2925 case SCF_ERROR_DELETED
:
2926 err
= mc_error_create(err
, SCF_ERROR_NOT_FOUND
,
2927 "Could not find property group \"%s\"",
2928 pg
== NULL
? SCF_PG_METHOD_CONTEXT
: mname
);
2931 case SCF_ERROR_HANDLE_MISMATCH
:
2932 case SCF_ERROR_INVALID_ARGUMENT
:
2933 case SCF_ERROR_NOT_SET
:
2935 bad_fail("scf_pg_get_property", scf_error());
2938 if (scf_property_type(prop
, &ty
) != SCF_SUCCESS
) {
2941 case SCF_ERROR_CONNECTION_BROKEN
:
2942 err
= mc_error_create(err
,
2943 SCF_ERROR_CONNECTION_BROKEN
, RCBROKEN
);
2946 case SCF_ERROR_DELETED
:
2947 err
= mc_error_create(err
,
2948 SCF_ERROR_NOT_FOUND
,
2949 "Could not find property group \"%s\"",
2950 pg
== NULL
? SCF_PG_METHOD_CONTEXT
: mname
);
2953 case SCF_ERROR_NOT_SET
:
2955 bad_fail("scf_property_type", ret
);
2961 if (ty
!= SCF_TYPE_BOOLEAN
) {
2962 err
= mc_error_create(err
,
2963 SCF_ERROR_TYPE_MISMATCH
,
2964 "\"%s\" property is not boolean in property group "
2965 "\"%s\".", SCF_PROPERTY_USE_PROFILE
,
2966 pg
== NULL
? SCF_PG_METHOD_CONTEXT
: mname
);
2970 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
2973 case SCF_ERROR_CONNECTION_BROKEN
:
2974 err
= mc_error_create(err
,
2975 SCF_ERROR_CONNECTION_BROKEN
, RCBROKEN
);
2978 case SCF_ERROR_CONSTRAINT_VIOLATED
:
2979 err
= mc_error_create(err
,
2980 SCF_ERROR_CONSTRAINT_VIOLATED
,
2981 "\"%s\" property has multiple values.",
2982 SCF_PROPERTY_USE_PROFILE
);
2985 case SCF_ERROR_NOT_FOUND
:
2986 err
= mc_error_create(err
,
2987 SCF_ERROR_NOT_FOUND
,
2988 "\"%s\" property has no values.",
2989 SCF_PROPERTY_USE_PROFILE
);
2992 bad_fail("scf_property_get_value", ret
);
2999 ret
= scf_value_get_boolean(val
, &use_profile
);
3000 assert(ret
== SCF_SUCCESS
);
3002 /* get ids & privileges */
3004 err
= get_profile(pg
, instpg
, prop
, val
, cmdline
,
3007 err
= get_ids(pg
, instpg
, prop
, val
, cip
, err
);
3013 /* get working directory */
3014 if ((methpg
!= NULL
&& scf_pg_get_property(methpg
,
3015 SCF_PROPERTY_WORKING_DIRECTORY
, prop
) == SCF_SUCCESS
) ||
3016 (instpg
!= NULL
&& scf_pg_get_property(instpg
,
3017 SCF_PROPERTY_WORKING_DIRECTORY
, prop
) == SCF_SUCCESS
)) {
3018 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
3021 case SCF_ERROR_CONNECTION_BROKEN
:
3022 err
= mc_error_create(err
, ret
, RCBROKEN
);
3025 case SCF_ERROR_CONSTRAINT_VIOLATED
:
3026 err
= mc_error_create(err
, ret
,
3027 "\"%s\" property has multiple values.",
3028 SCF_PROPERTY_WORKING_DIRECTORY
);
3031 case SCF_ERROR_NOT_FOUND
:
3032 err
= mc_error_create(err
, ret
,
3033 "\"%s\" property has no values.",
3034 SCF_PROPERTY_WORKING_DIRECTORY
);
3038 bad_fail("scf_property_get_value", ret
);
3045 ret
= scf_value_get_astring(val
, cip
->vbuf
, cip
->vbuf_sz
);
3050 case SCF_ERROR_NOT_FOUND
:
3051 /* okay if missing. */
3052 (void) strcpy(cip
->vbuf
, ":default");
3055 case SCF_ERROR_CONNECTION_BROKEN
:
3056 err
= mc_error_create(err
, ret
, RCBROKEN
);
3059 case SCF_ERROR_DELETED
:
3060 err
= mc_error_create(err
, ret
,
3061 "Property group could not be found");
3064 case SCF_ERROR_HANDLE_MISMATCH
:
3065 case SCF_ERROR_INVALID_ARGUMENT
:
3066 case SCF_ERROR_NOT_SET
:
3068 bad_fail("scf_pg_get_property", ret
);
3072 if (strcmp(cip
->vbuf
, ":default") == 0 ||
3073 strcmp(cip
->vbuf
, ":home") == 0) {
3074 switch (ret
= lookup_pwd(cip
)) {
3079 err
= mc_error_create(err
, ret
, "Out of memory.");
3086 err
= mc_error_create(err
, ret
,
3087 "Could not get passwd entry.");
3091 bad_fail("lookup_pwd", ret
);
3094 cip
->working_dir
= strdup(cip
->pwd
.pw_dir
);
3095 if (cip
->working_dir
== NULL
) {
3096 err
= mc_error_create(err
, ENOMEM
, ALLOCFAIL
);
3100 cip
->working_dir
= strdup(cip
->vbuf
);
3101 if (cip
->working_dir
== NULL
) {
3102 err
= mc_error_create(err
, ENOMEM
, ALLOCFAIL
);
3107 /* get security flags */
3108 if ((methpg
!= NULL
&& scf_pg_get_property(methpg
,
3109 SCF_PROPERTY_SECFLAGS
, prop
) == SCF_SUCCESS
) ||
3110 (instpg
!= NULL
&& scf_pg_get_property(instpg
,
3111 SCF_PROPERTY_SECFLAGS
, prop
) == SCF_SUCCESS
)) {
3112 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
3115 case SCF_ERROR_CONNECTION_BROKEN
:
3116 err
= mc_error_create(err
, ret
, RCBROKEN
);
3119 case SCF_ERROR_CONSTRAINT_VIOLATED
:
3120 err
= mc_error_create(err
, ret
,
3121 "\"%s\" property has multiple values.",
3122 SCF_PROPERTY_SECFLAGS
);
3125 case SCF_ERROR_NOT_FOUND
:
3126 err
= mc_error_create(err
, ret
,
3127 "\"%s\" property has no values.",
3128 SCF_PROPERTY_SECFLAGS
);
3132 bad_fail("scf_property_get_value", ret
);
3135 (void) strlcpy(cip
->vbuf
, ":default", cip
->vbuf_sz
);
3137 ret
= scf_value_get_astring(val
, cip
->vbuf
,
3145 case SCF_ERROR_NOT_FOUND
:
3146 /* okay if missing. */
3147 (void) strlcpy(cip
->vbuf
, ":default", cip
->vbuf_sz
);
3150 case SCF_ERROR_CONNECTION_BROKEN
:
3151 err
= mc_error_create(err
, ret
, RCBROKEN
);
3154 case SCF_ERROR_DELETED
:
3155 err
= mc_error_create(err
, ret
,
3156 "Property group could not be found");
3159 case SCF_ERROR_HANDLE_MISMATCH
:
3160 case SCF_ERROR_INVALID_ARGUMENT
:
3161 case SCF_ERROR_NOT_SET
:
3163 bad_fail("scf_pg_get_property", ret
);
3168 if (scf_default_secflags(h
, &cip
->def_secflags
) != 0) {
3169 err
= mc_error_create(err
, EINVAL
, "couldn't fetch "
3170 "default security-flags");
3174 if (strcmp(cip
->vbuf
, ":default") != 0) {
3175 if (secflags_parse(NULL
, cip
->vbuf
,
3176 &cip
->secflag_delta
) != 0) {
3177 err
= mc_error_create(err
, EINVAL
, "couldn't parse "
3178 "security flags: %s", cip
->vbuf
);
3183 /* get (optional) corefile pattern */
3184 if ((methpg
!= NULL
&& scf_pg_get_property(methpg
,
3185 SCF_PROPERTY_COREFILE_PATTERN
, prop
) == SCF_SUCCESS
) ||
3186 (instpg
!= NULL
&& scf_pg_get_property(instpg
,
3187 SCF_PROPERTY_COREFILE_PATTERN
, prop
) == SCF_SUCCESS
)) {
3188 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
3191 case SCF_ERROR_CONNECTION_BROKEN
:
3192 err
= mc_error_create(err
, ret
, RCBROKEN
);
3195 case SCF_ERROR_CONSTRAINT_VIOLATED
:
3196 err
= mc_error_create(err
, ret
,
3197 "\"%s\" property has multiple values.",
3198 SCF_PROPERTY_COREFILE_PATTERN
);
3201 case SCF_ERROR_NOT_FOUND
:
3202 err
= mc_error_create(err
, ret
,
3203 "\"%s\" property has no values.",
3204 SCF_PROPERTY_COREFILE_PATTERN
);
3208 bad_fail("scf_property_get_value", ret
);
3213 ret
= scf_value_get_astring(val
, cip
->vbuf
,
3217 cip
->corefile_pattern
= strdup(cip
->vbuf
);
3218 if (cip
->corefile_pattern
== NULL
) {
3219 err
= mc_error_create(err
, ENOMEM
, ALLOCFAIL
);
3228 case SCF_ERROR_NOT_FOUND
:
3229 /* okay if missing. */
3232 case SCF_ERROR_CONNECTION_BROKEN
:
3233 err
= mc_error_create(err
, ret
, RCBROKEN
);
3236 case SCF_ERROR_DELETED
:
3237 err
= mc_error_create(err
, ret
,
3238 "Property group could not be found");
3241 case SCF_ERROR_HANDLE_MISMATCH
:
3242 case SCF_ERROR_INVALID_ARGUMENT
:
3243 case SCF_ERROR_NOT_SET
:
3245 bad_fail("scf_pg_get_property", ret
);
3249 if (restarter_rm_libs_loadable()) {
3251 if ((methpg
!= NULL
&& scf_pg_get_property(methpg
,
3252 SCF_PROPERTY_PROJECT
, prop
) == SCF_SUCCESS
) ||
3253 (instpg
!= NULL
&& scf_pg_get_property(instpg
,
3254 SCF_PROPERTY_PROJECT
, prop
) == SCF_SUCCESS
)) {
3255 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
3258 case SCF_ERROR_CONNECTION_BROKEN
:
3259 err
= mc_error_create(err
, ret
,
3263 case SCF_ERROR_CONSTRAINT_VIOLATED
:
3264 err
= mc_error_create(err
, ret
,
3265 "\"%s\" property has multiple "
3266 "values.", SCF_PROPERTY_PROJECT
);
3269 case SCF_ERROR_NOT_FOUND
:
3270 err
= mc_error_create(err
, ret
,
3271 "\"%s\" property has no values.",
3272 SCF_PROPERTY_PROJECT
);
3276 bad_fail("scf_property_get_value", ret
);
3279 (void) strcpy(cip
->vbuf
, ":default");
3281 ret
= scf_value_get_astring(val
, cip
->vbuf
,
3288 (void) strcpy(cip
->vbuf
, ":default");
3291 switch (ret
= get_projid(cip
->vbuf
, cip
)) {
3296 err
= mc_error_create(err
, ret
, "Out of memory.");
3300 err
= mc_error_create(err
, ret
,
3301 "Missing passwd or project entry for \"%s\".",
3306 err
= mc_error_create(err
, ret
, "I/O error.");
3311 err
= mc_error_create(err
, ret
,
3312 "Out of file descriptors.");
3316 err
= mc_error_create(err
, ret
,
3317 "Name service switch is misconfigured.");
3322 err
= mc_error_create(err
, ret
,
3323 "Project ID \"%s\" too big.", cip
->vbuf
);
3327 err
= mc_error_create(err
, ret
,
3328 "Project ID \"%s\" is invalid.", cip
->vbuf
);
3332 bad_fail("get_projid", ret
);
3335 /* get resource pool */
3336 if ((methpg
!= NULL
&& scf_pg_get_property(methpg
,
3337 SCF_PROPERTY_RESOURCE_POOL
, prop
) == SCF_SUCCESS
) ||
3338 (instpg
!= NULL
&& scf_pg_get_property(instpg
,
3339 SCF_PROPERTY_RESOURCE_POOL
, prop
) == SCF_SUCCESS
)) {
3340 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
3343 case SCF_ERROR_CONNECTION_BROKEN
:
3344 err
= mc_error_create(err
, ret
,
3348 case SCF_ERROR_CONSTRAINT_VIOLATED
:
3349 err
= mc_error_create(err
, ret
,
3350 "\"%s\" property has multiple "
3352 SCF_PROPERTY_RESOURCE_POOL
);
3355 case SCF_ERROR_NOT_FOUND
:
3356 err
= mc_error_create(err
, ret
,
3357 "\"%s\" property has no "
3359 SCF_PROPERTY_RESOURCE_POOL
);
3363 bad_fail("scf_property_get_value", ret
);
3366 (void) strcpy(cip
->vbuf
, ":default");
3368 ret
= scf_value_get_astring(val
, cip
->vbuf
,
3377 case SCF_ERROR_NOT_FOUND
:
3378 /* okay if missing. */
3379 (void) strcpy(cip
->vbuf
, ":default");
3382 case SCF_ERROR_CONNECTION_BROKEN
:
3383 err
= mc_error_create(err
, ret
, RCBROKEN
);
3386 case SCF_ERROR_DELETED
:
3387 err
= mc_error_create(err
, ret
,
3388 "property group could not be found.");
3391 case SCF_ERROR_HANDLE_MISMATCH
:
3392 case SCF_ERROR_INVALID_ARGUMENT
:
3393 case SCF_ERROR_NOT_SET
:
3395 bad_fail("scf_pg_get_property", ret
);
3399 if (strcmp(cip
->vbuf
, ":default") != 0) {
3400 cip
->resource_pool
= strdup(cip
->vbuf
);
3401 if (cip
->resource_pool
== NULL
) {
3402 err
= mc_error_create(err
, ENOMEM
, ALLOCFAIL
);
3409 * A method_context was not used for any configurable
3410 * elements or attributes, so reset and use the simple
3411 * defaults that provide historic init behavior.
3416 free(cip
->working_dir
);
3418 (void) memset(cip
, 0, sizeof (*cip
));
3421 cip
->euid
= (uid_t
)-1;
3422 cip
->egid
= (gid_t
)-1;
3424 if (scf_default_secflags(h
, &cip
->def_secflags
) != 0) {
3425 err
= mc_error_create(err
, EINVAL
, "couldn't fetch "
3426 "default security-flags");
3434 (void) scf_value_destroy(val
);
3435 scf_property_destroy(prop
);
3436 scf_pg_destroy(instpg
);
3437 scf_pg_destroy(methpg
);
3439 if (cip
->pwbuf
!= NULL
) {
3446 if (err
->type
!= 0) {
3447 restarter_free_method_context(cip
);
3449 restarter_mc_error_destroy(err
);
3457 * Modify the current process per the given method_context. On success, returns
3458 * 0. Note that the environment is not modified by this function to include the
3459 * environment variables in cip->env.
3461 * On failure, sets *fp to NULL or the name of the function which failed,
3462 * and returns one of the following error codes. The words in parentheses are
3463 * the values to which *fp may be set for the error case.
3464 * ENOMEM - malloc() failed
3465 * EIO - an I/O error occurred (getpwuid_r, chdir)
3466 * EMFILE - process is out of file descriptors (getpwuid_r)
3467 * ENFILE - system is out of file handles (getpwuid_r)
3468 * EINVAL - gid or egid is out of range (setregid)
3469 * ngroups is too big (setgroups)
3470 * project's project id is bad (setproject)
3471 * uid or euid is out of range (setreuid)
3472 * poolname is invalid (pool_set_binding)
3473 * EPERM - insufficient privilege (setregid, initgroups, setgroups, setppriv,
3474 * setproject, setreuid, settaskid)
3475 * ENOENT - uid has a passwd entry but no shadow entry
3476 * working_dir does not exist (chdir)
3477 * uid has no passwd entry
3478 * the pool could not be found (pool_set_binding)
3479 * EFAULT - lpriv_set or priv_set has a bad address (setppriv)
3480 * working_dir has a bad address (chdir)
3481 * EACCES - could not access working_dir (chdir)
3482 * in a TASK_FINAL task (setproject, settaskid)
3483 * no resource pool accepting default binding exists (setproject)
3484 * ELOOP - too many symbolic links in working_dir (chdir)
3485 * ENAMETOOLONG - working_dir is too long (chdir)
3486 * ENOLINK - working_dir is on an inaccessible remote machine (chdir)
3487 * ENOTDIR - working_dir is not a directory (chdir)
3488 * ESRCH - uid is not a user of project (setproject)
3489 * project is invalid (setproject)
3490 * the resource pool specified for project is unknown (setproject)
3491 * EBADF - the configuration for the pool is invalid (pool_set_binding)
3492 * -1 - core_set_process_path() failed (core_set_process_path)
3493 * a resource control assignment failed (setproject)
3494 * a system error occurred during pool_set_binding (pool_set_binding)
3497 restarter_set_method_context(struct method_context
*cip
, const char **fp
)
3505 if (cip
->gid
!= (gid_t
)-1) {
3506 if (setregid(cip
->gid
,
3507 cip
->egid
!= (gid_t
)-1 ? cip
->egid
: cip
->gid
) != 0) {
3511 assert(ret
== EINVAL
|| ret
== EPERM
);
3515 if (cip
->pwbuf
== NULL
) {
3516 switch (ret
= lookup_pwd(cip
)) {
3532 bad_fail("lookup_pwd", ret
);
3536 if (setregid(cip
->pwd
.pw_gid
,
3537 cip
->egid
!= (gid_t
)-1 ?
3538 cip
->egid
: cip
->pwd
.pw_gid
) != 0) {
3542 assert(ret
== EINVAL
|| ret
== EPERM
);
3547 if (cip
->ngroups
== -1) {
3548 if (cip
->pwbuf
== NULL
) {
3549 switch (ret
= lookup_pwd(cip
)) {
3565 bad_fail("lookup_pwd", ret
);
3569 /* Ok if cip->gid == -1 */
3570 if (initgroups(cip
->pwd
.pw_name
, cip
->gid
) != 0) {
3573 assert(ret
== EPERM
);
3576 } else if (cip
->ngroups
> 0 &&
3577 setgroups(cip
->ngroups
, cip
->groups
) != 0) {
3581 assert(ret
== EINVAL
|| ret
== EPERM
);
3585 if (cip
->corefile_pattern
!= NULL
) {
3588 if (core_set_process_path(cip
->corefile_pattern
,
3589 strlen(cip
->corefile_pattern
) + 1, mypid
) != 0) {
3590 *fp
= "core_set_process_path";
3597 if (psecflags(P_PID
, P_MYID
, PSF_INHERIT
,
3598 &cip
->def_secflags
.ss_default
) != 0) {
3599 *fp
= "psecflags (default inherit)";
3604 if (psecflags(P_PID
, P_MYID
, PSF_LOWER
,
3605 &cip
->def_secflags
.ss_lower
) != 0) {
3606 *fp
= "psecflags (default lower)";
3611 if (psecflags(P_PID
, P_MYID
, PSF_UPPER
,
3612 &cip
->def_secflags
.ss_upper
) != 0) {
3613 *fp
= "psecflags (default upper)";
3618 if (psecflags(P_PID
, P_MYID
, PSF_INHERIT
,
3619 &cip
->secflag_delta
) != 0) {
3620 *fp
= "psecflags (from manifest)";
3625 if (restarter_rm_libs_loadable()) {
3626 if (cip
->project
== NULL
) {
3627 if (settaskid(getprojid(), TASK_NORMAL
) == -1) {
3637 bad_fail("settaskid", errno
);
3641 switch (ret
= lookup_pwd(cip
)) {
3657 bad_fail("lookup_pwd", ret
);
3662 switch (setproject(cip
->project
, cip
->pwd
.pw_name
,
3667 case SETPROJ_ERR_TASK
:
3668 case SETPROJ_ERR_POOL
:
3678 if (cip
->resource_pool
!= NULL
) {
3682 *fp
= "pool_set_binding";
3684 if (pool_set_binding(cip
->resource_pool
, P_PID
,
3685 mypid
) != PO_SUCCESS
) {
3686 switch (pool_error()) {
3687 case POE_INVALID_SEARCH
:
3695 case POE_INVALID_CONF
:
3704 bad_fail("pool_set_binding",
3714 * Now, we have to assume our ID. If the UID is 0, we want it to be
3715 * privilege-aware, otherwise the limit set gets used instead of E/P.
3716 * We can do this by setting P as well, which keeps
3717 * PA status (see priv_can_clear_PA()).
3722 if (cip
->lpriv_set
!= NULL
) {
3723 if (setppriv(PRIV_SET
, PRIV_LIMIT
, cip
->lpriv_set
) != 0) {
3725 assert(ret
== EFAULT
|| ret
== EPERM
);
3729 if (cip
->priv_set
!= NULL
) {
3730 if (setppriv(PRIV_SET
, PRIV_INHERITABLE
, cip
->priv_set
) != 0) {
3732 assert(ret
== EFAULT
|| ret
== EPERM
);
3738 * If the limit privset is already set, then must be privilege
3739 * aware. Otherwise, don't assume anything, and force privilege
3743 if (cip
->lpriv_set
== NULL
&& cip
->priv_set
!= NULL
) {
3744 ret
= setpflags(PRIV_AWARE
, 1);
3749 if (setreuid(cip
->uid
,
3750 cip
->euid
!= (uid_t
)-1 ? cip
->euid
: cip
->uid
) != 0) {
3752 assert(ret
== EINVAL
|| ret
== EPERM
);
3757 if (cip
->priv_set
!= NULL
) {
3758 if (setppriv(PRIV_SET
, PRIV_PERMITTED
, cip
->priv_set
) != 0) {
3760 assert(ret
== EFAULT
|| ret
== EPERM
);
3766 * The last thing to do is chdir to the specified working directory.
3767 * This should come after the uid switching as only the user might
3768 * have access to the specified directory.
3770 if (cip
->working_dir
!= NULL
) {
3772 r
= chdir(cip
->working_dir
);
3773 } while (r
!= 0 && errno
== EINTR
);
3789 restarter_free_method_context(struct method_context
*mcp
)
3793 if (mcp
->lpriv_set
!= NULL
)
3794 priv_freeset(mcp
->lpriv_set
);
3795 if (mcp
->priv_set
!= NULL
)
3796 priv_freeset(mcp
->priv_set
);
3798 if (mcp
->env
!= NULL
) {
3799 for (i
= 0; i
< mcp
->env_sz
; i
++)
3804 free(mcp
->working_dir
);
3805 free(mcp
->corefile_pattern
);
3807 free(mcp
->resource_pool
);
3812 * Method keyword functions
3816 restarter_is_null_method(const char *meth
)
3818 return (strcmp(meth
, MKW_TRUE
) == 0);
3822 is_kill_method(const char *method
, const char *kill_str
,
3823 size_t kill_str_len
)
3828 if (strncmp(method
, kill_str
, kill_str_len
) != 0 ||
3829 (method
[kill_str_len
] != '\0' &&
3830 !isspace(method
[kill_str_len
])))
3833 cp
= method
+ kill_str_len
;
3834 while (*cp
!= '\0' && isspace(*cp
))
3843 return (str2sig(cp
+ 1, &sig
) == 0 ? sig
: -1);
3847 restarter_is_kill_proc_method(const char *method
)
3849 return (is_kill_method(method
, MKW_KILL_PROC
,
3850 sizeof (MKW_KILL_PROC
) - 1));
3854 restarter_is_kill_method(const char *method
)
3856 return (is_kill_method(method
, MKW_KILL
, sizeof (MKW_KILL
) - 1));
3865 restarter_event_get_enabled(restarter_event_t
*e
)
3872 restarter_event_get_seq(restarter_event_t
*e
)
3879 restarter_event_get_time(restarter_event_t
*e
, hrtime_t
*time
)
3884 * Check for and validate fmri specified in restarter_actions/auxiliary_fmri
3889 restarter_inst_validate_ractions_aux_fmri(scf_instance_t
*inst
)
3892 scf_propertygroup_t
*pg
;
3893 scf_property_t
*prop
;
3896 size_t size
= scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
3899 if ((aux_fmri
= malloc(size
)) == NULL
)
3902 h
= scf_instance_handle(inst
);
3904 pg
= scf_pg_create(h
);
3905 prop
= scf_property_create(h
);
3906 val
= scf_value_create(h
);
3907 if (pg
== NULL
|| prop
== NULL
|| val
== NULL
)
3910 if (instance_get_or_add_pg(inst
, SCF_PG_RESTARTER_ACTIONS
,
3911 SCF_PG_RESTARTER_ACTIONS_TYPE
, SCF_PG_RESTARTER_ACTIONS_FLAGS
,
3915 if (get_astring_val(pg
, SCF_PROPERTY_AUX_FMRI
, aux_fmri
, size
,
3916 prop
, val
) != SCF_SUCCESS
)
3919 if (scf_parse_fmri(aux_fmri
, NULL
, NULL
, NULL
, NULL
, NULL
,
3920 NULL
) != SCF_SUCCESS
)
3927 scf_value_destroy(val
);
3928 scf_property_destroy(prop
);
3934 * Get instance's boolean value in restarter_actions/auxiliary_tty
3935 * Return -1 on failure
3938 restarter_inst_ractions_from_tty(scf_instance_t
*inst
)
3941 scf_propertygroup_t
*pg
;
3942 scf_property_t
*prop
;
3947 h
= scf_instance_handle(inst
);
3948 pg
= scf_pg_create(h
);
3949 prop
= scf_property_create(h
);
3950 val
= scf_value_create(h
);
3951 if (pg
== NULL
|| prop
== NULL
|| val
== NULL
)
3954 if (instance_get_or_add_pg(inst
, SCF_PG_RESTARTER_ACTIONS
,
3955 SCF_PG_RESTARTER_ACTIONS_TYPE
, SCF_PG_RESTARTER_ACTIONS_FLAGS
,
3959 if (get_boolean_val(pg
, SCF_PROPERTY_AUX_TTY
, &has_tty
, prop
,
3960 val
) != SCF_SUCCESS
)
3966 scf_value_destroy(val
);
3967 scf_property_destroy(prop
);
3973 * If the instance's dump-on-restart property exists, remove it and return true,
3974 * otherwise return false.
3977 restarter_inst_dump(scf_instance_t
*inst
)
3980 scf_propertygroup_t
*pg
;
3981 scf_property_t
*prop
;
3985 h
= scf_instance_handle(inst
);
3986 pg
= scf_pg_create(h
);
3987 prop
= scf_property_create(h
);
3988 val
= scf_value_create(h
);
3989 if (pg
== NULL
|| prop
== NULL
|| val
== NULL
)
3992 if (scf_instance_get_pg(inst
, SCF_PG_RESTARTER_ACTIONS
, pg
) !=
3994 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN
)
3999 if (scf_pg_get_property(pg
, SCF_PROPERTY_DODUMP
, prop
) != SCF_SUCCESS
) {
4000 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN
)
4007 if (scf_instance_delete_prop(inst
, SCF_PG_RESTARTER_ACTIONS
,
4008 SCF_PROPERTY_DODUMP
) != SCF_SUCCESS
) {
4009 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN
)
4015 scf_value_destroy(val
);
4016 scf_property_destroy(prop
);
4022 restarter_inst_set_astring_prop(scf_instance_t
*inst
, const char *pgname
,
4023 const char *pgtype
, uint32_t pgflags
, const char *pname
, const char *str
)
4026 scf_propertygroup_t
*pg
;
4027 scf_transaction_t
*t
;
4028 scf_transaction_entry_t
*e
;
4032 h
= scf_instance_handle(inst
);
4034 pg
= scf_pg_create(h
);
4035 t
= scf_transaction_create(h
);
4036 e
= scf_entry_create(h
);
4037 v
= scf_value_create(h
);
4038 if (pg
== NULL
|| t
== NULL
|| e
== NULL
|| v
== NULL
)
4041 if (instance_get_or_add_pg(inst
, pgname
, pgtype
, pgflags
, pg
))
4044 if (scf_value_set_astring(v
, str
) != SCF_SUCCESS
)
4048 if (scf_transaction_start(t
, pg
) != 0)
4051 if (tx_set_value(t
, e
, pname
, SCF_TYPE_ASTRING
, v
) != 0)
4054 if ((r
= scf_transaction_commit(t
)) == 1)
4060 scf_transaction_reset(t
);
4061 if (scf_pg_update(pg
) == -1)
4067 scf_transaction_destroy(t
);
4068 scf_entry_destroy(e
);
4069 scf_value_destroy(v
);
4076 restarter_inst_set_aux_fmri(scf_instance_t
*inst
)
4079 scf_propertygroup_t
*pg
;
4080 scf_property_t
*prop
;
4083 size_t size
= scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
4086 if ((aux_fmri
= malloc(size
)) == NULL
)
4089 h
= scf_instance_handle(inst
);
4091 pg
= scf_pg_create(h
);
4092 prop
= scf_property_create(h
);
4093 val
= scf_value_create(h
);
4094 if (pg
== NULL
|| prop
== NULL
|| val
== NULL
)
4098 * Get auxiliary_fmri value from restarter_actions pg
4100 if (instance_get_or_add_pg(inst
, SCF_PG_RESTARTER_ACTIONS
,
4101 SCF_PG_RESTARTER_ACTIONS_TYPE
, SCF_PG_RESTARTER_ACTIONS_FLAGS
,
4105 if (get_astring_val(pg
, SCF_PROPERTY_AUX_FMRI
, aux_fmri
, size
,
4106 prop
, val
) != SCF_SUCCESS
)
4110 * Populate restarter/auxiliary_fmri with the obtained fmri.
4112 ret
= restarter_inst_set_astring_prop(inst
, SCF_PG_RESTARTER
,
4113 SCF_PG_RESTARTER_TYPE
, SCF_PG_RESTARTER_FLAGS
,
4114 SCF_PROPERTY_AUX_FMRI
, aux_fmri
);
4118 scf_value_destroy(val
);
4119 scf_property_destroy(prop
);
4125 restarter_inst_reset_aux_fmri(scf_instance_t
*inst
)
4127 return (scf_instance_delete_prop(inst
,
4128 SCF_PG_RESTARTER
, SCF_PROPERTY_AUX_FMRI
));
4132 restarter_inst_reset_ractions_aux_fmri(scf_instance_t
*inst
)
4134 return (scf_instance_delete_prop(inst
,
4135 SCF_PG_RESTARTER_ACTIONS
, SCF_PROPERTY_AUX_FMRI
));