summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cmd-inet/usr.lib/inetd/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.lib/inetd/config.c')
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/config.c810
1 files changed, 810 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/config.c b/usr/src/cmd/cmd-inet/usr.lib/inetd/config.c
new file mode 100644
index 0000000000..68fb319c30
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/config.c
@@ -0,0 +1,810 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines used by inetd to read inetd's configuration from the repository,
+ * to validate it and setup inetd's data structures appropriately based on
+ * in.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <libintl.h>
+#include <nss_dbdefs.h>
+#include <signal.h>
+#include <wait.h>
+#include "inetd_impl.h"
+
+
+/* method timeout used if one isn't explicitly specified */
+#define DEFAULT_METHOD_TIMEOUT 10
+
+
+/* supported method properties and their attributes */
+static inetd_prop_t method_props[] = {
+{PR_EXEC_NAME, "", SCF_TYPE_ASTRING, B_FALSE, IVE_UNSET, NULL},
+{PR_ARG0_NAME, "", SCF_TYPE_ASTRING, B_TRUE, IVE_UNSET, NULL},
+{NULL, "", SCF_TYPE_COUNT, B_TRUE, IVE_UNSET, NULL}
+};
+
+/* enumeration of method properties; used to index into method_props[] */
+typedef enum {
+ MP_EXEC,
+ MP_ARG0,
+ MP_TIMEOUT,
+ NUM_METHOD_PROPS
+} method_prop_t;
+
+
+/* handle used for repository access in read_prop() */
+static scf_handle_t *rep_handle = NULL;
+
+/* pool used to create proto_info_t lists (generic proto info structure) */
+static uu_list_pool_t *proto_info_pool = NULL;
+
+static void destroy_method_props(inetd_prop_t *);
+static int proto_info_compare(const void *, const void *, void *);
+
+int
+config_init(void)
+{
+ if ((rep_handle = scf_handle_create(SCF_VERSION)) == NULL) {
+ error_msg("%s: %s",
+ gettext("Failed to create repository handle"),
+ scf_strerror(scf_error()));
+ return (-1);
+ } else if (make_handle_bound(rep_handle) == -1) {
+ /* let config_fini clean-up */
+ return (-1);
+ }
+
+ /*
+ * Work around the (const *) nature of SCF property #defines in
+ * libscf.h that prevent us from directly initializing the name
+ * element of members of the method properties table.
+ */
+ if ((method_props[MP_TIMEOUT].ip_name = strdup(SCF_PROPERTY_TIMEOUT))
+ == NULL) {
+ error_msg(strerror(errno));
+ return (-1);
+ }
+
+ if ((proto_info_pool = uu_list_pool_create("proto_info_pool",
+ sizeof (proto_info_t), offsetof(proto_info_t, link),
+ proto_info_compare, UU_LIST_POOL_DEBUG)) == NULL) {
+ error_msg(gettext("Failed to create uu list pool: %s"),
+ uu_strerror(uu_error()));
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+config_fini(void)
+{
+ if (rep_handle == NULL)
+ return;
+
+ if (proto_info_pool != NULL) {
+ uu_list_pool_destroy(proto_info_pool);
+ proto_info_pool = NULL;
+ }
+
+ (void) scf_handle_unbind(rep_handle);
+ scf_handle_destroy(rep_handle);
+ rep_handle = NULL;
+}
+
+static void
+destroy_method_info(method_info_t *mi)
+{
+ if (mi == NULL)
+ return;
+
+ if (mi->wordexp_arg0_backup != NULL) {
+ /*
+ * Return the wordexp structure back to its original
+ * state so it can be consumed by wordfree.
+ */
+ free(mi->exec_args_we.we_wordv[0]);
+ mi->exec_args_we.we_wordv[0] =
+ (char *)mi->wordexp_arg0_backup;
+ }
+
+ free(mi->exec_path);
+
+ wordfree(&mi->exec_args_we);
+
+ free(mi);
+}
+
+/*
+ * Transforms the properties read from the repository for a method into a
+ * method_info_t and returns a pointer to it. If expansion of the exec
+ * property fails, due to an invalid string or memory allocation failure,
+ * NULL is returned and exec_invalid is set appropriately to indicate whether
+ * it was a memory allocation failure or an invalid exec string.
+ */
+static method_info_t *
+create_method_info(const inetd_prop_t *mprops, boolean_t *exec_invalid)
+{
+ method_info_t *ret;
+ int i;
+
+ debug_msg("Entering create_method_info");
+
+ if ((ret = calloc(1, sizeof (method_info_t))) == NULL)
+ goto alloc_fail;
+
+ /* Expand the exec string. */
+ if ((i = wordexp(get_prop_value(mprops, PR_EXEC_NAME),
+ &ret->exec_args_we, WRDE_NOCMD|WRDE_UNDEF)) != 0) {
+ if (i == WRDE_NOSPACE)
+ goto alloc_fail;
+
+ *exec_invalid = B_TRUE;
+ free(ret);
+ return (NULL);
+ }
+
+ if ((ret->exec_path = strdup(ret->exec_args_we.we_wordv[0])) == NULL)
+ goto alloc_fail;
+
+ if (mprops[MP_ARG0].ip_error == IVE_VALID) { /* arg0 is set */
+ /*
+ * Keep a copy of arg0 of the wordexp structure so that
+ * wordfree() gets passed what wordexp() originally returned,
+ * as documented as required in the man page.
+ */
+ ret->wordexp_arg0_backup = ret->exec_args_we.we_wordv[0];
+ if ((ret->exec_args_we.we_wordv[0] =
+ strdup(get_prop_value(mprops, PR_ARG0_NAME))) == NULL)
+ goto alloc_fail;
+ }
+
+ if (mprops[MP_TIMEOUT].ip_error == IVE_VALID) {
+ ret->timeout = *(int64_t *)get_prop_value(mprops,
+ (char *)SCF_PROPERTY_TIMEOUT);
+ } else {
+ ret->timeout = DEFAULT_METHOD_TIMEOUT;
+ }
+
+ /* exec_invalid not set on success */
+
+ return (ret);
+
+alloc_fail:
+ error_msg(strerror(errno));
+ destroy_method_info(ret);
+ *exec_invalid = B_FALSE;
+ return (NULL);
+}
+
+/*
+ * Returns B_TRUE if the contents of the 2 method_info_t structures are
+ * equivalent, else B_FALSE.
+ */
+boolean_t
+method_info_equal(const method_info_t *mi, const method_info_t *mi2)
+{
+ int i;
+
+ debug_msg("Entering method_info_equal");
+
+ if ((mi == NULL) && (mi2 == NULL)) {
+ return (B_TRUE);
+ } else if (((mi == NULL) || (mi2 == NULL)) ||
+ (mi->exec_args_we.we_wordc != mi2->exec_args_we.we_wordc) ||
+ (strcmp(mi->exec_path, mi2->exec_path) != 0)) {
+ return (B_FALSE);
+ }
+
+ for (i = 0; i < mi->exec_args_we.we_wordc; i++) {
+ if (strcmp(mi->exec_args_we.we_wordv[i],
+ mi2->exec_args_we.we_wordv[i]) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Checks if the contents of the 2 socket_info_t structures are equivalent.
+ * If 'isrpc' is false, the address components of the two structures are
+ * compared for equality as part of this. If the two structures are
+ * equivalent B_TRUE is returned, else B_FALSE.
+ */
+boolean_t
+socket_info_equal(const socket_info_t *si, const socket_info_t *si2,
+ boolean_t isrpc)
+{
+ return ((isrpc || (memcmp(&si->local_addr, &si2->local_addr,
+ sizeof (si->local_addr)) == 0)) &&
+ (si->type == si2->type));
+
+}
+
+/*
+ * proto_info_t comparison function. Returns 0 on match, else -1, as required
+ * by uu_list_find().
+ */
+static int
+proto_info_compare(const void *lv, const void *rv, void *istlx)
+{
+ proto_info_t *pi = (proto_info_t *)lv;
+ proto_info_t *pi2 = (proto_info_t *)rv;
+
+ /* check their RPC configuration matches */
+ if (pi->ri != NULL) {
+ if ((pi2->ri == NULL) || !rpc_info_equal(pi->ri, pi2->ri))
+ return (-1);
+ } else if (pi2->ri != NULL) {
+ return (-1);
+ }
+
+ if (pi->v6only != pi2->v6only)
+ return (-1);
+
+ if (*(boolean_t *)istlx) {
+ if (tlx_info_equal((tlx_info_t *)lv, (tlx_info_t *)rv,
+ pi->ri != NULL))
+ return (0);
+ } else {
+ if (socket_info_equal((socket_info_t *)lv,
+ (socket_info_t *)rv, pi->ri != NULL))
+ return (0);
+ }
+ return (-1);
+}
+
+/*
+ * Returns B_TRUE if the bind configuration of the two instance_cfg_t
+ * structures are equivalent, else B_FALSE.
+ */
+boolean_t
+bind_config_equal(const basic_cfg_t *c1, const basic_cfg_t *c2)
+{
+ proto_info_t *pi;
+
+ debug_msg("Entering bind_config_equal");
+
+ if ((c1->iswait != c2->iswait) ||
+ (c1->istlx != c2->istlx))
+ return (B_FALSE);
+
+ if (uu_list_numnodes(c1->proto_list) !=
+ uu_list_numnodes(c2->proto_list))
+ return (B_FALSE);
+ /*
+ * For each element in the first configuration's socket/tlx list,
+ * check there's a matching one in the other list.
+ */
+ for (pi = uu_list_first(c1->proto_list); pi != NULL;
+ pi = uu_list_next(c1->proto_list, pi)) {
+ uu_list_index_t idx;
+
+ if (uu_list_find(c2->proto_list, pi, (void *)&c1->istlx,
+ &idx) == NULL)
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Write the default values contained in 'bprops', read by
+ * read_instance_props(), into 'cfg'.
+ * Returns -1 if memory allocation fails, else 0.
+ */
+static int
+populate_defaults(inetd_prop_t *bprops, basic_cfg_t *cfg)
+{
+ debug_msg("Entering populate_defaults");
+
+ /*
+ * All time related values below are stored as 32 bits values because
+ * the consumers of the data rely on this, and so we cast them all
+ * to int's here.
+ */
+ cfg->do_tcp_wrappers =
+ *(boolean_t *)get_prop_value(bprops, PR_DO_TCP_WRAPPERS_NAME);
+ cfg->do_tcp_trace =
+ *(boolean_t *)get_prop_value(bprops, PR_DO_TCP_TRACE_NAME);
+ cfg->inherit_env =
+ *(boolean_t *)get_prop_value(bprops, PR_INHERIT_ENV_NAME);
+ cfg->wait_fail_cnt =
+ *(int64_t *)get_prop_value(bprops, PR_MAX_FAIL_RATE_CNT_NAME);
+ cfg->wait_fail_interval = (int)*(int64_t *)get_prop_value(bprops,
+ PR_MAX_FAIL_RATE_INTVL_NAME);
+ cfg->max_copies =
+ *(int64_t *)get_prop_value(bprops, PR_MAX_COPIES_NAME);
+ cfg->conn_rate_offline =
+ (int)*(int64_t *)get_prop_value(bprops, PR_CON_RATE_OFFLINE_NAME);
+ cfg->conn_rate_max =
+ *(int64_t *)get_prop_value(bprops, PR_CON_RATE_MAX_NAME);
+ cfg->bind_fail_interval =
+ (int)*(int64_t *)get_prop_value(bprops, PR_BIND_FAIL_INTVL_NAME);
+ cfg->bind_fail_max =
+ *(int64_t *)get_prop_value(bprops, PR_BIND_FAIL_MAX_NAME);
+ if ((cfg->bind_addr =
+ strdup(get_prop_value(bprops, PR_BIND_ADDR_NAME))) == NULL) {
+ error_msg(strerror(errno));
+ return (-1);
+ }
+ return (0);
+}
+
+void
+destroy_method_infos(method_info_t **mis)
+{
+ int i;
+
+ for (i = 0; i < NUM_METHODS; i++) {
+ destroy_method_info(mis[i]);
+ mis[i] = NULL;
+ }
+}
+
+/*
+ * For each method, if it was specifed convert its entry in 'mprops',
+ * into an entry in 'mis'. Returns -1 if memory allocation fails or one of the
+ * exec strings was invalid, else 0.
+ */
+static int
+create_method_infos(const char *fmri, inetd_prop_t **mprops,
+ method_info_t **mis)
+{
+ int i;
+
+ debug_msg("Entering create_method_infos, inst: %s", fmri);
+
+ for (i = 0; i < NUM_METHODS; i++) {
+ /*
+ * Only create a method info structure if the method properties
+ * contain an exec string, which we take to mean the method
+ * is specified.
+ */
+ if (mprops[i][MP_EXEC].ip_error == IVE_VALID) {
+ boolean_t exec_invalid;
+
+ if ((mis[i] = create_method_info(mprops[i],
+ &exec_invalid)) == NULL) {
+ if (exec_invalid) {
+ error_msg(gettext("Property %s for "
+ "method %s of instance %s is "
+ "invalid"), PR_EXEC_NAME,
+ methods[i].name, fmri);
+ }
+ return (-1);
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * Try and read each of the method properties for the method 'method' of
+ * instance 'inst', and return a table containing all method properties. If an
+ * error occurs, NULL is returned, with 'err' set to indicate the cause.
+ * Otherwise, a pointer to an inetd_prop_t table is returned containing all
+ * the method properties, and each of the properties is flagged according to
+ * whether it was present or not, and if it was present its value is set in
+ * the property's entry in the table.
+ */
+static inetd_prop_t *
+read_method_props(const char *inst, instance_method_t method, scf_error_t *err)
+{
+ inetd_prop_t *ret;
+ int i;
+
+ debug_msg("Entering read_method_props");
+
+ if ((ret = calloc(1, sizeof (method_props))) == NULL) {
+ *err = SCF_ERROR_NO_MEMORY;
+ return (NULL);
+ }
+
+ (void) memcpy(ret, method_props, sizeof (method_props));
+ for (i = 0; i < NUM_METHOD_PROPS; i++) {
+ *err = read_prop(rep_handle, &ret[i], i, inst,
+ methods[method].name);
+ if ((*err != 0) && (*err != SCF_ERROR_NOT_FOUND)) {
+ destroy_method_props(ret);
+ return (NULL);
+ }
+ }
+
+ return (ret);
+}
+
+static void
+destroy_method_props(inetd_prop_t *mprop)
+{
+ int i;
+
+ if (mprop == NULL)
+ return;
+
+ for (i = 0; i < NUM_METHOD_PROPS; i++) {
+ if (mprop[i].ip_type == SCF_TYPE_ASTRING)
+ free(mprop[i].ip_value.iv_astring);
+ }
+
+ free(mprop);
+}
+
+/*
+ * Destroy the basic and method properties returned by read_inst_props().
+ */
+static void
+destroy_inst_props(inetd_prop_t *bprops, inetd_prop_t **mprops)
+{
+ int i;
+
+ free_instance_props(bprops);
+ for (i = 0; i < NUM_METHODS; i++)
+ destroy_method_props(mprops[i]);
+}
+
+/*
+ * Read all the basic and method properties for instance 'inst', as inetd_prop_t
+ * tables, into the spaces referenced by 'bprops' and 'mprops' respectively.
+ * Each of the properties in the tables are flagged to indicate if the
+ * property was present or not, and if it was the value is stored within it.
+ * If an error occurs at any time -1 is returned and 'err' is set to
+ * indicate the reason, else 0 is returned.
+ */
+static int
+read_inst_props(const char *fmri, inetd_prop_t **bprops,
+ inetd_prop_t **mprops, scf_error_t *err)
+{
+ size_t nprops;
+ int i;
+
+ debug_msg("Entering read_inst_props");
+
+ if ((*bprops = read_instance_props(rep_handle, (char *)fmri, &nprops,
+ err)) == NULL)
+ return (-1);
+
+ for (i = 0; i < NUM_METHODS; i++) {
+ if ((mprops[i] =
+ read_method_props(fmri, (instance_method_t)i, err)) ==
+ NULL) {
+ for (i--; i >= 0; i--)
+ destroy_method_props(mprops[i]);
+ free_instance_props(*bprops);
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Returns B_TRUE if all required properties were read from the repository
+ * (whether taken from the defaults or directly from the instance), they
+ * all had valid values, all the required methods were present, and they
+ * each had the required properties with valid values. Else, returns B_FALSE.
+ * If the function returns B_TRUE, the storage referenced by 'cfg' is set
+ * to point at an allocated instance_cfg_t initialized based on the basic
+ * properties (not method or defaults).
+ */
+static boolean_t
+valid_inst_props(const char *fmri, inetd_prop_t *bprops, inetd_prop_t **mprops,
+ basic_cfg_t **cfg)
+{
+ boolean_t valid;
+ size_t num_bprops;
+ int i;
+
+ debug_msg("Entering valid_inst_props: inst: %s, bprops: %x, mprops: %x",
+ fmri, bprops, *mprops);
+
+ valid = valid_props(bprops, fmri, cfg, proto_info_pool, conn_ind_pool);
+
+ /*
+ * Double check we've got all necessary properties (valid_props()
+ * doesn't enforce the presence of defaults), and output error messages
+ * for each invalid/ missing property.
+ */
+ (void) get_prop_table(&num_bprops);
+ for (i = 0; i < num_bprops; i++) {
+ switch (bprops[i].ip_error) {
+ case IVE_UNSET:
+ if (!bprops[i].ip_default)
+ continue;
+ if ((i == PT_ARG0_INDEX) || (i == PT_EXEC_INDEX))
+ continue;
+ /* FALLTHROUGH */
+ case IVE_INVALID:
+ error_msg(gettext("Property '%s' of instance "
+ "%s is missing, inconsistent or invalid"),
+ bprops[i].ip_name, fmri);
+ valid = B_FALSE;
+ }
+ }
+
+ for (i = 0; i < NUM_METHODS; i++) {
+ int j;
+
+ /* check if any properties are set */
+ for (j = 0; j < NUM_METHOD_PROPS; j++) {
+ if (mprops[i][j].ip_error != IVE_UNSET)
+ break;
+ }
+
+ if (j == NUM_METHOD_PROPS) {
+ /* an unspecified method */
+ if ((instance_method_t)i == IM_START) {
+ error_msg(gettext(
+ "Unspecified %s method for instance %s"),
+ START_METHOD_NAME, fmri);
+ valid = B_FALSE;
+ }
+ } else if (mprops[i][MP_EXEC].ip_error == IVE_UNSET) {
+ error_msg(gettext("Missing %s property from method %s "
+ "of instance %s"), PR_EXEC_NAME,
+ methods[(instance_method_t)i].name, fmri);
+ valid = B_FALSE;
+ }
+ }
+
+ if (!valid)
+ destroy_basic_cfg(*cfg);
+
+ return (valid);
+}
+
+void
+destroy_instance_cfg(instance_cfg_t *cfg)
+{
+ if (cfg != NULL) {
+ destroy_basic_cfg(cfg->basic);
+ destroy_method_infos(cfg->methods);
+ free(cfg);
+ }
+}
+
+/*
+ * Returns an allocated instance_cfg_t representation of an instance's
+ * configuration read from the repository. If the configuration is invalid, a
+ * repository error occurred, or a memory allocation occurred returns NULL,
+ * else returns a pointer to the allocated instance_cfg_t.
+ */
+instance_cfg_t *
+read_instance_cfg(const char *fmri)
+{
+ uint_t retries;
+ inetd_prop_t *bprops;
+ inetd_prop_t *mprops[NUM_METHODS];
+ instance_cfg_t *ret = NULL;
+ scf_error_t err;
+
+ debug_msg("Entering read_instance_cfg");
+
+ if ((ret = calloc(1, sizeof (instance_cfg_t))) == NULL)
+ return (NULL);
+
+ for (retries = 0; retries <= REP_OP_RETRIES; retries++) {
+ if (make_handle_bound(rep_handle) == -1) {
+ err = scf_error();
+ goto read_error;
+ }
+
+ if (read_inst_props(fmri, &bprops, mprops, &err) == 0)
+ break;
+ if (err != SCF_ERROR_CONNECTION_BROKEN)
+ goto read_error;
+ (void) scf_handle_unbind(rep_handle);
+ }
+ if (retries > REP_OP_RETRIES)
+ goto read_error;
+
+ /*
+ * Switch off validation of the start method's exec string, since
+ * during boot the filesystem it resides on may not have been
+ * mounted yet, which would result in a false validation failure.
+ * We'll catch any real errors when the start method is first run
+ * in passes_basic_exec_checks().
+ */
+ bprops[PT_EXEC_INDEX].ip_error = IVE_UNSET;
+
+ if ((!valid_inst_props(fmri, bprops, mprops, &ret->basic)) ||
+ (populate_defaults(bprops, ret->basic) != 0) ||
+ (create_method_infos(fmri, mprops, ret->methods) != 0)) {
+ destroy_instance_cfg(ret);
+ ret = NULL;
+ }
+
+ destroy_inst_props(bprops, mprops);
+ return (ret);
+
+read_error:
+ error_msg(gettext(
+ "Failed to read the configuration of instance %s: %s"), fmri,
+ scf_strerror(err));
+ free(ret);
+ return (NULL);
+}
+
+/*
+ * Returns a pointer to an allocated method context for the specified method
+ * of the specified instance if it could retrieve it. Else, if there were
+ * errors retrieving it, NULL is returned and the pointer referenced by
+ * 'errstr' is set to point at an appropriate error string.
+ */
+struct method_context *
+read_method_context(const char *inst_fmri, const char *method, const char *path,
+ const char **errstr)
+{
+ scf_instance_t *scf_inst = NULL;
+ struct method_context *ret;
+ uint_t retries;
+ const char *tmpstr;
+
+ debug_msg("Entering read_method_context: inst: %s, method: %s, "
+ "path: %s", inst_fmri, method, path);
+
+ for (retries = 0; retries <= REP_OP_RETRIES; retries++) {
+ if (make_handle_bound(rep_handle) == -1)
+ goto inst_failure;
+
+ if (((scf_inst = scf_instance_create(rep_handle)) != NULL) &&
+ (scf_handle_decode_fmri(rep_handle, inst_fmri, NULL, NULL,
+ scf_inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) == 0))
+ break;
+ if (scf_error() != SCF_ERROR_CONNECTION_BROKEN) {
+ scf_instance_destroy(scf_inst);
+ goto inst_failure;
+ }
+
+ (void) scf_instance_destroy(scf_inst);
+ scf_inst = NULL;
+
+ (void) scf_handle_unbind(rep_handle);
+ }
+ if (retries > REP_OP_RETRIES)
+ goto inst_failure;
+
+ if ((tmpstr = restarter_get_method_context(
+ RESTARTER_METHOD_CONTEXT_VERSION, scf_inst, NULL, method, path,
+ &ret)) != NULL) {
+ ret = NULL;
+ *errstr = tmpstr;
+ }
+
+ scf_instance_destroy(scf_inst);
+ return (ret);
+
+inst_failure:
+ /*
+ * We can rely on this string not becoming invalid
+ * since we don't call bind_textdomain_codeset() or
+ * setlocale(3C) after initialization.
+ */
+ *errstr = gettext("failed to get instance from repository");
+ return (NULL);
+}
+
+/*
+ * Reads the value of the enabled property from the named property group
+ * of the given instance.
+ * If an error occurs, the SCF error code is returned. The possible errors are:
+ * - SCF_ERROR_INVALID_ARGUMENT: The enabled property is not a boolean.
+ * - SCF_ERROR_NONE: No value exists for the enabled property.
+ * - SCF_ERROR_CONNECTION_BROKEN: Repository connection broken.
+ * - SCF_ERROR_NOT_FOUND: The property wasn't found.
+ * - SCF_ERROR_NO_MEMORY: allocation failure.
+ * Else 0 is returned and 'enabled' set appropriately.
+ */
+static scf_error_t
+read_enable_prop(const char *fmri, boolean_t *enabled, const char *pg)
+{
+ scf_simple_prop_t *sp;
+ uint8_t *u8p;
+
+ if ((sp = scf_simple_prop_get(rep_handle, fmri, pg,
+ SCF_PROPERTY_ENABLED)) == NULL)
+ return (scf_error());
+
+ if ((u8p = scf_simple_prop_next_boolean(sp)) == NULL) {
+ scf_simple_prop_free(sp);
+ return (scf_error());
+ }
+
+ *enabled = (*u8p != 0);
+ scf_simple_prop_free(sp);
+ return (0);
+}
+
+/*
+ * Reads the enabled value for the given instance FMRI. The read value
+ * is based on a merge of the 'standard' enabled property, and the temporary
+ * override one; the merge involves using the latter properties value if
+ * present, else resporting to the formers. If an error occurs -1 is returned,
+ * else 0 is returned and 'enabled' set approriately.
+ */
+int
+read_enable_merged(const char *fmri, boolean_t *enabled)
+{
+ uint_t retries;
+
+ debug_msg("Entering read_enabled_prop: inst: %s", fmri);
+
+ for (retries = 0; retries <= REP_OP_RETRIES; retries++) {
+ if (make_handle_bound(rep_handle) == -1)
+ goto gen_fail;
+
+ switch (read_enable_prop(fmri, enabled, SCF_PG_GENERAL_OVR)) {
+ case 0:
+ debug_msg("read %d from override", *enabled);
+ return (0);
+ case SCF_ERROR_CONNECTION_BROKEN:
+ break;
+ case SCF_ERROR_NOT_FOUND:
+ case SCF_ERROR_NONE:
+ case SCF_ERROR_INVALID_ARGUMENT:
+ switch (read_enable_prop(fmri, enabled,
+ SCF_PG_GENERAL)) {
+ case 0:
+ debug_msg("read %d from non_override",
+ *enabled);
+ return (0);
+ case SCF_ERROR_CONNECTION_BROKEN:
+ break;
+ case SCF_ERROR_NOT_FOUND:
+ case SCF_ERROR_NONE:
+ case SCF_ERROR_INVALID_ARGUMENT:
+ error_msg(gettext("Missing %s property/value "
+ "for instance %s"), SCF_PROPERTY_ENABLED,
+ fmri);
+ return (-1);
+ default:
+ goto gen_fail;
+ }
+ break;
+ default:
+ goto gen_fail;
+ }
+
+ (void) scf_handle_unbind(rep_handle);
+ continue;
+ }
+
+gen_fail:
+ error_msg(gettext("Failed to read the %s property of instance %s: %s"),
+ SCF_PROPERTY_ENABLED, fmri, scf_strerror(scf_error()));
+ return (-1);
+}