diff options
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.c | 810 |
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); +} |