/*
* 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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* helper functions for using libscf with sharemgr */
#include <libscf.h>
#include "libshare.h"
#include "libshare_impl.h"
#include "scfutil.h"
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <libintl.h>
extern struct sa_proto_plugin *sap_proto_list;
static void set_transaction_tstamp(sa_handle_impl_t);
/*
* 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",
};
/*
* 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
{
int unbind = 0;
unbind = 1;
}
if (unbind)
}
}
}
/*
* sa_scf_init()
*
* Must be called before using any of the SCF functions. Called by
* sa_init() during the API setup.
*/
{
if (scf_max_name_len <= 0)
return (handle);
(void) printf("libshare could not access SMF repository: %s\n",
scf_strerror(scf_error()));
return (handle);
}
goto err;
/* Make sure we have sufficient SMF running */
goto err;
goto err;
goto err;
/* Only NFS enabled for "default" group. */
}
return (handle);
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
{
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
{
int i;
for (i = 0; skip_props[i] != NULL; i++)
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
{
}
/*
* 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
{
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
{
char *name;
char *valuestr;
return (ret);
/*
* Have node to work with so iterate over the properties
* in the pg and create option sub nodes.
*/
/*
* Want to iterate through the properties and add them
* to the base optionset.
*/
ret = SA_NO_MEMORY;
goto out;
}
/* Now iterate the properties in the group */
/* have a property */
scf_max_name_len) > 0) {
/* Some properties are part of the framework */
if (skip_property(name))
continue;
continue;
vallen) < 0)
continue;
/*
* Since in SMF, don't
* recurse. Use xmlAddChild
* directly, instead.
*/
(void) xmlAddChild(node,
(xmlNodePtr) saprop);
}
}
}
}
out:
/* cleanup to avoid memory leaks */
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
{
char *valuestr;
goto out;
}
/*
* Have a property group with desired name so now get
* the known attributes.
*/
/* Found the property so get the value */
vallen) >= 0) {
}
}
}
/* Found the property so get the value */
vallen) > 0) {
}
}
}
out:
}
/*
* List of known share attributes.
*/
static char *share_attr[] = {
"path",
"id",
"drive-letter",
"exclude",
NULL,
};
static int
{
int i;
for (i = 0; share_attr[i] != NULL; i++)
return (1);
return (0);
}
/*
* _sa_make_resource(node, valuestr)
*
* Make a resource node on the share node. The valusestr will either
* be old format (SMF acceptable string) or new format (pretty much an
* arbitrary string with "nnn:" prefixing in order to persist
* mapping). The input valuestr will get modified in place. This is
* only used in SMF repository parsing. A possible third field will be
* a "description" string.
*/
static void
{
char *idx;
char *name;
/* this is old form so give an index of "0" */
idx = "0";
} else {
/* NUL the ':' and move past it */
*name++ = '\0';
/* There could also be a description string */
if (description != NULL)
*description++ = '\0';
}
/* SMF values are always persistent */
(xmlChar *)"persist");
(xmlChar *)description);
}
}
}
/*
* 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
{
char *name;
char *valuestr;
int have_path = 0;
/*
* 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.
*/
SA_SHARE_PG_PREFIXLEN) != 0 ||
return;
} else {
return;
}
/*
* 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.
*/
/*
* Make sure the UUID part of the property group is
* stored in the share "id" property. We use this
* later.
*/
(xmlChar *)"persist");
}
goto out;
/* Iterate over the share pg properties */
goto out;
vallen) >= 0) {
}
}
}
continue;
/*
* Check that we have the "path" property in
* name. The string in name will always be nul
* terminated if scf_property_get_name()
* succeeded.
*/
have_path = 1;
if (is_share_attr(name)) {
/*
* If a share attr, then simple -
* usually path and id name
*/
/*
* Resource names handled differently since
* there can be multiple on each share. The
* "resource" id must be preserved since this
* will be used by some protocols in mapping
* "property spaces" to names and is always
* used to create SMF property groups specific
* to resources. CIFS needs this. The first
* value is present so add and then loop for
* any additional. Since this is new and
* previous values may exist, handle
* conversions.
*/
/* Have a value so process it */
/* have a ustring */
valuestr);
} else if (scf_value_get_astring(value,
/* have an astring */
valuestr);
}
}
}
} else {
/* We have a description node */
}
}
}
out:
/*
* A share without a path is broken so we want to not include
* these. They shouldn't happen but if you kill a sharemgr in
* the process of creating a share, it could happen. They
* should be harmless. It is also possible that another
* sharemgr is running and in the process of creating a share.
*/
}
}
/*
* find_share_by_id(shareid)
*
* Search all shares in all groups until we find the share represented
* by "id".
*/
static sa_share_t
{
int done = 0;
done++;
break;
}
}
}
}
return (share);
}
/*
* find_resource_by_index(share, index)
*
* Search the resource records on the share for the id index.
*/
static sa_resource_t
{
char *id;
/* found it so save in "found" */
}
}
}
return (found);
}
/*
* sa_share_props_from_pgroup(root, handle, pg, id, sahandle)
*
* 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
{
char *proto;
/*
* 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
* versions.
*/
/*
* It is ok to not have what we thought since someone might
* have added a name via SMF.
*/
return (ret);
}
return (ret);
*proto++ = '\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++ = '\0';
if (!valid_protocol(proto))
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.
*/
return (SA_BAD_PATH);
else {
/*
* If sectype[0] is a digit, then it is an index into
* the resource names. We need to find a resource
* record and then get the properties into an
* optionset. The optionset becomes the "node" and the
* rest is hung off of the share.
*/
} else {
/* This shouldn't happen. */
ret = SA_SYSTEM_ERR;
goto out;
}
} else {
/*
* If not a digit, then it is a security type
* (alternate option space). Security types start with
* an alphabetic.
*/
NULL);
}
}
ret = SA_NO_MEMORY;
goto out;
}
/* now find the properties */
goto out;
/* iterate over the share pg properties */
scf_max_name_len) > 0) {
}
}
} else {
ret = SA_SYSTEM_ERR;
}
(xmlNodePtr)prop);
else
ret = SA_NO_MEMORY;
}
}
} else {
ret = SA_SYSTEM_ERR;
}
out:
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
{
char *buff;
char *proto;
char *sectype;
int err;
return (SA_NO_MEMORY);
ret = SA_NO_MEMORY;
goto out;
}
ret = SA_NO_MEMORY;
goto out;
}
is_default = B_TRUE;
/*
* 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().
*/
ret = SA_NO_MEMORY;
goto out;
}
/* Have a pgroup so sort it out */
if (ret <= 0)
continue;
if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
/* Have an optionset */
*proto++ = '\0';
*sectype++ = '\0';
nodetype = "security";
}
/*
* This can only occur if
* someone has made changes
* via an SMF command. Since
* this would be an unknown
* syntax, we just ignore it.
*/
continue;
}
/*
* If the group is not "default" or is
* "default" and is_nfs, then extract the
* pgroup. If it is_default and !is_nfs,
* then we have an error and should remove
* the extraneous protocols. We don't care
* about errors on scf_pg_delete since we
* might not have permission during an
* extract only.
*/
if (!is_default || is_nfs) {
sectype);
} else {
if (err == 0)
"Removed protocol \"%s\" "
"from group \"default\"\n"),
proto);
}
/*
* Have a security (note that
* this should change in the
* future)
*/
*proto++ = '\0';
*sectype++ = '\0';
}
}
/* Ignore everything else */
}
/*
* 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). "default" only has NFS enabled on it.
*/
if (is_default) {
"state");
/* set attribute to enabled */
"state", "enabled");
"nfs");
} else {
}
}
/* Do a second pass if shares were found */
/*
* Have a pgroup so see if it is a
* share optionset
*/
if (err <= 0)
continue;
if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
sahandle);
}
}
}
}
out:
return (ret);
}
/*
* sa_extract_defaults(root, handle, instance)
*
* Local function to find the default properties that live in the
* default instance's "operation" property group.
*/
static void
{
char *valuestr;
goto out;
goto out;
/* Found the property so get the value */
NULL);
(xmlChar *)SA_LEGACY_DFSTAB);
}
}
}
out:
}
/*
* sa_get_config(handle, root, doc, sahandle)
*
* instances. These become group names. Then add the XML structure
* below the groups based on property groups and properties.
*/
int
{
instance)) > 0) {
sizeof (buff)) > 0) {
}
}
}
}
/* Always cleanup these */
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
{
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
{
/* better error returns need to be added based on real error */
if (scf_error() == SCF_ERROR_PERMISSION_DENIED)
else
} else {
/* have the service created, so enable it */
(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
{
int ret;
} else {
/* need better analysis */
}
}
return (ret);
}
/*
* sa_create_pgroup(handle, pgroup)
*
* create a new property group
*/
int
{
int persist = 0;
/*
* 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.
*/
/*
* Special case for a non-persistent property group. This is
* internal use only.
*/
if (*pgroup == '*') {
pgroup++;
}
/*
* If the pgroup exists, we are done. If it doesn't, then we
* need to actually add one to the service instance.
*/
/* Doesn't exist so create one */
switch (scf_error()) {
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
{
/*
* Only delete if it does exist.
*/
/* does exist so delete it */
ret = SA_SYSTEM_ERR;
} else {
ret = SA_SYSTEM_ERR;
}
if (ret == SA_SYSTEM_ERR &&
scf_error() == SCF_ERROR_PERMISSION_DENIED) {
}
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
* 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
{
/*
* Lookup the property group and create it if it doesn't already
* exist.
*/
return (SA_CONFIG_ERR);
ret = SA_SYSTEM_ERR;
}
}
} else {
ret = SA_SYSTEM_ERR;
}
}
}
if (ret == SA_SYSTEM_ERR &&
scf_error() == SCF_ERROR_PERMISSION_DENIED) {
}
return (ret);
}
/*
* sa_end_transaction(scfhandle, sahandle)
*
* Commit the changes that were added to the transaction in the
* handle. Do all necessary cleanup.
*/
int
{
ret = SA_SYSTEM_ERR;
} else {
ret = SA_SYSTEM_ERR;
}
return (ret);
}
/*
* sa_abort_transaction(handle)
*
* Abort the changes that were added to the transaction in the
* handle. Do all necessary cleanup.
*/
void
{
}
}
/*
* set_transaction_tstamp(sahandle)
*
* After a successful transaction commit, update the timestamp of the
* last transaction. This lets us detect changes from other processes.
*/
static void
{
return;
return;
return;
return;
SA_OK) {
/*
* While best if it succeeds, a failure doesn't cause
* problems and we will ignore it anyway.
*/
} else {
}
}
/*
* sa_set_property(handle, prop, value)
*
* Set a property transaction entry into the pending SMF transaction.
*/
int
{
/*
* Properties must be set in transactions and don't take
*/
propname, SCF_TYPE_ASTRING) == 0 ||
propname, SCF_TYPE_ASTRING) == 0) {
ret = SA_SYSTEM_ERR;
}
/* The value is in the transaction */
} else {
/* Value couldn't be constructed */
ret = SA_SYSTEM_ERR;
}
/* The entry is in the transaction */
} else {
ret = SA_SYSTEM_ERR;
}
} else {
ret = SA_SYSTEM_ERR;
}
if (ret == SA_SYSTEM_ERR) {
switch (scf_error()) {
break;
}
}
/*
* Cleanup if there were any errors that didn't leave these
* values where they would be cleaned up later.
*/
return (ret);
}
/*
* check_resource(share)
*
* Check to see if share has any persistent resources. We don't want
* to save if they are all transient.
*/
static int
{
char *type;
}
}
}
return (ret);
}
/*
* sa_set_resource_property(handle, prop, value)
*
* set a property transaction entry into the pending SMF
* transaction. We don't want to include any transient resources
*/
static int
{
char *valstr;
char *idstr;
char *description;
/* don't bother if no persistent resources */
return (ret);
/*
* properties must be set in transactions and don't take
*/
return (SA_SYSTEM_ERR);
"resource", SCF_TYPE_ASTRING) != 0 &&
"resource", SCF_TYPE_ASTRING) != 0) {
return (SA_SYSTEM_ERR);
}
ret = SA_NO_MEMORY;
break;
}
/* Get size of complete string */
if (strsize > 0) {
ret = SA_NO_MEMORY;
goto err;
}
else
ret = SA_SYSTEM_ERR;
break;
}
ret = SA_SYSTEM_ERR;
break;
}
/* the value is in the transaction */
}
err:
}
}
if (description != NULL) {
description = NULL;
}
}
/* the entry is in the transaction */
if (description != NULL)
if (ret == SA_SYSTEM_ERR) {
switch (scf_error()) {
break;
}
}
/*
* cleanup if there were any errors that didn't leave
* these values where they would be cleaned up later.
*/
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
{
char *groupname;
char *name;
char *description;
char *sharename;
char *propstring;
/*
* Don't commit in the zfs group. We do commit legacy
* through the ZFS configuration rather than SMF.
*/
/*
* 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.
*/
return (ret);
}
}
if (propstring == NULL)
ret = SA_NO_MEMORY;
/* slipped by */
}
/*
* Have a share name allocated so create a pgroup for
* it. It may already exist, but that is OK. In order
* to avoid creating a share pgroup that doesn't have
* a path property, block signals around the critical
* region of creating the share pgroup and props.
*/
/*
* Now start the transaction for the
* properties that define this share. They may
* exist so attempt to update before create.
*/
}
/*
* There needs to be a path
* for a share to exist.
*/
name);
} else {
ret = SA_NO_MEMORY;
}
}
/* A drive letter may exist for SMB */
"drive-letter", name);
}
}
/*
* In special cases need to
* exclude proto enable.
*/
"exclude", name);
}
}
/*
* If there are resource names, bundle them up
* and save appropriately.
*/
}
if (description != NULL) {
"description",
}
}
/* Make sure we cleanup the transaction */
sahandle);
else
ret = SA_SYSTEM_ERR;
} else {
}
}
}
if (ret == SA_SYSTEM_ERR) {
if (err == SCF_ERROR_PERMISSION_DENIED)
}
if (propstring != NULL)
return (ret);
}
/*
* remove_resources(handle, share, shareid)
*
* If the share has resources, remove all of them and their
* optionsets.
*/
static int
{
char *proto;
char *id;
char *propstring;
if (propstring == NULL)
return (SA_NO_MEMORY);
continue;
}
}
}
return (ret);
}
/*
* sa_delete_share(handle, group, share)
*
* Remove the specified share from the group (and service instance).
*/
int
{
char *propstring;
if (propstring == NULL)
ret = SA_NO_MEMORY;
ret = SA_CONFIG_ERR;
goto out;
}
/* If a share has resources, remove them */
/* If a share has properties, remove them */
char *proto;
(void) snprintf(propstring,
proto);
} else {
ret = SA_NO_MEMORY;
}
}
/*
* If a share has security/negotiable
* properties, remove them.
*/
char *proto;
char *sectype;
sectype);
} else {
ret = SA_NO_MEMORY;
}
}
}
}
out:
if (propstring != NULL)
return (ret);
}