/*
* 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 (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
*/
/*
* This module contains the private layer API.
*/
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/param.h>
#include <libelf.h>
#include <gelf.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <dlfcn.h>
#include <glob.h>
#include <fcntl.h>
#include <libinetutil.h>
#include <dhcp_svc_public.h>
#include <dhcp_svc_private.h>
/*
* Threading notes for private layer consumers:
*
* The handles returned from open_dd() may be shared across multiple
* threads with no adverse side effects. However, it's up to that consumer
* to ensure that all threads have finished using an instance before
* closing the instance or removing the container it's referencing.
* Phrased differently:
*
* * Consumers must ensure all threads sharing a handle are
* finished before calling close_dd().
*
* * Consumers must ensure all threads referencing a container are
* closed before calling remove_dd().
*/
static boolean_t validate_dd_entry(dsvc_handle_t, const void *, boolean_t);
static int synch_init(dsvc_handle_t, const char *, uint_t);
static void synch_fini(dsvc_handle_t);
/*
* Order here should match the function array in <dhcp_svc_private.h>
*/
static char *funcnames[] = {
"status", "version", "mklocation",
"list_dt", "open_dt", "close_dt", "remove_dt",
"lookup_dt", "add_dt", "modify_dt", "delete_dt",
"list_dn", "open_dn", "close_dn", "remove_dn",
"lookup_dn", "add_dn", "modify_dn", "delete_dn"
};
extern dsvc_synch_ops_t dsvcd_synch_ops;
/*
* Retrieve the current version associated with the datastore named by
* `resource' and store in `converp'. One might think we could do this via
* a simple readlink(2), but on internal release builds $(ROOTLINKS)
* installs using hardlinks, not symlinks. For this reason and to make it
* harder for us to be fooled, we'll dredge up the actual soname through
* ELF. Close your eyes, it's gonna get ugly.
*/
static int
get_conver(const char *resource, int *converp)
{
int elf_fd;
int i;
GElf_Shdr gelf_shdr;
GElf_Dyn gelf_dyn;
Elf_Scn *elf_scn = NULL;
Elf *elf_file;
Elf_Data *elf_data;
char *soname = NULL;
char path[MAXPATHLEN];
(void) snprintf(path, sizeof (path), "%s%s/%s_%s.so", DHCP_CONFOPT_ROOT,
DSVC_MODULE_DIR, DSVC_PUBLIC_PREFIX, resource);
elf_fd = open(path, O_RDONLY);
if (elf_fd == -1)
return (DSVC_MODULE_ERR);
if (elf_version(EV_CURRENT) == EV_NONE) {
(void) close(elf_fd);
return (DSVC_INTERNAL);
}
elf_file = elf_begin(elf_fd, ELF_C_READ, NULL);
if (elf_file == NULL || elf_kind(elf_file) != ELF_K_ELF) {
(void) close(elf_fd);
return (DSVC_INTERNAL);
}
while ((elf_scn = elf_nextscn(elf_file, elf_scn)) != NULL) {
if (gelf_getshdr(elf_scn, &gelf_shdr) == 0)
continue;
if ((gelf_shdr.sh_type != SHT_DYNAMIC) ||
(gelf_shdr.sh_flags & SHF_SUNW_ABSENT))
continue;
elf_data = elf_getdata(elf_scn, NULL);
if (elf_data == NULL)
continue;
i = 0;
do {
(void) gelf_getdyn(elf_data, i++, &gelf_dyn);
if (gelf_dyn.d_tag == DT_SONAME)
soname = elf_strptr(elf_file, gelf_shdr.sh_link,
gelf_dyn.d_un.d_ptr);
} while (gelf_dyn.d_tag != DT_NULL && soname == NULL);
}
if (soname == NULL || sscanf(soname, "%*[^.].so.%d", converp) != 1) {
(void) elf_end(elf_file);
(void) close(elf_fd);
return (DSVC_MODULE_ERR);
}
(void) elf_end(elf_file);
(void) close(elf_fd);
return (DSVC_SUCCESS);
}
/*
* Unload a public datastore module.
*/
static int
unload_public_module(void **instance, dsvc_splapi_t *api)
{
static dsvc_splapi_t null_api;
if (dlclose(*instance) != 0)
return (DSVC_MODULE_UNLOAD_ERR);
*instance = NULL;
*api = null_api;
return (DSVC_SUCCESS);
}
/*
* Load public datastore module. Validates version of module. Returns
* instance of opened module, and populates the api argument with the
* function addresses exporting the API.
*/
static int
load_public_module(dsvc_datastore_t *ddp, void **instance, dsvc_splapi_t *api)
{
int i, v;
dsvc_splfuncp_t configure;
char path[MAXPATHLEN];
(void) snprintf(path, sizeof (path), "%s%s/%s_%s.so", DHCP_CONFOPT_ROOT,
DSVC_MODULE_DIR, DSVC_PUBLIC_PREFIX, ddp->d_resource);
if (ddp->d_conver != DSVC_CUR_CONVER)
(void) snprintf(path, sizeof (path), "%s.%d", path,
ddp->d_conver);
*instance = dlopen(path, RTLD_LAZY|RTLD_GROUP|RTLD_WORLD);
if (*instance == NULL)
return (DSVC_MODULE_LOAD_ERR);
/*
* No requirement to duplicate the names - we can always reference
* the same set.
*/
api->version = (dsvc_splfuncp_t)dlsym(*instance, "version");
if (api->version == NULL || api->version(&v) != DSVC_SUCCESS ||
v != DSVC_PUBLIC_VERSION) {
(void) unload_public_module(instance, api);
return (DSVC_MODULE_VERSION);
}
configure = (dsvc_splfuncp_t)dlsym(*instance, "configure");
if (configure != NULL) {
if (configure(ddp->d_config) != DSVC_SUCCESS) {
(void) unload_public_module(instance, api);
return (DSVC_MODULE_CFG_ERR);
}
}
for (i = 0; i < DSVC_NSPLFUNCS; i++) {
if ((((dsvc_splfuncp_t *)api)[i] =
(dsvc_splfuncp_t)dlsym(*instance, funcnames[i])) == NULL) {
(void) unload_public_module(instance, api);
return (DSVC_MODULE_ERR);
}
}
/*
* Caller requested the current version; fill in what that current
* version is.
*/
if (ddp->d_conver == DSVC_CUR_CONVER) {
int error;
error = get_conver(ddp->d_resource, &ddp->d_conver);
if (error != DSVC_SUCCESS) {
(void) unload_public_module(instance, api);
return (error);
}
}
return (DSVC_SUCCESS);
}
/*
* Return a dynamically-allocated null-terminated list of the available
* modules stored in the module directory. A count of the available
* modules is stored in the num argument. Caller is responsible for
* freeing the list.
*/
int
enumerate_dd(char ***modules, int *nump)
{
int i, retval;
char *ptr;
glob_t globbuf;
char globpat[MAXPATHLEN];
if (modules == NULL || nump == NULL)
return (DSVC_INVAL);
(void) snprintf(globpat, sizeof (globpat), "%s%s/%s_*\\.so",
DHCP_CONFOPT_ROOT, DSVC_MODULE_DIR, DSVC_PUBLIC_PREFIX);
retval = glob(globpat, GLOB_NOSORT, NULL, &globbuf);
if (retval != 0) {
globfree(&globbuf);
switch (retval) {
case GLOB_NOMATCH:
*nump = 0;
*modules = NULL;
return (DSVC_SUCCESS);
case GLOB_NOSPACE:
return (DSVC_NO_MEMORY);
default:
return (DSVC_INTERNAL);
}
}
*modules = calloc(globbuf.gl_pathc, sizeof (char **));
if (*modules == NULL) {
globfree(&globbuf);
return (DSVC_NO_MEMORY);
}
for (i = 0; i < globbuf.gl_pathc; i++) {
ptr = strrchr(globbuf.gl_pathv[i], '/');
if (ptr == NULL)
ptr = globbuf.gl_pathv[i];
else
ptr++;
(*modules)[i] = malloc(strlen(ptr) + 1);
if ((*modules)[i] == NULL) {
while (i--)
free((*modules)[i]);
free(modules);
globfree(&globbuf);
return (DSVC_NO_MEMORY);
}
(void) sscanf(ptr, "%*[^_]_%[^.]", (*modules)[i]);
}
globfree(&globbuf);
*nump = i;
return (DSVC_SUCCESS);
}
/*
* Check the status of the underlying service supporting the data store.
* Caller is responsible for freeing any dynamically allocated arguments.
*/
int
status_dd(dsvc_datastore_t *ddp)
{
void *instance;
dsvc_splapi_t api;
int error;
error = load_public_module(ddp, &instance, &api);
if (error != DSVC_SUCCESS)
return (error);
error = api.status(ddp->d_location);
(void) unload_public_module(&instance, &api);
return (error);
}
/*
* Create within the data store the "location" where containers will be
* stored.
*/
int
mklocation_dd(dsvc_datastore_t *ddp)
{
void *instance;
dsvc_splapi_t api;
int error;
error = load_public_module(ddp, &instance, &api);
if (error != DSVC_SUCCESS)
return (error);
error = api.mklocation(ddp->d_location);
(void) unload_public_module(&instance, &api);
return (error);
}
/*
* Return a list of the current container objects of type 'type' located at
* 'location' in listppp. Return the number of list elements in 'count'.
*/
int
list_dd(dsvc_datastore_t *ddp, dsvc_contype_t type, char ***listppp,
uint_t *count)
{
void *instance;
dsvc_splapi_t api;
int error;
error = load_public_module(ddp, &instance, &api);
if (error != DSVC_SUCCESS)
return (error);
if (type == DSVC_DHCPTAB)
error = api.list_dt(ddp->d_location, listppp, count);
else
error = api.list_dn(ddp->d_location, listppp, count);
(void) unload_public_module(&instance, &api);
return (error);
}
/*
* Creates or opens the DHCP container of type called name within the
* specific datastore referenced by ddp, and returns a handle to this
* container in the handp argument. New containers are created with
* the identity of the caller. Caller is responsible for freeing any
* dynamically allocated arguments. The returned handle instance must
* be released by calling close_dd().
*/
int
open_dd(dsvc_handle_t *handp, dsvc_datastore_t *ddp, dsvc_contype_t type,
const char *name, uint_t flags)
{
int error;
dsvc_handle_t hp;
*handp = NULL;
if (type == DSVC_DHCPNETWORK && name == NULL)
return (DSVC_INVAL);
if (flags & DSVC_CREATE && (flags & DSVC_WRITE) == 0)
return (DSVC_INVAL);
if ((hp = calloc(1, sizeof (struct dsvc_handle))) == NULL)
return (DSVC_NO_MEMORY);
if (type == DSVC_DHCPNETWORK) {
hp->d_conid.c_net.s_addr = ntohl(inet_addr(name));
if (hp->d_conid.c_net.s_addr == INADDR_BROADCAST) {
free(hp);
return (DSVC_INVAL);
}
get_netmask4(&hp->d_conid.c_net, &hp->d_conid.c_mask);
}
error = load_public_module(ddp, &hp->d_instance, &hp->d_api);
if (error != DSVC_SUCCESS) {
free(hp);
return (error);
}
hp->d_type = type;
hp->d_desc.d_conver = ddp->d_conver;
hp->d_desc.d_resource = strdup(ddp->d_resource);
hp->d_desc.d_location = strdup(ddp->d_location);
if (hp->d_desc.d_resource == NULL || hp->d_desc.d_location == NULL) {
error = DSVC_NO_MEMORY;
goto error;
}
/*
* Initialize the synchronization strategy (may not be any).
*/
error = synch_init(hp, name, flags);
if (error != DSVC_SUCCESS)
goto error;
if (type == DSVC_DHCPTAB)
error = hp->d_api.open_dt(&hp->d_hand, ddp->d_location, flags);
else
error = hp->d_api.open_dn(&hp->d_hand, ddp->d_location, flags,
&hp->d_conid.c_net, &hp->d_conid.c_mask);
if (error != DSVC_SUCCESS) {
if (hp->d_synch != NULL)
synch_fini(hp);
goto error;
}
*handp = hp;
return (DSVC_SUCCESS);
error:
(void) unload_public_module(&hp->d_instance, &hp->d_api);
free(hp->d_desc.d_resource);
free(hp->d_desc.d_location);
free(hp);
return (error);
}
/*
* Remove DHCP container called name of type within the specific datastore
* referenced by ddp. Caller is responsible for freeing any dynamically
* allocated arguments.
*/
int
remove_dd(dsvc_datastore_t *ddp, dsvc_contype_t type, const char *name)
{
void *instance;
int error;
dsvc_splapi_t api;
struct in_addr ip, mask;
if (type != DSVC_DHCPTAB) {
if ((ip.s_addr = inet_addr(name)) == INADDR_BROADCAST)
return (DSVC_INVAL);
ip.s_addr = ntohl(ip.s_addr);
get_netmask4(&ip, &mask);
}
error = load_public_module(ddp, &instance, &api);
if (error != DSVC_SUCCESS)
return (error);
/* remove the DHCP container */
if (type == DSVC_DHCPTAB)
error = api.remove_dt(ddp->d_location);
else
error = api.remove_dn(ddp->d_location, &ip, &mask);
(void) unload_public_module(&instance, &api);
return (error);
}
/*
* Delete the handle instance referenced by hand. Frees hand if the close
* operation was successful. NOTE: Caller is responsible for synchronizing
* multiple threads such that close_dd() is called when all consuming
* threads have exited.
*/
int
close_dd(dsvc_handle_t *handp)
{
int error;
if (handp == NULL || DSVC_HANDLE_INVAL(*handp))
return (DSVC_INVAL);
if ((*handp)->d_type == DSVC_DHCPTAB)
error = (*handp)->d_api.close_dt(&((*handp)->d_hand));
else
error = (*handp)->d_api.close_dn(&((*handp)->d_hand));
if (error == DSVC_SUCCESS) {
error = unload_public_module(&(*handp)->d_instance,
&(*handp)->d_api);
if ((*handp)->d_synch != NULL)
synch_fini(*handp);
free((*handp)->d_desc.d_resource);
free((*handp)->d_desc.d_location);
free(*handp);
*handp = NULL;
}
return (error);
}
/*
* Searches hand container for records that match the query described by
* the combination of query and targetp. If the partial field is true, then
* lookup operations that have located some records but are unable to
* complete entirely are allowed. The query argument consists of 2 fields,
* each 16 bits long. The lower 16 bits selects which fields in the targetp
* record are to be considered in the query. The upper 16 bits identifies
* whether a particular field value must match (bit set) or not match (bit
* clear). Unused bits in both 16 bit fields must be 0. The count argument
* specifies the maximum number of matching records to return. A count
* value of -1 requests that all matching records be returned. recordsp is
* set to point to the resulting list of records; if recordsp is NULL then
* no records are actually returned. Note that these records are
* dynamically allocated, thus the caller is responsible for freeing them.
* The number of records found is returned in nrecordsp; a value of 0 means
* that no records matched the query.
*/
int
lookup_dd(dsvc_handle_t hand, boolean_t partial, uint_t query,
int count, const void *targetp, void **recordsp, uint_t *nrecordsp)
{
uint_t mask = 0;
int error;
void *unlock_cookie;
int (*lookup)();
if (targetp == NULL || nrecordsp == NULL || DSVC_HANDLE_INVAL(hand))
return (DSVC_INVAL);
if (hand->d_type == DSVC_DHCPTAB) {
mask = (uint_t)~DT_QALL;
lookup = hand->d_api.lookup_dt;
} else {
mask = (uint_t)~DN_QALL;
lookup = hand->d_api.lookup_dn;
}
/* validate query */
if (((query & 0xffff) & mask) || ((query >> 16) & mask))
return (DSVC_INVAL);
/*
* XXX: need to validate the `targetp' -- what a mess cuz only the
* fields lit in `query' need to be valid.
*/
if (hand->d_synch != NULL) {
error = DSVC_SYNCH_RDLOCK(hand->d_synch, &unlock_cookie);
if (error != DSVC_SUCCESS)
return (error);
}
error = lookup(hand->d_hand, partial, query, count, targetp, recordsp,
nrecordsp);
if (hand->d_synch != NULL)
(void) DSVC_SYNCH_UNLOCK(hand->d_synch, unlock_cookie);
return (error);
}
/*
* Frees the record pointed to by entryp.
*/
void
free_dd(dsvc_handle_t hand, void *entryp)
{
if (DSVC_HANDLE_INVAL(hand) || entryp == NULL)
return;
if (hand->d_type == DSVC_DHCPTAB)
free_dtrec((dt_rec_t *)entryp);
else
free_dnrec((dn_rec_t *)entryp);
}
/*
* Frees the list of records pointed to by listp.
*/
void
free_dd_list(dsvc_handle_t hand, void *listp)
{
if (DSVC_HANDLE_INVAL(hand) || listp == NULL)
return;
if (hand->d_type == DSVC_DHCPTAB)
free_dtrec_list((dt_rec_list_t *)listp);
else
free_dnrec_list((dn_rec_list_t *)listp);
}
/*
* Add the record newp to the DHCP container hand. newp's update signature
* will be updated by the public layer module doing the update. Caller is
* responsible for freeing newp if it was dynamically allocated.
*/
int
add_dd_entry(dsvc_handle_t hand, void *newp)
{
int error;
void *unlock_cookie;
if (DSVC_HANDLE_INVAL(hand))
return (DSVC_INVAL);
if (!validate_dd_entry(hand, newp, B_FALSE))
return (DSVC_INVAL);
if (hand->d_synch != NULL) {
error = DSVC_SYNCH_WRLOCK(hand->d_synch, &unlock_cookie);
if (error != DSVC_SUCCESS)
return (error);
}
if (hand->d_type == DSVC_DHCPTAB)
error = hand->d_api.add_dt(hand->d_hand, newp);
else
error = hand->d_api.add_dn(hand->d_hand, newp);
if (hand->d_synch != NULL)
(void) DSVC_SYNCH_UNLOCK(hand->d_synch, unlock_cookie);
return (error);
}
/*
* Modify the record origp with the record newp in the DHCP container hand.
* newp's update signature will be updated by the public layer module doing
* the update. Caller is responsible for freeing origp and/or newp if they
* were dynamically allocated.
*/
int
modify_dd_entry(dsvc_handle_t hand, const void *origp, void *newp)
{
int error;
void *unlock_cookie;
if (DSVC_HANDLE_INVAL(hand))
return (DSVC_INVAL);
if (!validate_dd_entry(hand, origp, B_TRUE))
return (DSVC_INVAL);
if (!validate_dd_entry(hand, newp, B_FALSE))
return (DSVC_INVAL);
if (hand->d_synch != NULL) {
error = DSVC_SYNCH_WRLOCK(hand->d_synch, &unlock_cookie);
if (error != DSVC_SUCCESS)
return (error);
}
if (hand->d_type == DSVC_DHCPTAB)
error = hand->d_api.modify_dt(hand->d_hand, origp, newp);
else
error = hand->d_api.modify_dn(hand->d_hand, origp, newp);
if (hand->d_synch != NULL)
(void) DSVC_SYNCH_UNLOCK(hand->d_synch, unlock_cookie);
return (error);
}
/*
* Deletes the record referred to by entryp from the DHCP container hand.
* Caller is responsible for freeing entryp if it was dynamically
* allocated.
*/
int
delete_dd_entry(dsvc_handle_t hand, void *entryp)
{
int error;
void *unlock_cookie;
if (DSVC_HANDLE_INVAL(hand))
return (DSVC_INVAL);
if (!validate_dd_entry(hand, entryp, B_TRUE))
return (DSVC_INVAL);
if (hand->d_synch != NULL) {
error = DSVC_SYNCH_WRLOCK(hand->d_synch, &unlock_cookie);
if (error != DSVC_SUCCESS)
return (error);
}
if (hand->d_type == DSVC_DHCPTAB)
error = hand->d_api.delete_dt(hand->d_hand, entryp);
else
error = hand->d_api.delete_dn(hand->d_hand, entryp);
if (hand->d_synch != NULL)
(void) DSVC_SYNCH_UNLOCK(hand->d_synch, unlock_cookie);
return (error);
}
/*
* Validate that the DHCP network record `dn' is correctly formed; returns
* B_TRUE if it is, B_FALSE if it's not. If `justkey' is set, then only
* validate the key.
*/
static boolean_t
validate_dnrec(dsvc_handle_t hand, const dn_rec_t *dn, boolean_t justkey)
{
/* CIP must be on container's network */
if (hand->d_conid.c_net.s_addr !=
(dn->dn_cip.s_addr & hand->d_conid.c_mask.s_addr))
return (B_FALSE);
if (justkey)
return (B_TRUE);
if (dn->dn_cid_len < 1 || dn->dn_cid_len > DN_MAX_CID_LEN)
return (B_FALSE);
if ((dn->dn_flags & ~DN_FALL) != 0)
return (B_FALSE);
return (B_TRUE);
}
/*
* Validate that the dhcptab record `dt' is correctly formed; returns
* B_TRUE if it is, B_FALSE if it's not. If `justkey' is set, then only
* validate the key.
*/
/* ARGSUSED */
static boolean_t
validate_dtrec(dsvc_handle_t hand, const dt_rec_t *dt, boolean_t justkey)
{
return (dt->dt_type == DT_SYMBOL || dt->dt_type == DT_MACRO);
}
/*
* Validate that a DHCP record of type `hand->d_type' is correctly formed;
* returns B_TRUE if it is, B_FALSE if it's not. If `justkey' is set, then
* only validate the key.
*/
static boolean_t
validate_dd_entry(dsvc_handle_t hand, const void *entryp, boolean_t justkey)
{
if (entryp == NULL)
return (B_FALSE);
if (hand->d_type == DSVC_DHCPTAB)
return (validate_dtrec(hand, (dt_rec_t *)entryp, justkey));
else if (hand->d_type == DSVC_DHCPNETWORK)
return (validate_dnrec(hand, (dn_rec_t *)entryp, justkey));
return (B_FALSE);
}
/*
* Get the type of synchronization needed for this module and store in
* `synchtypep'. Returns a DSVC_* code. This function is exported so that
* dsvclockd(1M) can use it.
*/
int
module_synchtype(dsvc_datastore_t *ddp, dsvc_synchtype_t *synchtypep)
{
void *instance;
dsvc_splapi_t api;
dsvc_synchtype_t *dsvc_synchtypep;
if (load_public_module(ddp, &instance, &api) != DSVC_SUCCESS)
return (DSVC_INTERNAL);
dsvc_synchtypep = dlsym(instance, "dsvc_synchtype");
if (dsvc_synchtypep != NULL)
*synchtypep = *dsvc_synchtypep;
else
*synchtypep = DSVC_SYNCH_NONE;
(void) unload_public_module(&instance, &api);
return (DSVC_SUCCESS);
}
/*
* Initialize private-layer synchronization on handle `hand' for container
* `conname'; `flags' is the same flags passed into open_dd(). If there's
* no synchronization needed, always succeeds. Returns a DSVC_* code.
*/
int
synch_init(dsvc_handle_t hand, const char *conname, uint_t flags)
{
dsvc_synchtype_t synchtype;
dsvc_synch_t *sp;
int error;
int (*mkloctoken)(const char *, char *, size_t);
error = module_synchtype(&hand->d_desc, &synchtype);
if (error != DSVC_SUCCESS)
return (error);
if (synchtype == DSVC_SYNCH_NONE)
return (DSVC_SUCCESS);
sp = malloc(sizeof (dsvc_synch_t));
if (sp == NULL)
return (DSVC_NO_MEMORY);
sp->s_conname = strdup(conname);
if (sp->s_conname == NULL) {
free(sp);
return (DSVC_NO_MEMORY);
}
sp->s_nonblock = flags & DSVC_NONBLOCK;
sp->s_datastore = &hand->d_desc;
mkloctoken = (int (*)())dlsym(hand->d_instance, "mkloctoken");
if (mkloctoken == NULL) {
(void) strlcpy(sp->s_loctoken, sp->s_datastore->d_location,
sizeof (sp->s_loctoken));
} else {
error = mkloctoken(sp->s_datastore->d_location, sp->s_loctoken,
sizeof (sp->s_loctoken));
if (error != DSVC_SUCCESS) {
free(sp->s_conname);
free(sp);
return (error);
}
}
/*
* The only synchtype supported is DSVC_SYNCH_DSVCD; if this
* changes, we'll need to enhance this.
*/
assert((synchtype & DSVC_SYNCH_STRATMASK) == DSVC_SYNCH_DSVCD);
sp->s_ops = &dsvcd_synch_ops;
error = DSVC_SYNCH_INIT(sp, synchtype & DSVC_SYNCH_FLAGMASK);
if (error != DSVC_SUCCESS) {
free(sp->s_conname);
free(sp);
return (error);
}
hand->d_synch = sp;
return (DSVC_SUCCESS);
}
/*
* Finish using private-layer synchronization on handle `hand'.
*/
void
synch_fini(dsvc_handle_t hand)
{
DSVC_SYNCH_FINI(hand->d_synch);
free(hand->d_synch->s_conname);
free(hand->d_synch);
hand->d_synch = NULL;
}