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) 2012, 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>
57 #include <sys/types.h>
62 #define min(a, b) ((a) > (b) ? (b) : (a))
64 #define MKW_TRUE ":true"
65 #define MKW_KILL ":kill"
66 #define MKW_KILL_PROC ":kill_process"
68 #define ALLOCFAIL ((char *)"Allocation failure.")
69 #define RCBROKEN ((char *)"Repository connection broken.")
71 #define MAX_COMMIT_RETRIES 10
72 #define MAX_COMMIT_RETRY_INT (5 * 1000000) /* 5 seconds */
73 #define INITIAL_COMMIT_RETRY_INT (10000) /* 1/100th second */
76 * bad_fail() catches bugs in this and lower layers by reporting supposedly
77 * impossible function failures. The NDEBUG case keeps the strings out of the
78 * library but still calls abort() so we can root-cause from the coredump.
81 #define bad_fail(func, err) { \
82 (void) fprintf(stderr, \
83 "At %s:%d, %s() failed with unexpected error %d. Aborting.\n", \
84 __FILE__, __LINE__, (func), (err)); \
88 #define bad_fail(func, err) abort()
91 struct restarter_event_handle
{
92 char *reh_restarter_name
;
93 char *reh_delegate_channel_name
;
94 evchan_t
*reh_delegate_channel
;
95 char *reh_delegate_subscriber_id
;
96 char *reh_master_channel_name
;
97 evchan_t
*reh_master_channel
;
98 char *reh_master_subscriber_id
;
99 int (*reh_handler
)(restarter_event_t
*);
102 struct restarter_event
{
103 sysevent_t
*re_sysevent
;
104 restarter_event_type_t re_type
;
105 char *re_instance_name
;
106 restarter_event_handle_t
*re_event_handle
;
107 restarter_instance_state_t re_state
;
108 restarter_instance_state_t re_next_state
;
112 * Long reasons must all parse/read correctly in the following contexts:
114 * "A service instance transitioned state: %s."
115 * "A service failed: %s."
117 * "The service transitioned state (%s) and ..."
119 * With the exception of restart_str_none they must also fit the following
122 * "An instance transitioned because %s, and ..."
123 * "An instance transitioned to <new-state> because %s, and ..."
125 * Note that whoever is rendering the long message must provide the
126 * terminal punctuation - don't include it here. Similarly, do not
127 * provide an initial capital letter in reason-long.
129 * The long reason strings are Volatile - within the grammatical constraints
130 * above we may improve them as need be. The intention is that a consumer
131 * may blindly render the string along the lines of the above examples,
132 * but has no other guarantees as to the exact wording. Long reasons
135 * We define revisions of the set of short reason strings in use. Within
136 * a given revision, all short reasons are Committed. Consumers must check
137 * the revision in use before relying on the semantics of the short reason
138 * codes - if the version exceeds that which they are familiar with they should
139 * fail gracefully. Having checked for version compatability, a consumer
142 * "short_reason_A iff semantic_A", provided:
144 * . the restarter uses this short reason code at all,
145 * . the short reason is not "none" (which a restarter could
146 * specifiy for any transition semantics)
148 * To split/refine such a Committed semantic_A into further cases,
149 * we are required to bump the revision number. This should be an
150 * infrequent occurence. If you bump the revision number you may
151 * need to make corresponding changes in any source that calls
152 * restarter_str_version (e.g., FMA event generation).
154 * To add additional reasons to the set you must also bump the version
159 * The following describes revision 0 of the set of transition reasons.
160 * Read the preceding block comment before making any changes.
162 static const struct restarter_state_transition_reason restarter_str
[] = {
164 * Any transition for which the restarter has not provided a reason.
169 "the restarter gave no reason"
173 * A transition to maintenance state due to a
174 * 'svcadm mark maintenance <fmri>'. *Not* used if the libscf
175 * interface smf_maintain_instance(3SCF) is used to request maintenance.
178 restarter_str_administrative_request
,
179 "administrative_request",
180 "maintenance was requested by an administrator"
184 * A transition to maintenance state if a repository inconsistency
185 * exists when the service/instance state is first read by startd
186 * into the graph engine (this can also happen during startd restart).
189 restarter_str_bad_repo_state
,
191 "an SMF repository inconsistecy exists"
195 * A transition 'maintenance -> uninitialized' resulting always
196 * from 'svcadm clear <fmri>'. *Not* used if the libscf interface
197 * smf_restore_instance(3SCF) is used.
200 restarter_str_clear_request
,
202 "maintenance clear was requested by an administrator"
206 * A transition 'online -> offline' due to a process core dump.
209 restarter_str_ct_ev_core
,
211 "a process dumped core"
215 * A transition 'online -> offline' due to an empty process contract,
216 * i.e., the last process in a contract type service has exited.
219 restarter_str_ct_ev_exit
,
221 "all processes in the service have exited"
225 * A transition 'online -> offline' due to a hardware error.
228 restarter_str_ct_ev_hwerr
,
230 "a process was killed due to uncorrectable hardware error"
234 * A transition 'online -> offline' due to a process in the service
235 * having received a fatal signal originating from outside the
236 * service process contract.
239 restarter_str_ct_ev_signal
,
241 "a process received a fatal signal from outside the service"
245 * A transition 'offline -> online' when all dependencies for the
246 * service have been met.
249 restarter_str_dependencies_satisfied
,
250 "dependencies_satisfied",
251 "all dependencies have been satisfied"
255 * A transition 'online -> offline' because some dependency for the
256 * service is no-longer met.
259 restarter_str_dependency_activity
,
260 "dependency_activity",
261 "a dependency activity required a stop"
265 * A transition to maintenance state due to a cycle in the
266 * service dependencies.
269 restarter_str_dependency_cycle
,
271 "a dependency cycle exists"
275 * A transition 'online -> offline -> disabled' due to a
276 * 'svcadm disable [-t] <fmri>' or smf_disable_instance(3SCF) call.
279 restarter_str_disable_request
,
281 "a disable was requested"
285 * A transition 'disabled -> offline' due to a
286 * 'svcadm enable [-t] <fmri>' or smf_enable_instance(3SCF) call.
289 restarter_str_enable_request
,
291 "an enable was requested"
295 * A transition to maintenance state when a method fails
296 * repeatedly for a retryable reason.
299 restarter_str_fault_threshold_reached
,
300 "fault_threshold_reached",
301 "a method is failing in a retryable manner but too often"
305 * A transition to uninitialized state when startd reads the service
306 * configuration and inserts it into the graph engine.
309 restarter_str_insert_in_graph
,
311 "the instance was inserted in the graph"
315 * A transition to maintenance state due to an invalid dependency
316 * declared for the service.
319 restarter_str_invalid_dependency
,
320 "invalid_dependency",
321 "a service has an invalid dependency"
325 * A transition to maintenance state because the service-declared
326 * restarter is invalid.
329 restarter_str_invalid_restarter
,
331 "the service restarter is invalid"
335 * A transition to maintenance state because a restarter method
336 * exited with one of SMF_EXIT_ERR_CONFIG, SMF_EXIT_ERR_NOSMF,
337 * SMF_EXIT_ERR_PERM, or SMF_EXIT_ERR_FATAL.
340 restarter_str_method_failed
,
342 "a start, stop or refresh method failed"
346 * A transition 'uninitialized -> {disabled|offline}' after
347 * "insert_in_graph" to match the state configured in the
351 restarter_str_per_configuration
,
353 "the SMF repository configuration specifies this state"
357 * Refresh requested - no state change.
360 restarter_str_refresh
,
362 "a refresh was requested (no change of state)"
366 * A transition 'online -> offline -> online' due to a
367 * 'svcadm restart <fmri> or equivlaent libscf API call.
368 * Both the 'online -> offline' and 'offline -> online' transtions
369 * specify this reason.
372 restarter_str_restart_request
,
374 "a restart was requested"
378 * A transition to maintenance state because the start method is
379 * being executed successfully but too frequently.
382 restarter_str_restarting_too_quickly
,
383 "restarting_too_quickly",
384 "the instance is restarting too quickly"
388 * A transition to maintenance state due a service requesting
389 * 'svcadm mark maintenance <fmri>' or equivalent libscf API call.
390 * A command line 'svcadm mark maintenance <fmri>' does not produce
391 * this reason - it produces administrative_request instead.
394 restarter_str_service_request
,
396 "maintenance was requested by another service"
400 * An instanced inserted into the graph at its existing state
401 * during a startd restart - no state change.
404 restarter_str_startd_restart
,
406 "the instance was inserted in the graph due to startd restart"
411 restarter_str_version(void)
413 return (RESTARTER_STRING_VERSION
);
417 restarter_get_str_short(restarter_str_t key
)
420 for (i
= 0; i
< sizeof (restarter_str
) /
421 sizeof (struct restarter_state_transition_reason
); i
++)
422 if (key
== restarter_str
[i
].str_key
)
423 return (restarter_str
[i
].str_short
);
428 restarter_get_str_long(restarter_str_t key
)
431 for (i
= 0; i
< sizeof (restarter_str
) /
432 sizeof (struct restarter_state_transition_reason
); i
++)
433 if (key
== restarter_str
[i
].str_key
)
434 return (dgettext(TEXT_DOMAIN
,
435 restarter_str
[i
].str_long
));
440 * A static no memory error message mc_error_t structure
441 * to be used in cases when memory errors are to be returned
442 * This avoids the need to attempt to allocate memory for the
443 * message, therefore getting into a cycle of no memory failures.
445 mc_error_t mc_nomem_err
= {
446 0, ENOMEM
, sizeof ("Out of memory") - 1, "Out of memory"
449 static const char * const allocfail
= "Allocation failure.\n";
450 static const char * const rcbroken
= "Repository connection broken.\n";
452 static int method_context_safety
= 0; /* Can safely call pools/projects. */
458 mc_error_create(mc_error_t
*e
, int type
, const char *format
, ...)
465 * If the type is ENOMEM and format is NULL, then
466 * go ahead and return the default nomem error.
467 * Otherwise, attempt to allocate the memory and if
468 * that fails then there is no reason to continue.
470 if (type
== ENOMEM
&& format
== NULL
)
471 return (&mc_nomem_err
);
473 if (e
== NULL
&& (le
= malloc(sizeof (mc_error_t
))) == NULL
)
474 return (&mc_nomem_err
);
480 va_start(args
, format
);
481 size
= vsnprintf(NULL
, 0, format
, args
) + 1;
482 if (size
>= RESTARTER_ERRMSGSZ
) {
483 if ((le
= realloc(e
, sizeof (mc_error_t
) +
484 (size
- RESTARTER_ERRMSGSZ
))) == NULL
) {
485 size
= RESTARTER_ERRMSGSZ
- 1;
491 (void) vsnprintf(le
->msg
, le
->size
, format
, args
);
498 restarter_mc_error_destroy(mc_error_t
*mc_err
)
504 * If the error messages was allocated then free.
506 if (mc_err
->destroy
) {
512 free_restarter_event_handle(struct restarter_event_handle
*h
)
518 * Just free the memory -- don't unbind the sysevent handle,
519 * as otherwise events may be lost if this is just a restarter
523 if (h
->reh_restarter_name
!= NULL
)
524 free(h
->reh_restarter_name
);
525 if (h
->reh_delegate_channel_name
!= NULL
)
526 free(h
->reh_delegate_channel_name
);
527 if (h
->reh_delegate_subscriber_id
!= NULL
)
528 free(h
->reh_delegate_subscriber_id
);
529 if (h
->reh_master_channel_name
!= NULL
)
530 free(h
->reh_master_channel_name
);
531 if (h
->reh_master_subscriber_id
!= NULL
)
532 free(h
->reh_master_subscriber_id
);
538 _restarter_get_channel_name(const char *fmri
, int type
)
541 char *chan_name
= malloc(MAX_CHNAME_LEN
);
545 if (chan_name
== NULL
)
548 if (type
== RESTARTER_CHANNEL_DELEGATE
)
549 (void) strcpy(prefix_name
, "d_");
550 else if (type
== RESTARTER_CHANNEL_MASTER
)
551 (void) strcpy(prefix_name
, "m_");
558 * Create a unique name
560 * Use the entire name, using a replacement of the /
561 * characters to get a better name.
563 * Remove the svc:/ from the beginning as this really
564 * isn't going to provide any uniqueness...
566 * An fmri name greater than MAX_CHNAME_LEN is going
567 * to be rejected as too long for the chan_name below
568 * in the snprintf call.
570 if ((name
= strdup(strchr(fmri
, '/') + 1)) == NULL
) {
576 if (name
[i
] == '/') {
584 * Should check for [a-z],[A-Z],[0-9],.,_,-,:
587 if (snprintf(chan_name
, MAX_CHNAME_LEN
, "com.sun:scf:%s%s",
588 prefix_name
, name
) > MAX_CHNAME_LEN
) {
598 cb(sysevent_t
*syse
, void *cookie
)
600 restarter_event_handle_t
*h
= (restarter_event_handle_t
*)cookie
;
601 restarter_event_t
*e
;
602 nvlist_t
*attr_list
= NULL
;
605 e
= uu_zalloc(sizeof (restarter_event_t
));
608 e
->re_event_handle
= h
;
609 e
->re_sysevent
= syse
;
611 if (sysevent_get_attr_list(syse
, &attr_list
) != 0)
614 if ((nvlist_lookup_uint32(attr_list
, RESTARTER_NAME_TYPE
,
615 &(e
->re_type
)) != 0) ||
616 (nvlist_lookup_string(attr_list
,
617 RESTARTER_NAME_INSTANCE
, &(e
->re_instance_name
)) != 0)) {
618 uu_warn("%s: Can't decode nvlist for event %p\n",
619 h
->reh_restarter_name
, (void *)syse
);
623 ret
= h
->reh_handler(e
);
627 nvlist_free(attr_list
);
632 * restarter_bind_handle(uint32_t, char *, int (*)(restarter_event_t *), int,
633 * restarter_event_handle_t **)
635 * Bind to a delegated restarter event channel.
636 * Each delegated restarter gets its own channel for resource management.
638 * Returns 0 on success or
639 * ENOTSUP version mismatch
640 * EINVAL restarter_name or event_handle is NULL
641 * ENOMEM out of memory, too many channels, or too many subscriptions
642 * EBUSY sysevent_evc_bind() could not establish binding
643 * EFAULT internal sysevent_evc_bind()/sysevent_evc_subscribe() error
644 * EMFILE out of file descriptors
645 * EPERM insufficient privilege for sysevent_evc_bind()
646 * EEXIST already subscribed
649 restarter_bind_handle(uint32_t version
, const char *restarter_name
,
650 int (*event_handler
)(restarter_event_t
*), int flags
,
651 restarter_event_handle_t
**rehp
)
653 restarter_event_handle_t
*h
;
657 if (version
!= RESTARTER_EVENT_VERSION
)
660 if (restarter_name
== NULL
|| event_handler
== NULL
)
663 if (flags
& RESTARTER_FLAG_DEBUG
)
666 if ((h
= uu_zalloc(sizeof (restarter_event_handle_t
))) == NULL
)
669 h
->reh_delegate_subscriber_id
= malloc(MAX_SUBID_LEN
);
670 h
->reh_master_subscriber_id
= malloc(MAX_SUBID_LEN
);
671 h
->reh_restarter_name
= strdup(restarter_name
);
672 if (h
->reh_delegate_subscriber_id
== NULL
||
673 h
->reh_master_subscriber_id
== NULL
||
674 h
->reh_restarter_name
== NULL
) {
675 free_restarter_event_handle(h
);
679 sz
= strlcpy(h
->reh_delegate_subscriber_id
, "del", MAX_SUBID_LEN
);
680 assert(sz
< MAX_SUBID_LEN
);
681 sz
= strlcpy(h
->reh_master_subscriber_id
, "master", MAX_SUBID_LEN
);
682 assert(sz
< MAX_SUBID_LEN
);
684 h
->reh_delegate_channel_name
=
685 _restarter_get_channel_name(restarter_name
,
686 RESTARTER_CHANNEL_DELEGATE
);
687 h
->reh_master_channel_name
=
688 _restarter_get_channel_name(restarter_name
,
689 RESTARTER_CHANNEL_MASTER
);
691 if (h
->reh_delegate_channel_name
== NULL
||
692 h
->reh_master_channel_name
== NULL
) {
693 free_restarter_event_handle(h
);
697 if (sysevent_evc_bind(h
->reh_delegate_channel_name
,
698 &h
->reh_delegate_channel
, EVCH_CREAT
|EVCH_HOLD_PEND
) != 0) {
700 assert(err
!= EINVAL
);
701 assert(err
!= ENOENT
);
702 free_restarter_event_handle(h
);
706 if (sysevent_evc_bind(h
->reh_master_channel_name
,
707 &h
->reh_master_channel
, EVCH_CREAT
|EVCH_HOLD_PEND
) != 0) {
709 assert(err
!= EINVAL
);
710 assert(err
!= ENOENT
);
711 free_restarter_event_handle(h
);
715 h
->reh_handler
= event_handler
;
717 assert(strlen(restarter_name
) <= MAX_CLASS_LEN
- 1);
718 assert(strlen(h
->reh_delegate_subscriber_id
) <= MAX_SUBID_LEN
- 1);
719 assert(strlen(h
->reh_master_subscriber_id
) <= MAX_SUBID_LEN
- 1);
721 if (sysevent_evc_subscribe(h
->reh_delegate_channel
,
722 h
->reh_delegate_subscriber_id
, EC_ALL
, cb
, h
, EVCH_SUB_KEEP
) != 0) {
724 assert(err
!= EINVAL
);
725 free_restarter_event_handle(h
);
733 restarter_event_handle_t
*
734 restarter_event_get_handle(restarter_event_t
*e
)
736 assert(e
!= NULL
&& e
->re_event_handle
!= NULL
);
737 return (e
->re_event_handle
);
740 restarter_event_type_t
741 restarter_event_get_type(restarter_event_t
*e
)
748 restarter_event_get_instance(restarter_event_t
*e
, char *inst
, size_t sz
)
750 assert(e
!= NULL
&& inst
!= NULL
);
751 return ((ssize_t
)strlcpy(inst
, e
->re_instance_name
, sz
));
755 restarter_event_get_current_states(restarter_event_t
*e
,
756 restarter_instance_state_t
*state
, restarter_instance_state_t
*next_state
)
760 *state
= e
->re_state
;
761 *next_state
= e
->re_next_state
;
766 * restarter_event_publish_retry() is a wrapper around sysevent_evc_publish().
767 * In case, the event cannot be sent at the first attempt (sysevent_evc_publish
768 * returned EAGAIN - sysevent queue full), this function retries a few time
769 * and return ENOSPC if it reaches the retry limit.
771 * The arguments to this function map the arguments of sysevent_evc_publish().
773 * On success, return 0. On error, return
775 * EFAULT - internal sysevent_evc_publish() error
776 * ENOMEM - internal sysevent_evc_publish() error
777 * EBADF - scp is invalid (sysevent_evc_publish() returned EINVAL)
778 * ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN)
781 restarter_event_publish_retry(evchan_t
*scp
, const char *class,
782 const char *subclass
, const char *vendor
, const char *pub_name
,
783 nvlist_t
*attr_list
, uint32_t flags
)
786 useconds_t retry_int
= INITIAL_COMMIT_RETRY_INT
;
788 for (retries
= 0; retries
< MAX_COMMIT_RETRIES
; retries
++) {
789 ret
= sysevent_evc_publish(scp
, class, subclass
, vendor
,
790 pub_name
, attr_list
, flags
);
797 (void) usleep(retry_int
);
799 retry_int
= min(retry_int
* 2, MAX_COMMIT_RETRY_INT
);
812 /* internal error - abort */
813 bad_fail("sysevent_evc_publish", ret
);
817 if (retries
== MAX_COMMIT_RETRIES
)
824 * Commit the state, next state, and auxiliary state into the repository.
825 * Let the graph engine know about the state change and error. On success,
826 * return 0. On error, return
827 * EPROTO - librestart compiled against different libscf
828 * ENOMEM - out of memory
829 * - repository server out of resources
830 * ENOTACTIVE - repository server not running
831 * ECONNABORTED - repository connection established, but then broken
832 * - unknown libscf error
833 * ENOENT - inst does not exist in the repository
834 * EPERM - insufficient permissions
835 * EACCESS - backend access denied
836 * EROFS - backend is readonly
837 * EFAULT - internal sysevent_evc_publish() error
838 * EBADF - h is invalid (sysevent_evc_publish() returned EINVAL)
839 * ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN)
842 restarter_set_states(restarter_event_handle_t
*h
, const char *inst
,
843 restarter_instance_state_t cur_state
,
844 restarter_instance_state_t new_cur_state
,
845 restarter_instance_state_t next_state
,
846 restarter_instance_state_t new_next_state
, restarter_error_t e
,
853 const char *p
= restarter_get_str_short(aux
);
855 assert(h
->reh_master_channel
!= NULL
);
856 assert(h
->reh_master_channel_name
!= NULL
);
857 assert(h
->reh_master_subscriber_id
!= NULL
);
859 if ((scf_h
= scf_handle_create(SCF_VERSION
)) == NULL
) {
860 switch (scf_error()) {
861 case SCF_ERROR_VERSION_MISMATCH
:
864 case SCF_ERROR_NO_MEMORY
:
868 bad_fail("scf_handle_create", scf_error());
872 if (scf_handle_bind(scf_h
) == -1) {
873 scf_handle_destroy(scf_h
);
874 switch (scf_error()) {
875 case SCF_ERROR_NO_SERVER
:
878 case SCF_ERROR_NO_RESOURCES
:
881 case SCF_ERROR_INVALID_ARGUMENT
:
882 case SCF_ERROR_IN_USE
:
884 bad_fail("scf_handle_bind", scf_error());
888 if (nvlist_alloc(&attr
, NV_UNIQUE_NAME
, 0) != 0 ||
889 nvlist_add_int32(attr
, RESTARTER_NAME_STATE
, new_cur_state
) != 0 ||
890 nvlist_add_int32(attr
, RESTARTER_NAME_NEXT_STATE
, new_next_state
)
892 nvlist_add_int32(attr
, RESTARTER_NAME_ERROR
, e
) != 0 ||
893 nvlist_add_string(attr
, RESTARTER_NAME_INSTANCE
, inst
) != 0 ||
894 nvlist_add_int32(attr
, RESTARTER_NAME_REASON
, aux
) != 0) {
898 id
.i_state
= cur_state
;
899 id
.i_next_state
= next_state
;
901 ret
= _restarter_commit_states(scf_h
, &id
, new_cur_state
,
905 ret
= restarter_event_publish_retry(
906 h
->reh_master_channel
, "master", "state_change",
907 "com.sun", "librestart", attr
, EVCH_NOSLEEP
);
912 (void) scf_handle_unbind(scf_h
);
913 scf_handle_destroy(scf_h
);
918 restarter_instance_state_t
919 restarter_string_to_state(char *string
)
921 assert(string
!= NULL
);
923 if (strcmp(string
, SCF_STATE_STRING_NONE
) == 0)
924 return (RESTARTER_STATE_NONE
);
925 else if (strcmp(string
, SCF_STATE_STRING_UNINIT
) == 0)
926 return (RESTARTER_STATE_UNINIT
);
927 else if (strcmp(string
, SCF_STATE_STRING_MAINT
) == 0)
928 return (RESTARTER_STATE_MAINT
);
929 else if (strcmp(string
, SCF_STATE_STRING_OFFLINE
) == 0)
930 return (RESTARTER_STATE_OFFLINE
);
931 else if (strcmp(string
, SCF_STATE_STRING_DISABLED
) == 0)
932 return (RESTARTER_STATE_DISABLED
);
933 else if (strcmp(string
, SCF_STATE_STRING_ONLINE
) == 0)
934 return (RESTARTER_STATE_ONLINE
);
935 else if (strcmp(string
, SCF_STATE_STRING_DEGRADED
) == 0)
936 return (RESTARTER_STATE_DEGRADED
);
938 return (RESTARTER_STATE_NONE
);
943 restarter_state_to_string(restarter_instance_state_t state
, char *string
,
946 assert(string
!= NULL
);
948 if (state
== RESTARTER_STATE_NONE
)
949 return ((ssize_t
)strlcpy(string
, SCF_STATE_STRING_NONE
, len
));
950 else if (state
== RESTARTER_STATE_UNINIT
)
951 return ((ssize_t
)strlcpy(string
, SCF_STATE_STRING_UNINIT
, len
));
952 else if (state
== RESTARTER_STATE_MAINT
)
953 return ((ssize_t
)strlcpy(string
, SCF_STATE_STRING_MAINT
, len
));
954 else if (state
== RESTARTER_STATE_OFFLINE
)
955 return ((ssize_t
)strlcpy(string
, SCF_STATE_STRING_OFFLINE
,
957 else if (state
== RESTARTER_STATE_DISABLED
)
958 return ((ssize_t
)strlcpy(string
, SCF_STATE_STRING_DISABLED
,
960 else if (state
== RESTARTER_STATE_ONLINE
)
961 return ((ssize_t
)strlcpy(string
, SCF_STATE_STRING_ONLINE
, len
));
962 else if (state
== RESTARTER_STATE_DEGRADED
)
963 return ((ssize_t
)strlcpy(string
, SCF_STATE_STRING_DEGRADED
,
966 return ((ssize_t
)strlcpy(string
, "unknown", len
));
970 * Sets pg to the name property group of s_inst. If it doesn't exist, it is
974 * ECONNABORTED - repository disconnection or unknown libscf error
975 * EBADF - inst is not set
976 * ECANCELED - inst is deleted
977 * EPERM - permission is denied
978 * EACCES - backend denied access
979 * EROFS - backend readonly
982 instance_get_or_add_pg(scf_instance_t
*inst
, const char *name
,
983 const char *type
, uint32_t flags
, scf_propertygroup_t
*pg
)
986 if (scf_instance_get_pg(inst
, name
, pg
) == 0)
989 switch (scf_error()) {
990 case SCF_ERROR_CONNECTION_BROKEN
:
992 return (ECONNABORTED
);
994 case SCF_ERROR_NOT_SET
:
997 case SCF_ERROR_DELETED
:
1000 case SCF_ERROR_NOT_FOUND
:
1003 case SCF_ERROR_HANDLE_MISMATCH
:
1004 case SCF_ERROR_INVALID_ARGUMENT
:
1005 bad_fail("scf_instance_get_pg", scf_error());
1008 if (scf_instance_add_pg(inst
, name
, type
, flags
, pg
) == 0)
1011 switch (scf_error()) {
1012 case SCF_ERROR_CONNECTION_BROKEN
:
1014 return (ECONNABORTED
);
1016 case SCF_ERROR_DELETED
:
1019 case SCF_ERROR_EXISTS
:
1022 case SCF_ERROR_PERMISSION_DENIED
:
1025 case SCF_ERROR_BACKEND_ACCESS
:
1028 case SCF_ERROR_BACKEND_READONLY
:
1031 case SCF_ERROR_HANDLE_MISMATCH
:
1032 case SCF_ERROR_INVALID_ARGUMENT
:
1033 case SCF_ERROR_NOT_SET
: /* should be caught above */
1034 bad_fail("scf_instance_add_pg", scf_error());
1043 * ECANCELED - pg was deleted
1046 tx_set_value(scf_transaction_t
*tx
, scf_transaction_entry_t
*ent
,
1047 const char *pname
, scf_type_t ty
, scf_value_t
*val
)
1052 if (scf_transaction_property_change_type(tx
, ent
, pname
,
1056 switch (scf_error()) {
1057 case SCF_ERROR_CONNECTION_BROKEN
:
1059 return (ECONNABORTED
);
1061 case SCF_ERROR_DELETED
:
1064 case SCF_ERROR_NOT_FOUND
:
1067 case SCF_ERROR_HANDLE_MISMATCH
:
1068 case SCF_ERROR_INVALID_ARGUMENT
:
1069 case SCF_ERROR_IN_USE
:
1070 case SCF_ERROR_NOT_SET
:
1071 bad_fail("scf_transaction_property_change_type",
1075 if (scf_transaction_property_new(tx
, ent
, pname
, ty
) == 0)
1078 switch (scf_error()) {
1079 case SCF_ERROR_CONNECTION_BROKEN
:
1081 return (ECONNABORTED
);
1083 case SCF_ERROR_DELETED
:
1086 case SCF_ERROR_EXISTS
:
1089 case SCF_ERROR_HANDLE_MISMATCH
:
1090 case SCF_ERROR_INVALID_ARGUMENT
:
1091 case SCF_ERROR_IN_USE
:
1092 case SCF_ERROR_NOT_SET
:
1093 bad_fail("scf_transaction_property_new", scf_error());
1097 r
= scf_entry_add_value(ent
, val
);
1104 * Commit new_state, new_next_state, and aux to the repository for id. If
1105 * successful, also set id's state and next-state as given, and return 0.
1107 * ENOMEM - out of memory
1108 * ECONNABORTED - repository connection broken
1109 * - unknown libscf error
1110 * EINVAL - id->i_fmri is invalid or not an instance FMRI
1111 * ENOENT - id->i_fmri does not exist
1112 * EPERM - insufficient permissions
1113 * EACCES - backend access denied
1114 * EROFS - backend is readonly
1117 _restarter_commit_states(scf_handle_t
*h
, instance_data_t
*id
,
1118 restarter_instance_state_t new_state
,
1119 restarter_instance_state_t new_state_next
, const char *aux
)
1121 char str_state
[MAX_SCF_STATE_STRING_SZ
];
1122 char str_new_state
[MAX_SCF_STATE_STRING_SZ
];
1123 char str_state_next
[MAX_SCF_STATE_STRING_SZ
];
1124 char str_new_state_next
[MAX_SCF_STATE_STRING_SZ
];
1129 scf_transaction_t
*t
= NULL
;
1130 scf_transaction_entry_t
*t_state
= NULL
, *t_state_next
= NULL
;
1131 scf_transaction_entry_t
*t_stime
= NULL
, *t_aux
= NULL
;
1132 scf_value_t
*v_state
= NULL
, *v_state_next
= NULL
, *v_stime
= NULL
;
1133 scf_value_t
*v_aux
= NULL
;
1134 scf_instance_t
*s_inst
= NULL
;
1135 scf_propertygroup_t
*pg
= NULL
;
1137 assert(new_state
!= RESTARTER_STATE_NONE
);
1139 if ((s_inst
= scf_instance_create(h
)) == NULL
||
1140 (pg
= scf_pg_create(h
)) == NULL
||
1141 (t
= scf_transaction_create(h
)) == NULL
||
1142 (t_state
= scf_entry_create(h
)) == NULL
||
1143 (t_state_next
= scf_entry_create(h
)) == NULL
||
1144 (t_stime
= scf_entry_create(h
)) == NULL
||
1145 (t_aux
= scf_entry_create(h
)) == NULL
||
1146 (v_state
= scf_value_create(h
)) == NULL
||
1147 (v_state_next
= scf_value_create(h
)) == NULL
||
1148 (v_stime
= scf_value_create(h
)) == NULL
||
1149 (v_aux
= scf_value_create(h
)) == NULL
) {
1154 sz
= restarter_state_to_string(new_state
, str_new_state
,
1155 sizeof (str_new_state
));
1156 assert(sz
< sizeof (str_new_state
));
1157 sz
= restarter_state_to_string(new_state_next
, str_new_state_next
,
1158 sizeof (str_new_state_next
));
1159 assert(sz
< sizeof (str_new_state_next
));
1160 sz
= restarter_state_to_string(id
->i_state
, str_state
,
1161 sizeof (str_state
));
1162 assert(sz
< sizeof (str_state
));
1163 sz
= restarter_state_to_string(id
->i_next_state
, str_state_next
,
1164 sizeof (str_state_next
));
1165 assert(sz
< sizeof (str_state_next
));
1167 ret
= gettimeofday(&now
, NULL
);
1170 if (scf_handle_decode_fmri(h
, id
->i_fmri
, NULL
, NULL
, s_inst
,
1171 NULL
, NULL
, SCF_DECODE_FMRI_EXACT
) == -1) {
1172 switch (scf_error()) {
1173 case SCF_ERROR_CONNECTION_BROKEN
:
1178 case SCF_ERROR_INVALID_ARGUMENT
:
1179 case SCF_ERROR_CONSTRAINT_VIOLATED
:
1183 case SCF_ERROR_NOT_FOUND
:
1187 case SCF_ERROR_HANDLE_MISMATCH
:
1188 bad_fail("scf_handle_decode_fmri", scf_error());
1194 if (scf_value_set_astring(v_state
, str_new_state
) != 0 ||
1195 scf_value_set_astring(v_state_next
, str_new_state_next
) != 0)
1196 bad_fail("scf_value_set_astring", scf_error());
1199 if (scf_value_set_astring(v_aux
, aux
) != 0)
1200 bad_fail("scf_value_set_astring", scf_error());
1203 if (scf_value_set_time(v_stime
, now
.tv_sec
, now
.tv_usec
* 1000) != 0)
1204 bad_fail("scf_value_set_time", scf_error());
1207 switch (r
= instance_get_or_add_pg(s_inst
, SCF_PG_RESTARTER
,
1208 SCF_PG_RESTARTER_TYPE
, SCF_PG_RESTARTER_FLAGS
, pg
)) {
1225 bad_fail("instance_get_or_add_pg", r
);
1229 if (scf_transaction_start(t
, pg
) != 0) {
1230 switch (scf_error()) {
1231 case SCF_ERROR_CONNECTION_BROKEN
:
1236 case SCF_ERROR_NOT_SET
:
1239 case SCF_ERROR_PERMISSION_DENIED
:
1243 case SCF_ERROR_BACKEND_ACCESS
:
1247 case SCF_ERROR_BACKEND_READONLY
:
1251 case SCF_ERROR_HANDLE_MISMATCH
:
1252 case SCF_ERROR_IN_USE
:
1253 bad_fail("scf_transaction_start", scf_error());
1257 if ((r
= tx_set_value(t
, t_state
, SCF_PROPERTY_STATE
,
1258 SCF_TYPE_ASTRING
, v_state
)) != 0 ||
1259 (r
= tx_set_value(t
, t_state_next
, SCF_PROPERTY_NEXT_STATE
,
1260 SCF_TYPE_ASTRING
, v_state_next
)) != 0 ||
1261 (r
= tx_set_value(t
, t_stime
, SCF_PROPERTY_STATE_TIMESTAMP
,
1262 SCF_TYPE_TIME
, v_stime
)) != 0) {
1269 scf_transaction_reset(t
);
1273 bad_fail("tx_set_value", r
);
1278 if ((r
= tx_set_value(t
, t_aux
, SCF_PROPERTY_AUX_STATE
,
1279 SCF_TYPE_ASTRING
, v_aux
)) != 0) {
1286 scf_transaction_reset(t
);
1290 bad_fail("tx_set_value", r
);
1295 ret
= scf_transaction_commit(t
);
1299 switch (scf_error()) {
1300 case SCF_ERROR_CONNECTION_BROKEN
:
1305 case SCF_ERROR_PERMISSION_DENIED
:
1309 case SCF_ERROR_BACKEND_ACCESS
:
1313 case SCF_ERROR_BACKEND_READONLY
:
1317 case SCF_ERROR_NOT_SET
:
1318 bad_fail("scf_transaction_commit", scf_error());
1322 scf_transaction_reset(t
);
1323 if (scf_pg_update(pg
) == -1) {
1324 switch (scf_error()) {
1325 case SCF_ERROR_CONNECTION_BROKEN
:
1330 case SCF_ERROR_NOT_SET
:
1336 id
->i_state
= new_state
;
1337 id
->i_next_state
= new_state_next
;
1341 scf_transaction_destroy(t
);
1342 scf_entry_destroy(t_state
);
1343 scf_entry_destroy(t_state_next
);
1344 scf_entry_destroy(t_stime
);
1345 scf_entry_destroy(t_aux
);
1346 scf_value_destroy(v_state
);
1347 scf_value_destroy(v_state_next
);
1348 scf_value_destroy(v_stime
);
1349 scf_value_destroy(v_aux
);
1351 scf_instance_destroy(s_inst
);
1358 * EINVAL - type is invalid
1360 * ECONNABORTED - repository connection broken
1361 * EBADF - s_inst is not set
1362 * ECANCELED - s_inst is deleted
1363 * EPERM - permission denied
1364 * EACCES - backend access denied
1365 * EROFS - backend readonly
1368 restarter_remove_contract(scf_instance_t
*s_inst
, ctid_t contract_id
,
1369 restarter_contract_type_t type
)
1372 scf_transaction_t
*t
= NULL
;
1373 scf_transaction_entry_t
*t_cid
= NULL
;
1374 scf_propertygroup_t
*pg
= NULL
;
1375 scf_property_t
*prop
= NULL
;
1377 scf_iter_t
*iter
= NULL
;
1379 int ret
= 0, primary
;
1383 case RESTARTER_CONTRACT_PRIMARY
:
1386 case RESTARTER_CONTRACT_TRANSIENT
:
1393 h
= scf_instance_handle(s_inst
);
1395 pg
= scf_pg_create(h
);
1396 prop
= scf_property_create(h
);
1397 iter
= scf_iter_create(h
);
1398 t
= scf_transaction_create(h
);
1400 if (pg
== NULL
|| prop
== NULL
|| iter
== NULL
|| t
== NULL
) {
1402 goto remove_contract_cleanup
;
1406 scf_transaction_destroy_children(t
);
1407 ret
= instance_get_or_add_pg(s_inst
, SCF_PG_RESTARTER
,
1408 SCF_PG_RESTARTER_TYPE
, SCF_PG_RESTARTER_FLAGS
, pg
);
1410 goto remove_contract_cleanup
;
1412 pname
= primary
? SCF_PROPERTY_CONTRACT
:
1413 SCF_PROPERTY_TRANSIENT_CONTRACT
;
1416 if (scf_transaction_start(t
, pg
) != 0) {
1417 switch (scf_error()) {
1418 case SCF_ERROR_CONNECTION_BROKEN
:
1421 goto remove_contract_cleanup
;
1423 case SCF_ERROR_DELETED
:
1426 case SCF_ERROR_PERMISSION_DENIED
:
1428 goto remove_contract_cleanup
;
1430 case SCF_ERROR_BACKEND_ACCESS
:
1432 goto remove_contract_cleanup
;
1434 case SCF_ERROR_BACKEND_READONLY
:
1436 goto remove_contract_cleanup
;
1438 case SCF_ERROR_HANDLE_MISMATCH
:
1439 case SCF_ERROR_IN_USE
:
1440 case SCF_ERROR_NOT_SET
:
1441 bad_fail("scf_transaction_start", scf_error());
1445 t_cid
= scf_entry_create(h
);
1447 if (scf_pg_get_property(pg
, pname
, prop
) == 0) {
1449 if (scf_transaction_property_change_type(t
, t_cid
,
1450 pname
, SCF_TYPE_COUNT
) != 0) {
1451 switch (scf_error()) {
1452 case SCF_ERROR_CONNECTION_BROKEN
:
1455 goto remove_contract_cleanup
;
1457 case SCF_ERROR_DELETED
:
1458 scf_entry_destroy(t_cid
);
1461 case SCF_ERROR_NOT_FOUND
:
1464 case SCF_ERROR_HANDLE_MISMATCH
:
1465 case SCF_ERROR_INVALID_ARGUMENT
:
1466 case SCF_ERROR_IN_USE
:
1467 case SCF_ERROR_NOT_SET
:
1469 "scf_transaction_property_changetype",
1474 if (scf_property_is_type(prop
, SCF_TYPE_COUNT
) == 0) {
1475 if (scf_iter_property_values(iter
, prop
) != 0) {
1476 switch (scf_error()) {
1477 case SCF_ERROR_CONNECTION_BROKEN
:
1480 goto remove_contract_cleanup
;
1482 case SCF_ERROR_NOT_SET
:
1483 case SCF_ERROR_HANDLE_MISMATCH
:
1485 "scf_iter_property_values",
1491 val
= scf_value_create(h
);
1493 assert(scf_error() ==
1494 SCF_ERROR_NO_MEMORY
);
1496 goto remove_contract_cleanup
;
1499 ret
= scf_iter_next_value(iter
, val
);
1501 switch (scf_error()) {
1502 case SCF_ERROR_CONNECTION_BROKEN
:
1504 goto remove_contract_cleanup
;
1506 case SCF_ERROR_DELETED
:
1507 scf_value_destroy(val
);
1510 case SCF_ERROR_HANDLE_MISMATCH
:
1511 case SCF_ERROR_INVALID_ARGUMENT
:
1512 case SCF_ERROR_PERMISSION_DENIED
:
1514 bad_fail("scf_iter_next_value",
1520 ret
= scf_value_get_count(val
, &c
);
1523 if (c
!= contract_id
) {
1524 ret
= scf_entry_add_value(t_cid
,
1528 scf_value_destroy(val
);
1534 scf_value_destroy(val
);
1536 switch (scf_error()) {
1537 case SCF_ERROR_CONNECTION_BROKEN
:
1540 goto remove_contract_cleanup
;
1542 case SCF_ERROR_TYPE_MISMATCH
:
1545 case SCF_ERROR_INVALID_ARGUMENT
:
1546 case SCF_ERROR_NOT_SET
:
1547 bad_fail("scf_property_is_type",
1552 switch (scf_error()) {
1553 case SCF_ERROR_CONNECTION_BROKEN
:
1556 goto remove_contract_cleanup
;
1558 case SCF_ERROR_DELETED
:
1559 scf_entry_destroy(t_cid
);
1562 case SCF_ERROR_NOT_FOUND
:
1565 case SCF_ERROR_HANDLE_MISMATCH
:
1566 case SCF_ERROR_INVALID_ARGUMENT
:
1567 case SCF_ERROR_NOT_SET
:
1568 bad_fail("scf_pg_get_property", scf_error());
1572 if (scf_transaction_property_new(t
, t_cid
, pname
,
1573 SCF_TYPE_COUNT
) != 0) {
1574 switch (scf_error()) {
1575 case SCF_ERROR_CONNECTION_BROKEN
:
1578 goto remove_contract_cleanup
;
1580 case SCF_ERROR_DELETED
:
1581 scf_entry_destroy(t_cid
);
1584 case SCF_ERROR_EXISTS
:
1587 case SCF_ERROR_HANDLE_MISMATCH
:
1588 case SCF_ERROR_INVALID_ARGUMENT
:
1589 case SCF_ERROR_NOT_SET
:
1590 bad_fail("scf_transaction_property_new",
1596 ret
= scf_transaction_commit(t
);
1598 switch (scf_error()) {
1599 case SCF_ERROR_CONNECTION_BROKEN
:
1602 goto remove_contract_cleanup
;
1604 case SCF_ERROR_DELETED
:
1607 case SCF_ERROR_PERMISSION_DENIED
:
1609 goto remove_contract_cleanup
;
1611 case SCF_ERROR_BACKEND_ACCESS
:
1613 goto remove_contract_cleanup
;
1615 case SCF_ERROR_BACKEND_READONLY
:
1617 goto remove_contract_cleanup
;
1619 case SCF_ERROR_NOT_SET
:
1620 bad_fail("scf_transaction_commit", scf_error());
1628 scf_transaction_destroy_children(t
);
1629 if (scf_pg_update(pg
) == -1) {
1630 switch (scf_error()) {
1631 case SCF_ERROR_CONNECTION_BROKEN
:
1634 goto remove_contract_cleanup
;
1636 case SCF_ERROR_DELETED
:
1639 case SCF_ERROR_NOT_SET
:
1640 bad_fail("scf_pg_update", scf_error());
1645 remove_contract_cleanup
:
1646 scf_transaction_destroy_children(t
);
1647 scf_transaction_destroy(t
);
1648 scf_iter_destroy(iter
);
1649 scf_property_destroy(prop
);
1657 * EINVAL - type is invalid
1659 * ECONNABORTED - repository disconnection
1660 * EBADF - s_inst is not set
1661 * ECANCELED - s_inst is deleted
1667 restarter_store_contract(scf_instance_t
*s_inst
, ctid_t contract_id
,
1668 restarter_contract_type_t type
)
1671 scf_transaction_t
*t
= NULL
;
1672 scf_transaction_entry_t
*t_cid
= NULL
;
1674 scf_propertygroup_t
*pg
= NULL
;
1675 scf_property_t
*prop
= NULL
;
1676 scf_iter_t
*iter
= NULL
;
1678 int ret
= 0, primary
;
1680 if (type
== RESTARTER_CONTRACT_PRIMARY
)
1682 else if (type
== RESTARTER_CONTRACT_TRANSIENT
)
1687 h
= scf_instance_handle(s_inst
);
1689 pg
= scf_pg_create(h
);
1690 prop
= scf_property_create(h
);
1691 iter
= scf_iter_create(h
);
1692 t
= scf_transaction_create(h
);
1694 if (pg
== NULL
|| prop
== NULL
|| iter
== NULL
|| t
== NULL
) {
1700 scf_transaction_destroy_children(t
);
1701 ret
= instance_get_or_add_pg(s_inst
, SCF_PG_RESTARTER
,
1702 SCF_PG_RESTARTER_TYPE
, SCF_PG_RESTARTER_FLAGS
, pg
);
1706 pname
= primary
? SCF_PROPERTY_CONTRACT
:
1707 SCF_PROPERTY_TRANSIENT_CONTRACT
;
1710 if (scf_transaction_start(t
, pg
) != 0) {
1711 switch (scf_error()) {
1712 case SCF_ERROR_CONNECTION_BROKEN
:
1717 case SCF_ERROR_DELETED
:
1720 case SCF_ERROR_PERMISSION_DENIED
:
1724 case SCF_ERROR_BACKEND_ACCESS
:
1728 case SCF_ERROR_BACKEND_READONLY
:
1732 case SCF_ERROR_HANDLE_MISMATCH
:
1733 case SCF_ERROR_IN_USE
:
1734 case SCF_ERROR_NOT_SET
:
1735 bad_fail("scf_transaction_start", scf_error());
1739 t_cid
= scf_entry_create(h
);
1740 if (t_cid
== NULL
) {
1745 if (scf_pg_get_property(pg
, pname
, prop
) == 0) {
1747 if (scf_transaction_property_change_type(t
, t_cid
,
1748 pname
, SCF_TYPE_COUNT
) != 0) {
1749 switch (scf_error()) {
1750 case SCF_ERROR_CONNECTION_BROKEN
:
1755 case SCF_ERROR_DELETED
:
1756 scf_entry_destroy(t_cid
);
1759 case SCF_ERROR_NOT_FOUND
:
1762 case SCF_ERROR_HANDLE_MISMATCH
:
1763 case SCF_ERROR_INVALID_ARGUMENT
:
1764 case SCF_ERROR_IN_USE
:
1765 case SCF_ERROR_NOT_SET
:
1767 "scf_transaction_propert_change_type",
1772 if (scf_property_is_type(prop
, SCF_TYPE_COUNT
) == 0) {
1773 if (scf_iter_property_values(iter
, prop
) != 0) {
1774 switch (scf_error()) {
1775 case SCF_ERROR_CONNECTION_BROKEN
:
1780 case SCF_ERROR_NOT_SET
:
1781 case SCF_ERROR_HANDLE_MISMATCH
:
1783 "scf_iter_property_values",
1789 val
= scf_value_create(h
);
1791 assert(scf_error() ==
1792 SCF_ERROR_NO_MEMORY
);
1797 ret
= scf_iter_next_value(iter
, val
);
1799 switch (scf_error()) {
1800 case SCF_ERROR_CONNECTION_BROKEN
:
1805 case SCF_ERROR_DELETED
:
1806 scf_value_destroy(val
);
1809 case SCF_ERROR_HANDLE_MISMATCH
:
1810 case SCF_ERROR_INVALID_ARGUMENT
:
1811 case SCF_ERROR_PERMISSION_DENIED
:
1813 "scf_iter_next_value",
1819 ret
= scf_entry_add_value(t_cid
, val
);
1825 scf_value_destroy(val
);
1827 switch (scf_error()) {
1828 case SCF_ERROR_CONNECTION_BROKEN
:
1833 case SCF_ERROR_TYPE_MISMATCH
:
1836 case SCF_ERROR_INVALID_ARGUMENT
:
1837 case SCF_ERROR_NOT_SET
:
1838 bad_fail("scf_property_is_type",
1843 switch (scf_error()) {
1844 case SCF_ERROR_CONNECTION_BROKEN
:
1849 case SCF_ERROR_DELETED
:
1850 scf_entry_destroy(t_cid
);
1853 case SCF_ERROR_NOT_FOUND
:
1856 case SCF_ERROR_HANDLE_MISMATCH
:
1857 case SCF_ERROR_INVALID_ARGUMENT
:
1858 case SCF_ERROR_NOT_SET
:
1859 bad_fail("scf_pg_get_property", scf_error());
1863 if (scf_transaction_property_new(t
, t_cid
, pname
,
1864 SCF_TYPE_COUNT
) != 0) {
1865 switch (scf_error()) {
1866 case SCF_ERROR_CONNECTION_BROKEN
:
1871 case SCF_ERROR_DELETED
:
1872 scf_entry_destroy(t_cid
);
1875 case SCF_ERROR_EXISTS
:
1878 case SCF_ERROR_HANDLE_MISMATCH
:
1879 case SCF_ERROR_INVALID_ARGUMENT
:
1880 case SCF_ERROR_NOT_SET
:
1881 bad_fail("scf_transaction_property_new",
1887 val
= scf_value_create(h
);
1889 assert(scf_error() == SCF_ERROR_NO_MEMORY
);
1894 scf_value_set_count(val
, contract_id
);
1895 ret
= scf_entry_add_value(t_cid
, val
);
1898 ret
= scf_transaction_commit(t
);
1900 switch (scf_error()) {
1901 case SCF_ERROR_CONNECTION_BROKEN
:
1906 case SCF_ERROR_DELETED
:
1909 case SCF_ERROR_PERMISSION_DENIED
:
1913 case SCF_ERROR_BACKEND_ACCESS
:
1917 case SCF_ERROR_BACKEND_READONLY
:
1921 case SCF_ERROR_NOT_SET
:
1922 bad_fail("scf_transaction_commit", scf_error());
1930 scf_transaction_destroy_children(t
);
1931 if (scf_pg_update(pg
) == -1) {
1932 switch (scf_error()) {
1933 case SCF_ERROR_CONNECTION_BROKEN
:
1938 case SCF_ERROR_DELETED
:
1941 case SCF_ERROR_NOT_SET
:
1942 bad_fail("scf_pg_update", scf_error());
1948 scf_transaction_destroy_children(t
);
1949 scf_transaction_destroy(t
);
1950 scf_iter_destroy(iter
);
1951 scf_property_destroy(prop
);
1958 restarter_rm_libs_loadable()
1962 if (method_context_safety
)
1965 if ((libhndl
= dlopen("libpool.so", RTLD_LAZY
| RTLD_LOCAL
)) == NULL
)
1968 (void) dlclose(libhndl
);
1970 if ((libhndl
= dlopen("libproject.so", RTLD_LAZY
| RTLD_LOCAL
)) == NULL
)
1973 (void) dlclose(libhndl
);
1975 method_context_safety
= 1;
1981 get_astring_val(scf_propertygroup_t
*pg
, const char *name
, char *buf
,
1982 size_t bufsz
, scf_property_t
*prop
, scf_value_t
*val
)
1989 if (scf_pg_get_property(pg
, name
, prop
) != SCF_SUCCESS
) {
1990 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN
)
1995 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
1996 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN
)
2001 szret
= scf_value_get_astring(val
, buf
, bufsz
);
2003 return (szret
>= 0 ? 0 : -1);
2007 get_boolean_val(scf_propertygroup_t
*pg
, const char *name
, uint8_t *b
,
2008 scf_property_t
*prop
, scf_value_t
*val
)
2010 if (scf_pg_get_property(pg
, name
, prop
) != SCF_SUCCESS
) {
2011 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN
)
2016 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
2017 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN
)
2022 if (scf_value_get_boolean(val
, b
))
2029 * Try to load mcp->pwd, if it isn't already.
2031 * ENOMEM - malloc() failed
2032 * ENOENT - no entry found
2034 * EMFILE - process out of file descriptors
2035 * ENFILE - system out of file handles
2038 lookup_pwd(struct method_context
*mcp
)
2040 struct passwd
*pwdp
;
2042 if (mcp
->pwbuf
!= NULL
&& mcp
->pwd
.pw_uid
== mcp
->uid
)
2045 if (mcp
->pwbuf
== NULL
) {
2046 mcp
->pwbufsz
= sysconf(_SC_GETPW_R_SIZE_MAX
);
2047 assert(mcp
->pwbufsz
>= 0);
2048 mcp
->pwbuf
= malloc(mcp
->pwbufsz
);
2049 if (mcp
->pwbuf
== NULL
)
2055 pwdp
= getpwuid_r(mcp
->uid
, &mcp
->pwd
, mcp
->pwbuf
,
2057 } while (pwdp
== NULL
&& errno
== EINTR
);
2068 * Until bug 5065780 is fixed, getpwuid_r() can fail with
2069 * ENOENT, particularly on the miniroot. Since the
2070 * documentation is inaccurate, we'll return ENOENT for unknown
2081 bad_fail("getpwuid_r", errno
);
2087 * Get the user id for str. Returns 0 on success or
2088 * ERANGE the uid is too big
2089 * EINVAL the string starts with a digit, but is not a valid uid
2090 * ENOMEM out of memory
2091 * ENOENT no passwd entry for str
2092 * EIO an I/O error has occurred
2093 * EMFILE/ENFILE out of file descriptors
2096 get_uid(const char *str
, struct method_context
*ci
, uid_t
*uidp
)
2098 if (isdigit(str
[0])) {
2103 uid
= strtol(str
, &cp
, 10);
2105 if (uid
== 0 && errno
!= 0) {
2106 assert(errno
!= EINVAL
);
2110 for (; *cp
!= '\0'; ++cp
)
2111 if (*cp
!= ' ' || *cp
!= '\t')
2120 struct passwd
*pwdp
;
2122 if (ci
->pwbuf
== NULL
) {
2123 ci
->pwbufsz
= sysconf(_SC_GETPW_R_SIZE_MAX
);
2124 ci
->pwbuf
= malloc(ci
->pwbufsz
);
2125 if (ci
->pwbuf
== NULL
)
2132 getpwnam_r(str
, &ci
->pwd
, ci
->pwbuf
, ci
->pwbufsz
);
2133 } while (pwdp
== NULL
&& errno
== EINTR
);
2136 *uidp
= ci
->pwd
.pw_uid
;
2153 bad_fail("getpwnam_r", errno
);
2161 get_gid(const char *str
)
2163 if (isdigit(str
[0])) {
2168 gid
= strtol(str
, &cp
, 10);
2170 if (gid
== 0 && errno
!= 0)
2173 for (; *cp
!= '\0'; ++cp
)
2174 if (*cp
!= ' ' || *cp
!= '\t')
2179 struct group grp
, *ret
;
2183 buflen
= sysconf(_SC_GETGR_R_SIZE_MAX
);
2184 buffer
= malloc(buflen
);
2189 ret
= getgrnam_r(str
, &grp
, buffer
, buflen
);
2192 return (ret
== NULL
? (gid_t
)-1 : grp
.gr_gid
);
2198 * ENOMEM - out of memory
2199 * ENOENT - no passwd entry
2201 * EIO - an I/O error occurred
2202 * EMFILE - the process is out of file descriptors
2203 * ENFILE - the system is out of file handles
2204 * ERANGE - the project id is out of range
2205 * EINVAL - str is invalid
2206 * E2BIG - the project entry was too big
2207 * -1 - the name service switch is misconfigured
2210 get_projid(const char *str
, struct method_context
*cip
)
2214 const size_t bufsz
= PROJECT_BUFSZ
;
2215 struct project proj
, *pp
;
2217 if (strcmp(str
, ":default") == 0) {
2218 if (cip
->uid
== 0) {
2219 /* Don't change project for root services */
2220 cip
->project
= NULL
;
2224 switch (ret
= lookup_pwd(cip
)) {
2236 bad_fail("lookup_pwd", ret
);
2239 buf
= malloc(bufsz
);
2245 pp
= getdefaultproj(cip
->pwd
.pw_name
, &proj
, buf
,
2247 } while (pp
== NULL
&& errno
== EINTR
);
2249 /* to be continued ... */
2254 if (!isdigit(str
[0])) {
2255 cip
->project
= strdup(str
);
2256 return (cip
->project
!= NULL
? 0 : ENOMEM
);
2260 projid
= strtol(str
, &cp
, 10);
2262 if (projid
== 0 && errno
!= 0) {
2263 assert(errno
== ERANGE
);
2267 for (; *cp
!= '\0'; ++cp
)
2268 if (*cp
!= ' ' || *cp
!= '\t')
2271 if (projid
> MAXPROJID
)
2274 buf
= malloc(bufsz
);
2280 pp
= getprojbyid(projid
, &proj
, buf
, bufsz
);
2281 } while (pp
== NULL
&& errno
== EINTR
);
2285 cip
->project
= strdup(pp
->pj_name
);
2287 return (cip
->project
!= NULL
? 0 : ENOMEM
);
2310 * Parse the supp_groups property value and populate ci->groups. Returns
2311 * EINVAL (get_gid() failed for one of the components), E2BIG (the property has
2312 * more than NGROUPS_MAX-1 groups), or 0 on success.
2315 get_groups(char *str
, struct method_context
*ci
)
2317 char *cp
, *end
, *next
;
2320 const char * const whitespace
= " \t";
2321 const char * const illegal
= ", \t";
2323 if (str
[0] == '\0') {
2328 for (cp
= str
, i
= 0; *cp
!= '\0'; ) {
2329 /* skip whitespace */
2330 cp
+= strspn(cp
, whitespace
);
2333 end
= cp
+ strcspn(cp
, illegal
);
2335 /* skip whitespace after end */
2336 next
= end
+ strspn(end
, whitespace
);
2338 /* if there's a comma, it separates the fields */
2344 if ((ci
->groups
[i
] = get_gid(cp
)) == (gid_t
)-1) {
2350 if (i
> NGROUPS_MAX
- 1) {
2364 * Return an error message structure containing the error message
2365 * with context, and the error so the caller can make a decision
2366 * on what to do next.
2368 * Because get_ids uses the mc_error_create() function which can
2369 * reallocate the merr, this function must return the merr pointer
2370 * in case it was reallocated.
2373 get_profile(scf_propertygroup_t
*methpg
, scf_propertygroup_t
*instpg
,
2374 scf_property_t
*prop
, scf_value_t
*val
, const char *cmdline
,
2375 struct method_context
*ci
, mc_error_t
*merr
)
2377 char *buf
= ci
->vbuf
;
2378 ssize_t buf_sz
= ci
->vbuf_sz
;
2383 mc_error_t
*err
= merr
;
2386 if (!(get_astring_val(methpg
, SCF_PROPERTY_PROFILE
, buf
, buf_sz
, prop
,
2387 val
) == 0 || get_astring_val(instpg
, SCF_PROPERTY_PROFILE
, buf
,
2388 buf_sz
, prop
, val
) == 0))
2389 return (mc_error_create(merr
, scf_error(),
2390 "Method context requires a profile, but the \"%s\" "
2391 "property could not be read. scf_error is %s",
2392 SCF_PROPERTY_PROFILE
, scf_strerror(scf_error())));
2394 /* Extract the command from the command line. */
2395 cp
= strpbrk(cmdline
, " \t");
2400 (void) strncpy(cmd
, cmdline
, cp
- cmdline
);
2401 cmd
[cp
- cmdline
] = '\0';
2405 /* Require that cmdp[0] == '/'? */
2407 eap
= getexecprof(buf
, KV_COMMAND
, cmdp
, GET_ONE
);
2409 return (mc_error_create(merr
, ENOENT
,
2410 "Could not find the execution profile \"%s\", "
2411 "command %s.", buf
, cmdp
));
2413 /* Based on pfexec.c */
2415 /* Get the euid first so we don't override ci->pwd for the uid. */
2416 if ((value
= kva_match(eap
->attr
, EXECATTR_EUID_KW
)) != NULL
) {
2417 if ((r
= get_uid(value
, ci
, &ci
->euid
)) != 0) {
2418 ci
->euid
= (uid_t
)-1;
2419 err
= mc_error_create(merr
, r
,
2420 "Could not interpret profile euid value \"%s\", "
2421 "from the execution profile \"%s\", error %d.",
2427 if ((value
= kva_match(eap
->attr
, EXECATTR_UID_KW
)) != NULL
) {
2428 if ((r
= get_uid(value
, ci
, &ci
->uid
)) != 0) {
2429 ci
->euid
= ci
->uid
= (uid_t
)-1;
2430 err
= mc_error_create(merr
, r
,
2431 "Could not interpret profile uid value \"%s\", "
2432 "from the execution profile \"%s\", error %d.",
2439 if ((value
= kva_match(eap
->attr
, EXECATTR_GID_KW
)) != NULL
) {
2440 ci
->egid
= ci
->gid
= get_gid(value
);
2441 if (ci
->gid
== (gid_t
)-1) {
2442 err
= mc_error_create(merr
, EINVAL
,
2443 "Could not interpret profile gid value \"%s\", "
2444 "from the execution profile \"%s\".", value
, buf
);
2449 if ((value
= kva_match(eap
->attr
, EXECATTR_EGID_KW
)) != NULL
) {
2450 ci
->egid
= get_gid(value
);
2451 if (ci
->egid
== (gid_t
)-1) {
2452 err
= mc_error_create(merr
, EINVAL
,
2453 "Could not interpret profile egid value \"%s\", "
2454 "from the execution profile \"%s\".", value
, buf
);
2459 if ((value
= kva_match(eap
->attr
, EXECATTR_LPRIV_KW
)) != NULL
) {
2460 ci
->lpriv_set
= priv_str_to_set(value
, ",", NULL
);
2461 if (ci
->lpriv_set
== NULL
) {
2462 if (errno
!= EINVAL
)
2463 err
= mc_error_create(merr
, ENOMEM
,
2466 err
= mc_error_create(merr
, EINVAL
,
2467 "Could not interpret profile "
2468 "limitprivs value \"%s\", from "
2469 "the execution profile \"%s\".",
2475 if ((value
= kva_match(eap
->attr
, EXECATTR_IPRIV_KW
)) != NULL
) {
2476 ci
->priv_set
= priv_str_to_set(value
, ",", NULL
);
2477 if (ci
->priv_set
== NULL
) {
2478 if (errno
!= EINVAL
)
2479 err
= mc_error_create(merr
, ENOMEM
,
2482 err
= mc_error_create(merr
, EINVAL
,
2483 "Could not interpret profile privs value "
2484 "\"%s\", from the execution profile "
2485 "\"%s\".", value
, buf
);
2497 * Return an error message structure containing the error message
2498 * with context, and the error so the caller can make a decision
2499 * on what to do next.
2501 * Because get_ids uses the mc_error_create() function which can
2502 * reallocate the merr, this function must return the merr pointer
2503 * in case it was reallocated.
2506 get_ids(scf_propertygroup_t
*methpg
, scf_propertygroup_t
*instpg
,
2507 scf_property_t
*prop
, scf_value_t
*val
, struct method_context
*ci
,
2510 char *vbuf
= ci
->vbuf
;
2511 ssize_t vbuf_sz
= ci
->vbuf_sz
;
2515 * This should never happen because the caller should fall through
2516 * another path of just setting the ids to defaults, instead of
2517 * attempting to get the ids here.
2519 if (methpg
== NULL
&& instpg
== NULL
)
2520 return (mc_error_create(merr
, ENOENT
,
2521 "No property groups to get ids from."));
2523 if (!(get_astring_val(methpg
, SCF_PROPERTY_USER
,
2524 vbuf
, vbuf_sz
, prop
, val
) == 0 || get_astring_val(instpg
,
2525 SCF_PROPERTY_USER
, vbuf
, vbuf_sz
, prop
,
2527 return (mc_error_create(merr
, ENOENT
,
2528 "Could not get \"%s\" property.", SCF_PROPERTY_USER
));
2530 if ((r
= get_uid(vbuf
, ci
, &ci
->uid
)) != 0) {
2531 ci
->uid
= (uid_t
)-1;
2532 return (mc_error_create(merr
, r
,
2533 "Could not interpret \"%s\" property value \"%s\", "
2534 "error %d.", SCF_PROPERTY_USER
, vbuf
, r
));
2537 if (!(get_astring_val(methpg
, SCF_PROPERTY_GROUP
, vbuf
, vbuf_sz
, prop
,
2538 val
) == 0 || get_astring_val(instpg
, SCF_PROPERTY_GROUP
, vbuf
,
2539 vbuf_sz
, prop
, val
) == 0)) {
2540 if (scf_error() == SCF_ERROR_NOT_FOUND
) {
2541 (void) strcpy(vbuf
, ":default");
2543 return (mc_error_create(merr
, ENOENT
,
2544 "Could not get \"%s\" property.",
2545 SCF_PROPERTY_GROUP
));
2549 if (strcmp(vbuf
, ":default") != 0) {
2550 ci
->gid
= get_gid(vbuf
);
2551 if (ci
->gid
== (gid_t
)-1) {
2552 return (mc_error_create(merr
, ENOENT
,
2553 "Could not interpret \"%s\" property value \"%s\".",
2554 SCF_PROPERTY_GROUP
, vbuf
));
2557 switch (r
= lookup_pwd(ci
)) {
2559 ci
->gid
= ci
->pwd
.pw_gid
;
2563 ci
->gid
= (gid_t
)-1;
2564 return (mc_error_create(merr
, ENOENT
,
2565 "No passwd entry for uid \"%d\".", ci
->uid
));
2568 return (mc_error_create(merr
, ENOMEM
,
2574 return (mc_error_create(merr
, ENFILE
,
2575 "getpwuid_r() failed, error %d.", r
));
2578 bad_fail("lookup_pwd", r
);
2582 if (!(get_astring_val(methpg
, SCF_PROPERTY_SUPP_GROUPS
, vbuf
, vbuf_sz
,
2583 prop
, val
) == 0 || get_astring_val(instpg
,
2584 SCF_PROPERTY_SUPP_GROUPS
, vbuf
, vbuf_sz
, prop
, val
) == 0)) {
2585 if (scf_error() == SCF_ERROR_NOT_FOUND
) {
2586 (void) strcpy(vbuf
, ":default");
2588 return (mc_error_create(merr
, ENOENT
,
2589 "Could not get supplemental groups (\"%s\") "
2590 "property.", SCF_PROPERTY_SUPP_GROUPS
));
2594 if (strcmp(vbuf
, ":default") != 0) {
2595 switch (r
= get_groups(vbuf
, ci
)) {
2600 return (mc_error_create(merr
, EINVAL
,
2601 "Could not interpret supplemental groups (\"%s\") "
2602 "property value \"%s\".", SCF_PROPERTY_SUPP_GROUPS
,
2606 return (mc_error_create(merr
, E2BIG
,
2607 "Too many supplemental groups values in \"%s\".",
2611 bad_fail("get_groups", r
);
2617 if (!(get_astring_val(methpg
, SCF_PROPERTY_PRIVILEGES
, vbuf
, vbuf_sz
,
2618 prop
, val
) == 0 || get_astring_val(instpg
, SCF_PROPERTY_PRIVILEGES
,
2619 vbuf
, vbuf_sz
, prop
, val
) == 0)) {
2620 if (scf_error() == SCF_ERROR_NOT_FOUND
) {
2621 (void) strcpy(vbuf
, ":default");
2623 return (mc_error_create(merr
, ENOENT
,
2624 "Could not get \"%s\" property.",
2625 SCF_PROPERTY_PRIVILEGES
));
2630 * For default privs, we need to keep priv_set == NULL, as
2631 * we use this test elsewhere.
2633 if (strcmp(vbuf
, ":default") != 0) {
2634 ci
->priv_set
= priv_str_to_set(vbuf
, ",", NULL
);
2635 if (ci
->priv_set
== NULL
) {
2636 if (errno
!= EINVAL
) {
2637 return (mc_error_create(merr
, ENOMEM
,
2640 return (mc_error_create(merr
, EINVAL
,
2641 "Could not interpret \"%s\" "
2642 "property value \"%s\".",
2643 SCF_PROPERTY_PRIVILEGES
, vbuf
));
2648 if (!(get_astring_val(methpg
, SCF_PROPERTY_LIMIT_PRIVILEGES
, vbuf
,
2649 vbuf_sz
, prop
, val
) == 0 || get_astring_val(instpg
,
2650 SCF_PROPERTY_LIMIT_PRIVILEGES
, vbuf
, vbuf_sz
, prop
, val
) == 0)) {
2651 if (scf_error() == SCF_ERROR_NOT_FOUND
) {
2652 (void) strcpy(vbuf
, ":default");
2654 return (mc_error_create(merr
, ENOENT
,
2655 "Could not get \"%s\" property.",
2656 SCF_PROPERTY_LIMIT_PRIVILEGES
));
2660 if (strcmp(vbuf
, ":default") == 0)
2662 * L must default to all privileges so root NPA services see
2663 * iE = all. "zone" is all privileges available in the current
2664 * zone, equivalent to "all" in the global zone.
2666 (void) strcpy(vbuf
, "zone");
2668 ci
->lpriv_set
= priv_str_to_set(vbuf
, ",", NULL
);
2669 if (ci
->lpriv_set
== NULL
) {
2670 if (errno
!= EINVAL
) {
2671 return (mc_error_create(merr
, ENOMEM
, ALLOCFAIL
));
2673 return (mc_error_create(merr
, EINVAL
,
2674 "Could not interpret \"%s\" property value \"%s\".",
2675 SCF_PROPERTY_LIMIT_PRIVILEGES
, vbuf
));
2683 get_environment(scf_handle_t
*h
, scf_propertygroup_t
*pg
,
2684 struct method_context
*mcp
, scf_property_t
*prop
, scf_value_t
*val
)
2691 if (scf_pg_get_property(pg
, SCF_PROPERTY_ENVIRONMENT
, prop
) != 0) {
2692 if (scf_error() == SCF_ERROR_NOT_FOUND
)
2694 return (scf_error());
2696 if (scf_property_type(prop
, &type
) != 0)
2697 return (scf_error());
2698 if (type
!= SCF_TYPE_ASTRING
)
2700 if ((iter
= scf_iter_create(h
)) == NULL
)
2701 return (scf_error());
2703 if (scf_iter_property_values(iter
, prop
) != 0) {
2705 scf_iter_destroy(iter
);
2711 if ((mcp
->env
= uu_zalloc(sizeof (*mcp
->env
) * mcp
->env_sz
)) == NULL
) {
2716 while ((ret
= scf_iter_next_value(iter
, val
)) == 1) {
2717 ret
= scf_value_get_as_string(val
, mcp
->vbuf
, mcp
->vbuf_sz
);
2723 if ((mcp
->env
[i
] = strdup(mcp
->vbuf
)) == NULL
) {
2728 if (++i
== mcp
->env_sz
) {
2731 env
= uu_zalloc(sizeof (*mcp
->env
) * mcp
->env_sz
);
2736 (void) memcpy(env
, mcp
->env
,
2737 sizeof (*mcp
->env
) * (mcp
->env_sz
/ 2));
2747 scf_iter_destroy(iter
);
2752 * Fetch method context information from the repository, allocate and fill
2753 * a method_context structure, return it in *mcpp, and return NULL.
2755 * If no method_context is defined, original init context is provided, where
2756 * the working directory is '/', and uid/gid are 0/0. But if a method_context
2757 * is defined at any level the smf_method(5) method_context defaults are used.
2759 * Return an error message structure containing the error message
2760 * with context, and the error so the caller can make a decision
2761 * on what to do next.
2764 * E2BIG Too many values or entry is too big
2765 * EINVAL Invalid value
2766 * EIO an I/O error has occured
2767 * ENOENT no entry for value
2768 * ENOMEM out of memory
2769 * ENOTSUP Version mismatch
2770 * ERANGE value is out of range
2771 * EMFILE/ENFILE out of file descriptors
2773 * SCF_ERROR_BACKEND_ACCESS
2774 * SCF_ERROR_CONNECTION_BROKEN
2776 * SCF_ERROR_CONSTRAINT_VIOLATED
2777 * SCF_ERROR_HANDLE_DESTROYED
2778 * SCF_ERROR_INTERNAL
2779 * SCF_ERROR_INVALID_ARGUMENT
2780 * SCF_ERROR_NO_MEMORY
2781 * SCF_ERROR_NO_RESOURCES
2782 * SCF_ERROR_NOT_BOUND
2783 * SCF_ERROR_NOT_FOUND
2785 * SCF_ERROR_TYPE_MISMATCH
2789 restarter_get_method_context(uint_t version
, scf_instance_t
*inst
,
2790 scf_snapshot_t
*snap
, const char *mname
, const char *cmdline
,
2791 struct method_context
**mcpp
)
2794 scf_propertygroup_t
*methpg
= NULL
;
2795 scf_propertygroup_t
*instpg
= NULL
;
2796 scf_propertygroup_t
*pg
= NULL
;
2797 scf_property_t
*prop
= NULL
;
2798 scf_value_t
*val
= NULL
;
2800 uint8_t use_profile
;
2803 mc_error_t
*err
= NULL
;
2804 struct method_context
*cip
;
2806 if ((err
= malloc(sizeof (mc_error_t
))) == NULL
)
2807 return (mc_error_create(NULL
, ENOMEM
, NULL
));
2809 /* Set the type to zero to track if an error occured. */
2812 if (version
!= RESTARTER_METHOD_CONTEXT_VERSION
)
2813 return (mc_error_create(err
, ENOTSUP
,
2814 "Invalid client version %d. (Expected %d)",
2815 version
, RESTARTER_METHOD_CONTEXT_VERSION
));
2817 /* Get the handle before we allocate anything. */
2818 h
= scf_instance_handle(inst
);
2820 return (mc_error_create(err
, scf_error(),
2821 scf_strerror(scf_error())));
2823 cip
= malloc(sizeof (*cip
));
2825 return (mc_error_create(err
, ENOMEM
, ALLOCFAIL
));
2827 (void) memset(cip
, 0, sizeof (*cip
));
2828 cip
->uid
= (uid_t
)-1;
2829 cip
->euid
= (uid_t
)-1;
2830 cip
->gid
= (gid_t
)-1;
2831 cip
->egid
= (gid_t
)-1;
2833 cip
->vbuf_sz
= scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
2834 assert(cip
->vbuf_sz
>= 0);
2835 cip
->vbuf
= malloc(cip
->vbuf_sz
);
2836 if (cip
->vbuf
== NULL
) {
2838 return (mc_error_create(err
, ENOMEM
, ALLOCFAIL
));
2841 if ((instpg
= scf_pg_create(h
)) == NULL
||
2842 (methpg
= scf_pg_create(h
)) == NULL
||
2843 (prop
= scf_property_create(h
)) == NULL
||
2844 (val
= scf_value_create(h
)) == NULL
) {
2845 err
= mc_error_create(err
, scf_error(),
2846 "Failed to create repository object: %s\n",
2847 scf_strerror(scf_error()));
2852 * The method environment, and the credentials/profile data,
2853 * may be found either in the pg for the method (methpg),
2854 * or in the instance/service SCF_PG_METHOD_CONTEXT pg (named
2858 if (scf_instance_get_pg_composed(inst
, snap
, mname
, methpg
) !=
2860 err
= mc_error_create(err
, scf_error(), "Unable to get the "
2861 "\"%s\" method, %s", mname
, scf_strerror(scf_error()));
2865 if (scf_instance_get_pg_composed(inst
, snap
, SCF_PG_METHOD_CONTEXT
,
2866 instpg
) != SCF_SUCCESS
) {
2867 if (scf_error() != SCF_ERROR_NOT_FOUND
) {
2868 err
= mc_error_create(err
, scf_error(),
2869 "Unable to retrieve the \"%s\" property group, %s",
2870 SCF_PG_METHOD_CONTEXT
, scf_strerror(scf_error()));
2873 scf_pg_destroy(instpg
);
2879 ret
= get_environment(h
, methpg
, cip
, prop
, val
);
2880 if (ret
== ENOENT
&& instpg
!= NULL
) {
2881 ret
= get_environment(h
, instpg
, cip
, prop
, val
);
2891 err
= mc_error_create(err
, ret
, "Out of memory.");
2894 err
= mc_error_create(err
, ret
, "Invalid method environment.");
2897 err
= mc_error_create(err
, ret
,
2898 "Get method environment failed : %s\n", scf_strerror(ret
));
2904 ret
= scf_pg_get_property(pg
, SCF_PROPERTY_USE_PROFILE
, prop
);
2905 if (ret
&& scf_error() == SCF_ERROR_NOT_FOUND
&& instpg
!= NULL
) {
2907 ret
= scf_pg_get_property(instpg
, SCF_PROPERTY_USE_PROFILE
,
2912 switch (scf_error()) {
2913 case SCF_ERROR_NOT_FOUND
:
2914 /* No profile context: use default credentials */
2919 case SCF_ERROR_CONNECTION_BROKEN
:
2920 err
= mc_error_create(err
, SCF_ERROR_CONNECTION_BROKEN
,
2924 case SCF_ERROR_DELETED
:
2925 err
= mc_error_create(err
, SCF_ERROR_NOT_FOUND
,
2926 "Could not find property group \"%s\"",
2927 pg
== NULL
? SCF_PG_METHOD_CONTEXT
: mname
);
2930 case SCF_ERROR_HANDLE_MISMATCH
:
2931 case SCF_ERROR_INVALID_ARGUMENT
:
2932 case SCF_ERROR_NOT_SET
:
2934 bad_fail("scf_pg_get_property", scf_error());
2937 if (scf_property_type(prop
, &ty
) != SCF_SUCCESS
) {
2940 case SCF_ERROR_CONNECTION_BROKEN
:
2941 err
= mc_error_create(err
,
2942 SCF_ERROR_CONNECTION_BROKEN
, RCBROKEN
);
2945 case SCF_ERROR_DELETED
:
2946 err
= mc_error_create(err
,
2947 SCF_ERROR_NOT_FOUND
,
2948 "Could not find property group \"%s\"",
2949 pg
== NULL
? SCF_PG_METHOD_CONTEXT
: mname
);
2952 case SCF_ERROR_NOT_SET
:
2954 bad_fail("scf_property_type", ret
);
2960 if (ty
!= SCF_TYPE_BOOLEAN
) {
2961 err
= mc_error_create(err
,
2962 SCF_ERROR_TYPE_MISMATCH
,
2963 "\"%s\" property is not boolean in property group "
2964 "\"%s\".", SCF_PROPERTY_USE_PROFILE
,
2965 pg
== NULL
? SCF_PG_METHOD_CONTEXT
: mname
);
2969 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
2972 case SCF_ERROR_CONNECTION_BROKEN
:
2973 err
= mc_error_create(err
,
2974 SCF_ERROR_CONNECTION_BROKEN
, RCBROKEN
);
2977 case SCF_ERROR_CONSTRAINT_VIOLATED
:
2978 err
= mc_error_create(err
,
2979 SCF_ERROR_CONSTRAINT_VIOLATED
,
2980 "\"%s\" property has multiple values.",
2981 SCF_PROPERTY_USE_PROFILE
);
2984 case SCF_ERROR_NOT_FOUND
:
2985 err
= mc_error_create(err
,
2986 SCF_ERROR_NOT_FOUND
,
2987 "\"%s\" property has no values.",
2988 SCF_PROPERTY_USE_PROFILE
);
2991 bad_fail("scf_property_get_value", ret
);
2998 ret
= scf_value_get_boolean(val
, &use_profile
);
2999 assert(ret
== SCF_SUCCESS
);
3001 /* get ids & privileges */
3003 err
= get_profile(pg
, instpg
, prop
, val
, cmdline
,
3006 err
= get_ids(pg
, instpg
, prop
, val
, cip
, err
);
3012 /* get working directory */
3013 if ((methpg
!= NULL
&& scf_pg_get_property(methpg
,
3014 SCF_PROPERTY_WORKING_DIRECTORY
, prop
) == SCF_SUCCESS
) ||
3015 (instpg
!= NULL
&& scf_pg_get_property(instpg
,
3016 SCF_PROPERTY_WORKING_DIRECTORY
, prop
) == SCF_SUCCESS
)) {
3017 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
3020 case SCF_ERROR_CONNECTION_BROKEN
:
3021 err
= mc_error_create(err
, ret
, RCBROKEN
);
3024 case SCF_ERROR_CONSTRAINT_VIOLATED
:
3025 err
= mc_error_create(err
, ret
,
3026 "\"%s\" property has multiple values.",
3027 SCF_PROPERTY_WORKING_DIRECTORY
);
3030 case SCF_ERROR_NOT_FOUND
:
3031 err
= mc_error_create(err
, ret
,
3032 "\"%s\" property has no values.",
3033 SCF_PROPERTY_WORKING_DIRECTORY
);
3037 bad_fail("scf_property_get_value", ret
);
3044 ret
= scf_value_get_astring(val
, cip
->vbuf
, cip
->vbuf_sz
);
3049 case SCF_ERROR_NOT_FOUND
:
3050 /* okay if missing. */
3051 (void) strcpy(cip
->vbuf
, ":default");
3054 case SCF_ERROR_CONNECTION_BROKEN
:
3055 err
= mc_error_create(err
, ret
, RCBROKEN
);
3058 case SCF_ERROR_DELETED
:
3059 err
= mc_error_create(err
, ret
,
3060 "Property group could not be found");
3063 case SCF_ERROR_HANDLE_MISMATCH
:
3064 case SCF_ERROR_INVALID_ARGUMENT
:
3065 case SCF_ERROR_NOT_SET
:
3067 bad_fail("scf_pg_get_property", ret
);
3071 if (strcmp(cip
->vbuf
, ":default") == 0 ||
3072 strcmp(cip
->vbuf
, ":home") == 0) {
3073 switch (ret
= lookup_pwd(cip
)) {
3078 err
= mc_error_create(err
, ret
, "Out of memory.");
3085 err
= mc_error_create(err
, ret
,
3086 "Could not get passwd entry.");
3090 bad_fail("lookup_pwd", ret
);
3093 cip
->working_dir
= strdup(cip
->pwd
.pw_dir
);
3094 if (cip
->working_dir
== NULL
) {
3095 err
= mc_error_create(err
, ENOMEM
, ALLOCFAIL
);
3099 cip
->working_dir
= strdup(cip
->vbuf
);
3100 if (cip
->working_dir
== NULL
) {
3101 err
= mc_error_create(err
, ENOMEM
, ALLOCFAIL
);
3106 /* get (optional) corefile pattern */
3107 if ((methpg
!= NULL
&& scf_pg_get_property(methpg
,
3108 SCF_PROPERTY_COREFILE_PATTERN
, prop
) == SCF_SUCCESS
) ||
3109 (instpg
!= NULL
&& scf_pg_get_property(instpg
,
3110 SCF_PROPERTY_COREFILE_PATTERN
, prop
) == SCF_SUCCESS
)) {
3111 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
3114 case SCF_ERROR_CONNECTION_BROKEN
:
3115 err
= mc_error_create(err
, ret
, RCBROKEN
);
3118 case SCF_ERROR_CONSTRAINT_VIOLATED
:
3119 err
= mc_error_create(err
, ret
,
3120 "\"%s\" property has multiple values.",
3121 SCF_PROPERTY_COREFILE_PATTERN
);
3124 case SCF_ERROR_NOT_FOUND
:
3125 err
= mc_error_create(err
, ret
,
3126 "\"%s\" property has no values.",
3127 SCF_PROPERTY_COREFILE_PATTERN
);
3131 bad_fail("scf_property_get_value", ret
);
3136 ret
= scf_value_get_astring(val
, cip
->vbuf
,
3140 cip
->corefile_pattern
= strdup(cip
->vbuf
);
3141 if (cip
->corefile_pattern
== NULL
) {
3142 err
= mc_error_create(err
, ENOMEM
, ALLOCFAIL
);
3151 case SCF_ERROR_NOT_FOUND
:
3152 /* okay if missing. */
3155 case SCF_ERROR_CONNECTION_BROKEN
:
3156 err
= mc_error_create(err
, ret
, RCBROKEN
);
3159 case SCF_ERROR_DELETED
:
3160 err
= mc_error_create(err
, ret
,
3161 "Property group could not be found");
3164 case SCF_ERROR_HANDLE_MISMATCH
:
3165 case SCF_ERROR_INVALID_ARGUMENT
:
3166 case SCF_ERROR_NOT_SET
:
3168 bad_fail("scf_pg_get_property", ret
);
3172 if (restarter_rm_libs_loadable()) {
3174 if ((methpg
!= NULL
&& scf_pg_get_property(methpg
,
3175 SCF_PROPERTY_PROJECT
, prop
) == SCF_SUCCESS
) ||
3176 (instpg
!= NULL
&& scf_pg_get_property(instpg
,
3177 SCF_PROPERTY_PROJECT
, prop
) == SCF_SUCCESS
)) {
3178 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
3181 case SCF_ERROR_CONNECTION_BROKEN
:
3182 err
= mc_error_create(err
, ret
,
3186 case SCF_ERROR_CONSTRAINT_VIOLATED
:
3187 err
= mc_error_create(err
, ret
,
3188 "\"%s\" property has multiple "
3189 "values.", SCF_PROPERTY_PROJECT
);
3192 case SCF_ERROR_NOT_FOUND
:
3193 err
= mc_error_create(err
, ret
,
3194 "\"%s\" property has no values.",
3195 SCF_PROPERTY_PROJECT
);
3199 bad_fail("scf_property_get_value", ret
);
3202 (void) strcpy(cip
->vbuf
, ":default");
3204 ret
= scf_value_get_astring(val
, cip
->vbuf
,
3211 (void) strcpy(cip
->vbuf
, ":default");
3214 switch (ret
= get_projid(cip
->vbuf
, cip
)) {
3219 err
= mc_error_create(err
, ret
, "Out of memory.");
3223 err
= mc_error_create(err
, ret
,
3224 "Missing passwd or project entry for \"%s\".",
3229 err
= mc_error_create(err
, ret
, "I/O error.");
3234 err
= mc_error_create(err
, ret
,
3235 "Out of file descriptors.");
3239 err
= mc_error_create(err
, ret
,
3240 "Name service switch is misconfigured.");
3245 err
= mc_error_create(err
, ret
,
3246 "Project ID \"%s\" too big.", cip
->vbuf
);
3250 err
= mc_error_create(err
, ret
,
3251 "Project ID \"%s\" is invalid.", cip
->vbuf
);
3255 bad_fail("get_projid", ret
);
3258 /* get resource pool */
3259 if ((methpg
!= NULL
&& scf_pg_get_property(methpg
,
3260 SCF_PROPERTY_RESOURCE_POOL
, prop
) == SCF_SUCCESS
) ||
3261 (instpg
!= NULL
&& scf_pg_get_property(instpg
,
3262 SCF_PROPERTY_RESOURCE_POOL
, prop
) == SCF_SUCCESS
)) {
3263 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
3266 case SCF_ERROR_CONNECTION_BROKEN
:
3267 err
= mc_error_create(err
, ret
,
3271 case SCF_ERROR_CONSTRAINT_VIOLATED
:
3272 err
= mc_error_create(err
, ret
,
3273 "\"%s\" property has multiple "
3275 SCF_PROPERTY_RESOURCE_POOL
);
3278 case SCF_ERROR_NOT_FOUND
:
3279 err
= mc_error_create(err
, ret
,
3280 "\"%s\" property has no "
3282 SCF_PROPERTY_RESOURCE_POOL
);
3286 bad_fail("scf_property_get_value", ret
);
3289 (void) strcpy(cip
->vbuf
, ":default");
3291 ret
= scf_value_get_astring(val
, cip
->vbuf
,
3300 case SCF_ERROR_NOT_FOUND
:
3301 /* okay if missing. */
3302 (void) strcpy(cip
->vbuf
, ":default");
3305 case SCF_ERROR_CONNECTION_BROKEN
:
3306 err
= mc_error_create(err
, ret
, RCBROKEN
);
3309 case SCF_ERROR_DELETED
:
3310 err
= mc_error_create(err
, ret
,
3311 "property group could not be found.");
3314 case SCF_ERROR_HANDLE_MISMATCH
:
3315 case SCF_ERROR_INVALID_ARGUMENT
:
3316 case SCF_ERROR_NOT_SET
:
3318 bad_fail("scf_pg_get_property", ret
);
3322 if (strcmp(cip
->vbuf
, ":default") != 0) {
3323 cip
->resource_pool
= strdup(cip
->vbuf
);
3324 if (cip
->resource_pool
== NULL
) {
3325 err
= mc_error_create(err
, ENOMEM
, ALLOCFAIL
);
3332 * A method_context was not used for any configurable
3333 * elements or attributes, so reset and use the simple
3334 * defaults that provide historic init behavior.
3339 free(cip
->working_dir
);
3341 (void) memset(cip
, 0, sizeof (*cip
));
3344 cip
->euid
= (uid_t
)-1;
3345 cip
->egid
= (gid_t
)-1;
3351 (void) scf_value_destroy(val
);
3352 scf_property_destroy(prop
);
3353 scf_pg_destroy(instpg
);
3354 scf_pg_destroy(methpg
);
3356 if (cip
->pwbuf
!= NULL
) {
3363 if (err
->type
!= 0) {
3364 restarter_free_method_context(cip
);
3366 restarter_mc_error_destroy(err
);
3374 * Modify the current process per the given method_context. On success, returns
3375 * 0. Note that the environment is not modified by this function to include the
3376 * environment variables in cip->env.
3378 * On failure, sets *fp to NULL or the name of the function which failed,
3379 * and returns one of the following error codes. The words in parentheses are
3380 * the values to which *fp may be set for the error case.
3381 * ENOMEM - malloc() failed
3382 * EIO - an I/O error occurred (getpwuid_r, chdir)
3383 * EMFILE - process is out of file descriptors (getpwuid_r)
3384 * ENFILE - system is out of file handles (getpwuid_r)
3385 * EINVAL - gid or egid is out of range (setregid)
3386 * ngroups is too big (setgroups)
3387 * project's project id is bad (setproject)
3388 * uid or euid is out of range (setreuid)
3389 * poolname is invalid (pool_set_binding)
3390 * EPERM - insufficient privilege (setregid, initgroups, setgroups, setppriv,
3391 * setproject, setreuid, settaskid)
3392 * ENOENT - uid has a passwd entry but no shadow entry
3393 * working_dir does not exist (chdir)
3394 * uid has no passwd entry
3395 * the pool could not be found (pool_set_binding)
3396 * EFAULT - lpriv_set or priv_set has a bad address (setppriv)
3397 * working_dir has a bad address (chdir)
3398 * EACCES - could not access working_dir (chdir)
3399 * in a TASK_FINAL task (setproject, settaskid)
3400 * no resource pool accepting default binding exists (setproject)
3401 * ELOOP - too many symbolic links in working_dir (chdir)
3402 * ENAMETOOLONG - working_dir is too long (chdir)
3403 * ENOLINK - working_dir is on an inaccessible remote machine (chdir)
3404 * ENOTDIR - working_dir is not a directory (chdir)
3405 * ESRCH - uid is not a user of project (setproject)
3406 * project is invalid (setproject)
3407 * the resource pool specified for project is unknown (setproject)
3408 * EBADF - the configuration for the pool is invalid (pool_set_binding)
3409 * -1 - core_set_process_path() failed (core_set_process_path)
3410 * a resource control assignment failed (setproject)
3411 * a system error occurred during pool_set_binding (pool_set_binding)
3414 restarter_set_method_context(struct method_context
*cip
, const char **fp
)
3422 if (cip
->gid
!= (gid_t
)-1) {
3423 if (setregid(cip
->gid
,
3424 cip
->egid
!= (gid_t
)-1 ? cip
->egid
: cip
->gid
) != 0) {
3428 assert(ret
== EINVAL
|| ret
== EPERM
);
3432 if (cip
->pwbuf
== NULL
) {
3433 switch (ret
= lookup_pwd(cip
)) {
3449 bad_fail("lookup_pwd", ret
);
3453 if (setregid(cip
->pwd
.pw_gid
,
3454 cip
->egid
!= (gid_t
)-1 ?
3455 cip
->egid
: cip
->pwd
.pw_gid
) != 0) {
3459 assert(ret
== EINVAL
|| ret
== EPERM
);
3464 if (cip
->ngroups
== -1) {
3465 if (cip
->pwbuf
== NULL
) {
3466 switch (ret
= lookup_pwd(cip
)) {
3482 bad_fail("lookup_pwd", ret
);
3486 /* Ok if cip->gid == -1 */
3487 if (initgroups(cip
->pwd
.pw_name
, cip
->gid
) != 0) {
3490 assert(ret
== EPERM
);
3493 } else if (cip
->ngroups
> 0 &&
3494 setgroups(cip
->ngroups
, cip
->groups
) != 0) {
3498 assert(ret
== EINVAL
|| ret
== EPERM
);
3502 if (cip
->corefile_pattern
!= NULL
) {
3505 if (core_set_process_path(cip
->corefile_pattern
,
3506 strlen(cip
->corefile_pattern
) + 1, mypid
) != 0) {
3507 *fp
= "core_set_process_path";
3513 if (restarter_rm_libs_loadable()) {
3514 if (cip
->project
== NULL
) {
3515 if (settaskid(getprojid(), TASK_NORMAL
) == -1) {
3525 bad_fail("settaskid", errno
);
3529 switch (ret
= lookup_pwd(cip
)) {
3545 bad_fail("lookup_pwd", ret
);
3550 switch (setproject(cip
->project
, cip
->pwd
.pw_name
,
3555 case SETPROJ_ERR_TASK
:
3556 case SETPROJ_ERR_POOL
:
3566 if (cip
->resource_pool
!= NULL
) {
3570 *fp
= "pool_set_binding";
3572 if (pool_set_binding(cip
->resource_pool
, P_PID
,
3573 mypid
) != PO_SUCCESS
) {
3574 switch (pool_error()) {
3575 case POE_INVALID_SEARCH
:
3583 case POE_INVALID_CONF
:
3592 bad_fail("pool_set_binding",
3602 * Now, we have to assume our ID. If the UID is 0, we want it to be
3603 * privilege-aware, otherwise the limit set gets used instead of E/P.
3604 * We can do this by setting P as well, which keeps
3605 * PA status (see priv_can_clear_PA()).
3610 if (cip
->lpriv_set
!= NULL
) {
3611 if (setppriv(PRIV_SET
, PRIV_LIMIT
, cip
->lpriv_set
) != 0) {
3613 assert(ret
== EFAULT
|| ret
== EPERM
);
3617 if (cip
->priv_set
!= NULL
) {
3618 if (setppriv(PRIV_SET
, PRIV_INHERITABLE
, cip
->priv_set
) != 0) {
3620 assert(ret
== EFAULT
|| ret
== EPERM
);
3626 * If the limit privset is already set, then must be privilege
3627 * aware. Otherwise, don't assume anything, and force privilege
3631 if (cip
->lpriv_set
== NULL
&& cip
->priv_set
!= NULL
) {
3632 ret
= setpflags(PRIV_AWARE
, 1);
3637 if (setreuid(cip
->uid
,
3638 cip
->euid
!= (uid_t
)-1 ? cip
->euid
: cip
->uid
) != 0) {
3640 assert(ret
== EINVAL
|| ret
== EPERM
);
3645 if (cip
->priv_set
!= NULL
) {
3646 if (setppriv(PRIV_SET
, PRIV_PERMITTED
, cip
->priv_set
) != 0) {
3648 assert(ret
== EFAULT
|| ret
== EPERM
);
3654 * The last thing to do is chdir to the specified working directory.
3655 * This should come after the uid switching as only the user might
3656 * have access to the specified directory.
3658 if (cip
->working_dir
!= NULL
) {
3660 r
= chdir(cip
->working_dir
);
3661 } while (r
!= 0 && errno
== EINTR
);
3677 restarter_free_method_context(struct method_context
*mcp
)
3681 if (mcp
->lpriv_set
!= NULL
)
3682 priv_freeset(mcp
->lpriv_set
);
3683 if (mcp
->priv_set
!= NULL
)
3684 priv_freeset(mcp
->priv_set
);
3686 if (mcp
->env
!= NULL
) {
3687 for (i
= 0; i
< mcp
->env_sz
; i
++)
3692 free(mcp
->working_dir
);
3693 free(mcp
->corefile_pattern
);
3695 free(mcp
->resource_pool
);
3700 * Method keyword functions
3704 restarter_is_null_method(const char *meth
)
3706 return (strcmp(meth
, MKW_TRUE
) == 0);
3710 is_kill_method(const char *method
, const char *kill_str
,
3711 size_t kill_str_len
)
3716 if (strncmp(method
, kill_str
, kill_str_len
) != 0 ||
3717 (method
[kill_str_len
] != '\0' &&
3718 !isspace(method
[kill_str_len
])))
3721 cp
= method
+ kill_str_len
;
3722 while (*cp
!= '\0' && isspace(*cp
))
3731 return (str2sig(cp
+ 1, &sig
) == 0 ? sig
: -1);
3735 restarter_is_kill_proc_method(const char *method
)
3737 return (is_kill_method(method
, MKW_KILL_PROC
,
3738 sizeof (MKW_KILL_PROC
) - 1));
3742 restarter_is_kill_method(const char *method
)
3744 return (is_kill_method(method
, MKW_KILL
, sizeof (MKW_KILL
) - 1));
3753 restarter_event_get_enabled(restarter_event_t
*e
)
3760 restarter_event_get_seq(restarter_event_t
*e
)
3767 restarter_event_get_time(restarter_event_t
*e
, hrtime_t
*time
)
3772 * Check for and validate fmri specified in restarter_actions/auxiliary_fmri
3777 restarter_inst_validate_ractions_aux_fmri(scf_instance_t
*inst
)
3780 scf_propertygroup_t
*pg
;
3781 scf_property_t
*prop
;
3784 size_t size
= scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
3787 if ((aux_fmri
= malloc(size
)) == NULL
)
3790 h
= scf_instance_handle(inst
);
3792 pg
= scf_pg_create(h
);
3793 prop
= scf_property_create(h
);
3794 val
= scf_value_create(h
);
3795 if (pg
== NULL
|| prop
== NULL
|| val
== NULL
)
3798 if (instance_get_or_add_pg(inst
, SCF_PG_RESTARTER_ACTIONS
,
3799 SCF_PG_RESTARTER_ACTIONS_TYPE
, SCF_PG_RESTARTER_ACTIONS_FLAGS
,
3803 if (get_astring_val(pg
, SCF_PROPERTY_AUX_FMRI
, aux_fmri
, size
,
3804 prop
, val
) != SCF_SUCCESS
)
3807 if (scf_parse_fmri(aux_fmri
, NULL
, NULL
, NULL
, NULL
, NULL
,
3808 NULL
) != SCF_SUCCESS
)
3815 scf_value_destroy(val
);
3816 scf_property_destroy(prop
);
3822 * Get instance's boolean value in restarter_actions/auxiliary_tty
3823 * Return -1 on failure
3826 restarter_inst_ractions_from_tty(scf_instance_t
*inst
)
3829 scf_propertygroup_t
*pg
;
3830 scf_property_t
*prop
;
3835 h
= scf_instance_handle(inst
);
3836 pg
= scf_pg_create(h
);
3837 prop
= scf_property_create(h
);
3838 val
= scf_value_create(h
);
3839 if (pg
== NULL
|| prop
== NULL
|| val
== NULL
)
3842 if (instance_get_or_add_pg(inst
, SCF_PG_RESTARTER_ACTIONS
,
3843 SCF_PG_RESTARTER_ACTIONS_TYPE
, SCF_PG_RESTARTER_ACTIONS_FLAGS
,
3847 if (get_boolean_val(pg
, SCF_PROPERTY_AUX_TTY
, &has_tty
, prop
,
3848 val
) != SCF_SUCCESS
)
3854 scf_value_destroy(val
);
3855 scf_property_destroy(prop
);
3861 restarter_inst_set_astring_prop(scf_instance_t
*inst
, const char *pgname
,
3862 const char *pgtype
, uint32_t pgflags
, const char *pname
, const char *str
)
3865 scf_propertygroup_t
*pg
;
3866 scf_transaction_t
*t
;
3867 scf_transaction_entry_t
*e
;
3871 h
= scf_instance_handle(inst
);
3873 pg
= scf_pg_create(h
);
3874 t
= scf_transaction_create(h
);
3875 e
= scf_entry_create(h
);
3876 v
= scf_value_create(h
);
3877 if (pg
== NULL
|| t
== NULL
|| e
== NULL
|| v
== NULL
)
3880 if (instance_get_or_add_pg(inst
, pgname
, pgtype
, pgflags
, pg
))
3883 if (scf_value_set_astring(v
, str
) != SCF_SUCCESS
)
3887 if (scf_transaction_start(t
, pg
) != 0)
3890 if (tx_set_value(t
, e
, pname
, SCF_TYPE_ASTRING
, v
) != 0)
3893 if ((r
= scf_transaction_commit(t
)) == 1)
3899 scf_transaction_reset(t
);
3900 if (scf_pg_update(pg
) == -1)
3906 scf_transaction_destroy(t
);
3907 scf_entry_destroy(e
);
3908 scf_value_destroy(v
);
3915 restarter_inst_set_aux_fmri(scf_instance_t
*inst
)
3918 scf_propertygroup_t
*pg
;
3919 scf_property_t
*prop
;
3922 size_t size
= scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
3925 if ((aux_fmri
= malloc(size
)) == NULL
)
3928 h
= scf_instance_handle(inst
);
3930 pg
= scf_pg_create(h
);
3931 prop
= scf_property_create(h
);
3932 val
= scf_value_create(h
);
3933 if (pg
== NULL
|| prop
== NULL
|| val
== NULL
)
3937 * Get auxiliary_fmri value from restarter_actions pg
3939 if (instance_get_or_add_pg(inst
, SCF_PG_RESTARTER_ACTIONS
,
3940 SCF_PG_RESTARTER_ACTIONS_TYPE
, SCF_PG_RESTARTER_ACTIONS_FLAGS
,
3944 if (get_astring_val(pg
, SCF_PROPERTY_AUX_FMRI
, aux_fmri
, size
,
3945 prop
, val
) != SCF_SUCCESS
)
3949 * Populate restarter/auxiliary_fmri with the obtained fmri.
3951 ret
= restarter_inst_set_astring_prop(inst
, SCF_PG_RESTARTER
,
3952 SCF_PG_RESTARTER_TYPE
, SCF_PG_RESTARTER_FLAGS
,
3953 SCF_PROPERTY_AUX_FMRI
, aux_fmri
);
3957 scf_value_destroy(val
);
3958 scf_property_destroy(prop
);
3964 restarter_inst_reset_aux_fmri(scf_instance_t
*inst
)
3966 return (scf_instance_delete_prop(inst
,
3967 SCF_PG_RESTARTER
, SCF_PROPERTY_AUX_FMRI
));
3971 restarter_inst_reset_ractions_aux_fmri(scf_instance_t
*inst
)
3973 return (scf_instance_delete_prop(inst
,
3974 SCF_PG_RESTARTER_ACTIONS
, SCF_PROPERTY_AUX_FMRI
));