summaryrefslogtreecommitdiff
path: root/usr/src/lib/libshare/common/scfutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libshare/common/scfutil.c')
-rw-r--r--usr/src/lib/libshare/common/scfutil.c1443
1 files changed, 1443 insertions, 0 deletions
diff --git a/usr/src/lib/libshare/common/scfutil.c b/usr/src/lib/libshare/common/scfutil.c
new file mode 100644
index 0000000000..88447471a6
--- /dev/null
+++ b/usr/src/lib/libshare/common/scfutil.c
@@ -0,0 +1,1443 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* helper functions for using libscf with sharemgr */
+
+#include <libscf.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include "libshare.h"
+#include "libshare_impl.h"
+#include "scfutil.h"
+#include <string.h>
+#include <errno.h>
+#include <uuid/uuid.h>
+#include <sys/param.h>
+
+ssize_t scf_max_name_len;
+extern struct sa_proto_plugin *sap_proto_list;
+
+/*
+ * The SMF facility uses some properties that must exist. We want to
+ * skip over these when processing protocol options.
+ */
+static char *skip_props[] = {
+ "modify_authorization",
+ "action_authorization",
+ "value_authorization",
+ NULL
+};
+
+/*
+ * sa_scf_fini(handle)
+ *
+ * must be called when done. Called with the handle allocated in
+ * sa_scf_init(), it cleans up the state and frees any SCF resources
+ * still in use. Called by sa_fini().
+ */
+
+void
+sa_scf_fini(scfutilhandle_t *handle)
+{
+ if (handle != NULL) {
+ int unbind = 0;
+ if (handle->scope != NULL) {
+ unbind = 1;
+ scf_scope_destroy(handle->scope);
+ }
+ if (handle->service != NULL)
+ scf_service_destroy(handle->service);
+ if (handle->pg != NULL)
+ scf_pg_destroy(handle->pg);
+ if (handle->handle != NULL) {
+ handle->scf_state = SCH_STATE_UNINIT;
+ if (unbind)
+ (void) scf_handle_unbind(handle->handle);
+ scf_handle_destroy(handle->handle);
+ }
+ free(handle);
+ }
+}
+
+/*
+ * sa_scf_init()
+ *
+ * must be called before using any of the SCF functions. Called by
+ * sa_init() during the API setup.
+ */
+
+scfutilhandle_t *
+sa_scf_init()
+{
+ scfutilhandle_t *handle;
+
+ scf_max_name_len = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
+ if (scf_max_name_len <= 0)
+ scf_max_name_len = SA_MAX_NAME_LEN + 1;
+
+ handle = calloc(1, sizeof (scfutilhandle_t));
+ if (handle != NULL) {
+ handle->scf_state = SCH_STATE_INITIALIZING;
+ handle->handle = scf_handle_create(SCF_VERSION);
+ if (handle->handle != NULL) {
+ if (scf_handle_bind(handle->handle) == 0) {
+ handle->scope = scf_scope_create(handle->handle);
+ handle->service = scf_service_create(handle->handle);
+ handle->pg = scf_pg_create(handle->handle);
+ handle->instance = scf_instance_create(handle->handle);
+ if (scf_handle_get_scope(handle->handle,
+ SCF_SCOPE_LOCAL, handle->scope) == 0) {
+ if (scf_scope_get_service(handle->scope,
+ SA_GROUP_SVC_NAME,
+ handle->service) != 0) {
+ goto err;
+ }
+ handle->scf_state = SCH_STATE_INIT;
+ if (sa_get_instance(handle, "default") != SA_OK) {
+ char **protolist;
+ int numprotos, i;
+ sa_group_t defgrp;
+ defgrp = sa_create_group("default", NULL);
+ if (defgrp != NULL) {
+ numprotos = sa_get_protocols(&protolist);
+ for (i = 0; i < numprotos; i++) {
+ (void) sa_create_optionset(defgrp,
+ protolist[i]);
+ }
+ if (protolist != NULL)
+ free(protolist);
+ }
+ }
+ } else {
+ goto err;
+ }
+ } else {
+ goto err;
+ }
+ } else {
+ free(handle);
+ handle = NULL;
+ (void) printf("libshare could not access SMF repository: %s\n",
+ scf_strerror(scf_error()));
+ }
+ }
+ return (handle);
+
+ /* error handling/unwinding */
+err:
+ (void) sa_scf_fini(handle);
+ (void) printf("libshare SMF initialization problem: %s\n",
+ scf_strerror(scf_error()));
+ return (NULL);
+}
+
+/*
+ * get_scf_limit(name)
+ *
+ * Since we use scf_limit a lot and do the same check and return the
+ * same value if it fails, implement as a function for code
+ * simplification. Basically, if name isn't found, return MAXPATHLEN
+ * (1024) so we have a reasonable default buffer size.
+ */
+static ssize_t
+get_scf_limit(uint32_t name)
+{
+ ssize_t vallen;
+
+ vallen = scf_limit(name);
+ if (vallen == (ssize_t)-1)
+ vallen = MAXPATHLEN;
+ return (vallen);
+}
+
+/*
+ * skip_property(name)
+ *
+ * internal function to check to see if a property is an SMF magic
+ * property that needs to be skipped.
+ */
+static int
+skip_property(char *name)
+{
+ int i;
+
+ for (i = 0; skip_props[i] != NULL; i++)
+ if (strcmp(name, skip_props[i]) == 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * generate_unique_sharename(sharename)
+ *
+ * Shares are represented in SMF as property groups. Due to share
+ * paths containing characters that are not allowed in SMF names and
+ * the need to be unique, we use UUIDs to construct a unique name.
+ */
+
+static void
+generate_unique_sharename(char *sharename)
+{
+ uuid_t uuid;
+
+ uuid_generate(uuid);
+ (void) strcpy(sharename, "S-");
+ uuid_unparse(uuid, sharename + 2);
+}
+
+/*
+ * valid_protocol(proto)
+ *
+ * check to see if the specified protocol is a valid one for the
+ * general sharemgr facility. We determine this by checking which
+ * plugin protocols were found.
+ */
+
+static int
+valid_protocol(char *proto)
+{
+ struct sa_proto_plugin *plugin;
+ for (plugin = sap_proto_list; plugin != NULL;
+ plugin = plugin->plugin_next)
+ if (strcmp(proto, plugin->plugin_ops->sa_protocol) == 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * sa_extract_pgroup(root, handle, pg, nodetype, proto, sectype)
+ *
+ * extract the name property group and create the specified type of
+ * node on the provided group. type will be optionset or security.
+ */
+
+static int
+sa_extract_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_propertygroup_t *pg,
+ char *nodetype, char *proto, char *sectype)
+{
+ xmlNodePtr node;
+ scf_iter_t *iter;
+ scf_property_t *prop;
+ scf_value_t *value;
+ char *name;
+ char *valuestr;
+ ssize_t vallen;
+ int ret = SA_OK;
+
+ vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+
+ node = xmlNewChild(root, NULL, (xmlChar *)nodetype, NULL);
+ if (node != NULL) {
+ if (proto != NULL)
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
+ if (sectype != NULL)
+ xmlSetProp(node, (xmlChar *)"sectype", (xmlChar *)sectype);
+ /*
+ * have node to work with so iterate over the properties
+ * in the pg and create option sub nodes.
+ */
+ iter = scf_iter_create(handle->handle);
+ value = scf_value_create(handle->handle);
+ prop = scf_property_create(handle->handle);
+ name = malloc(scf_max_name_len);
+ valuestr = malloc(vallen);
+ /*
+ * want to iterate through the properties and add them
+ * to the base optionset.
+ */
+ if (iter != NULL && value != NULL && prop != NULL &&
+ valuestr != NULL && name != NULL) {
+ if (scf_iter_pg_properties(iter, pg) == 0) {
+ /* now iterate the properties in the group */
+ while (scf_iter_next_property(iter, prop) > 0) {
+ /* have a property */
+ if (scf_property_get_name(prop, name,
+ scf_max_name_len) > 0) {
+ /* some properties are part of the framework */
+ if (skip_property(name))
+ continue;
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr,
+ vallen) >= 0) {
+ sa_property_t saprop;
+ saprop = sa_create_property(name,
+ valuestr);
+ if (saprop != NULL) {
+ /*
+ * since in SMF, don't
+ * recurse. Use xmlAddChild
+ * directly, instead.
+ */
+ xmlAddChild(node,
+ (xmlNodePtr) saprop);
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ /* cleanup to avoid memory leaks */
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (iter != NULL)
+ scf_iter_destroy(iter);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+ if (name != NULL)
+ free(name);
+ if (valuestr != NULL)
+ free(valuestr);
+ }
+ return (ret);
+}
+
+/*
+ * sa_extract_attrs(root, handle, instance)
+ *
+ * local function to extract the actual attributes/properties from the
+ * property group of the service instance. These are the well known
+ * attributes of "state" and "zfs". If additional attributes are
+ * added, they should be added here.
+ */
+
+static void
+sa_extract_attrs(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_instance_t *instance)
+{
+ scf_property_t *prop;
+ scf_value_t *value;
+ char *valuestr;
+ ssize_t vallen;
+
+ vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+ prop = scf_property_create(handle->handle);
+ value = scf_value_create(handle->handle);
+ valuestr = malloc(vallen);
+ if (prop != NULL && value != NULL && valuestr != NULL &&
+ scf_instance_get_pg(instance, "operation",
+ handle->pg) == 0) {
+ /*
+ * have a property group with desired name so now get
+ * the known attributes.
+ */
+ if (scf_pg_get_property(handle->pg, "state", prop) == 0) {
+ /* found the property so get the value */
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr, vallen) >= 0) {
+ xmlSetProp(root, (xmlChar *)"state",
+ (xmlChar *)valuestr);
+ }
+ }
+ }
+ if (scf_pg_get_property(handle->pg, "zfs", prop) == 0) {
+ /* found the property so get the value */
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr, vallen) > 0) {
+ xmlSetProp(root, (xmlChar *)"zfs",
+ (xmlChar *)valuestr);
+ }
+ }
+ }
+ }
+ if (valuestr != NULL)
+ free(valuestr);
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+}
+
+/*
+ * list of known share attributes.
+ */
+
+static char *share_attr[] = {
+ "path",
+ "id",
+ "resource",
+ NULL,
+};
+
+static int
+is_share_attr(char *name)
+{
+ int i;
+ for (i = 0; share_attr[i] != NULL; i++)
+ if (strcmp(name, share_attr[i]) == 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * sa_share_from_pgroup
+ *
+ * extract the share definition from the share property group. We do
+ * some sanity checking to avoid bad data.
+ *
+ * Since this is only constructing the internal data structures, we
+ * don't use the sa_* functions most of the time.
+ */
+void
+sa_share_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_propertygroup_t *pg, char *id)
+{
+ xmlNodePtr node;
+ char *name;
+ scf_iter_t *iter;
+ scf_property_t *prop;
+ scf_value_t *value;
+ ssize_t vallen;
+ char *valuestr;
+ int ret = SA_OK;
+
+ /*
+ * While preliminary check (starts with 'S') passed before
+ * getting here. Need to make sure it is in ID syntax
+ * (Snnnnnn). Note that shares with properties have similar
+ * pgroups.
+ */
+ vallen = strlen(id);
+ if (*id == SA_SHARE_PG_PREFIX[0] && vallen == SA_SHARE_PG_LEN) {
+ uuid_t uuid;
+ if (strncmp(id, SA_SHARE_PG_PREFIX, SA_SHARE_PG_PREFIXLEN) != 0 ||
+ uuid_parse(id + 2, uuid) < 0)
+ return;
+ } else {
+ return;
+ }
+
+ vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+
+ iter = scf_iter_create(handle->handle);
+ value = scf_value_create(handle->handle);
+ prop = scf_property_create(handle->handle);
+ name = malloc(scf_max_name_len);
+ valuestr = malloc(vallen);
+
+ /*
+ * construct the share XML node. It is similar to sa_add_share
+ * but never changes the repository. Also, there won't be any
+ * ZFS or transient shares. Root will be the group it is
+ * associated with.
+ */
+ node = xmlNewChild(root, NULL, (xmlChar *)"share", NULL);
+ if (node != NULL) {
+ /*
+ * make sure the UUID part of the property group is
+ * stored in the share "id" property. We use this
+ * later.
+ */
+ xmlSetProp(node, (xmlChar *)"id", (xmlChar *)id);
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)"persist");
+ }
+
+ if (iter != NULL && value != NULL && prop != NULL && name != NULL) {
+ /* iterate over the share pg properties */
+ if (scf_iter_pg_properties(iter, pg) == 0) {
+ while (scf_iter_next_property(iter, prop) > 0) {
+ ret = SA_SYSTEM_ERR; /* assume the worst */
+ if (scf_property_get_name(prop, name,
+ scf_max_name_len) > 0) {
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr,
+ vallen) >= 0) {
+ ret = SA_OK;
+ }
+ }
+ }
+ if (ret == SA_OK) {
+ if (is_share_attr(name)) {
+ /*
+ * if a share attr, then simple -
+ * usually path and resource name
+ */
+ xmlSetProp(node, (xmlChar *)name,
+ (xmlChar *)valuestr);
+ } else {
+ if (strcmp(name, "description") == 0) {
+ /* we have a description node */
+ xmlNodePtr desc;
+ desc = xmlNewChild(node, NULL,
+ (xmlChar *)"description",
+ NULL);
+ if (desc != NULL)
+ xmlNodeSetContent(desc,
+ (xmlChar *)valuestr);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (name != NULL)
+ free(name);
+ if (valuestr != NULL)
+ free(valuestr);
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (iter != NULL)
+ scf_iter_destroy(iter);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+}
+
+/*
+ * find_share_by_id(shareid)
+ *
+ * Search all shares in all groups until we find the share represented
+ * by "id".
+ */
+
+static sa_share_t
+find_share_by_id(char *shareid)
+{
+ sa_group_t group;
+ sa_share_t share = NULL;
+ char *id = NULL;
+ int done = 0;
+
+ for (group = sa_get_group(NULL); group != NULL && !done;
+ group = sa_get_next_group(group)) {
+ for (share = sa_get_share(group, NULL); share != NULL;
+ share = sa_get_next_share(share)) {
+ id = sa_get_share_attr(share, "id");
+ if (id != NULL && strcmp(id, shareid) == 0) {
+ sa_free_attr_string(id);
+ id = NULL;
+ done++;
+ break;
+ }
+ if (id != NULL) {
+ sa_free_attr_string(id);
+ id = NULL;
+ }
+ }
+ }
+ return (share);
+}
+
+/*
+ * sa_share_props_from_pgroup(root, handle, pg, id)
+ *
+ * extract share properties from the SMF property group. More sanity
+ * checks are done and the share object is created. We ignore some
+ * errors that could exist in the repository and only worry about
+ * property groups that validate in naming.
+ */
+
+static int
+sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_propertygroup_t *pg, char *id)
+{
+ xmlNodePtr node;
+ char *name;
+ scf_iter_t *iter;
+ scf_property_t *prop;
+ scf_value_t *value;
+ ssize_t vallen;
+ char *valuestr;
+ int ret = SA_OK;
+ char *sectype = NULL;
+ char *proto;
+ sa_share_t share;
+
+ /*
+ * While preliminary check (starts with 'S') passed before
+ * getting here. Need to make sure it is in ID syntax
+ * (Snnnnnn). Note that shares with properties have similar
+ * pgroups. If the pg name is more than SA_SHARE_PG_LEN
+ * characters, it is likely one of the protocol/security
+ * versions.
+ */
+ vallen = strlen(id);
+ if (*id == SA_SHARE_PG_PREFIX[0] && vallen > SA_SHARE_PG_LEN) {
+ uuid_t uuid;
+ if (strncmp(id, SA_SHARE_PG_PREFIX, SA_SHARE_PG_PREFIXLEN) == 0) {
+ proto = strchr(id, '_');
+ if (proto == NULL)
+ return (ret);
+ *proto++ = '\0';
+ if (uuid_parse(id + SA_SHARE_PG_PREFIXLEN, uuid) < 0)
+ return (ret);
+ /*
+ * probably a legal optionset so check a few more
+ * syntax points below.
+ */
+ if (*proto == '\0') {
+ /* not a valid proto (null) */
+ return (ret);
+ }
+ sectype = strchr(proto, '_');
+ if (sectype != NULL)
+ *sectype++ = '\0';
+ if (!valid_protocol(proto))
+ return (ret);
+ }
+ } else {
+ /*
+ * it is ok to not have what we thought since someone might
+ * have added a name via SMF.
+ */
+ return (ret);
+ }
+
+ /*
+ * to get here, we have a valid protocol and possibly a
+ * security. We now have to find the share that it is really
+ * associated with. The "id" portion of the pgroup name will
+ * match.
+ */
+
+ share = find_share_by_id(id);
+ if (share == NULL)
+ return (SA_BAD_PATH);
+
+ root = (xmlNodePtr)share;
+
+ vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+
+ iter = scf_iter_create(handle->handle);
+ value = scf_value_create(handle->handle);
+ prop = scf_property_create(handle->handle);
+ name = malloc(scf_max_name_len);
+ valuestr = malloc(vallen);
+
+ if (sectype == NULL)
+ node = xmlNewChild(root, NULL, (xmlChar *)"optionset", NULL);
+ else {
+ node = xmlNewChild(root, NULL, (xmlChar *)"security", NULL);
+ if (node != NULL)
+ xmlSetProp(node, (xmlChar *)"sectype", (xmlChar *)sectype);
+ }
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
+ /* now find the properties */
+ if (iter != NULL && value != NULL && prop != NULL && name != NULL) {
+ /* iterate over the share pg properties */
+ if (scf_iter_pg_properties(iter, pg) == 0) {
+ while (scf_iter_next_property(iter, prop) > 0) {
+ ret = SA_SYSTEM_ERR; /* assume the worst */
+ if (scf_property_get_name(prop, name,
+ scf_max_name_len) > 0) {
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr,
+ vallen) >= 0) {
+ ret = SA_OK;
+ }
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ if (ret == SA_OK) {
+ sa_property_t prop;
+ prop = sa_create_property(name, valuestr);
+ if (prop != NULL)
+ prop = (sa_property_t)xmlAddChild(node,
+ (xmlNodePtr)prop);
+ else
+ ret = SA_NO_MEMORY;
+ }
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ }
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ if (iter != NULL)
+ scf_iter_destroy(iter);
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+ if (name != NULL)
+ free(name);
+ if (valuestr != NULL)
+ free(valuestr);
+ return (ret);
+}
+
+/*
+ * sa_extract_group(root, handle, instance)
+ *
+ * get the config info for this instance of a group and create the XML
+ * subtree from it.
+ */
+
+static int
+sa_extract_group(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_instance_t *instance)
+{
+ char *buff;
+ xmlNodePtr node;
+ scf_iter_t *iter;
+ char *proto;
+ char *sectype;
+ int have_shares = 0;
+ int has_proto = 0;
+ int is_default = 0;
+ int ret = SA_OK;
+ int err;
+
+ buff = malloc(scf_max_name_len);
+ iter = scf_iter_create(handle->handle);
+ if (buff != NULL) {
+ if (scf_instance_get_name(instance, buff,
+ scf_max_name_len) > 0) {
+ node = xmlNewChild(root, NULL, (xmlChar *)"group", NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"name", (xmlChar *)buff);
+ if (strcmp(buff, "default") == 0)
+ is_default++;
+ sa_extract_attrs(node, handle, instance);
+ /*
+ * Iterate through all the property groups
+ * looking for those with security or
+ * optionset prefixes. The names of the
+ * matching pgroups are parsed to get the
+ * protocol, and for security, the sectype.
+ * Syntax is as follows:
+ * optionset | optionset_<proto>
+ * security_default | security_<proto>_<sectype>
+ * "operation" is handled by
+ * sa_extract_attrs().
+ */
+ if (iter != NULL) {
+ if (scf_iter_instance_pgs(iter, instance) == 0) {
+ while (scf_iter_next_pg(iter, handle->pg) > 0) {
+ /* have a pgroup so sort it out */
+ ret = scf_pg_get_name(handle->pg, buff,
+ scf_max_name_len);
+ if (ret > 0) {
+ if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
+ sa_share_from_pgroup(node, handle,
+ handle->pg,
+ buff);
+ have_shares++;
+ } else if (strncmp(buff, "optionset", 9) ==
+ 0) {
+ char *nodetype = "optionset";
+ /* have an optionset */
+ sectype = NULL;
+ proto = strchr(buff, '_');
+ if (proto != NULL) {
+ *proto++ = '\0';
+ sectype = strchr(proto, '_');
+ if (sectype != NULL) {
+ *sectype++ = '\0';
+ nodetype = "security";
+ }
+ }
+ ret = sa_extract_pgroup(node, handle,
+ handle->pg,
+ nodetype,
+ proto, sectype);
+ has_proto++;
+ } else if (strncmp(buff,
+ "security", 8) == 0) {
+ /*
+ * have a security (note that
+ * this should change in the
+ * future)
+ */
+ proto = strchr(buff, '_');
+ sectype = NULL;
+ if (proto != NULL) {
+ *proto++ = '\0';
+ sectype = strchr(proto, '_');
+ if (sectype != NULL)
+ *sectype++ = '\0';
+ if (strcmp(proto, "default") == 0)
+ proto = NULL;
+ }
+ ret = sa_extract_pgroup(node, handle,
+ handle->pg,
+ "security", proto,
+ sectype);
+ has_proto++;
+ }
+ /* ignore everything else */
+ }
+ }
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ /*
+ * Make sure we have a valid default group.
+ * On first boot, default won't have any
+ * protocols defined and won't be enabled (but
+ * should be).
+ */
+ if (is_default) {
+ char *state = sa_get_group_attr((sa_group_t)node,
+ "state");
+ char **protos;
+ int numprotos;
+ int i;
+
+ if (state == NULL) {
+ /* set attribute to enabled */
+ (void) sa_set_group_attr((sa_group_t)node,
+ "state",
+ "enabled");
+ /* we can assume no protocols */
+ numprotos = sa_get_protocols(&protos);
+ for (i = 0; i < numprotos; i++)
+ (void) sa_create_optionset((sa_group_t)node,
+ protos[i]);
+ if (numprotos > 0)
+ free(protos);
+ } else {
+ sa_free_attr_string(state);
+ }
+ }
+ /* do a second pass if shares were found */
+ if (have_shares &&
+ scf_iter_instance_pgs(iter, instance) == 0) {
+ while (scf_iter_next_pg(iter, handle->pg) > 0) {
+ /*
+ * have a pgroup so see if it is a
+ * share optionset
+ */
+ err = scf_pg_get_name(handle->pg, buff,
+ scf_max_name_len);
+ if (err > 0) {
+ if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
+ ret = sa_share_props_from_pgroup(node,
+ handle,
+ handle->pg,
+ buff);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (iter != NULL)
+ scf_iter_destroy(iter);
+ if (buff != NULL)
+ free(buff);
+ return (ret);
+}
+
+/*
+ * sa_extract_defaults(root, handle, instance)
+ *
+ * local function to find the default properties that live in the
+ * default instance's "operation" proprerty group.
+ */
+
+static void
+sa_extract_defaults(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_instance_t *instance)
+{
+ xmlNodePtr node;
+ scf_property_t *prop;
+ scf_value_t *value;
+ char *valuestr;
+ ssize_t vallen;
+
+ vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+ prop = scf_property_create(handle->handle);
+ value = scf_value_create(handle->handle);
+ valuestr = malloc(vallen);
+ if (prop != NULL && value != NULL && vallen != NULL &&
+ scf_instance_get_pg(instance, "operation",
+ handle->pg) == 0) {
+ if (scf_pg_get_property(handle->pg,
+ "legacy-timestamp", prop) == 0) {
+ /* found the property so get the value */
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr, vallen) > 0) {
+ node = xmlNewChild(root, NULL, (xmlChar *)"legacy",
+ NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"timestamp",
+ (xmlChar *)valuestr);
+ xmlSetProp(node, (xmlChar *)"path",
+ (xmlChar *)SA_LEGACY_DFSTAB);
+ }
+ }
+ }
+ }
+ }
+ if (valuestr != NULL)
+ free(valuestr);
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+}
+
+
+/*
+ * sa_get_config(handle, root, doc)
+ *
+ * walk the SMF repository for /network/shares/group and find all the
+ * instances. These become group names. Then add the XML structure
+ * below the groups based on property groups and properties.
+ */
+int
+sa_get_config(scfutilhandle_t *handle, xmlNodePtr *root, xmlDocPtr *doc)
+{
+ int ret = SA_OK;
+ scf_instance_t *instance;
+ scf_iter_t *iter;
+ char buff[BUFSIZ * 2];
+
+ *doc = xmlNewDoc((xmlChar *)"1.0");
+ *root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
+ instance = scf_instance_create(handle->handle);
+ iter = scf_iter_create(handle->handle);
+ if (*doc != NULL && *root != NULL && instance != NULL && iter != NULL) {
+ xmlDocSetRootElement(*doc, *root);
+ if ((ret = scf_iter_service_instances(iter,
+ handle->service)) == 0) {
+ while ((ret = scf_iter_next_instance(iter,
+ instance)) > 0) {
+ if (scf_instance_get_name(instance, buff,
+ sizeof (buff)) > 0) {
+ if (strcmp(buff, "default") == 0)
+ sa_extract_defaults(*root, handle, instance);
+ ret = sa_extract_group(*root, handle, instance);
+ }
+ }
+ }
+ } else {
+ /* if we can't create the document, cleanup */
+ if (*doc != NULL)
+ xmlFreeDoc(*doc);
+ if (*root != NULL)
+ xmlFreeNode(*root);
+ *doc = NULL;
+ *root = NULL;
+ }
+ /* always cleanup these */
+ if (instance != NULL)
+ scf_instance_destroy(instance);
+ if (iter != NULL)
+ scf_iter_destroy(iter);
+ return (ret);
+}
+
+/*
+ * sa_get_instance(handle, instance)
+ *
+ * get the instance of the group service. This is actually the
+ * specific group name. The instance is needed for all property and
+ * control operations.
+ */
+
+int
+sa_get_instance(scfutilhandle_t *handle, char *instname)
+{
+ if (scf_service_get_instance(handle->service, instname,
+ handle->instance) != 0) {
+ return (SA_NO_SUCH_GROUP);
+ }
+ return (SA_OK);
+}
+
+/*
+ * sa_create_instance(handle, instname)
+ *
+ * Create a new SMF service instance. There can only be one with a
+ * given name.
+ */
+
+int
+sa_create_instance(scfutilhandle_t *handle, char *instname)
+{
+ int ret = SA_OK;
+ char instance[SA_GROUP_INST_LEN];
+ if (scf_service_add_instance(handle->service, instname,
+ handle->instance) != 0) {
+ /* better error returns need to be added based on real error */
+ if (scf_error() == SCF_ERROR_PERMISSION_DENIED)
+ ret = SA_NO_PERMISSION;
+ else
+ ret = SA_DUPLICATE_NAME;
+ } else {
+ /* have the service created, so enable it */
+ (void) snprintf(instance, sizeof (instance), "%s:%s",
+ SA_SVC_FMRI_BASE, instname);
+ (void) smf_enable_instance(instance, 0);
+ }
+ return (ret);
+}
+
+/*
+ * sa_delete_instance(handle, instname)
+ *
+ * When a group goes away, we also remove the service instance.
+ */
+
+int
+sa_delete_instance(scfutilhandle_t *handle, char *instname)
+{
+ int ret;
+
+ if (strcmp(instname, "default") == 0) {
+ ret = SA_NO_PERMISSION;
+ } else {
+ if ((ret = sa_get_instance(handle, instname)) == SA_OK) {
+ if (scf_instance_delete(handle->instance) != 0)
+ /* need better analysis */
+ ret = SA_NO_PERMISSION;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_create_pgroup(handle, pgroup)
+ *
+ * create a new property group
+ */
+
+int
+sa_create_pgroup(scfutilhandle_t *handle, char *pgroup)
+{
+ int ret = SA_OK;
+ /*
+ * 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->pg == NULL) {
+ handle->pg = scf_pg_create(handle->handle);
+ }
+ /*
+ * if the pgroup exists, we are done. If it doesn't, then we
+ * need to actually add one to the service instance.
+ */
+ if (scf_instance_get_pg(handle->instance,
+ pgroup, handle->pg) != 0) {
+ /* doesn't exist so create one */
+ if (scf_instance_add_pg(handle->instance, pgroup,
+ SCF_GROUP_APPLICATION, 0,
+ handle->pg) != 0) {
+ switch (scf_error()) {
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = SA_NO_PERMISSION;
+ break;
+ default:
+ ret = SA_SYSTEM_ERR;
+ break;
+ }
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_delete_pgroup(handle, pgroup)
+ *
+ * remove the property group from the current instance of the service,
+ * but only if it actually exists.
+ */
+
+int
+sa_delete_pgroup(scfutilhandle_t *handle, char *pgroup)
+{
+ int ret = SA_OK;
+ /*
+ * only delete if it does exist.
+ */
+ if (scf_instance_get_pg(handle->instance,
+ pgroup, handle->pg) == 0) {
+ /* does exist so delete it */
+ if (scf_pg_delete(handle->pg) != 0) {
+ ret = SA_SYSTEM_ERR;
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ if (ret == SA_SYSTEM_ERR &&
+ scf_error() == SCF_ERROR_PERMISSION_DENIED) {
+ ret = SA_NO_PERMISSION;
+ }
+ return (ret);
+}
+
+/*
+ * sa_start_transaction(handle, pgroup)
+ *
+ * Start an SMF transaction so we can deal with properties. it would
+ * be nice to not have to expose this, but we have to in order to
+ * optimize.
+ *
+ * Basic model is to hold the transaction in the handle and allow
+ * property adds/deletes/updates to be added then close the
+ * transaction (or abort). There may eventually be a need to handle
+ * other types of transaction mechanisms but we don't do that now.
+ *
+ * An sa_start_transaction must be followed by either an
+ * sa_end_transaction or sa_abort_transaction before another
+ * sa_start_transaction can be done.
+ */
+
+int
+sa_start_transaction(scfutilhandle_t *handle, char *propgroup)
+{
+ int ret = SA_OK;
+ /*
+ * lookup the property group and create it if it doesn't already
+ * exist.
+ */
+ if (handle->scf_state == SCH_STATE_INIT) {
+ ret = sa_create_pgroup(handle, propgroup);
+ if (ret == SA_OK) {
+ handle->trans = scf_transaction_create(handle->handle);
+ if (handle->trans != NULL) {
+ if (scf_transaction_start(handle->trans, handle->pg) != 0) {
+ ret = SA_SYSTEM_ERR;
+ }
+ if (ret != SA_OK) {
+ scf_transaction_destroy(handle->trans);
+ handle->trans = NULL;
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ }
+ }
+ if (ret == SA_SYSTEM_ERR &&
+ scf_error() == SCF_ERROR_PERMISSION_DENIED) {
+ ret = SA_NO_PERMISSION;
+ }
+ return (ret);
+}
+
+/*
+ * sa_end_transaction(handle)
+ *
+ * Commit the changes that were added to the transaction in the
+ * handle. Do all necessary cleanup.
+ */
+
+int
+sa_end_transaction(scfutilhandle_t *handle)
+{
+ int ret = SA_OK;
+
+ if (handle->trans == NULL) {
+ ret = SA_SYSTEM_ERR;
+ } else {
+ if (scf_transaction_commit(handle->trans) < 0)
+ ret = SA_SYSTEM_ERR;
+ scf_transaction_destroy_children(handle->trans);
+ scf_transaction_destroy(handle->trans);
+ handle->trans = NULL;
+ }
+ return (ret);
+}
+
+/*
+ * sa_abort_transaction(handle)
+ *
+ * Abort the changes that were added to the transaction in the
+ * handle. Do all necessary cleanup.
+ */
+
+void
+sa_abort_transaction(scfutilhandle_t *handle)
+{
+ if (handle->trans != NULL) {
+ scf_transaction_reset_all(handle->trans);
+ scf_transaction_destroy_children(handle->trans);
+ scf_transaction_destroy(handle->trans);
+ handle->trans = NULL;
+ }
+}
+
+/*
+ * sa_set_property(handle, prop, value)
+ *
+ * set a property transaction entry into the pending SMF transaction.
+ */
+
+int
+sa_set_property(scfutilhandle_t *handle, char *propname, char *valstr)
+{
+ int ret = SA_OK;
+ scf_value_t *value;
+ scf_transaction_entry_t *entry;
+ /*
+ * properties must be set in transactions and don't take
+ * effect until the transaction has been ended/committed.
+ */
+ value = scf_value_create(handle->handle);
+ entry = scf_entry_create(handle->handle);
+ if (value != NULL && entry != NULL) {
+ if (scf_transaction_property_change(handle->trans, entry,
+ propname,
+ SCF_TYPE_ASTRING) == 0 ||
+ scf_transaction_property_new(handle->trans, entry,
+ propname,
+ SCF_TYPE_ASTRING) == 0) {
+ if (scf_value_set_astring(value, valstr) == 0) {
+ if (scf_entry_add_value(entry, value) != 0) {
+ ret = SA_SYSTEM_ERR;
+ scf_value_destroy(value);
+ }
+ /* the value is in the transaction */
+ value = NULL;
+ } else {
+ /* value couldn't be constructed */
+ ret = SA_SYSTEM_ERR;
+ }
+ /* the entry is in the transaction */
+ entry = NULL;
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ if (ret == SA_SYSTEM_ERR) {
+ switch (scf_error()) {
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = SA_NO_PERMISSION;
+ break;
+ }
+ }
+ /*
+ * cleanup if there were any errors that didn't leave these
+ * values where they would be cleaned up later.
+ */
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (entry != NULL)
+ scf_entry_destroy(entry);
+ return (ret);
+}
+
+/*
+ * sa_commit_share(handle, group, share)
+ *
+ * commit this share to the repository.
+ * properties are added if they exist but can be added later.
+ * Need to add to dfstab and sharetab, if appropriate.
+ */
+int
+sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
+{
+ int ret = SA_OK;
+ char *groupname;
+ char *name;
+ char *resource;
+ char *description;
+ char *sharename;
+ ssize_t proplen;
+ char *propstring;
+
+ /*
+ * don't commit in the zfs group. We do commit legacy
+ * (default) and all other groups/shares. ZFS is handled
+ * through the ZFS configuration rather than SMF.
+ */
+
+ groupname = sa_get_group_attr(group, "name");
+ if (groupname != NULL) {
+ if (strcmp(groupname, "zfs") == 0) {
+ /*
+ * adding to the ZFS group will result in the sharenfs
+ * property being set but we don't want to do anything
+ * SMF related at this point.
+ */
+ sa_free_attr_string(groupname);
+ return (ret);
+ }
+ }
+
+ proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+ propstring = malloc(proplen);
+ if (propstring == NULL)
+ ret = SA_NO_MEMORY;
+
+ if (groupname != NULL && ret == SA_OK) {
+ ret = sa_get_instance(handle, groupname);
+ sa_free_attr_string(groupname);
+ groupname = NULL;
+ sharename = sa_get_share_attr(share, "id");
+ if (sharename == NULL) {
+ /* slipped by */
+ char shname[SA_SHARE_UUID_BUFLEN];
+ generate_unique_sharename(shname);
+ xmlSetProp((xmlNodePtr)share, (xmlChar *)"id",
+ (xmlChar *)shname);
+ sharename = strdup(shname);
+ }
+ if (sharename != NULL) {
+ /*
+ * have a share name allocated so create a pgroup
+ * for it. It may already exist, but that is OK.
+ */
+ ret = sa_create_pgroup(handle, sharename);
+ if (ret == SA_OK) {
+ /*
+ * now start the transaction for the
+ * properties that define this share. They may
+ * exist so attempt to update before create.
+ */
+ ret = sa_start_transaction(handle, sharename);
+ }
+ if (ret == SA_OK) {
+ name = sa_get_share_attr(share, "path");
+ if (name != NULL) {
+ /* there needs to be a path for a share to exist */
+ ret = sa_set_property(handle, "path", name);
+ sa_free_attr_string(name);
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ }
+ if (ret == SA_OK) {
+ resource = sa_get_share_attr(share, "resource");
+ if (resource != NULL) {
+ ret = sa_set_property(handle, "resource", resource);
+ sa_free_attr_string(resource);
+ }
+ }
+ if (ret == SA_OK) {
+ description = sa_get_share_description(share);
+ if (description != NULL) {
+ ret = sa_set_property(handle, "description",
+ description);
+ sa_free_share_description(description);
+ }
+ }
+ /* make sure we cleanup the transaction */
+ if (ret == SA_OK) {
+ ret = sa_end_transaction(handle);
+ } else {
+ sa_abort_transaction(handle);
+ }
+ free(sharename);
+ }
+ }
+ if (ret == SA_SYSTEM_ERR) {
+ int err = scf_error();
+ if (err == SCF_ERROR_PERMISSION_DENIED)
+ ret = SA_NO_PERMISSION;
+ }
+ if (propstring != NULL)
+ free(propstring);
+ if (groupname != NULL)
+ sa_free_attr_string(groupname);
+
+ return (ret);
+}
+
+/*
+ * sa_delete_share(handle, group, share)
+ *
+ * remove the specified share from the group (and service instance).
+ */
+
+int
+sa_delete_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
+{
+ int ret = SA_OK;
+ char *groupname = NULL;
+ char *shareid = NULL;
+ sa_optionset_t opt;
+ sa_security_t sec;
+ ssize_t proplen;
+ char *propstring;
+
+ proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+ propstring = malloc(proplen);
+ if (propstring == NULL)
+ ret = SA_NO_MEMORY;
+
+ if (ret == SA_OK) {
+ groupname = sa_get_group_attr(group, "name");
+ shareid = sa_get_share_attr(share, "id");
+ if (groupname != NULL && shareid != NULL) {
+ ret = sa_get_instance(handle, groupname);
+ if (ret == SA_OK) {
+ /* if a share has properties, remove them */
+ ret = sa_delete_pgroup(handle, shareid);
+ for (opt = sa_get_optionset(share, NULL); opt != NULL;
+ opt = sa_get_next_optionset(opt)) {
+ char *proto;
+ proto = sa_get_optionset_attr(opt, "type");
+ if (proto != NULL) {
+ (void) snprintf(propstring, proplen, "%s_%s",
+ shareid, proto);
+ ret = sa_delete_pgroup(handle, propstring);
+ sa_free_attr_string(proto);
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ }
+ /*
+ * if a share has security/negotiable
+ * properties, remove them.
+ */
+ for (sec = sa_get_security(share, NULL, NULL); sec != NULL;
+ sec = sa_get_next_security(sec)) {
+ char *proto;
+ char *sectype;
+ proto = sa_get_security_attr(sec, "type");
+ sectype = sa_get_security_attr(sec, "sectype");
+ if (proto != NULL && sectype != NULL) {
+ (void) snprintf(propstring, proplen, "%s_%s_%s",
+ shareid,
+ proto, sectype);
+ ret = sa_delete_pgroup(handle, propstring);
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+ if (sectype != NULL)
+ sa_free_attr_string(sectype);
+ }
+ }
+ } else {
+ ret = SA_CONFIG_ERR;
+ }
+ }
+ if (groupname != NULL)
+ sa_free_attr_string(groupname);
+ if (shareid != NULL)
+ sa_free_attr_string(shareid);
+ if (propstring != NULL)
+ free(propstring);
+
+ return (ret);
+}