8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / librestart / common / librestart.c
blobcebaf54884be94eb709af6d019b372d453bd752b
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
27 #include <libintl.h>
28 #include <librestart.h>
29 #include <librestart_priv.h>
30 #include <libscf.h>
31 #include <libscf_priv.h>
33 #include <assert.h>
34 #include <ctype.h>
35 #include <dlfcn.h>
36 #include <errno.h>
37 #include <exec_attr.h>
38 #include <grp.h>
39 #include <libsysevent.h>
40 #include <libuutil.h>
41 #include <limits.h>
42 #include <link.h>
43 #include <malloc.h>
44 #include <pool.h>
45 #include <priv.h>
46 #include <project.h>
47 #include <pthread.h>
48 #include <pwd.h>
49 #include <secdb.h>
50 #include <signal.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <syslog.h>
54 #include <sys/corectl.h>
55 #include <sys/machelf.h>
56 #include <sys/secflags.h>
57 #include <sys/task.h>
58 #include <sys/types.h>
59 #include <time.h>
60 #include <unistd.h>
61 #include <ucontext.h>
63 #define min(a, b) ((a) > (b) ? (b) : (a))
65 #define MKW_TRUE ":true"
66 #define MKW_KILL ":kill"
67 #define MKW_KILL_PROC ":kill_process"
69 #define ALLOCFAIL ((char *)"Allocation failure.")
70 #define RCBROKEN ((char *)"Repository connection broken.")
72 #define MAX_COMMIT_RETRIES 10
73 #define MAX_COMMIT_RETRY_INT (5 * 1000000) /* 5 seconds */
74 #define INITIAL_COMMIT_RETRY_INT (10000) /* 1/100th second */
77 * bad_fail() catches bugs in this and lower layers by reporting supposedly
78 * impossible function failures. The NDEBUG case keeps the strings out of the
79 * library but still calls abort() so we can root-cause from the coredump.
81 #ifndef NDEBUG
82 #define bad_fail(func, err) { \
83 (void) fprintf(stderr, \
84 "At %s:%d, %s() failed with unexpected error %d. Aborting.\n", \
85 __FILE__, __LINE__, (func), (err)); \
86 abort(); \
88 #else
89 #define bad_fail(func, err) abort()
90 #endif
92 struct restarter_event_handle {
93 char *reh_restarter_name;
94 char *reh_delegate_channel_name;
95 evchan_t *reh_delegate_channel;
96 char *reh_delegate_subscriber_id;
97 char *reh_master_channel_name;
98 evchan_t *reh_master_channel;
99 char *reh_master_subscriber_id;
100 int (*reh_handler)(restarter_event_t *);
103 struct restarter_event {
104 sysevent_t *re_sysevent;
105 restarter_event_type_t re_type;
106 char *re_instance_name;
107 restarter_event_handle_t *re_event_handle;
108 restarter_instance_state_t re_state;
109 restarter_instance_state_t re_next_state;
113 * Long reasons must all parse/read correctly in the following contexts:
115 * "A service instance transitioned state: %s."
116 * "A service failed: %s."
117 * "Reason: %s."
118 * "The service transitioned state (%s) and ..."
120 * With the exception of restart_str_none they must also fit the following
121 * moulds:
123 * "An instance transitioned because %s, and ..."
124 * "An instance transitioned to <new-state> because %s, and ..."
126 * Note that whoever is rendering the long message must provide the
127 * terminal punctuation - don't include it here. Similarly, do not
128 * provide an initial capital letter in reason-long.
130 * The long reason strings are Volatile - within the grammatical constraints
131 * above we may improve them as need be. The intention is that a consumer
132 * may blindly render the string along the lines of the above examples,
133 * but has no other guarantees as to the exact wording. Long reasons
134 * are localized.
136 * We define revisions of the set of short reason strings in use. Within
137 * a given revision, all short reasons are Committed. Consumers must check
138 * the revision in use before relying on the semantics of the short reason
139 * codes - if the version exceeds that which they are familiar with they should
140 * fail gracefully. Having checked for version compatability, a consumer
141 * is assured that
143 * "short_reason_A iff semantic_A", provided:
145 * . the restarter uses this short reason code at all,
146 * . the short reason is not "none" (which a restarter could
147 * specifiy for any transition semantics)
149 * To split/refine such a Committed semantic_A into further cases,
150 * we are required to bump the revision number. This should be an
151 * infrequent occurence. If you bump the revision number you may
152 * need to make corresponding changes in any source that calls
153 * restarter_str_version (e.g., FMA event generation).
155 * To add additional reasons to the set you must also bump the version
156 * number.
160 * The following describes revision 0 of the set of transition reasons.
161 * Read the preceding block comment before making any changes.
163 static const struct restarter_state_transition_reason restarter_str[] = {
165 * Any transition for which the restarter has not provided a reason.
168 restarter_str_none,
169 "none",
170 "the restarter gave no reason"
174 * A transition to maintenance state due to a
175 * 'svcadm mark maintenance <fmri>'. *Not* used if the libscf
176 * interface smf_maintain_instance(3SCF) is used to request maintenance.
179 restarter_str_administrative_request,
180 "administrative_request",
181 "maintenance was requested by an administrator"
185 * A transition to maintenance state if a repository inconsistency
186 * exists when the service/instance state is first read by startd
187 * into the graph engine (this can also happen during startd restart).
190 restarter_str_bad_repo_state,
191 "bad_repo_state",
192 "an SMF repository inconsistecy exists"
196 * A transition 'maintenance -> uninitialized' resulting always
197 * from 'svcadm clear <fmri>'. *Not* used if the libscf interface
198 * smf_restore_instance(3SCF) is used.
201 restarter_str_clear_request,
202 "clear_request",
203 "maintenance clear was requested by an administrator"
207 * A transition 'online -> offline' due to a process core dump.
210 restarter_str_ct_ev_core,
211 "ct_ev_core",
212 "a process dumped core"
216 * A transition 'online -> offline' due to an empty process contract,
217 * i.e., the last process in a contract type service has exited.
220 restarter_str_ct_ev_exit,
221 "ct_ev_exit",
222 "all processes in the service have exited"
226 * A transition 'online -> offline' due to a hardware error.
229 restarter_str_ct_ev_hwerr,
230 "ct_ev_hwerr",
231 "a process was killed due to uncorrectable hardware error"
235 * A transition 'online -> offline' due to a process in the service
236 * having received a fatal signal originating from outside the
237 * service process contract.
240 restarter_str_ct_ev_signal,
241 "ct_ev_signal",
242 "a process received a fatal signal from outside the service"
246 * A transition 'offline -> online' when all dependencies for the
247 * service have been met.
250 restarter_str_dependencies_satisfied,
251 "dependencies_satisfied",
252 "all dependencies have been satisfied"
256 * A transition 'online -> offline' because some dependency for the
257 * service is no-longer met.
260 restarter_str_dependency_activity,
261 "dependency_activity",
262 "a dependency activity required a stop"
266 * A transition to maintenance state due to a cycle in the
267 * service dependencies.
270 restarter_str_dependency_cycle,
271 "dependency_cycle",
272 "a dependency cycle exists"
276 * A transition 'online -> offline -> disabled' due to a
277 * 'svcadm disable [-t] <fmri>' or smf_disable_instance(3SCF) call.
280 restarter_str_disable_request,
281 "disable_request",
282 "a disable was requested"
286 * A transition 'disabled -> offline' due to a
287 * 'svcadm enable [-t] <fmri>' or smf_enable_instance(3SCF) call.
290 restarter_str_enable_request,
291 "enable_request",
292 "an enable was requested"
296 * A transition to maintenance state when a method fails
297 * repeatedly for a retryable reason.
300 restarter_str_fault_threshold_reached,
301 "fault_threshold_reached",
302 "a method is failing in a retryable manner but too often"
306 * A transition to uninitialized state when startd reads the service
307 * configuration and inserts it into the graph engine.
310 restarter_str_insert_in_graph,
311 "insert_in_graph",
312 "the instance was inserted in the graph"
316 * A transition to maintenance state due to an invalid dependency
317 * declared for the service.
320 restarter_str_invalid_dependency,
321 "invalid_dependency",
322 "a service has an invalid dependency"
326 * A transition to maintenance state because the service-declared
327 * restarter is invalid.
330 restarter_str_invalid_restarter,
331 "invalid_restarter",
332 "the service restarter is invalid"
336 * A transition to maintenance state because a restarter method
337 * exited with one of SMF_EXIT_ERR_CONFIG, SMF_EXIT_ERR_NOSMF,
338 * SMF_EXIT_ERR_PERM, or SMF_EXIT_ERR_FATAL.
341 restarter_str_method_failed,
342 "method_failed",
343 "a start, stop or refresh method failed"
347 * A transition 'uninitialized -> {disabled|offline}' after
348 * "insert_in_graph" to match the state configured in the
349 * repository.
352 restarter_str_per_configuration,
353 "per_configuration",
354 "the SMF repository configuration specifies this state"
358 * Refresh requested - no state change.
361 restarter_str_refresh,
362 NULL,
363 "a refresh was requested (no change of state)"
367 * A transition 'online -> offline -> online' due to a
368 * 'svcadm restart <fmri> or equivlaent libscf API call.
369 * Both the 'online -> offline' and 'offline -> online' transtions
370 * specify this reason.
373 restarter_str_restart_request,
374 "restart_request",
375 "a restart was requested"
379 * A transition to maintenance state because the start method is
380 * being executed successfully but too frequently.
383 restarter_str_restarting_too_quickly,
384 "restarting_too_quickly",
385 "the instance is restarting too quickly"
389 * A transition to maintenance state due a service requesting
390 * 'svcadm mark maintenance <fmri>' or equivalent libscf API call.
391 * A command line 'svcadm mark maintenance <fmri>' does not produce
392 * this reason - it produces administrative_request instead.
395 restarter_str_service_request,
396 "service_request",
397 "maintenance was requested by another service"
401 * An instanced inserted into the graph at its existing state
402 * during a startd restart - no state change.
405 restarter_str_startd_restart,
406 NULL,
407 "the instance was inserted in the graph due to startd restart"
411 uint32_t
412 restarter_str_version(void)
414 return (RESTARTER_STRING_VERSION);
417 const char *
418 restarter_get_str_short(restarter_str_t key)
420 int i;
421 for (i = 0; i < sizeof (restarter_str) /
422 sizeof (struct restarter_state_transition_reason); i++)
423 if (key == restarter_str[i].str_key)
424 return (restarter_str[i].str_short);
425 return (NULL);
428 const char *
429 restarter_get_str_long(restarter_str_t key)
431 int i;
432 for (i = 0; i < sizeof (restarter_str) /
433 sizeof (struct restarter_state_transition_reason); i++)
434 if (key == restarter_str[i].str_key)
435 return (dgettext(TEXT_DOMAIN,
436 restarter_str[i].str_long));
437 return (NULL);
441 * A static no memory error message mc_error_t structure
442 * to be used in cases when memory errors are to be returned
443 * This avoids the need to attempt to allocate memory for the
444 * message, therefore getting into a cycle of no memory failures.
446 mc_error_t mc_nomem_err = {
447 0, ENOMEM, sizeof ("Out of memory") - 1, "Out of memory"
450 static const char * const allocfail = "Allocation failure.\n";
451 static const char * const rcbroken = "Repository connection broken.\n";
453 static int method_context_safety = 0; /* Can safely call pools/projects. */
455 int ndebug = 1;
457 /* PRINTFLIKE3 */
458 static mc_error_t *
459 mc_error_create(mc_error_t *e, int type, const char *format, ...)
461 mc_error_t *le;
462 va_list args;
463 int size;
466 * If the type is ENOMEM and format is NULL, then
467 * go ahead and return the default nomem error.
468 * Otherwise, attempt to allocate the memory and if
469 * that fails then there is no reason to continue.
471 if (type == ENOMEM && format == NULL)
472 return (&mc_nomem_err);
474 if (e == NULL && (le = malloc(sizeof (mc_error_t))) == NULL)
475 return (&mc_nomem_err);
476 else
477 le = e;
479 le->type = type;
480 le->destroy = 1;
481 va_start(args, format);
482 size = vsnprintf(NULL, 0, format, args) + 1;
483 if (size >= RESTARTER_ERRMSGSZ) {
484 if ((le = realloc(e, sizeof (mc_error_t) +
485 (size - RESTARTER_ERRMSGSZ))) == NULL) {
486 size = RESTARTER_ERRMSGSZ - 1;
487 le = e;
491 le->size = size;
492 (void) vsnprintf(le->msg, le->size, format, args);
493 va_end(args);
495 return (le);
498 void
499 restarter_mc_error_destroy(mc_error_t *mc_err)
501 if (mc_err == NULL)
502 return;
505 * If the error messages was allocated then free.
507 if (mc_err->destroy) {
508 free(mc_err);
512 static void
513 free_restarter_event_handle(struct restarter_event_handle *h)
515 if (h == NULL)
516 return;
519 * Just free the memory -- don't unbind the sysevent handle,
520 * as otherwise events may be lost if this is just a restarter
521 * restart.
524 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);
535 free(h);
538 char *
539 _restarter_get_channel_name(const char *fmri, int type)
541 char *name;
542 char *chan_name = malloc(MAX_CHNAME_LEN);
543 char prefix_name[3];
544 int i;
546 if (chan_name == NULL)
547 return (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_");
553 else {
554 free(chan_name);
555 return (NULL);
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) {
572 free(chan_name);
573 return (NULL);
575 i = 0;
576 while (name[i]) {
577 if (name[i] == '/') {
578 name[i] = '_';
581 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) {
590 free(chan_name);
591 chan_name = NULL;
594 free(name);
595 return (chan_name);
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;
604 int ret = 0;
606 e = uu_zalloc(sizeof (restarter_event_t));
607 if (e == NULL)
608 uu_die(allocfail);
609 e->re_event_handle = h;
610 e->re_sysevent = syse;
612 if (sysevent_get_attr_list(syse, &attr_list) != 0)
613 uu_die(allocfail);
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);
622 ret = 0;
623 } else {
624 ret = h->reh_handler(e);
627 uu_free(e);
628 nvlist_free(attr_list);
629 return (ret);
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;
655 size_t sz;
656 int err;
658 if (version != RESTARTER_EVENT_VERSION)
659 return (ENOTSUP);
661 if (restarter_name == NULL || event_handler == NULL)
662 return (EINVAL);
664 if (flags & RESTARTER_FLAG_DEBUG)
665 ndebug++;
667 if ((h = uu_zalloc(sizeof (restarter_event_handle_t))) == NULL)
668 return (ENOMEM);
670 h->reh_delegate_subscriber_id = malloc(MAX_SUBID_LEN);
671 h->reh_master_subscriber_id = malloc(MAX_SUBID_LEN);
672 h->reh_restarter_name = strdup(restarter_name);
673 if (h->reh_delegate_subscriber_id == NULL ||
674 h->reh_master_subscriber_id == NULL ||
675 h->reh_restarter_name == NULL) {
676 free_restarter_event_handle(h);
677 return (ENOMEM);
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);
695 return (ENOMEM);
698 if (sysevent_evc_bind(h->reh_delegate_channel_name,
699 &h->reh_delegate_channel, EVCH_CREAT|EVCH_HOLD_PEND) != 0) {
700 err = errno;
701 assert(err != EINVAL);
702 assert(err != ENOENT);
703 free_restarter_event_handle(h);
704 return (err);
707 if (sysevent_evc_bind(h->reh_master_channel_name,
708 &h->reh_master_channel, EVCH_CREAT|EVCH_HOLD_PEND) != 0) {
709 err = errno;
710 assert(err != EINVAL);
711 assert(err != ENOENT);
712 free_restarter_event_handle(h);
713 return (err);
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) {
724 err = errno;
725 assert(err != EINVAL);
726 free_restarter_event_handle(h);
727 return (err);
730 *rehp = h;
731 return (0);
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)
744 assert(e != NULL);
745 return (e->re_type);
748 ssize_t
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)
759 if (e == NULL)
760 return (-1);
761 *state = e->re_state;
762 *next_state = e->re_next_state;
763 return (0);
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)
786 int retries, ret;
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);
792 if (ret == 0)
793 break;
795 switch (ret) {
796 case EAGAIN:
797 /* Queue is full */
798 (void) usleep(retry_int);
800 retry_int = min(retry_int * 2, MAX_COMMIT_RETRY_INT);
801 break;
803 case EINVAL:
804 ret = EBADF;
805 /* FALLTHROUGH */
807 case EFAULT:
808 case ENOMEM:
809 return (ret);
811 case EOVERFLOW:
812 default:
813 /* internal error - abort */
814 bad_fail("sysevent_evc_publish", ret);
818 if (retries == MAX_COMMIT_RETRIES)
819 ret = ENOSPC;
821 return (ret);
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,
848 restarter_str_t aux)
850 nvlist_t *attr;
851 scf_handle_t *scf_h;
852 instance_data_t id;
853 int ret = 0;
854 const char *p = restarter_get_str_short(aux);
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:
863 return (EPROTO);
865 case SCF_ERROR_NO_MEMORY:
866 return (ENOMEM);
868 default:
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:
877 return (ENOTACTIVE);
879 case SCF_ERROR_NO_RESOURCES:
880 return (ENOMEM);
882 case SCF_ERROR_INVALID_ARGUMENT:
883 case SCF_ERROR_IN_USE:
884 default:
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)
892 != 0 ||
893 nvlist_add_int32(attr, RESTARTER_NAME_ERROR, e) != 0 ||
894 nvlist_add_string(attr, RESTARTER_NAME_INSTANCE, inst) != 0 ||
895 nvlist_add_int32(attr, RESTARTER_NAME_REASON, aux) != 0) {
896 ret = ENOMEM;
897 } else {
898 id.i_fmri = inst;
899 id.i_state = cur_state;
900 id.i_next_state = next_state;
902 ret = _restarter_commit_states(scf_h, &id, new_cur_state,
903 new_next_state, p);
905 if (ret == 0) {
906 ret = restarter_event_publish_retry(
907 h->reh_master_channel, "master", "state_change",
908 "com.sun", "librestart", attr, EVCH_NOSLEEP);
912 nvlist_free(attr);
913 (void) scf_handle_unbind(scf_h);
914 scf_handle_destroy(scf_h);
916 return (ret);
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);
938 else {
939 return (RESTARTER_STATE_NONE);
943 ssize_t
944 restarter_state_to_string(restarter_instance_state_t state, char *string,
945 size_t len)
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,
957 len));
958 else if (state == RESTARTER_STATE_DISABLED)
959 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_DISABLED,
960 len));
961 else if (state == RESTARTER_STATE_ONLINE)
962 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_ONLINE, len));
963 else if (state == RESTARTER_STATE_DEGRADED)
964 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_DEGRADED,
965 len));
966 else
967 return ((ssize_t)strlcpy(string, "unknown", len));
971 * Sets pg to the name property group of s_inst. If it doesn't exist, it is
972 * added.
974 * Fails with
975 * ECONNABORTED - repository disconnection or unknown libscf error
976 * EBADF - inst is not set
977 * ECANCELED - inst is deleted
978 * EPERM - permission is denied
979 * EACCES - backend denied access
980 * EROFS - backend readonly
982 static int
983 instance_get_or_add_pg(scf_instance_t *inst, const char *name,
984 const char *type, uint32_t flags, scf_propertygroup_t *pg)
986 again:
987 if (scf_instance_get_pg(inst, name, pg) == 0)
988 return (0);
990 switch (scf_error()) {
991 case SCF_ERROR_CONNECTION_BROKEN:
992 default:
993 return (ECONNABORTED);
995 case SCF_ERROR_NOT_SET:
996 return (EBADF);
998 case SCF_ERROR_DELETED:
999 return (ECANCELED);
1001 case SCF_ERROR_NOT_FOUND:
1002 break;
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)
1010 return (0);
1012 switch (scf_error()) {
1013 case SCF_ERROR_CONNECTION_BROKEN:
1014 default:
1015 return (ECONNABORTED);
1017 case SCF_ERROR_DELETED:
1018 return (ECANCELED);
1020 case SCF_ERROR_EXISTS:
1021 goto again;
1023 case SCF_ERROR_PERMISSION_DENIED:
1024 return (EPERM);
1026 case SCF_ERROR_BACKEND_ACCESS:
1027 return (EACCES);
1029 case SCF_ERROR_BACKEND_READONLY:
1030 return (EROFS);
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());
1038 return (0);
1042 * Fails with
1043 * ECONNABORTED
1044 * ECANCELED - pg was deleted
1046 static int
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)
1050 int r;
1052 for (;;) {
1053 if (scf_transaction_property_change_type(tx, ent, pname,
1054 ty) == 0)
1055 break;
1057 switch (scf_error()) {
1058 case SCF_ERROR_CONNECTION_BROKEN:
1059 default:
1060 return (ECONNABORTED);
1062 case SCF_ERROR_DELETED:
1063 return (ECANCELED);
1065 case SCF_ERROR_NOT_FOUND:
1066 break;
1068 case SCF_ERROR_HANDLE_MISMATCH:
1069 case SCF_ERROR_INVALID_ARGUMENT:
1070 case SCF_ERROR_IN_USE:
1071 case SCF_ERROR_NOT_SET:
1072 bad_fail("scf_transaction_property_change_type",
1073 scf_error());
1076 if (scf_transaction_property_new(tx, ent, pname, ty) == 0)
1077 break;
1079 switch (scf_error()) {
1080 case SCF_ERROR_CONNECTION_BROKEN:
1081 default:
1082 return (ECONNABORTED);
1084 case SCF_ERROR_DELETED:
1085 return (ECANCELED);
1087 case SCF_ERROR_EXISTS:
1088 break;
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);
1099 assert(r == 0);
1101 return (0);
1105 * Commit new_state, new_next_state, and aux to the repository for id. If
1106 * successful, also set id's state and next-state as given, and return 0.
1107 * Fails with
1108 * ENOMEM - out of memory
1109 * ECONNABORTED - repository connection broken
1110 * - unknown libscf error
1111 * EINVAL - id->i_fmri is invalid or not an instance FMRI
1112 * ENOENT - id->i_fmri does not exist
1113 * EPERM - insufficient permissions
1114 * EACCES - backend access denied
1115 * EROFS - backend is readonly
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];
1126 int ret = 0, r;
1127 struct timeval now;
1128 ssize_t 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) {
1151 ret = ENOMEM;
1152 goto out;
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);
1169 assert(ret != -1);
1171 if (scf_handle_decode_fmri(h, id->i_fmri, NULL, NULL, s_inst,
1172 NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
1173 switch (scf_error()) {
1174 case SCF_ERROR_CONNECTION_BROKEN:
1175 default:
1176 ret = ECONNABORTED;
1177 break;
1179 case SCF_ERROR_INVALID_ARGUMENT:
1180 case SCF_ERROR_CONSTRAINT_VIOLATED:
1181 ret = EINVAL;
1182 break;
1184 case SCF_ERROR_NOT_FOUND:
1185 ret = ENOENT;
1186 break;
1188 case SCF_ERROR_HANDLE_MISMATCH:
1189 bad_fail("scf_handle_decode_fmri", scf_error());
1191 goto out;
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());
1199 if (aux) {
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());
1207 add_pg:
1208 switch (r = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
1209 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg)) {
1210 case 0:
1211 break;
1213 case ECONNABORTED:
1214 case EPERM:
1215 case EACCES:
1216 case EROFS:
1217 ret = r;
1218 goto out;
1220 case ECANCELED:
1221 ret = ENOENT;
1222 goto out;
1224 case EBADF:
1225 default:
1226 bad_fail("instance_get_or_add_pg", r);
1229 for (;;) {
1230 if (scf_transaction_start(t, pg) != 0) {
1231 switch (scf_error()) {
1232 case SCF_ERROR_CONNECTION_BROKEN:
1233 default:
1234 ret = ECONNABORTED;
1235 goto out;
1237 case SCF_ERROR_NOT_SET:
1238 goto add_pg;
1240 case SCF_ERROR_PERMISSION_DENIED:
1241 ret = EPERM;
1242 goto out;
1244 case SCF_ERROR_BACKEND_ACCESS:
1245 ret = EACCES;
1246 goto out;
1248 case SCF_ERROR_BACKEND_READONLY:
1249 ret = EROFS;
1250 goto out;
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) {
1264 switch (r) {
1265 case ECONNABORTED:
1266 ret = ECONNABORTED;
1267 goto out;
1269 case ECANCELED:
1270 scf_transaction_reset(t);
1271 goto add_pg;
1273 default:
1274 bad_fail("tx_set_value", r);
1278 if (aux) {
1279 if ((r = tx_set_value(t, t_aux, SCF_PROPERTY_AUX_STATE,
1280 SCF_TYPE_ASTRING, v_aux)) != 0) {
1281 switch (r) {
1282 case ECONNABORTED:
1283 ret = ECONNABORTED;
1284 goto out;
1286 case ECANCELED:
1287 scf_transaction_reset(t);
1288 goto add_pg;
1290 default:
1291 bad_fail("tx_set_value", r);
1296 ret = scf_transaction_commit(t);
1297 if (ret == 1)
1298 break;
1299 if (ret == -1) {
1300 switch (scf_error()) {
1301 case SCF_ERROR_CONNECTION_BROKEN:
1302 default:
1303 ret = ECONNABORTED;
1304 goto out;
1306 case SCF_ERROR_PERMISSION_DENIED:
1307 ret = EPERM;
1308 goto out;
1310 case SCF_ERROR_BACKEND_ACCESS:
1311 ret = EACCES;
1312 goto out;
1314 case SCF_ERROR_BACKEND_READONLY:
1315 ret = EROFS;
1316 goto out;
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:
1327 default:
1328 ret = ECONNABORTED;
1329 goto out;
1331 case SCF_ERROR_NOT_SET:
1332 goto add_pg;
1337 id->i_state = new_state;
1338 id->i_next_state = new_state_next;
1339 ret = 0;
1341 out:
1342 scf_transaction_destroy(t);
1343 scf_entry_destroy(t_state);
1344 scf_entry_destroy(t_state_next);
1345 scf_entry_destroy(t_stime);
1346 scf_entry_destroy(t_aux);
1347 scf_value_destroy(v_state);
1348 scf_value_destroy(v_state_next);
1349 scf_value_destroy(v_stime);
1350 scf_value_destroy(v_aux);
1351 scf_pg_destroy(pg);
1352 scf_instance_destroy(s_inst);
1354 return (ret);
1358 * Fails with
1359 * EINVAL - type is invalid
1360 * ENOMEM
1361 * ECONNABORTED - repository connection broken
1362 * EBADF - s_inst is not set
1363 * ECANCELED - s_inst is deleted
1364 * EPERM - permission denied
1365 * EACCES - backend access denied
1366 * EROFS - backend readonly
1369 restarter_remove_contract(scf_instance_t *s_inst, ctid_t contract_id,
1370 restarter_contract_type_t type)
1372 scf_handle_t *h;
1373 scf_transaction_t *t = NULL;
1374 scf_transaction_entry_t *t_cid = NULL;
1375 scf_propertygroup_t *pg = NULL;
1376 scf_property_t *prop = NULL;
1377 scf_value_t *val;
1378 scf_iter_t *iter = NULL;
1379 const char *pname;
1380 int ret = 0, primary;
1381 uint64_t c;
1383 switch (type) {
1384 case RESTARTER_CONTRACT_PRIMARY:
1385 primary = 1;
1386 break;
1387 case RESTARTER_CONTRACT_TRANSIENT:
1388 primary = 0;
1389 break;
1390 default:
1391 return (EINVAL);
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) {
1402 ret = ENOMEM;
1403 goto remove_contract_cleanup;
1406 add:
1407 scf_transaction_destroy_children(t);
1408 ret = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
1409 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
1410 if (ret != 0)
1411 goto remove_contract_cleanup;
1413 pname = primary? SCF_PROPERTY_CONTRACT :
1414 SCF_PROPERTY_TRANSIENT_CONTRACT;
1416 for (;;) {
1417 if (scf_transaction_start(t, pg) != 0) {
1418 switch (scf_error()) {
1419 case SCF_ERROR_CONNECTION_BROKEN:
1420 default:
1421 ret = ECONNABORTED;
1422 goto remove_contract_cleanup;
1424 case SCF_ERROR_DELETED:
1425 goto add;
1427 case SCF_ERROR_PERMISSION_DENIED:
1428 ret = EPERM;
1429 goto remove_contract_cleanup;
1431 case SCF_ERROR_BACKEND_ACCESS:
1432 ret = EACCES;
1433 goto remove_contract_cleanup;
1435 case SCF_ERROR_BACKEND_READONLY:
1436 ret = EROFS;
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) {
1449 replace:
1450 if (scf_transaction_property_change_type(t, t_cid,
1451 pname, SCF_TYPE_COUNT) != 0) {
1452 switch (scf_error()) {
1453 case SCF_ERROR_CONNECTION_BROKEN:
1454 default:
1455 ret = ECONNABORTED;
1456 goto remove_contract_cleanup;
1458 case SCF_ERROR_DELETED:
1459 scf_entry_destroy(t_cid);
1460 goto add;
1462 case SCF_ERROR_NOT_FOUND:
1463 goto new;
1465 case SCF_ERROR_HANDLE_MISMATCH:
1466 case SCF_ERROR_INVALID_ARGUMENT:
1467 case SCF_ERROR_IN_USE:
1468 case SCF_ERROR_NOT_SET:
1469 bad_fail(
1470 "scf_transaction_property_changetype",
1471 scf_error());
1475 if (scf_property_is_type(prop, SCF_TYPE_COUNT) == 0) {
1476 if (scf_iter_property_values(iter, prop) != 0) {
1477 switch (scf_error()) {
1478 case SCF_ERROR_CONNECTION_BROKEN:
1479 default:
1480 ret = ECONNABORTED;
1481 goto remove_contract_cleanup;
1483 case SCF_ERROR_NOT_SET:
1484 case SCF_ERROR_HANDLE_MISMATCH:
1485 bad_fail(
1486 "scf_iter_property_values",
1487 scf_error());
1491 next_val:
1492 val = scf_value_create(h);
1493 if (val == NULL) {
1494 assert(scf_error() ==
1495 SCF_ERROR_NO_MEMORY);
1496 ret = ENOMEM;
1497 goto remove_contract_cleanup;
1500 ret = scf_iter_next_value(iter, val);
1501 if (ret == -1) {
1502 switch (scf_error()) {
1503 case SCF_ERROR_CONNECTION_BROKEN:
1504 ret = ECONNABORTED;
1505 goto remove_contract_cleanup;
1507 case SCF_ERROR_DELETED:
1508 scf_value_destroy(val);
1509 goto add;
1511 case SCF_ERROR_HANDLE_MISMATCH:
1512 case SCF_ERROR_INVALID_ARGUMENT:
1513 case SCF_ERROR_PERMISSION_DENIED:
1514 default:
1515 bad_fail("scf_iter_next_value",
1516 scf_error());
1520 if (ret == 1) {
1521 ret = scf_value_get_count(val, &c);
1522 assert(ret == 0);
1524 if (c != contract_id) {
1525 ret = scf_entry_add_value(t_cid,
1526 val);
1527 assert(ret == 0);
1528 } else {
1529 scf_value_destroy(val);
1532 goto next_val;
1535 scf_value_destroy(val);
1536 } else {
1537 switch (scf_error()) {
1538 case SCF_ERROR_CONNECTION_BROKEN:
1539 default:
1540 ret = ECONNABORTED;
1541 goto remove_contract_cleanup;
1543 case SCF_ERROR_TYPE_MISMATCH:
1544 break;
1546 case SCF_ERROR_INVALID_ARGUMENT:
1547 case SCF_ERROR_NOT_SET:
1548 bad_fail("scf_property_is_type",
1549 scf_error());
1552 } else {
1553 switch (scf_error()) {
1554 case SCF_ERROR_CONNECTION_BROKEN:
1555 default:
1556 ret = ECONNABORTED;
1557 goto remove_contract_cleanup;
1559 case SCF_ERROR_DELETED:
1560 scf_entry_destroy(t_cid);
1561 goto add;
1563 case SCF_ERROR_NOT_FOUND:
1564 break;
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());
1572 new:
1573 if (scf_transaction_property_new(t, t_cid, pname,
1574 SCF_TYPE_COUNT) != 0) {
1575 switch (scf_error()) {
1576 case SCF_ERROR_CONNECTION_BROKEN:
1577 default:
1578 ret = ECONNABORTED;
1579 goto remove_contract_cleanup;
1581 case SCF_ERROR_DELETED:
1582 scf_entry_destroy(t_cid);
1583 goto add;
1585 case SCF_ERROR_EXISTS:
1586 goto replace;
1588 case SCF_ERROR_HANDLE_MISMATCH:
1589 case SCF_ERROR_INVALID_ARGUMENT:
1590 case SCF_ERROR_NOT_SET:
1591 bad_fail("scf_transaction_property_new",
1592 scf_error());
1597 ret = scf_transaction_commit(t);
1598 if (ret == -1) {
1599 switch (scf_error()) {
1600 case SCF_ERROR_CONNECTION_BROKEN:
1601 default:
1602 ret = ECONNABORTED;
1603 goto remove_contract_cleanup;
1605 case SCF_ERROR_DELETED:
1606 goto add;
1608 case SCF_ERROR_PERMISSION_DENIED:
1609 ret = EPERM;
1610 goto remove_contract_cleanup;
1612 case SCF_ERROR_BACKEND_ACCESS:
1613 ret = EACCES;
1614 goto remove_contract_cleanup;
1616 case SCF_ERROR_BACKEND_READONLY:
1617 ret = EROFS;
1618 goto remove_contract_cleanup;
1620 case SCF_ERROR_NOT_SET:
1621 bad_fail("scf_transaction_commit", scf_error());
1624 if (ret == 1) {
1625 ret = 0;
1626 break;
1629 scf_transaction_destroy_children(t);
1630 if (scf_pg_update(pg) == -1) {
1631 switch (scf_error()) {
1632 case SCF_ERROR_CONNECTION_BROKEN:
1633 default:
1634 ret = ECONNABORTED;
1635 goto remove_contract_cleanup;
1637 case SCF_ERROR_DELETED:
1638 goto add;
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);
1651 scf_pg_destroy(pg);
1653 return (ret);
1657 * Fails with
1658 * EINVAL - type is invalid
1659 * ENOMEM
1660 * ECONNABORTED - repository disconnection
1661 * EBADF - s_inst is not set
1662 * ECANCELED - s_inst is deleted
1663 * EPERM
1664 * EACCES
1665 * EROFS
1668 restarter_store_contract(scf_instance_t *s_inst, ctid_t contract_id,
1669 restarter_contract_type_t type)
1671 scf_handle_t *h;
1672 scf_transaction_t *t = NULL;
1673 scf_transaction_entry_t *t_cid = NULL;
1674 scf_value_t *val;
1675 scf_propertygroup_t *pg = NULL;
1676 scf_property_t *prop = NULL;
1677 scf_iter_t *iter = NULL;
1678 const char *pname;
1679 int ret = 0, primary;
1681 if (type == RESTARTER_CONTRACT_PRIMARY)
1682 primary = 1;
1683 else if (type == RESTARTER_CONTRACT_TRANSIENT)
1684 primary = 0;
1685 else
1686 return (EINVAL);
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) {
1696 ret = ENOMEM;
1697 goto out;
1700 add:
1701 scf_transaction_destroy_children(t);
1702 ret = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
1703 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
1704 if (ret != 0)
1705 goto out;
1707 pname = primary ? SCF_PROPERTY_CONTRACT :
1708 SCF_PROPERTY_TRANSIENT_CONTRACT;
1710 for (;;) {
1711 if (scf_transaction_start(t, pg) != 0) {
1712 switch (scf_error()) {
1713 case SCF_ERROR_CONNECTION_BROKEN:
1714 default:
1715 ret = ECONNABORTED;
1716 goto out;
1718 case SCF_ERROR_DELETED:
1719 goto add;
1721 case SCF_ERROR_PERMISSION_DENIED:
1722 ret = EPERM;
1723 goto out;
1725 case SCF_ERROR_BACKEND_ACCESS:
1726 ret = EACCES;
1727 goto out;
1729 case SCF_ERROR_BACKEND_READONLY:
1730 ret = EROFS;
1731 goto out;
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) {
1742 ret = ENOMEM;
1743 goto out;
1746 if (scf_pg_get_property(pg, pname, prop) == 0) {
1747 replace:
1748 if (scf_transaction_property_change_type(t, t_cid,
1749 pname, SCF_TYPE_COUNT) != 0) {
1750 switch (scf_error()) {
1751 case SCF_ERROR_CONNECTION_BROKEN:
1752 default:
1753 ret = ECONNABORTED;
1754 goto out;
1756 case SCF_ERROR_DELETED:
1757 scf_entry_destroy(t_cid);
1758 goto add;
1760 case SCF_ERROR_NOT_FOUND:
1761 goto new;
1763 case SCF_ERROR_HANDLE_MISMATCH:
1764 case SCF_ERROR_INVALID_ARGUMENT:
1765 case SCF_ERROR_IN_USE:
1766 case SCF_ERROR_NOT_SET:
1767 bad_fail(
1768 "scf_transaction_propert_change_type",
1769 scf_error());
1773 if (scf_property_is_type(prop, SCF_TYPE_COUNT) == 0) {
1774 if (scf_iter_property_values(iter, prop) != 0) {
1775 switch (scf_error()) {
1776 case SCF_ERROR_CONNECTION_BROKEN:
1777 default:
1778 ret = ECONNABORTED;
1779 goto out;
1781 case SCF_ERROR_NOT_SET:
1782 case SCF_ERROR_HANDLE_MISMATCH:
1783 bad_fail(
1784 "scf_iter_property_values",
1785 scf_error());
1789 next_val:
1790 val = scf_value_create(h);
1791 if (val == NULL) {
1792 assert(scf_error() ==
1793 SCF_ERROR_NO_MEMORY);
1794 ret = ENOMEM;
1795 goto out;
1798 ret = scf_iter_next_value(iter, val);
1799 if (ret == -1) {
1800 switch (scf_error()) {
1801 case SCF_ERROR_CONNECTION_BROKEN:
1802 default:
1803 ret = ECONNABORTED;
1804 goto out;
1806 case SCF_ERROR_DELETED:
1807 scf_value_destroy(val);
1808 goto add;
1810 case SCF_ERROR_HANDLE_MISMATCH:
1811 case SCF_ERROR_INVALID_ARGUMENT:
1812 case SCF_ERROR_PERMISSION_DENIED:
1813 bad_fail(
1814 "scf_iter_next_value",
1815 scf_error());
1819 if (ret == 1) {
1820 ret = scf_entry_add_value(t_cid, val);
1821 assert(ret == 0);
1823 goto next_val;
1826 scf_value_destroy(val);
1827 } else {
1828 switch (scf_error()) {
1829 case SCF_ERROR_CONNECTION_BROKEN:
1830 default:
1831 ret = ECONNABORTED;
1832 goto out;
1834 case SCF_ERROR_TYPE_MISMATCH:
1835 break;
1837 case SCF_ERROR_INVALID_ARGUMENT:
1838 case SCF_ERROR_NOT_SET:
1839 bad_fail("scf_property_is_type",
1840 scf_error());
1843 } else {
1844 switch (scf_error()) {
1845 case SCF_ERROR_CONNECTION_BROKEN:
1846 default:
1847 ret = ECONNABORTED;
1848 goto out;
1850 case SCF_ERROR_DELETED:
1851 scf_entry_destroy(t_cid);
1852 goto add;
1854 case SCF_ERROR_NOT_FOUND:
1855 break;
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());
1863 new:
1864 if (scf_transaction_property_new(t, t_cid, pname,
1865 SCF_TYPE_COUNT) != 0) {
1866 switch (scf_error()) {
1867 case SCF_ERROR_CONNECTION_BROKEN:
1868 default:
1869 ret = ECONNABORTED;
1870 goto out;
1872 case SCF_ERROR_DELETED:
1873 scf_entry_destroy(t_cid);
1874 goto add;
1876 case SCF_ERROR_EXISTS:
1877 goto replace;
1879 case SCF_ERROR_HANDLE_MISMATCH:
1880 case SCF_ERROR_INVALID_ARGUMENT:
1881 case SCF_ERROR_NOT_SET:
1882 bad_fail("scf_transaction_property_new",
1883 scf_error());
1888 val = scf_value_create(h);
1889 if (val == NULL) {
1890 assert(scf_error() == SCF_ERROR_NO_MEMORY);
1891 ret = ENOMEM;
1892 goto out;
1895 scf_value_set_count(val, contract_id);
1896 ret = scf_entry_add_value(t_cid, val);
1897 assert(ret == 0);
1899 ret = scf_transaction_commit(t);
1900 if (ret == -1) {
1901 switch (scf_error()) {
1902 case SCF_ERROR_CONNECTION_BROKEN:
1903 default:
1904 ret = ECONNABORTED;
1905 goto out;
1907 case SCF_ERROR_DELETED:
1908 goto add;
1910 case SCF_ERROR_PERMISSION_DENIED:
1911 ret = EPERM;
1912 goto out;
1914 case SCF_ERROR_BACKEND_ACCESS:
1915 ret = EACCES;
1916 goto out;
1918 case SCF_ERROR_BACKEND_READONLY:
1919 ret = EROFS;
1920 goto out;
1922 case SCF_ERROR_NOT_SET:
1923 bad_fail("scf_transaction_commit", scf_error());
1926 if (ret == 1) {
1927 ret = 0;
1928 break;
1931 scf_transaction_destroy_children(t);
1932 if (scf_pg_update(pg) == -1) {
1933 switch (scf_error()) {
1934 case SCF_ERROR_CONNECTION_BROKEN:
1935 default:
1936 ret = ECONNABORTED;
1937 goto out;
1939 case SCF_ERROR_DELETED:
1940 goto add;
1942 case SCF_ERROR_NOT_SET:
1943 bad_fail("scf_pg_update", scf_error());
1948 out:
1949 scf_transaction_destroy_children(t);
1950 scf_transaction_destroy(t);
1951 scf_iter_destroy(iter);
1952 scf_property_destroy(prop);
1953 scf_pg_destroy(pg);
1955 return (ret);
1959 restarter_rm_libs_loadable()
1961 void *libhndl;
1963 if (method_context_safety)
1964 return (1);
1966 if ((libhndl = dlopen("libpool.so", RTLD_LAZY | RTLD_LOCAL)) == NULL)
1967 return (0);
1969 (void) dlclose(libhndl);
1971 if ((libhndl = dlopen("libproject.so", RTLD_LAZY | RTLD_LOCAL)) == NULL)
1972 return (0);
1974 (void) dlclose(libhndl);
1976 method_context_safety = 1;
1978 return (1);
1981 static int
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)
1985 ssize_t szret;
1987 if (pg == NULL)
1988 return (-1);
1990 if (scf_pg_get_property(pg, name, prop) != SCF_SUCCESS) {
1991 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
1992 uu_die(rcbroken);
1993 return (-1);
1996 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
1997 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
1998 uu_die(rcbroken);
1999 return (-1);
2002 szret = scf_value_get_astring(val, buf, bufsz);
2004 return (szret >= 0 ? 0 : -1);
2007 static int
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)
2013 uu_die(rcbroken);
2014 return (-1);
2017 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
2018 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
2019 uu_die(rcbroken);
2020 return (-1);
2023 if (scf_value_get_boolean(val, b))
2024 return (-1);
2026 return (0);
2030 * Try to load mcp->pwd, if it isn't already.
2031 * Fails with
2032 * ENOMEM - malloc() failed
2033 * ENOENT - no entry found
2034 * EIO - I/O error
2035 * EMFILE - process out of file descriptors
2036 * ENFILE - system out of file handles
2038 static int
2039 lookup_pwd(struct method_context *mcp)
2041 struct passwd *pwdp;
2043 if (mcp->pwbuf != NULL && mcp->pwd.pw_uid == mcp->uid)
2044 return (0);
2046 if (mcp->pwbuf == NULL) {
2047 mcp->pwbufsz = sysconf(_SC_GETPW_R_SIZE_MAX);
2048 assert(mcp->pwbufsz >= 0);
2049 mcp->pwbuf = malloc(mcp->pwbufsz);
2050 if (mcp->pwbuf == NULL)
2051 return (ENOMEM);
2054 do {
2055 errno = 0;
2056 pwdp = getpwuid_r(mcp->uid, &mcp->pwd, mcp->pwbuf,
2057 mcp->pwbufsz);
2058 } while (pwdp == NULL && errno == EINTR);
2059 if (pwdp != NULL)
2060 return (0);
2062 free(mcp->pwbuf);
2063 mcp->pwbuf = NULL;
2065 switch (errno) {
2066 case 0:
2067 default:
2069 * Until bug 5065780 is fixed, getpwuid_r() can fail with
2070 * ENOENT, particularly on the miniroot. Since the
2071 * documentation is inaccurate, we'll return ENOENT for unknown
2072 * errors.
2074 return (ENOENT);
2076 case EIO:
2077 case EMFILE:
2078 case ENFILE:
2079 return (errno);
2081 case ERANGE:
2082 bad_fail("getpwuid_r", errno);
2083 /* NOTREACHED */
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])) {
2100 uid_t uid;
2101 char *cp;
2103 errno = 0;
2104 uid = strtol(str, &cp, 10);
2106 if (uid == 0 && errno != 0) {
2107 assert(errno != EINVAL);
2108 return (errno);
2111 for (; *cp != '\0'; ++cp)
2112 if (*cp != ' ' || *cp != '\t')
2113 return (EINVAL);
2115 if (uid > UID_MAX)
2116 return (EINVAL);
2118 *uidp = uid;
2119 return (0);
2120 } else {
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)
2127 return (ENOMEM);
2130 do {
2131 errno = 0;
2132 pwdp =
2133 getpwnam_r(str, &ci->pwd, ci->pwbuf, ci->pwbufsz);
2134 } while (pwdp == NULL && errno == EINTR);
2136 if (pwdp != NULL) {
2137 *uidp = ci->pwd.pw_uid;
2138 return (0);
2139 } else {
2140 free(ci->pwbuf);
2141 ci->pwbuf = NULL;
2142 switch (errno) {
2143 case 0:
2144 return (ENOENT);
2146 case ENOENT:
2147 case EIO:
2148 case EMFILE:
2149 case ENFILE:
2150 return (errno);
2152 case ERANGE:
2153 default:
2154 bad_fail("getpwnam_r", errno);
2155 /* NOTREACHED */
2161 gid_t
2162 get_gid(const char *str)
2164 if (isdigit(str[0])) {
2165 gid_t gid;
2166 char *cp;
2168 errno = 0;
2169 gid = strtol(str, &cp, 10);
2171 if (gid == 0 && errno != 0)
2172 return ((gid_t)-1);
2174 for (; *cp != '\0'; ++cp)
2175 if (*cp != ' ' || *cp != '\t')
2176 return ((gid_t)-1);
2178 return (gid);
2179 } else {
2180 struct group grp, *ret;
2181 char *buffer;
2182 size_t buflen;
2184 buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
2185 buffer = malloc(buflen);
2186 if (buffer == NULL)
2187 uu_die(allocfail);
2189 errno = 0;
2190 ret = getgrnam_r(str, &grp, buffer, buflen);
2191 free(buffer);
2193 return (ret == NULL ? (gid_t)-1 : grp.gr_gid);
2198 * Fails with
2199 * ENOMEM - out of memory
2200 * ENOENT - no passwd entry
2201 * no project entry
2202 * EIO - an I/O error occurred
2203 * EMFILE - the process is out of file descriptors
2204 * ENFILE - the system is out of file handles
2205 * ERANGE - the project id is out of range
2206 * EINVAL - str is invalid
2207 * E2BIG - the project entry was too big
2208 * -1 - the name service switch is misconfigured
2211 get_projid(const char *str, struct method_context *cip)
2213 int ret;
2214 void *buf;
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;
2222 return (0);
2225 switch (ret = lookup_pwd(cip)) {
2226 case 0:
2227 break;
2229 case ENOMEM:
2230 case ENOENT:
2231 case EIO:
2232 case EMFILE:
2233 case ENFILE:
2234 return (ret);
2236 default:
2237 bad_fail("lookup_pwd", ret);
2240 buf = malloc(bufsz);
2241 if (buf == NULL)
2242 return (ENOMEM);
2244 do {
2245 errno = 0;
2246 pp = getdefaultproj(cip->pwd.pw_name, &proj, buf,
2247 bufsz);
2248 } while (pp == NULL && errno == EINTR);
2250 /* to be continued ... */
2251 } else {
2252 projid_t projid;
2253 char *cp;
2255 if (!isdigit(str[0])) {
2256 cip->project = strdup(str);
2257 return (cip->project != NULL ? 0 : ENOMEM);
2260 errno = 0;
2261 projid = strtol(str, &cp, 10);
2263 if (projid == 0 && errno != 0) {
2264 assert(errno == ERANGE);
2265 return (errno);
2268 for (; *cp != '\0'; ++cp)
2269 if (*cp != ' ' || *cp != '\t')
2270 return (EINVAL);
2272 if (projid > MAXPROJID)
2273 return (ERANGE);
2275 buf = malloc(bufsz);
2276 if (buf == NULL)
2277 return (ENOMEM);
2279 do {
2280 errno = 0;
2281 pp = getprojbyid(projid, &proj, buf, bufsz);
2282 } while (pp == NULL && errno == EINTR);
2285 if (pp) {
2286 cip->project = strdup(pp->pj_name);
2287 free(buf);
2288 return (cip->project != NULL ? 0 : ENOMEM);
2291 free(buf);
2293 switch (errno) {
2294 case 0:
2295 return (ENOENT);
2297 case EIO:
2298 case EMFILE:
2299 case ENFILE:
2300 return (errno);
2302 case ERANGE:
2303 return (E2BIG);
2305 default:
2306 return (-1);
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;
2319 uint_t i;
2321 const char * const whitespace = " \t";
2322 const char * const illegal = ", \t";
2324 if (str[0] == '\0') {
2325 ci->ngroups = 0;
2326 return (0);
2329 for (cp = str, i = 0; *cp != '\0'; ) {
2330 /* skip whitespace */
2331 cp += strspn(cp, whitespace);
2333 /* find the end */
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 */
2340 if (*next == ',')
2341 ++next;
2343 *end = '\0';
2345 if ((ci->groups[i] = get_gid(cp)) == (gid_t)-1) {
2346 ci->ngroups = 0;
2347 return (EINVAL);
2350 ++i;
2351 if (i > NGROUPS_MAX - 1) {
2352 ci->ngroups = 0;
2353 return (E2BIG);
2356 cp = next;
2359 ci->ngroups = i;
2360 return (0);
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.
2373 static mc_error_t *
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;
2380 char cmd[PATH_MAX];
2381 char *cp, *value;
2382 const char *cmdp;
2383 execattr_t *eap;
2384 mc_error_t *err = merr;
2385 int r;
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");
2398 if (cp == NULL) {
2399 cmdp = cmdline;
2400 } else {
2401 (void) strncpy(cmd, cmdline, cp - cmdline);
2402 cmd[cp - cmdline] = '\0';
2403 cmdp = cmd;
2406 /* Require that cmdp[0] == '/'? */
2408 eap = getexecprof(buf, KV_COMMAND, cmdp, GET_ONE);
2409 if (eap == NULL)
2410 return (mc_error_create(merr, ENOENT,
2411 "Could not find the execution profile \"%s\", "
2412 "command %s.", buf, cmdp));
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.",
2423 value, buf, r);
2424 goto out;
2428 if ((value = kva_match(eap->attr, EXECATTR_UID_KW)) != NULL) {
2429 if ((r = get_uid(value, ci, &ci->uid)) != 0) {
2430 ci->euid = ci->uid = (uid_t)-1;
2431 err = mc_error_create(merr, r,
2432 "Could not interpret profile uid value \"%s\", "
2433 "from the execution profile \"%s\", error %d.",
2434 value, buf, r);
2435 goto out;
2437 ci->euid = ci->uid;
2440 if ((value = kva_match(eap->attr, EXECATTR_GID_KW)) != NULL) {
2441 ci->egid = ci->gid = get_gid(value);
2442 if (ci->gid == (gid_t)-1) {
2443 err = mc_error_create(merr, EINVAL,
2444 "Could not interpret profile gid value \"%s\", "
2445 "from the execution profile \"%s\".", value, buf);
2446 goto out;
2450 if ((value = kva_match(eap->attr, EXECATTR_EGID_KW)) != NULL) {
2451 ci->egid = get_gid(value);
2452 if (ci->egid == (gid_t)-1) {
2453 err = mc_error_create(merr, EINVAL,
2454 "Could not interpret profile egid value \"%s\", "
2455 "from the execution profile \"%s\".", value, buf);
2456 goto out;
2460 if ((value = kva_match(eap->attr, EXECATTR_LPRIV_KW)) != NULL) {
2461 ci->lpriv_set = priv_str_to_set(value, ",", NULL);
2462 if (ci->lpriv_set == NULL) {
2463 if (errno != EINVAL)
2464 err = mc_error_create(merr, ENOMEM,
2465 ALLOCFAIL);
2466 else
2467 err = mc_error_create(merr, EINVAL,
2468 "Could not interpret profile "
2469 "limitprivs value \"%s\", from "
2470 "the execution profile \"%s\".",
2471 value, buf);
2472 goto out;
2476 if ((value = kva_match(eap->attr, EXECATTR_IPRIV_KW)) != NULL) {
2477 ci->priv_set = priv_str_to_set(value, ",", NULL);
2478 if (ci->priv_set == NULL) {
2479 if (errno != EINVAL)
2480 err = mc_error_create(merr, ENOMEM,
2481 ALLOCFAIL);
2482 else
2483 err = mc_error_create(merr, EINVAL,
2484 "Could not interpret profile privs value "
2485 "\"%s\", from the execution profile "
2486 "\"%s\".", value, buf);
2487 goto out;
2491 out:
2492 free_execattr(eap);
2494 return (err);
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.
2506 static mc_error_t *
2507 get_ids(scf_propertygroup_t *methpg, scf_propertygroup_t *instpg,
2508 scf_property_t *prop, scf_value_t *val, struct method_context *ci,
2509 mc_error_t *merr)
2511 char *vbuf = ci->vbuf;
2512 ssize_t vbuf_sz = ci->vbuf_sz;
2513 int r;
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,
2527 val) == 0))
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");
2543 } else {
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));
2557 } else {
2558 switch (r = lookup_pwd(ci)) {
2559 case 0:
2560 ci->gid = ci->pwd.pw_gid;
2561 break;
2563 case ENOENT:
2564 ci->gid = (gid_t)-1;
2565 return (mc_error_create(merr, ENOENT,
2566 "No passwd entry for uid \"%d\".", ci->uid));
2568 case ENOMEM:
2569 return (mc_error_create(merr, ENOMEM,
2570 "Out of memory."));
2572 case EIO:
2573 case EMFILE:
2574 case ENFILE:
2575 return (mc_error_create(merr, ENFILE,
2576 "getpwuid_r() failed, error %d.", r));
2578 default:
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");
2588 } else {
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)) {
2597 case 0:
2598 break;
2600 case EINVAL:
2601 return (mc_error_create(merr, EINVAL,
2602 "Could not interpret supplemental groups (\"%s\") "
2603 "property value \"%s\".", SCF_PROPERTY_SUPP_GROUPS,
2604 vbuf));
2606 case E2BIG:
2607 return (mc_error_create(merr, E2BIG,
2608 "Too many supplemental groups values in \"%s\".",
2609 vbuf));
2611 default:
2612 bad_fail("get_groups", r);
2614 } else {
2615 ci->ngroups = -1;
2618 if (!(get_astring_val(methpg, SCF_PROPERTY_PRIVILEGES, vbuf, vbuf_sz,
2619 prop, val) == 0 || get_astring_val(instpg, SCF_PROPERTY_PRIVILEGES,
2620 vbuf, vbuf_sz, prop, val) == 0)) {
2621 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2622 (void) strcpy(vbuf, ":default");
2623 } else {
2624 return (mc_error_create(merr, ENOENT,
2625 "Could not get \"%s\" property.",
2626 SCF_PROPERTY_PRIVILEGES));
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,
2639 ALLOCFAIL));
2640 } else {
2641 return (mc_error_create(merr, EINVAL,
2642 "Could not interpret \"%s\" "
2643 "property value \"%s\".",
2644 SCF_PROPERTY_PRIVILEGES, vbuf));
2649 if (!(get_astring_val(methpg, SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf,
2650 vbuf_sz, prop, val) == 0 || get_astring_val(instpg,
2651 SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf, vbuf_sz, prop, val) == 0)) {
2652 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2653 (void) strcpy(vbuf, ":default");
2654 } else {
2655 return (mc_error_create(merr, ENOENT,
2656 "Could not get \"%s\" property.",
2657 SCF_PROPERTY_LIMIT_PRIVILEGES));
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));
2673 } else {
2674 return (mc_error_create(merr, EINVAL,
2675 "Could not interpret \"%s\" property value \"%s\".",
2676 SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf));
2680 return (merr);
2683 static int
2684 get_environment(scf_handle_t *h, scf_propertygroup_t *pg,
2685 struct method_context *mcp, scf_property_t *prop, scf_value_t *val)
2687 scf_iter_t *iter;
2688 scf_type_t type;
2689 size_t i = 0;
2690 int ret;
2692 if (scf_pg_get_property(pg, SCF_PROPERTY_ENVIRONMENT, prop) != 0) {
2693 if (scf_error() == SCF_ERROR_NOT_FOUND)
2694 return (ENOENT);
2695 return (scf_error());
2697 if (scf_property_type(prop, &type) != 0)
2698 return (scf_error());
2699 if (type != SCF_TYPE_ASTRING)
2700 return (EINVAL);
2701 if ((iter = scf_iter_create(h)) == NULL)
2702 return (scf_error());
2704 if (scf_iter_property_values(iter, prop) != 0) {
2705 ret = scf_error();
2706 scf_iter_destroy(iter);
2707 return (ret);
2710 mcp->env_sz = 10;
2712 if ((mcp->env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz)) == NULL) {
2713 ret = ENOMEM;
2714 goto out;
2717 while ((ret = scf_iter_next_value(iter, val)) == 1) {
2718 ret = scf_value_get_as_string(val, mcp->vbuf, mcp->vbuf_sz);
2719 if (ret == -1) {
2720 ret = scf_error();
2721 goto out;
2724 if ((mcp->env[i] = strdup(mcp->vbuf)) == NULL) {
2725 ret = ENOMEM;
2726 goto out;
2729 if (++i == mcp->env_sz) {
2730 char **env;
2731 mcp->env_sz *= 2;
2732 env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz);
2733 if (env == NULL) {
2734 ret = ENOMEM;
2735 goto out;
2737 (void) memcpy(env, mcp->env,
2738 sizeof (*mcp->env) * (mcp->env_sz / 2));
2739 free(mcp->env);
2740 mcp->env = env;
2744 if (ret == -1)
2745 ret = scf_error();
2747 out:
2748 scf_iter_destroy(iter);
2749 return (ret);
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.
2764 * Error Types :
2765 * E2BIG Too many values or entry is too big
2766 * EINVAL Invalid value
2767 * EIO an I/O error has occured
2768 * ENOENT no entry for value
2769 * ENOMEM out of memory
2770 * ENOTSUP Version mismatch
2771 * ERANGE value is out of range
2772 * EMFILE/ENFILE out of file descriptors
2774 * SCF_ERROR_BACKEND_ACCESS
2775 * SCF_ERROR_CONNECTION_BROKEN
2776 * SCF_ERROR_DELETED
2777 * SCF_ERROR_CONSTRAINT_VIOLATED
2778 * SCF_ERROR_HANDLE_DESTROYED
2779 * SCF_ERROR_INTERNAL
2780 * SCF_ERROR_INVALID_ARGUMENT
2781 * SCF_ERROR_NO_MEMORY
2782 * SCF_ERROR_NO_RESOURCES
2783 * SCF_ERROR_NOT_BOUND
2784 * SCF_ERROR_NOT_FOUND
2785 * SCF_ERROR_NOT_SET
2786 * SCF_ERROR_TYPE_MISMATCH
2789 mc_error_t *
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)
2794 scf_handle_t *h;
2795 scf_propertygroup_t *methpg = NULL;
2796 scf_propertygroup_t *instpg = NULL;
2797 scf_propertygroup_t *pg = NULL;
2798 scf_property_t *prop = NULL;
2799 scf_value_t *val = NULL;
2800 scf_type_t ty;
2801 uint8_t use_profile;
2802 int ret = 0;
2803 int mc_used = 0;
2804 mc_error_t *err = NULL;
2805 struct method_context *cip;
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. */
2811 err->type = 0;
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);
2820 if (h == NULL)
2821 return (mc_error_create(err, scf_error(),
2822 scf_strerror(scf_error())));
2824 cip = malloc(sizeof (*cip));
2825 if (cip == NULL)
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) {
2838 free(cip);
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()));
2849 goto out;
2853 * The method environment, and the credentials/profile data,
2854 * may be found either in the pg for the method (methpg),
2855 * or in the instance/service SCF_PG_METHOD_CONTEXT pg (named
2856 * instpg below).
2859 if (scf_instance_get_pg_composed(inst, snap, mname, methpg) !=
2860 SCF_SUCCESS) {
2861 err = mc_error_create(err, scf_error(), "Unable to get the "
2862 "\"%s\" method, %s", mname, scf_strerror(scf_error()));
2863 goto out;
2866 if (scf_instance_get_pg_composed(inst, snap, SCF_PG_METHOD_CONTEXT,
2867 instpg) != SCF_SUCCESS) {
2868 if (scf_error() != SCF_ERROR_NOT_FOUND) {
2869 err = mc_error_create(err, scf_error(),
2870 "Unable to retrieve the \"%s\" property group, %s",
2871 SCF_PG_METHOD_CONTEXT, scf_strerror(scf_error()));
2872 goto out;
2874 scf_pg_destroy(instpg);
2875 instpg = NULL;
2876 } else {
2877 mc_used++;
2880 ret = get_environment(h, methpg, cip, prop, val);
2881 if (ret == ENOENT && instpg != NULL) {
2882 ret = get_environment(h, instpg, cip, prop, val);
2885 switch (ret) {
2886 case 0:
2887 mc_used++;
2888 break;
2889 case ENOENT:
2890 break;
2891 case ENOMEM:
2892 err = mc_error_create(err, ret, "Out of memory.");
2893 goto out;
2894 case EINVAL:
2895 err = mc_error_create(err, ret, "Invalid method environment.");
2896 goto out;
2897 default:
2898 err = mc_error_create(err, ret,
2899 "Get method environment failed: %s", scf_strerror(ret));
2900 goto out;
2903 pg = methpg;
2905 ret = scf_pg_get_property(pg, SCF_PROPERTY_USE_PROFILE, prop);
2906 if (ret && scf_error() == SCF_ERROR_NOT_FOUND && instpg != NULL) {
2907 pg = NULL;
2908 ret = scf_pg_get_property(instpg, SCF_PROPERTY_USE_PROFILE,
2909 prop);
2912 if (ret) {
2913 switch (scf_error()) {
2914 case SCF_ERROR_NOT_FOUND:
2915 /* No profile context: use default credentials */
2916 cip->uid = 0;
2917 cip->gid = 0;
2918 break;
2920 case SCF_ERROR_CONNECTION_BROKEN:
2921 err = mc_error_create(err, SCF_ERROR_CONNECTION_BROKEN,
2922 RCBROKEN);
2923 goto out;
2925 case SCF_ERROR_DELETED:
2926 err = mc_error_create(err, SCF_ERROR_NOT_FOUND,
2927 "Could not find property group \"%s\"",
2928 pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
2929 goto out;
2931 case SCF_ERROR_HANDLE_MISMATCH:
2932 case SCF_ERROR_INVALID_ARGUMENT:
2933 case SCF_ERROR_NOT_SET:
2934 default:
2935 bad_fail("scf_pg_get_property", scf_error());
2937 } else {
2938 if (scf_property_type(prop, &ty) != SCF_SUCCESS) {
2939 ret = scf_error();
2940 switch (ret) {
2941 case SCF_ERROR_CONNECTION_BROKEN:
2942 err = mc_error_create(err,
2943 SCF_ERROR_CONNECTION_BROKEN, RCBROKEN);
2944 break;
2946 case SCF_ERROR_DELETED:
2947 err = mc_error_create(err,
2948 SCF_ERROR_NOT_FOUND,
2949 "Could not find property group \"%s\"",
2950 pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
2951 break;
2953 case SCF_ERROR_NOT_SET:
2954 default:
2955 bad_fail("scf_property_type", ret);
2958 goto out;
2961 if (ty != SCF_TYPE_BOOLEAN) {
2962 err = mc_error_create(err,
2963 SCF_ERROR_TYPE_MISMATCH,
2964 "\"%s\" property is not boolean in property group "
2965 "\"%s\".", SCF_PROPERTY_USE_PROFILE,
2966 pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
2967 goto out;
2970 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
2971 ret = scf_error();
2972 switch (ret) {
2973 case SCF_ERROR_CONNECTION_BROKEN:
2974 err = mc_error_create(err,
2975 SCF_ERROR_CONNECTION_BROKEN, RCBROKEN);
2976 break;
2978 case SCF_ERROR_CONSTRAINT_VIOLATED:
2979 err = mc_error_create(err,
2980 SCF_ERROR_CONSTRAINT_VIOLATED,
2981 "\"%s\" property has multiple values.",
2982 SCF_PROPERTY_USE_PROFILE);
2983 break;
2985 case SCF_ERROR_NOT_FOUND:
2986 err = mc_error_create(err,
2987 SCF_ERROR_NOT_FOUND,
2988 "\"%s\" property has no values.",
2989 SCF_PROPERTY_USE_PROFILE);
2990 break;
2991 default:
2992 bad_fail("scf_property_get_value", ret);
2995 goto out;
2998 mc_used++;
2999 ret = scf_value_get_boolean(val, &use_profile);
3000 assert(ret == SCF_SUCCESS);
3002 /* get ids & privileges */
3003 if (use_profile)
3004 err = get_profile(pg, instpg, prop, val, cmdline,
3005 cip, err);
3006 else
3007 err = get_ids(pg, instpg, prop, val, cip, err);
3009 if (err->type != 0)
3010 goto out;
3013 /* get working directory */
3014 if ((methpg != NULL && scf_pg_get_property(methpg,
3015 SCF_PROPERTY_WORKING_DIRECTORY, prop) == SCF_SUCCESS) ||
3016 (instpg != NULL && scf_pg_get_property(instpg,
3017 SCF_PROPERTY_WORKING_DIRECTORY, prop) == SCF_SUCCESS)) {
3018 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3019 ret = scf_error();
3020 switch (ret) {
3021 case SCF_ERROR_CONNECTION_BROKEN:
3022 err = mc_error_create(err, ret, RCBROKEN);
3023 break;
3025 case SCF_ERROR_CONSTRAINT_VIOLATED:
3026 err = mc_error_create(err, ret,
3027 "\"%s\" property has multiple values.",
3028 SCF_PROPERTY_WORKING_DIRECTORY);
3029 break;
3031 case SCF_ERROR_NOT_FOUND:
3032 err = mc_error_create(err, ret,
3033 "\"%s\" property has no values.",
3034 SCF_PROPERTY_WORKING_DIRECTORY);
3035 break;
3037 default:
3038 bad_fail("scf_property_get_value", ret);
3041 goto out;
3044 mc_used++;
3045 ret = scf_value_get_astring(val, cip->vbuf, cip->vbuf_sz);
3046 assert(ret != -1);
3047 } else {
3048 ret = scf_error();
3049 switch (ret) {
3050 case SCF_ERROR_NOT_FOUND:
3051 /* okay if missing. */
3052 (void) strcpy(cip->vbuf, ":default");
3053 break;
3055 case SCF_ERROR_CONNECTION_BROKEN:
3056 err = mc_error_create(err, ret, RCBROKEN);
3057 goto out;
3059 case SCF_ERROR_DELETED:
3060 err = mc_error_create(err, ret,
3061 "Property group could not be found");
3062 goto out;
3064 case SCF_ERROR_HANDLE_MISMATCH:
3065 case SCF_ERROR_INVALID_ARGUMENT:
3066 case SCF_ERROR_NOT_SET:
3067 default:
3068 bad_fail("scf_pg_get_property", ret);
3072 if (strcmp(cip->vbuf, ":default") == 0 ||
3073 strcmp(cip->vbuf, ":home") == 0) {
3074 switch (ret = lookup_pwd(cip)) {
3075 case 0:
3076 break;
3078 case ENOMEM:
3079 err = mc_error_create(err, ret, "Out of memory.");
3080 goto out;
3082 case ENOENT:
3083 case EIO:
3084 case EMFILE:
3085 case ENFILE:
3086 err = mc_error_create(err, ret,
3087 "Could not get passwd entry.");
3088 goto out;
3090 default:
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);
3097 goto out;
3099 } else {
3100 cip->working_dir = strdup(cip->vbuf);
3101 if (cip->working_dir == NULL) {
3102 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3103 goto out;
3107 /* get security flags */
3108 if ((methpg != NULL && scf_pg_get_property(methpg,
3109 SCF_PROPERTY_SECFLAGS, prop) == SCF_SUCCESS) ||
3110 (instpg != NULL && scf_pg_get_property(instpg,
3111 SCF_PROPERTY_SECFLAGS, prop) == SCF_SUCCESS)) {
3112 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3113 ret = scf_error();
3114 switch (ret) {
3115 case SCF_ERROR_CONNECTION_BROKEN:
3116 err = mc_error_create(err, ret, RCBROKEN);
3117 break;
3119 case SCF_ERROR_CONSTRAINT_VIOLATED:
3120 err = mc_error_create(err, ret,
3121 "\"%s\" property has multiple values.",
3122 SCF_PROPERTY_SECFLAGS);
3123 break;
3125 case SCF_ERROR_NOT_FOUND:
3126 err = mc_error_create(err, ret,
3127 "\"%s\" property has no values.",
3128 SCF_PROPERTY_SECFLAGS);
3129 break;
3131 default:
3132 bad_fail("scf_property_get_value", ret);
3135 (void) strlcpy(cip->vbuf, ":default", cip->vbuf_sz);
3136 } else {
3137 ret = scf_value_get_astring(val, cip->vbuf,
3138 cip->vbuf_sz);
3139 assert(ret != -1);
3141 mc_used++;
3142 } else {
3143 ret = scf_error();
3144 switch (ret) {
3145 case SCF_ERROR_NOT_FOUND:
3146 /* okay if missing. */
3147 (void) strlcpy(cip->vbuf, ":default", cip->vbuf_sz);
3148 break;
3150 case SCF_ERROR_CONNECTION_BROKEN:
3151 err = mc_error_create(err, ret, RCBROKEN);
3152 goto out;
3154 case SCF_ERROR_DELETED:
3155 err = mc_error_create(err, ret,
3156 "Property group could not be found");
3157 goto out;
3159 case SCF_ERROR_HANDLE_MISMATCH:
3160 case SCF_ERROR_INVALID_ARGUMENT:
3161 case SCF_ERROR_NOT_SET:
3162 default:
3163 bad_fail("scf_pg_get_property", ret);
3168 if (scf_default_secflags(h, &cip->def_secflags) != 0) {
3169 err = mc_error_create(err, EINVAL, "couldn't fetch "
3170 "default security-flags");
3171 goto out;
3174 if (strcmp(cip->vbuf, ":default") != 0) {
3175 if (secflags_parse(NULL, cip->vbuf,
3176 &cip->secflag_delta) != 0) {
3177 err = mc_error_create(err, EINVAL, "couldn't parse "
3178 "security flags: %s", cip->vbuf);
3179 goto out;
3183 /* get (optional) corefile pattern */
3184 if ((methpg != NULL && scf_pg_get_property(methpg,
3185 SCF_PROPERTY_COREFILE_PATTERN, prop) == SCF_SUCCESS) ||
3186 (instpg != NULL && scf_pg_get_property(instpg,
3187 SCF_PROPERTY_COREFILE_PATTERN, prop) == SCF_SUCCESS)) {
3188 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3189 ret = scf_error();
3190 switch (ret) {
3191 case SCF_ERROR_CONNECTION_BROKEN:
3192 err = mc_error_create(err, ret, RCBROKEN);
3193 break;
3195 case SCF_ERROR_CONSTRAINT_VIOLATED:
3196 err = mc_error_create(err, ret,
3197 "\"%s\" property has multiple values.",
3198 SCF_PROPERTY_COREFILE_PATTERN);
3199 break;
3201 case SCF_ERROR_NOT_FOUND:
3202 err = mc_error_create(err, ret,
3203 "\"%s\" property has no values.",
3204 SCF_PROPERTY_COREFILE_PATTERN);
3205 break;
3207 default:
3208 bad_fail("scf_property_get_value", ret);
3211 } else {
3213 ret = scf_value_get_astring(val, cip->vbuf,
3214 cip->vbuf_sz);
3215 assert(ret != -1);
3217 cip->corefile_pattern = strdup(cip->vbuf);
3218 if (cip->corefile_pattern == NULL) {
3219 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3220 goto out;
3224 mc_used++;
3225 } else {
3226 ret = scf_error();
3227 switch (ret) {
3228 case SCF_ERROR_NOT_FOUND:
3229 /* okay if missing. */
3230 break;
3232 case SCF_ERROR_CONNECTION_BROKEN:
3233 err = mc_error_create(err, ret, RCBROKEN);
3234 goto out;
3236 case SCF_ERROR_DELETED:
3237 err = mc_error_create(err, ret,
3238 "Property group could not be found");
3239 goto out;
3241 case SCF_ERROR_HANDLE_MISMATCH:
3242 case SCF_ERROR_INVALID_ARGUMENT:
3243 case SCF_ERROR_NOT_SET:
3244 default:
3245 bad_fail("scf_pg_get_property", ret);
3249 if (restarter_rm_libs_loadable()) {
3250 /* get project */
3251 if ((methpg != NULL && scf_pg_get_property(methpg,
3252 SCF_PROPERTY_PROJECT, prop) == SCF_SUCCESS) ||
3253 (instpg != NULL && scf_pg_get_property(instpg,
3254 SCF_PROPERTY_PROJECT, prop) == SCF_SUCCESS)) {
3255 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3256 ret = scf_error();
3257 switch (ret) {
3258 case SCF_ERROR_CONNECTION_BROKEN:
3259 err = mc_error_create(err, ret,
3260 RCBROKEN);
3261 break;
3263 case SCF_ERROR_CONSTRAINT_VIOLATED:
3264 err = mc_error_create(err, ret,
3265 "\"%s\" property has multiple "
3266 "values.", SCF_PROPERTY_PROJECT);
3267 break;
3269 case SCF_ERROR_NOT_FOUND:
3270 err = mc_error_create(err, ret,
3271 "\"%s\" property has no values.",
3272 SCF_PROPERTY_PROJECT);
3273 break;
3275 default:
3276 bad_fail("scf_property_get_value", ret);
3279 (void) strcpy(cip->vbuf, ":default");
3280 } else {
3281 ret = scf_value_get_astring(val, cip->vbuf,
3282 cip->vbuf_sz);
3283 assert(ret != -1);
3286 mc_used++;
3287 } else {
3288 (void) strcpy(cip->vbuf, ":default");
3291 switch (ret = get_projid(cip->vbuf, cip)) {
3292 case 0:
3293 break;
3295 case ENOMEM:
3296 err = mc_error_create(err, ret, "Out of memory.");
3297 goto out;
3299 case ENOENT:
3300 err = mc_error_create(err, ret,
3301 "Missing passwd or project entry for \"%s\".",
3302 cip->vbuf);
3303 goto out;
3305 case EIO:
3306 err = mc_error_create(err, ret, "I/O error.");
3307 goto out;
3309 case EMFILE:
3310 case ENFILE:
3311 err = mc_error_create(err, ret,
3312 "Out of file descriptors.");
3313 goto out;
3315 case -1:
3316 err = mc_error_create(err, ret,
3317 "Name service switch is misconfigured.");
3318 goto out;
3320 case ERANGE:
3321 case E2BIG:
3322 err = mc_error_create(err, ret,
3323 "Project ID \"%s\" too big.", cip->vbuf);
3324 goto out;
3326 case EINVAL:
3327 err = mc_error_create(err, ret,
3328 "Project ID \"%s\" is invalid.", cip->vbuf);
3329 goto out;
3331 default:
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) {
3341 ret = scf_error();
3342 switch (ret) {
3343 case SCF_ERROR_CONNECTION_BROKEN:
3344 err = mc_error_create(err, ret,
3345 RCBROKEN);
3346 break;
3348 case SCF_ERROR_CONSTRAINT_VIOLATED:
3349 err = mc_error_create(err, ret,
3350 "\"%s\" property has multiple "
3351 "values.",
3352 SCF_PROPERTY_RESOURCE_POOL);
3353 break;
3355 case SCF_ERROR_NOT_FOUND:
3356 err = mc_error_create(err, ret,
3357 "\"%s\" property has no "
3358 "values.",
3359 SCF_PROPERTY_RESOURCE_POOL);
3360 break;
3362 default:
3363 bad_fail("scf_property_get_value", ret);
3366 (void) strcpy(cip->vbuf, ":default");
3367 } else {
3368 ret = scf_value_get_astring(val, cip->vbuf,
3369 cip->vbuf_sz);
3370 assert(ret != -1);
3373 mc_used++;
3374 } else {
3375 ret = scf_error();
3376 switch (ret) {
3377 case SCF_ERROR_NOT_FOUND:
3378 /* okay if missing. */
3379 (void) strcpy(cip->vbuf, ":default");
3380 break;
3382 case SCF_ERROR_CONNECTION_BROKEN:
3383 err = mc_error_create(err, ret, RCBROKEN);
3384 goto out;
3386 case SCF_ERROR_DELETED:
3387 err = mc_error_create(err, ret,
3388 "property group could not be found.");
3389 goto out;
3391 case SCF_ERROR_HANDLE_MISMATCH:
3392 case SCF_ERROR_INVALID_ARGUMENT:
3393 case SCF_ERROR_NOT_SET:
3394 default:
3395 bad_fail("scf_pg_get_property", ret);
3399 if (strcmp(cip->vbuf, ":default") != 0) {
3400 cip->resource_pool = strdup(cip->vbuf);
3401 if (cip->resource_pool == NULL) {
3402 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3403 goto out;
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.
3413 if (mc_used == 0) {
3414 free(cip->pwbuf);
3415 free(cip->vbuf);
3416 free(cip->working_dir);
3418 (void) memset(cip, 0, sizeof (*cip));
3419 cip->uid = 0;
3420 cip->gid = 0;
3421 cip->euid = (uid_t)-1;
3422 cip->egid = (gid_t)-1;
3424 if (scf_default_secflags(h, &cip->def_secflags) != 0) {
3425 err = mc_error_create(err, EINVAL, "couldn't fetch "
3426 "default security-flags");
3427 goto out;
3431 *mcpp = cip;
3433 out:
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) {
3440 free(cip->pwbuf);
3441 cip->pwbuf = NULL;
3444 free(cip->vbuf);
3446 if (err->type != 0) {
3447 restarter_free_method_context(cip);
3448 } else {
3449 restarter_mc_error_destroy(err);
3450 err = NULL;
3453 return (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)
3499 pid_t mypid = -1;
3500 int r, ret;
3502 cip->pwbuf = NULL;
3503 *fp = NULL;
3505 if (cip->gid != (gid_t)-1) {
3506 if (setregid(cip->gid,
3507 cip->egid != (gid_t)-1 ? cip->egid : cip->gid) != 0) {
3508 *fp = "setregid";
3510 ret = errno;
3511 assert(ret == EINVAL || ret == EPERM);
3512 goto out;
3514 } else {
3515 if (cip->pwbuf == NULL) {
3516 switch (ret = lookup_pwd(cip)) {
3517 case 0:
3518 break;
3520 case ENOMEM:
3521 case ENOENT:
3522 *fp = NULL;
3523 goto out;
3525 case EIO:
3526 case EMFILE:
3527 case ENFILE:
3528 *fp = "getpwuid_r";
3529 goto out;
3531 default:
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) {
3539 *fp = "setregid";
3541 ret = errno;
3542 assert(ret == EINVAL || ret == EPERM);
3543 goto out;
3547 if (cip->ngroups == -1) {
3548 if (cip->pwbuf == NULL) {
3549 switch (ret = lookup_pwd(cip)) {
3550 case 0:
3551 break;
3553 case ENOMEM:
3554 case ENOENT:
3555 *fp = NULL;
3556 goto out;
3558 case EIO:
3559 case EMFILE:
3560 case ENFILE:
3561 *fp = "getpwuid_r";
3562 goto out;
3564 default:
3565 bad_fail("lookup_pwd", ret);
3569 /* Ok if cip->gid == -1 */
3570 if (initgroups(cip->pwd.pw_name, cip->gid) != 0) {
3571 *fp = "initgroups";
3572 ret = errno;
3573 assert(ret == EPERM);
3574 goto out;
3576 } else if (cip->ngroups > 0 &&
3577 setgroups(cip->ngroups, cip->groups) != 0) {
3578 *fp = "setgroups";
3580 ret = errno;
3581 assert(ret == EINVAL || ret == EPERM);
3582 goto out;
3585 if (cip->corefile_pattern != NULL) {
3586 mypid = getpid();
3588 if (core_set_process_path(cip->corefile_pattern,
3589 strlen(cip->corefile_pattern) + 1, mypid) != 0) {
3590 *fp = "core_set_process_path";
3591 ret = -1;
3592 goto out;
3597 if (psecflags(P_PID, P_MYID, PSF_INHERIT,
3598 &cip->def_secflags.ss_default) != 0) {
3599 *fp = "psecflags (default inherit)";
3600 ret = errno;
3601 goto out;
3604 if (psecflags(P_PID, P_MYID, PSF_LOWER,
3605 &cip->def_secflags.ss_lower) != 0) {
3606 *fp = "psecflags (default lower)";
3607 ret = errno;
3608 goto out;
3611 if (psecflags(P_PID, P_MYID, PSF_UPPER,
3612 &cip->def_secflags.ss_upper) != 0) {
3613 *fp = "psecflags (default upper)";
3614 ret = errno;
3615 goto out;
3618 if (psecflags(P_PID, P_MYID, PSF_INHERIT,
3619 &cip->secflag_delta) != 0) {
3620 *fp = "psecflags (from manifest)";
3621 ret = errno;
3622 goto out;
3625 if (restarter_rm_libs_loadable()) {
3626 if (cip->project == NULL) {
3627 if (settaskid(getprojid(), TASK_NORMAL) == -1) {
3628 switch (errno) {
3629 case EACCES:
3630 case EPERM:
3631 *fp = "settaskid";
3632 ret = errno;
3633 goto out;
3635 case EINVAL:
3636 default:
3637 bad_fail("settaskid", errno);
3640 } else {
3641 switch (ret = lookup_pwd(cip)) {
3642 case 0:
3643 break;
3645 case ENOMEM:
3646 case ENOENT:
3647 *fp = NULL;
3648 goto out;
3650 case EIO:
3651 case EMFILE:
3652 case ENFILE:
3653 *fp = "getpwuid_r";
3654 goto out;
3656 default:
3657 bad_fail("lookup_pwd", ret);
3660 *fp = "setproject";
3662 switch (setproject(cip->project, cip->pwd.pw_name,
3663 TASK_NORMAL)) {
3664 case 0:
3665 break;
3667 case SETPROJ_ERR_TASK:
3668 case SETPROJ_ERR_POOL:
3669 ret = errno;
3670 goto out;
3672 default:
3673 ret = -1;
3674 goto out;
3678 if (cip->resource_pool != NULL) {
3679 if (mypid == -1)
3680 mypid = getpid();
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:
3688 ret = ENOENT;
3689 break;
3691 case POE_BADPARAM:
3692 ret = EINVAL;
3693 break;
3695 case POE_INVALID_CONF:
3696 ret = EBADF;
3697 break;
3699 case POE_SYSTEM:
3700 ret = -1;
3701 break;
3703 default:
3704 bad_fail("pool_set_binding",
3705 pool_error());
3708 goto out;
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()).
3720 *fp = "setppriv";
3722 if (cip->lpriv_set != NULL) {
3723 if (setppriv(PRIV_SET, PRIV_LIMIT, cip->lpriv_set) != 0) {
3724 ret = errno;
3725 assert(ret == EFAULT || ret == EPERM);
3726 goto out;
3729 if (cip->priv_set != NULL) {
3730 if (setppriv(PRIV_SET, PRIV_INHERITABLE, cip->priv_set) != 0) {
3731 ret = errno;
3732 assert(ret == EFAULT || ret == EPERM);
3733 goto out;
3738 * If the limit privset is already set, then must be privilege
3739 * aware. Otherwise, don't assume anything, and force privilege
3740 * aware status.
3743 if (cip->lpriv_set == NULL && cip->priv_set != NULL) {
3744 ret = setpflags(PRIV_AWARE, 1);
3745 assert(ret == 0);
3748 *fp = "setreuid";
3749 if (setreuid(cip->uid,
3750 cip->euid != (uid_t)-1 ? cip->euid : cip->uid) != 0) {
3751 ret = errno;
3752 assert(ret == EINVAL || ret == EPERM);
3753 goto out;
3756 *fp = "setppriv";
3757 if (cip->priv_set != NULL) {
3758 if (setppriv(PRIV_SET, PRIV_PERMITTED, cip->priv_set) != 0) {
3759 ret = errno;
3760 assert(ret == EFAULT || ret == EPERM);
3761 goto out;
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) {
3771 do {
3772 r = chdir(cip->working_dir);
3773 } while (r != 0 && errno == EINTR);
3774 if (r != 0) {
3775 *fp = "chdir";
3776 ret = errno;
3777 goto out;
3781 ret = 0;
3782 out:
3783 free(cip->pwbuf);
3784 cip->pwbuf = NULL;
3785 return (ret);
3788 void
3789 restarter_free_method_context(struct method_context *mcp)
3791 size_t i;
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++)
3800 free(mcp->env[i]);
3801 free(mcp->env);
3804 free(mcp->working_dir);
3805 free(mcp->corefile_pattern);
3806 free(mcp->project);
3807 free(mcp->resource_pool);
3808 free(mcp);
3812 * Method keyword functions
3816 restarter_is_null_method(const char *meth)
3818 return (strcmp(meth, MKW_TRUE) == 0);
3821 static int
3822 is_kill_method(const char *method, const char *kill_str,
3823 size_t kill_str_len)
3825 const char *cp;
3826 int sig;
3828 if (strncmp(method, kill_str, kill_str_len) != 0 ||
3829 (method[kill_str_len] != '\0' &&
3830 !isspace(method[kill_str_len])))
3831 return (-1);
3833 cp = method + kill_str_len;
3834 while (*cp != '\0' && isspace(*cp))
3835 ++cp;
3837 if (*cp == '\0')
3838 return (SIGTERM);
3840 if (*cp != '-')
3841 return (-1);
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));
3860 * Stubs for now.
3863 /* ARGSUSED */
3865 restarter_event_get_enabled(restarter_event_t *e)
3867 return (-1);
3870 /* ARGSUSED */
3871 uint64_t
3872 restarter_event_get_seq(restarter_event_t *e)
3874 return (-1);
3877 /* ARGSUSED */
3878 void
3879 restarter_event_get_time(restarter_event_t *e, hrtime_t *time)
3884 * Check for and validate fmri specified in restarter_actions/auxiliary_fmri
3885 * 0 - Success
3886 * 1 - Failure
3889 restarter_inst_validate_ractions_aux_fmri(scf_instance_t *inst)
3891 scf_handle_t *h;
3892 scf_propertygroup_t *pg;
3893 scf_property_t *prop;
3894 scf_value_t *val;
3895 char *aux_fmri;
3896 size_t size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
3897 int ret = 1;
3899 if ((aux_fmri = malloc(size)) == NULL)
3900 return (1);
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)
3908 goto out;
3910 if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
3911 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
3912 pg) != SCF_SUCCESS)
3913 goto out;
3915 if (get_astring_val(pg, SCF_PROPERTY_AUX_FMRI, aux_fmri, size,
3916 prop, val) != SCF_SUCCESS)
3917 goto out;
3919 if (scf_parse_fmri(aux_fmri, NULL, NULL, NULL, NULL, NULL,
3920 NULL) != SCF_SUCCESS)
3921 goto out;
3923 ret = 0;
3925 out:
3926 free(aux_fmri);
3927 scf_value_destroy(val);
3928 scf_property_destroy(prop);
3929 scf_pg_destroy(pg);
3930 return (ret);
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)
3940 scf_handle_t *h;
3941 scf_propertygroup_t *pg;
3942 scf_property_t *prop;
3943 scf_value_t *val;
3944 uint8_t has_tty;
3945 int ret = -1;
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)
3952 goto out;
3954 if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
3955 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
3956 pg) != SCF_SUCCESS)
3957 goto out;
3959 if (get_boolean_val(pg, SCF_PROPERTY_AUX_TTY, &has_tty, prop,
3960 val) != SCF_SUCCESS)
3961 goto out;
3963 ret = has_tty;
3965 out:
3966 scf_value_destroy(val);
3967 scf_property_destroy(prop);
3968 scf_pg_destroy(pg);
3969 return (ret);
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)
3979 scf_handle_t *h;
3980 scf_propertygroup_t *pg;
3981 scf_property_t *prop;
3982 scf_value_t *val;
3983 int ret = 0;
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)
3990 goto out;
3992 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER_ACTIONS, pg) !=
3993 SCF_SUCCESS) {
3994 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
3995 uu_die(rcbroken);
3996 goto out;
3999 if (scf_pg_get_property(pg, SCF_PROPERTY_DODUMP, prop) != SCF_SUCCESS) {
4000 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
4001 uu_die(rcbroken);
4002 goto out;
4005 ret = 1;
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)
4010 uu_die(rcbroken);
4011 goto out;
4014 out:
4015 scf_value_destroy(val);
4016 scf_property_destroy(prop);
4017 scf_pg_destroy(pg);
4018 return (ret);
4021 static int
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)
4025 scf_handle_t *h;
4026 scf_propertygroup_t *pg;
4027 scf_transaction_t *t;
4028 scf_transaction_entry_t *e;
4029 scf_value_t *v;
4030 int ret = 1, r;
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)
4039 goto out;
4041 if (instance_get_or_add_pg(inst, pgname, pgtype, pgflags, pg))
4042 goto out;
4044 if (scf_value_set_astring(v, str) != SCF_SUCCESS)
4045 goto out;
4047 for (;;) {
4048 if (scf_transaction_start(t, pg) != 0)
4049 goto out;
4051 if (tx_set_value(t, e, pname, SCF_TYPE_ASTRING, v) != 0)
4052 goto out;
4054 if ((r = scf_transaction_commit(t)) == 1)
4055 break;
4057 if (r == -1)
4058 goto out;
4060 scf_transaction_reset(t);
4061 if (scf_pg_update(pg) == -1)
4062 goto out;
4064 ret = 0;
4066 out:
4067 scf_transaction_destroy(t);
4068 scf_entry_destroy(e);
4069 scf_value_destroy(v);
4070 scf_pg_destroy(pg);
4072 return (ret);
4076 restarter_inst_set_aux_fmri(scf_instance_t *inst)
4078 scf_handle_t *h;
4079 scf_propertygroup_t *pg;
4080 scf_property_t *prop;
4081 scf_value_t *val;
4082 char *aux_fmri;
4083 size_t size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
4084 int ret = 1;
4086 if ((aux_fmri = malloc(size)) == NULL)
4087 return (1);
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)
4095 goto out;
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,
4102 pg) != SCF_SUCCESS)
4103 goto out;
4105 if (get_astring_val(pg, SCF_PROPERTY_AUX_FMRI, aux_fmri, size,
4106 prop, val) != SCF_SUCCESS)
4107 goto out;
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);
4116 out:
4117 free(aux_fmri);
4118 scf_value_destroy(val);
4119 scf_property_destroy(prop);
4120 scf_pg_destroy(pg);
4121 return (ret);
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));