/* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* * BSD 3 Clause License * * Copyright (c) 2007, The Storage Networking Industry Association. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * - Neither the name of The Storage Networking Industry Association (SNIA) * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * NDMP configuration management */ #include #include #include #include #include #include /* NDMP properties configuration */ #define NDMP_GROUP_FMRI_PREFIX "system/ndmpd" #define NDMP_INST "svc:/system/ndmpd:default" #define NDMP_PROP_LEN 600 static char *ndmp_pg[] = { "ndmpd", "read" }; #define NPG (sizeof (ndmp_pg) / sizeof (ndmp_pg[0])) /* Handle Init states */ #define NDMP_SCH_STATE_UNINIT 0 #define NDMP_SCH_STATE_INITIALIZING 1 #define NDMP_SCH_STATE_INIT 2 /* NDMP scf handle structure */ typedef struct ndmp_scfhandle { scf_handle_t *scf_handle; int scf_state; scf_service_t *scf_service; scf_scope_t *scf_scope; scf_transaction_t *scf_trans; scf_propertygroup_t *scf_pg; } ndmp_scfhandle_t; static int ndmp_config_saveenv(ndmp_scfhandle_t *, boolean_t); static ndmp_scfhandle_t *ndmp_smf_scf_init(const char *); static void ndmp_smf_scf_fini(ndmp_scfhandle_t *); static int ndmp_smf_start_transaction(ndmp_scfhandle_t *); static int ndmp_smf_end_transaction(ndmp_scfhandle_t *, boolean_t); static int ndmp_smf_set_property(ndmp_scfhandle_t *, const char *, const char *); static int ndmp_smf_get_property(ndmp_scfhandle_t *, const char *, char *, size_t); static int ndmp_smf_create_service_pgroup(ndmp_scfhandle_t *, const char *); static int ndmp_smf_delete_property(ndmp_scfhandle_t *, const char *); static int ndmp_smf_get_pg_name(ndmp_scfhandle_t *, const char *, char **); /* * This routine send a refresh signal to ndmpd service which cause ndmpd * property table to be refeshed with current ndmpd properties value from SMF. */ int ndmp_service_refresh(void) { int rc = smf_refresh_instance(NDMP_INST); if (rc != 0) ndmp_errno = ENDMP_SMF_INTERNAL; return (rc); } /* * Returns value of the specified variable/property. The return value is a * string pointer to the locally allocated memory if the config param is * defined otherwise it would be NULL. */ int ndmp_get_prop(const char *prop, char **value) { ndmp_scfhandle_t *handle; char *lval; char *pgname; *value = NULL; if ((handle = ndmp_smf_scf_init(NDMP_GROUP_FMRI_PREFIX)) == NULL) { return (-1); } if (ndmp_smf_get_pg_name(handle, prop, &pgname)) { ndmp_smf_scf_fini(handle); ndmp_errno = ENDMP_SMF_PROP_GRP; return (-1); } if (ndmp_smf_create_service_pgroup(handle, pgname)) { ndmp_smf_scf_fini(handle); return (-1); } if ((lval = malloc(NDMP_PROP_LEN)) == NULL) { ndmp_smf_scf_fini(handle); ndmp_errno = ENDMP_MEM_ALLOC; return (-1); } if (ndmp_smf_get_property(handle, prop, lval, NDMP_PROP_LEN) != 0) { ndmp_smf_scf_fini(handle); free(lval); ndmp_errno = ENDMP_SMF_PROP; return (-1); } *value = lval; ndmp_smf_scf_fini(handle); return (0); } int ndmp_set_prop(const char *env, const char *env_val) { ndmp_scfhandle_t *handle; char *pgname; int rc; if ((handle = ndmp_smf_scf_init(NDMP_GROUP_FMRI_PREFIX)) == NULL) return (-1); if (ndmp_smf_get_pg_name(handle, env, &pgname)) { ndmp_smf_scf_fini(handle); ndmp_errno = ENDMP_SMF_PROP_GRP; return (-1); } if (ndmp_smf_create_service_pgroup(handle, pgname)) { ndmp_smf_scf_fini(handle); return (-1); } if (ndmp_smf_start_transaction(handle)) { ndmp_smf_scf_fini(handle); return (-1); } if (env_val) rc = ndmp_smf_set_property(handle, env, env_val); else rc = ndmp_smf_delete_property(handle, env); if (ndmp_config_saveenv(handle, (rc == 0)) == 0) return (rc); else return (-1); } static int ndmp_smf_get_pg_name(ndmp_scfhandle_t *h, const char *pname, char **pgname) { scf_value_t *value; scf_property_t *prop; int i; for (i = 0; i < NPG; i++) { if (scf_service_get_pg(h->scf_service, ndmp_pg[i], h->scf_pg) != 0) return (-1); if ((value = scf_value_create(h->scf_handle)) == NULL) return (-1); if ((prop = scf_property_create(h->scf_handle)) == NULL) { scf_value_destroy(value); return (-1); } /* * This will fail if property does not exist in the property * group. Check the next property group in case of failure. */ if ((scf_pg_get_property(h->scf_pg, pname, prop)) != 0) { scf_value_destroy(value); scf_property_destroy(prop); continue; } *pgname = ndmp_pg[i]; scf_value_destroy(value); scf_property_destroy(prop); return (0); } return (-1); } /* * Basically commit the transaction. */ static int ndmp_config_saveenv(ndmp_scfhandle_t *handle, boolean_t commit) { int ret = 0; ret = ndmp_smf_end_transaction(handle, commit); ndmp_smf_scf_fini(handle); return (ret); } /* * Must be called when done. Called with the handle allocated in * ndmp_smf_scf_init(), it cleans up the state and frees any SCF resources * still in use. */ static void ndmp_smf_scf_fini(ndmp_scfhandle_t *handle) { if (handle == NULL) return; scf_scope_destroy(handle->scf_scope); scf_service_destroy(handle->scf_service); scf_pg_destroy(handle->scf_pg); handle->scf_state = NDMP_SCH_STATE_UNINIT; (void) scf_handle_unbind(handle->scf_handle); scf_handle_destroy(handle->scf_handle); free(handle); } /* * Must be called before using any of the SCF functions. Returns * ndmp_scfhandle_t pointer if success. */ static ndmp_scfhandle_t * ndmp_smf_scf_init(const char *svc_name) { ndmp_scfhandle_t *handle; handle = (ndmp_scfhandle_t *)calloc(1, sizeof (ndmp_scfhandle_t)); if (handle != NULL) { handle->scf_state = NDMP_SCH_STATE_INITIALIZING; if (((handle->scf_handle = scf_handle_create(SCF_VERSION)) != NULL) && (scf_handle_bind(handle->scf_handle) == 0)) { if ((handle->scf_scope = scf_scope_create(handle->scf_handle)) == NULL) goto err; if (scf_handle_get_local_scope(handle->scf_handle, handle->scf_scope) != 0) goto err; if ((handle->scf_service = scf_service_create(handle->scf_handle)) == NULL) goto err; if (scf_scope_get_service(handle->scf_scope, svc_name, handle->scf_service) != SCF_SUCCESS) goto err; if ((handle->scf_pg = scf_pg_create(handle->scf_handle)) == NULL) goto err; handle->scf_state = NDMP_SCH_STATE_INIT; } else { goto err; } } else { ndmp_errno = ENDMP_MEM_ALLOC; handle = NULL; } return (handle); /* Error handling/unwinding */ err: (void) ndmp_smf_scf_fini(handle); ndmp_errno = ENDMP_SMF_INTERNAL; return (NULL); } /* * Create a new property group at service level. */ static int ndmp_smf_create_service_pgroup(ndmp_scfhandle_t *handle, const char *pgroup) { int err; /* * Only create a handle if it doesn't exist. It is ok to exist since * the pg handle will be set as a side effect. */ if (handle->scf_pg == NULL) { if ((handle->scf_pg = scf_pg_create(handle->scf_handle)) == NULL) { ndmp_errno = ENDMP_SMF_INTERNAL; return (-1); } } /* * If the pgroup exists, we are done. If it doesn't, then we need to * actually add one to the service instance. */ if (scf_service_get_pg(handle->scf_service, pgroup, handle->scf_pg) != 0) { /* Doesn't exist so create one */ if (scf_service_add_pg(handle->scf_service, pgroup, SCF_GROUP_FRAMEWORK, 0, handle->scf_pg) != 0) { err = scf_error(); switch (err) { case SCF_ERROR_PERMISSION_DENIED: ndmp_errno = ENDMP_SMF_PERM; return (-1); default: ndmp_errno = ENDMP_SMF_INTERNAL; return (-1); } } } return (0); } /* * Start transaction on current pg in handle. The pg could be service or * instance level. Must be called after pg handle is obtained from create or * get. */ static int ndmp_smf_start_transaction(ndmp_scfhandle_t *handle) { /* * Lookup the property group and create it if it doesn't already * exist. */ if (handle->scf_state == NDMP_SCH_STATE_INIT) { if ((handle->scf_trans = scf_transaction_create(handle->scf_handle)) != NULL) { if (scf_transaction_start(handle->scf_trans, handle->scf_pg) != 0) { scf_transaction_destroy(handle->scf_trans); handle->scf_trans = NULL; ndmp_errno = ENDMP_SMF_INTERNAL; return (-1); } } else { ndmp_errno = ENDMP_SMF_INTERNAL; return (-1); } } if (scf_error() == SCF_ERROR_PERMISSION_DENIED) { ndmp_errno = ENDMP_SMF_PERM; return (-1); } return (0); } /* * Commit the changes that were added to the transaction in the handle. Do all * necessary cleanup. */ static int ndmp_smf_end_transaction(ndmp_scfhandle_t *handle, boolean_t commit) { int rc = 0; if (commit) { if (scf_transaction_commit(handle->scf_trans) < 0) { ndmp_errno = ENDMP_SMF_INTERNAL; rc = -1; } } scf_transaction_destroy_children(handle->scf_trans); scf_transaction_destroy(handle->scf_trans); handle->scf_trans = NULL; return (rc); } /* * Deletes property in current pg */ static int ndmp_smf_delete_property(ndmp_scfhandle_t *handle, const char *propname) { scf_transaction_entry_t *entry = NULL; /* * Properties must be set in transactions and don't take effect until * the transaction has been ended/committed. */ if ((entry = scf_entry_create(handle->scf_handle)) != NULL) { if (scf_transaction_property_delete(handle->scf_trans, entry, propname) != 0) { scf_entry_destroy(entry); ndmp_errno = ENDMP_SMF_INTERNAL; return (-1); } } else { ndmp_errno = ENDMP_SMF_INTERNAL; return (-1); } if ((scf_error()) == SCF_ERROR_PERMISSION_DENIED) { ndmp_errno = ENDMP_SMF_PERM; scf_entry_destroy(entry); return (-1); } return (0); } /* * Sets property in current pg */ static int ndmp_smf_set_property(ndmp_scfhandle_t *handle, const char *propname, const char *valstr) { int ret = 0; scf_value_t *value = NULL; scf_transaction_entry_t *entry = NULL; scf_property_t *prop = NULL; scf_type_t type; int64_t valint; uint8_t valbool; /* * Properties must be set in transactions and don't take effect until * the transaction has been ended/committed. */ if (((value = scf_value_create(handle->scf_handle)) == NULL) || ((entry = scf_entry_create(handle->scf_handle)) == NULL) || ((prop = scf_property_create(handle->scf_handle)) == NULL) || (scf_pg_get_property(handle->scf_pg, propname, prop) != 0) || (scf_property_get_value(prop, value) != 0)) { ret = -1; goto out; } type = scf_value_type(value); if ((scf_transaction_property_change(handle->scf_trans, entry, propname, type) != 0) && (scf_transaction_property_new(handle->scf_trans, entry, propname, type) != 0)) { ret = -1; goto out; } switch (type) { case SCF_TYPE_ASTRING: if ((scf_value_set_astring(value, valstr)) != SCF_SUCCESS) ret = -1; break; case SCF_TYPE_INTEGER: valint = strtoll(valstr, 0, 0); scf_value_set_integer(value, valint); break; case SCF_TYPE_BOOLEAN: if (strncmp(valstr, "yes", 3)) valbool = 0; else valbool = 1; scf_value_set_boolean(value, valbool); break; default: ret = -1; } if (scf_entry_add_value(entry, value) == 0) { /* The value is in the transaction */ value = NULL; } else { ret = -1; } /* The entry is in the transaction */ entry = NULL; out: if (ret == -1) { if ((scf_error() == SCF_ERROR_PERMISSION_DENIED)) ndmp_errno = ENDMP_SMF_PERM; else ndmp_errno = ENDMP_SMF_INTERNAL; } scf_property_destroy(prop); scf_value_destroy(value); scf_entry_destroy(entry); return (ret); } /* * Gets a property value.upto sz size. Caller is responsible to have enough * memory allocated. */ static int ndmp_smf_get_property(ndmp_scfhandle_t *handle, const char *propname, char *valstr, size_t sz) { int ret = 0; scf_value_t *value = NULL; scf_property_t *prop = NULL; scf_type_t type; int64_t valint; uint8_t valbool; char valstrbuf[NDMP_PROP_LEN]; if (((value = scf_value_create(handle->scf_handle)) != NULL) && ((prop = scf_property_create(handle->scf_handle)) != NULL) && (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) { if (scf_property_get_value(prop, value) == 0) { type = scf_value_type(value); switch (type) { case SCF_TYPE_ASTRING: if (scf_value_get_astring(value, valstr, sz) < 0) { ret = -1; } break; case SCF_TYPE_INTEGER: if (scf_value_get_integer(value, &valint) != 0) { ret = -1; break; } valstrbuf[NDMP_PROP_LEN - 1] = '\0'; (void) strncpy(valstr, lltostr(valint, &valstrbuf[NDMP_PROP_LEN - 1]), NDMP_PROP_LEN); break; case SCF_TYPE_BOOLEAN: if (scf_value_get_boolean(value, &valbool) != 0) { ret = -1; break; } if (valbool == 1) (void) strncpy(valstr, "yes", 4); else (void) strncpy(valstr, "no", 3); break; default: ret = -1; } } else { ret = -1; } } else { ret = -1; } scf_value_destroy(value); scf_property_destroy(prop); return (ret); }