#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 <ctype.h>
#include <errno.h>
#include <uuid/uuid.h>
#include <sys/param.h>
#include <signal.h>
#include <sys/time.h>
#include <libintl.h>
ssize_t scf_max_name_len;
extern struct sa_proto_plugin *sap_proto_list;
extern sa_handle_impl_t get_handle_for_root(xmlNodePtr);
static void set_transaction_tstamp(sa_handle_impl_t);
static char *skip_props[] = {
"modify_authorization",
"action_authorization",
"value_authorization",
NULL
};
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->instance != NULL)
scf_instance_destroy(handle->instance);
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);
}
}
scfutilhandle_t *
sa_scf_init(sa_handle_impl_t ihandle)
{
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)
return (handle);
ihandle->scfhandle = handle;
handle->scf_state = SCH_STATE_INITIALIZING;
handle->handle = scf_handle_create(SCF_VERSION);
if (handle->handle == NULL) {
free(handle);
handle = NULL;
(void) printf("libshare could not access SMF repository: %s\n",
scf_strerror(scf_error()));
return (handle);
}
if (scf_handle_bind(handle->handle) != 0)
goto err;
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 (handle->scope == NULL || handle->service == NULL ||
handle->pg == NULL || handle->instance == NULL)
goto err;
if (scf_handle_get_scope(handle->handle,
SCF_SCOPE_LOCAL, handle->scope) != 0)
goto err;
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) {
sa_group_t defgrp;
defgrp = sa_create_group((sa_handle_t)ihandle, "default", NULL);
if (defgrp != NULL)
(void) sa_create_optionset(defgrp, "nfs");
}
return (handle);
err:
(void) sa_scf_fini(handle);
if (scf_error() != SCF_ERROR_NOT_FOUND) {
(void) printf("libshare SMF initialization problem: %s\n",
scf_strerror(scf_error()));
}
return (NULL);
}
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);
}
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);
}
static void
generate_unique_sharename(char *sharename)
{
uuid_t uuid;
uuid_generate(uuid);
(void) strcpy(sharename, "S-");
uuid_unparse(uuid, sharename + 2);
}
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);
}
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)
return (ret);
if (proto != NULL)
(void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
if (sectype != NULL)
(void) xmlSetProp(node, (xmlChar *)"sectype",
(xmlChar *)sectype);
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 (iter == NULL || value == NULL || prop == NULL ||
valuestr == NULL || name == NULL) {
ret = SA_NO_MEMORY;
goto out;
}
if (scf_iter_pg_properties(iter, pg) == 0) {
while (scf_iter_next_property(iter, prop) > 0) {
if (scf_property_get_name(prop, name,
scf_max_name_len) > 0) {
sa_property_t saprop;
if (skip_property(name))
continue;
if (scf_property_get_value(prop, value) != 0)
continue;
if (scf_value_get_astring(value, valuestr,
vallen) < 0)
continue;
saprop = sa_create_property(name, valuestr);
if (saprop != NULL) {
(void) xmlAddChild(node,
(xmlNodePtr) saprop);
}
}
}
}
out:
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);
}
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) {
goto out;
}
if (scf_pg_get_property(handle->pg, "state", prop) == 0) {
if (scf_property_get_value(prop, value) == 0) {
if (scf_value_get_astring(value, valuestr,
vallen) >= 0) {
(void) xmlSetProp(root, (xmlChar *)"state",
(xmlChar *)valuestr);
}
}
}
if (scf_pg_get_property(handle->pg, "zfs", prop) == 0) {
if (scf_property_get_value(prop, value) == 0) {
if (scf_value_get_astring(value, valuestr,
vallen) > 0) {
(void) xmlSetProp(root, (xmlChar *)"zfs",
(xmlChar *)valuestr);
}
}
}
out:
if (valuestr != NULL)
free(valuestr);
if (value != NULL)
scf_value_destroy(value);
if (prop != NULL)
scf_property_destroy(prop);
}
static char *share_attr[] = {
"path",
"id",
"drive-letter",
"exclude",
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);
}
static void
_sa_make_resource(xmlNodePtr node, char *valuestr)
{
char *idx;
char *name;
char *description = NULL;
idx = valuestr;
name = strchr(valuestr, ':');
if (name == NULL) {
idx = "0";
name = valuestr;
} else {
*name++ = '\0';
description = strchr(name, ':');
if (description != NULL)
*description++ = '\0';
}
node = xmlNewChild(node, NULL, (xmlChar *)"resource", NULL);
if (node != NULL) {
(void) xmlSetProp(node, (xmlChar *)"name", (xmlChar *)name);
(void) xmlSetProp(node, (xmlChar *)"id", (xmlChar *)idx);
(void) xmlSetProp(node, (xmlChar *)"type",
(xmlChar *)"persist");
if (description != NULL && strlen(description) > 0) {
(void) xmlNewChild(node, NULL, (xmlChar *)"description",
(xmlChar *)description);
}
}
}
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;
int have_path = 0;
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);
node = xmlNewChild(root, NULL, (xmlChar *)"share", NULL);
if (node != NULL) {
(void) xmlSetProp(node, (xmlChar *)"id", (xmlChar *)id);
(void) xmlSetProp(node, (xmlChar *)"type",
(xmlChar *)"persist");
}
if (iter == NULL || value == NULL || prop == NULL || name == NULL)
goto out;
if (scf_iter_pg_properties(iter, pg) != 0)
goto out;
while (scf_iter_next_property(iter, prop) > 0) {
ret = SA_SYSTEM_ERR;
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 if (strcmp(name, "resource") == 0) {
ret = SA_OK;
}
}
if (ret != SA_OK)
continue;
if (strcmp(name, "path") == 0)
have_path = 1;
if (is_share_attr(name)) {
(void) xmlSetProp(node, (xmlChar *)name,
(xmlChar *)valuestr);
} else if (strcmp(name, "resource") == 0) {
scf_iter_t *viter;
viter = scf_iter_create(handle->handle);
if (viter != NULL &&
scf_iter_property_values(viter, prop) == 0) {
while (scf_iter_next_value(viter, value) > 0) {
if (scf_value_get_ustring(value,
valuestr, vallen) >= 0) {
_sa_make_resource(node,
valuestr);
} else if (scf_value_get_astring(value,
valuestr, vallen) >= 0) {
_sa_make_resource(node,
valuestr);
}
}
scf_iter_destroy(viter);
}
} else {
if (strcmp(name, "description") == 0) {
xmlNodePtr desc;
desc = xmlNewChild(node, NULL,
(xmlChar *)"description", NULL);
if (desc != NULL)
(void) xmlNodeSetContent(desc,
(xmlChar *)valuestr);
}
}
}
out:
if (have_path == 0 && node != NULL) {
xmlUnlinkNode(node);
xmlFreeNode(node);
}
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);
}
static sa_share_t
find_share_by_id(sa_handle_t handle, char *shareid)
{
sa_group_t group;
sa_share_t share = NULL;
char *id = NULL;
int done = 0;
for (group = sa_get_group(handle, 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);
}
static sa_resource_t
find_resource_by_index(sa_share_t share, char *index)
{
sa_resource_t resource;
sa_resource_t found = NULL;
char *id;
for (resource = sa_get_share_resource(share, NULL);
resource != NULL && found == NULL;
resource = sa_get_next_resource(resource)) {
id = (char *)xmlGetProp((xmlNodePtr)resource, (xmlChar *)"id");
if (id != NULL) {
if (strcmp(id, index) == 0) {
found = resource;
}
sa_free_attr_string(id);
}
}
return (found);
}
static int
sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
scf_propertygroup_t *pg, char *id, sa_handle_t sahandle)
{
xmlNodePtr node;
char *name = NULL;
scf_iter_t *iter = NULL;
scf_property_t *prop = NULL;
scf_value_t *value = NULL;
ssize_t vallen;
char *valuestr = NULL;
int ret = SA_OK;
char *sectype = NULL;
char *proto = NULL;
sa_share_t share;
uuid_t uuid;
vallen = strlen(id);
if (*id != SA_SHARE_PG_PREFIX[0] || vallen <= SA_SHARE_PG_LEN) {
return (ret);
}
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);
if (*proto == '\0') {
return (ret);
}
sectype = strchr(proto, '_');
if (sectype != NULL)
*sectype++ = '\0';
if (!valid_protocol(proto))
return (ret);
}
share = find_share_by_id(sahandle, id);
if (share == NULL)
return (SA_BAD_PATH);
root = (xmlNodePtr)share;
vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
if (sectype == NULL)
node = xmlNewChild(root, NULL, (xmlChar *)"optionset", NULL);
else {
if (isdigit((int)*sectype)) {
sa_resource_t resource;
resource = find_resource_by_index(share, sectype);
if (resource != NULL) {
node = xmlNewChild(resource, NULL,
(xmlChar *)"optionset", NULL);
} else {
ret = SA_SYSTEM_ERR;
goto out;
}
} else {
node = xmlNewChild(root, NULL, (xmlChar *)"security",
NULL);
if (node != NULL)
(void) xmlSetProp(node, (xmlChar *)"sectype",
(xmlChar *)sectype);
}
}
if (node == NULL) {
ret = SA_NO_MEMORY;
goto out;
}
(void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
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 (iter == NULL || value == NULL || prop == NULL || name == NULL)
goto out;
if (scf_iter_pg_properties(iter, pg) == 0) {
while (scf_iter_next_property(iter, prop) > 0) {
ret = SA_SYSTEM_ERR;
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;
}
out:
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);
}
static int
sa_extract_group(xmlNodePtr root, scfutilhandle_t *handle,
scf_instance_t *instance, sa_handle_t sahandle)
{
char *buff;
xmlNodePtr node;
scf_iter_t *iter;
char *proto;
char *sectype;
boolean_t have_shares = B_FALSE;
boolean_t is_default = B_FALSE;
boolean_t is_nfs = B_FALSE;
int ret = SA_OK;
int err;
buff = malloc(scf_max_name_len);
if (buff == NULL)
return (SA_NO_MEMORY);
iter = scf_iter_create(handle->handle);
if (iter == NULL) {
ret = SA_NO_MEMORY;
goto out;
}
if (scf_instance_get_name(instance, buff, scf_max_name_len) > 0) {
node = xmlNewChild(root, NULL, (xmlChar *)"group", NULL);
if (node == NULL) {
ret = SA_NO_MEMORY;
goto out;
}
(void) xmlSetProp(node, (xmlChar *)"name", (xmlChar *)buff);
if (strcmp(buff, "default") == 0)
is_default = B_TRUE;
sa_extract_attrs(node, handle, instance);
if (scf_iter_instance_pgs(iter, instance) != 0) {
ret = SA_NO_MEMORY;
goto out;
}
while (scf_iter_next_pg(iter, handle->pg) > 0) {
ret = scf_pg_get_name(handle->pg, buff,
scf_max_name_len);
if (ret <= 0)
continue;
is_nfs = B_FALSE;
if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
sa_share_from_pgroup(node, handle,
handle->pg, buff);
have_shares = B_TRUE;
} else if (strncmp(buff, "optionset", 9) == 0) {
char *nodetype = "optionset";
sectype = NULL;
proto = strchr(buff, '_');
if (proto != NULL) {
*proto++ = '\0';
sectype = strchr(proto, '_');
if (sectype != NULL) {
*sectype++ = '\0';
nodetype = "security";
}
is_nfs = strcmp(proto, "nfs") == 0;
} else if (strlen(buff) > 9) {
continue;
}
if (!is_default || is_nfs) {
ret = sa_extract_pgroup(node, handle,
handle->pg, nodetype, proto,
sectype);
} else {
err = scf_pg_delete(handle->pg);
if (err == 0)
(void) fprintf(stderr,
dgettext(TEXT_DOMAIN,
"Removed protocol \"%s\" "
"from group \"default\"\n"),
proto);
}
} else if (strncmp(buff, "security", 8) == 0) {
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);
}
}
if (is_default) {
char *state = sa_get_group_attr((sa_group_t)node,
"state");
if (state == NULL) {
(void) sa_set_group_attr((sa_group_t)node,
"state", "enabled");
(void) sa_create_optionset((sa_group_t)node,
"nfs");
} else {
sa_free_attr_string(state);
}
}
if (have_shares && scf_iter_instance_pgs(iter, instance) == 0) {
while (scf_iter_next_pg(iter, handle->pg) > 0) {
err = scf_pg_get_name(handle->pg, buff,
scf_max_name_len);
if (err <= 0)
continue;
if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
ret = sa_share_props_from_pgroup(node,
handle, handle->pg, buff,
sahandle);
}
}
}
}
out:
if (iter != NULL)
scf_iter_destroy(iter);
if (buff != NULL)
free(buff);
return (ret);
}
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 == 0 ||
scf_instance_get_pg(instance, "operation", handle->pg) != 0)
goto out;
if (scf_pg_get_property(handle->pg, "legacy-timestamp", prop) != 0)
goto out;
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) {
(void) xmlSetProp(node, (xmlChar *)"timestamp",
(xmlChar *)valuestr);
(void) xmlSetProp(node, (xmlChar *)"path",
(xmlChar *)SA_LEGACY_DFSTAB);
}
}
}
out:
if (valuestr != NULL)
free(valuestr);
if (value != NULL)
scf_value_destroy(value);
if (prop != NULL)
scf_property_destroy(prop);
}
int
sa_get_config(scfutilhandle_t *handle, xmlNodePtr root, sa_handle_t sahandle)
{
int ret = SA_OK;
scf_instance_t *instance;
scf_iter_t *iter;
char buff[BUFSIZ * 2];
instance = scf_instance_create(handle->handle);
iter = scf_iter_create(handle->handle);
if (instance != NULL && iter != NULL) {
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, sahandle);
}
}
}
}
if (instance != NULL)
scf_instance_destroy(instance);
if (iter != NULL)
scf_iter_destroy(iter);
return (ret);
}
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);
}
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) {
if (scf_error() == SCF_ERROR_PERMISSION_DENIED)
ret = SA_NO_PERMISSION;
else
ret = SA_DUPLICATE_NAME;
} else {
(void) snprintf(instance, sizeof (instance), "%s:%s",
SA_SVC_FMRI_BASE, instname);
(void) smf_enable_instance(instance, 0);
}
return (ret);
}
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)
ret = SA_NO_PERMISSION;
}
}
return (ret);
}
int
sa_create_pgroup(scfutilhandle_t *handle, char *pgroup)
{
int ret = SA_OK;
int persist = 0;
if (handle->pg == NULL)
handle->pg = scf_pg_create(handle->handle);
if (*pgroup == '*') {
persist = SCF_PG_FLAG_NONPERSISTENT;
pgroup++;
}
if (scf_instance_get_pg(handle->instance,
pgroup, handle->pg) != 0) {
if (scf_instance_add_pg(handle->instance, pgroup,
SCF_GROUP_APPLICATION, persist, handle->pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_PERMISSION_DENIED:
ret = SA_NO_PERMISSION;
break;
default:
ret = SA_SYSTEM_ERR;
break;
}
}
}
return (ret);
}
int
sa_delete_pgroup(scfutilhandle_t *handle, char *pgroup)
{
int ret = SA_OK;
if (scf_instance_get_pg(handle->instance, pgroup, handle->pg) == 0) {
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);
}
int
sa_start_transaction(scfutilhandle_t *handle, char *propgroup)
{
int ret = SA_OK;
if (handle == NULL)
return (SA_CONFIG_ERR);
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);
}
int
sa_end_transaction(scfutilhandle_t *handle, sa_handle_impl_t sahandle)
{
int ret = SA_OK;
if (handle == NULL || handle->trans == NULL || sahandle == 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);
if (ret == SA_OK)
set_transaction_tstamp(sahandle);
handle->trans = NULL;
}
return (ret);
}
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;
}
}
static void
set_transaction_tstamp(sa_handle_impl_t sahandle)
{
char tstring[32];
struct timeval tv;
scfutilhandle_t *scfhandle;
if (sahandle == NULL || sahandle->scfhandle == NULL)
return;
scfhandle = sahandle->scfhandle;
if (sa_get_instance(scfhandle, "default") != SA_OK)
return;
if (gettimeofday(&tv, NULL) != 0)
return;
if (sa_start_transaction(scfhandle, "*state") != SA_OK)
return;
sahandle->tstrans = TSTAMP((*(timestruc_t *)&tv));
(void) snprintf(tstring, sizeof (tstring), "%lld", sahandle->tstrans);
if (sa_set_property(sahandle->scfhandle, "lastupdate", tstring) ==
SA_OK) {
(void) scf_transaction_commit(scfhandle->trans);
scf_transaction_destroy_children(scfhandle->trans);
scf_transaction_destroy(scfhandle->trans);
} else {
sa_abort_transaction(scfhandle);
}
}
int
sa_set_property(scfutilhandle_t *handle, char *propname, char *valstr)
{
int ret = SA_OK;
scf_value_t *value;
scf_transaction_entry_t *entry;
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);
}
value = NULL;
} else {
ret = SA_SYSTEM_ERR;
}
entry = NULL;
} else {
ret = SA_SYSTEM_ERR;
}
} else {
ret = SA_SYSTEM_ERR;
}
if (ret == SA_SYSTEM_ERR) {
if (scf_error() == SCF_ERROR_PERMISSION_DENIED) {
ret = SA_NO_PERMISSION;
}
}
if (value != NULL)
scf_value_destroy(value);
if (entry != NULL)
scf_entry_destroy(entry);
return (ret);
}
static int
check_resource(sa_share_t share)
{
sa_resource_t resource;
int ret = B_FALSE;
for (resource = sa_get_share_resource(share, NULL);
resource != NULL && ret == B_FALSE;
resource = sa_get_next_resource(resource)) {
char *type;
type = sa_get_resource_attr(resource, "type");
if (type != NULL) {
if (strcmp(type, "transient") != 0) {
ret = B_TRUE;
}
sa_free_attr_string(type);
}
}
return (ret);
}
static int
sa_set_resource_property(scfutilhandle_t *handle, sa_share_t share)
{
int ret = SA_OK;
scf_value_t *value;
scf_transaction_entry_t *entry;
sa_resource_t resource;
char *valstr = NULL;
char *idstr = NULL;
char *description = NULL;
char *propstr = NULL;
size_t strsize;
if (check_resource(share) == B_FALSE)
return (ret);
entry = scf_entry_create(handle->handle);
if (entry == NULL)
return (SA_SYSTEM_ERR);
if (scf_transaction_property_change(handle->trans, entry,
"resource", SCF_TYPE_ASTRING) != 0 &&
scf_transaction_property_new(handle->trans, entry,
"resource", SCF_TYPE_ASTRING) != 0) {
scf_entry_destroy(entry);
return (SA_SYSTEM_ERR);
}
for (resource = sa_get_share_resource(share, NULL);
resource != NULL;
resource = sa_get_next_resource(resource)) {
value = scf_value_create(handle->handle);
if (value == NULL) {
ret = SA_NO_MEMORY;
break;
}
valstr = sa_get_resource_attr(resource, "name");
idstr = sa_get_resource_attr(resource, "id");
description = sa_get_resource_description(resource);
strsize = (valstr != NULL) ? strlen(valstr) : 0;
strsize += (idstr != NULL) ? strlen(idstr) : 0;
strsize += (description != NULL) ? strlen(description) : 0;
if (strsize > 0) {
strsize += 3;
propstr = (char *)malloc(strsize);
if (propstr == NULL) {
scf_value_destroy(value);
ret = SA_NO_MEMORY;
goto err;
}
if (idstr == NULL)
(void) snprintf(propstr, strsize, "%s",
valstr ? valstr : "");
else
(void) snprintf(propstr, strsize, "%s:%s:%s",
idstr, valstr ? valstr : "",
description ? description : "");
if (scf_value_set_astring(value, propstr) != 0) {
ret = SA_SYSTEM_ERR;
free(propstr);
scf_value_destroy(value);
break;
}
if (scf_entry_add_value(entry, value) != 0) {
ret = SA_SYSTEM_ERR;
free(propstr);
scf_value_destroy(value);
break;
}
value = NULL;
free(propstr);
}
err:
if (valstr != NULL) {
sa_free_attr_string(valstr);
valstr = NULL;
}
if (idstr != NULL) {
sa_free_attr_string(idstr);
idstr = NULL;
}
if (description != NULL) {
sa_free_share_description(description);
description = NULL;
}
}
entry = NULL;
if (valstr != NULL)
sa_free_attr_string(valstr);
if (idstr != NULL)
sa_free_attr_string(idstr);
if (description != NULL)
sa_free_share_description(description);
if (ret == SA_SYSTEM_ERR) {
if (scf_error() == SCF_ERROR_PERMISSION_DENIED) {
ret = SA_NO_PERMISSION;
}
}
if (entry != NULL)
scf_entry_destroy(entry);
return (ret);
}
int
sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
{
int ret = SA_OK;
char *groupname;
char *name;
char *description;
char *sharename;
ssize_t proplen;
char *propstring;
groupname = sa_get_group_attr(group, "name");
if (groupname != NULL) {
if (strcmp(groupname, "zfs") == 0) {
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) {
char shname[SA_SHARE_UUID_BUFLEN];
generate_unique_sharename(shname);
(void) xmlSetProp((xmlNodePtr)share, (xmlChar *)"id",
(xmlChar *)shname);
sharename = strdup(shname);
}
if (sharename != NULL) {
sigset_t old, new;
(void) sigprocmask(SIG_BLOCK, NULL, &new);
(void) sigaddset(&new, SIGHUP);
(void) sigaddset(&new, SIGINT);
(void) sigaddset(&new, SIGQUIT);
(void) sigaddset(&new, SIGTSTP);
(void) sigprocmask(SIG_SETMASK, &new, &old);
ret = sa_create_pgroup(handle, sharename);
if (ret == SA_OK) {
ret = sa_start_transaction(handle, sharename);
}
if (ret == SA_OK) {
name = sa_get_share_attr(share, "path");
if (name != NULL) {
ret = sa_set_property(handle, "path",
name);
sa_free_attr_string(name);
} else {
ret = SA_NO_MEMORY;
}
}
if (ret == SA_OK) {
name = sa_get_share_attr(share, "drive-letter");
if (name != NULL) {
ret = sa_set_property(handle,
"drive-letter", name);
sa_free_attr_string(name);
}
}
if (ret == SA_OK) {
name = sa_get_share_attr(share, "exclude");
if (name != NULL) {
ret = sa_set_property(handle,
"exclude", name);
sa_free_attr_string(name);
}
}
if (ret == SA_OK) {
ret = sa_set_resource_property(handle, share);
}
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);
}
}
if (ret == SA_OK) {
sa_handle_impl_t sahandle;
sahandle = (sa_handle_impl_t)
sa_find_group_handle(group);
if (sahandle != NULL)
ret = sa_end_transaction(handle,
sahandle);
else
ret = SA_SYSTEM_ERR;
} else {
sa_abort_transaction(handle);
}
(void) sigprocmask(SIG_SETMASK, &old, NULL);
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);
}
static int
remove_resources(scfutilhandle_t *handle, sa_share_t share, char *shareid)
{
sa_resource_t resource;
sa_optionset_t opt;
char *proto;
char *id;
ssize_t proplen;
char *propstring;
int ret = SA_OK;
proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
propstring = malloc(proplen);
if (propstring == NULL)
return (SA_NO_MEMORY);
for (resource = sa_get_share_resource(share, NULL);
resource != NULL; resource = sa_get_next_resource(resource)) {
id = sa_get_resource_attr(resource, "id");
if (id == NULL)
continue;
for (opt = sa_get_optionset(resource, NULL);
opt != NULL; opt = sa_get_next_optionset(resource)) {
proto = sa_get_optionset_attr(opt, "type");
if (proto != NULL) {
(void) snprintf(propstring, proplen,
"%s_%s_%s", shareid, proto, id);
ret = sa_delete_pgroup(handle, propstring);
sa_free_attr_string(proto);
}
}
sa_free_attr_string(id);
}
free(propstring);
return (ret);
}
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_CONFIG_ERR;
goto out;
}
ret = sa_get_instance(handle, groupname);
if (ret == SA_OK) {
ret = remove_resources(handle, share, shareid);
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;
}
}
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);
}
}
}
out:
if (groupname != NULL)
sa_free_attr_string(groupname);
if (shareid != NULL)
sa_free_attr_string(shareid);
if (propstring != NULL)
free(propstring);
return (ret);
}