/*
 * 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) 2002-2003, Network Appliance, Inc. All rights reserved.
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*
 *
 * MODULE: udat.c
 *
 * PURPOSE: DAT Provider and Consumer registry functions.
 *
 * $Id: udat.c,v 1.13 2003/08/20 14:28:40 hobie16 Exp $
 */

#include <dat/udat.h>
#include <dat/dat_registry.h> /* Provider API function prototypes */

#include "dat_dr.h"
#include "dat_init.h"
#include "dat_osd.h"
#ifndef	DAT_NO_STATIC_REGISTRY
#include "dat_sr.h"
#endif


#define	UDAT_IS_BAD_POINTER(p) (NULL == (p))

/*
 *
 * Internal Function Declarations
 *
 */

DAT_BOOLEAN
udat_check_state(void);


/*
 *
 * External Function Definitions
 *
 */


/*
 *
 * Provider API
 *
 */


/*
 * Function: dat_registry_add_provider
 */

DAT_RETURN
dat_registry_add_provider(
	IN DAT_PROVIDER			*provider,
	IN const DAT_PROVIDER_INFO	*provider_info)
{
	DAT_DR_ENTRY 		entry;

	dat_os_dbg_print(DAT_OS_DBG_TYPE_PROVIDER_API,
	    "DAT Registry: dat_registry_add_provider() called\n");

	if (UDAT_IS_BAD_POINTER(provider)) {
		return (DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG1));
	}

	if (UDAT_IS_BAD_POINTER(provider_info)) {
		return (DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG2));
	}

	if (DAT_FALSE == udat_check_state()) {
		return (DAT_ERROR(DAT_INVALID_STATE, 0));
	}

	entry.ref_count = 0;
	entry.ia_open_func = provider->ia_open_func;
	entry.info = *provider_info;

	return (dat_dr_insert(provider_info, &entry));
}


/*
 * Function: dat_registry_remove_provider
 */

DAT_RETURN
dat_registry_remove_provider(
	IN DAT_PROVIDER 		*provider,
	IN  const DAT_PROVIDER_INFO	*provider_info)
{
	dat_os_dbg_print(DAT_OS_DBG_TYPE_PROVIDER_API,
	    "DAT Registry: dat_registry_remove_provider() called\n");

	if (UDAT_IS_BAD_POINTER(provider)) {
		return (DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG1));
	}

	if (DAT_FALSE == udat_check_state()) {
		return (DAT_ERROR(DAT_INVALID_STATE, 0));
	}

	return (dat_dr_remove(provider_info));
}


/*
 *
 * Consumer API
 *
 */

/*
 * Function: dat_ia_open
 */

DAT_RETURN
dat_ia_openv(
	IN	const DAT_NAME_PTR	name,
	IN	DAT_COUNT		async_event_qlen,
	INOUT	DAT_EVD_HANDLE		*async_event_handle,
	OUT	DAT_IA_HANDLE		*ia_handle,
	IN	DAT_UINT32		dapl_major,
	IN	DAT_UINT32		dapl_minor,
	IN	DAT_BOOLEAN		thread_safety)
{
	DAT_IA_OPEN_FUNC		ia_open_func;
	DAT_PROVIDER_INFO 		info;
	DAT_RETURN 			status;
	DAT_OS_SIZE 			len;
#define	RO_AWARE_PREFIX	"RO_AWARE_"
	boolean_t			ro_aware_client;
	const char			*_name = name;

	dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
	    "DAT Registry: dat_ia_open() called\n");

	if (UDAT_IS_BAD_POINTER(_name)) {
		return (DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG1));
	}

	len = dat_os_strlen(_name);

	if (DAT_NAME_MAX_LENGTH <= len) {
		return (DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG1));
	}

	if (UDAT_IS_BAD_POINTER(ia_handle)) {
		return (DAT_ERROR(DAT_INVALID_HANDLE, DAT_INVALID_HANDLE_IA));
	}

	if (DAT_FALSE == udat_check_state()) {
		return (DAT_ERROR(DAT_INVALID_STATE, 0));
	}

	/* Find out if this is an RO aware client and if so, strip the prefix */
	ro_aware_client =
	    (strncmp(RO_AWARE_PREFIX, _name, sizeof (RO_AWARE_PREFIX) - 1) ==
	    0);

	/* strip off the prefix from the provider's name if present */
	if (ro_aware_client) {
		_name = _name + sizeof (RO_AWARE_PREFIX) - 1;
		len -= sizeof (RO_AWARE_PREFIX) - 1;
	}

	(void) dat_os_strncpy(info.ia_name, _name, len);
	info.ia_name[len] = '\0';

	info.dapl_version_major = dapl_major;
	info.dapl_version_minor = dapl_minor;
	info.is_thread_safe = thread_safety;

	/*
	 * Since DAT allows providers to be loaded by either the static
	 * registry or explicitly through OS dependent methods, do not
	 * return an error if no providers are loaded via the static registry.
	 * Don't even bother calling the static registry if DAT is compiled
	 * with no static registry support.
	 */

#ifndef DAT_NO_STATIC_REGISTRY
	(void) dat_sr_provider_open(&info);
#endif

	status = dat_dr_provider_open(&info, &ia_open_func);
	if (status != DAT_SUCCESS) {
		dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
		    "DAT Registry: dat_ia_open() provider information "
		    "for IA name %s not found in dynamic registry\n",
		    _name);
		return (status);
	}

	return (*ia_open_func)((const DAT_NAME_PTR) _name,
	    async_event_qlen,
	    async_event_handle,
	    ia_handle,
	    ro_aware_client);
}


/*
 * Function: dat_ia_close
 */

DAT_RETURN
dat_ia_close(
	IN DAT_IA_HANDLE	ia_handle,
	IN DAT_CLOSE_FLAGS	ia_flags)
{
	DAT_PROVIDER	*provider;
	DAT_PROVIDER_ATTR   provider_attr = {0};
	DAT_RETURN 		status;
	const char 		*ia_name;

	dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
	    "DAT Registry: dat_ia_close() called\n");

	if (UDAT_IS_BAD_POINTER(ia_handle)) {
		return (DAT_ERROR(DAT_INVALID_HANDLE, DAT_INVALID_HANDLE_IA));
	}

	if (DAT_FALSE == udat_check_state()) {
		return (DAT_ERROR(DAT_INVALID_STATE, 0));
	}

	provider = DAT_HANDLE_TO_PROVIDER(ia_handle);
	ia_name = provider->device_name;

	if (DAT_SUCCESS != (status = dat_ia_query(ia_handle,
	    NULL,
	    0,
	    NULL,
	    DAT_PROVIDER_FIELD_ALL,
	    &provider_attr))) {
		dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
		    "DAT Registry: query function for %s provider failed\n",
		    ia_name);
	} else if (DAT_SUCCESS != (status =
	    (*provider->ia_close_func)(ia_handle, ia_flags))) {
		dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
		    "DAT Registry: close function for %s provider failed\n",
		    ia_name);
	} else {
		DAT_PROVIDER_INFO info;
		DAT_OS_SIZE len;

		len = dat_os_strlen(ia_name);

		dat_os_assert(len <= DAT_NAME_MAX_LENGTH);

		(void) dat_os_strncpy(info.ia_name, ia_name, len);
		info.ia_name[len] = '\0';

		info.dapl_version_major = provider_attr.dapl_version_major;
		info.dapl_version_minor = provider_attr.dapl_version_minor;
		info.is_thread_safe = provider_attr.is_thread_safe;

		status = dat_dr_provider_close(&info);
		if (DAT_SUCCESS != status) {
			dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
			    "DAT Registry: dynamic registry unable to close "
			    "provider for IA name %s\n",
			    ia_name);
		}

#ifndef DAT_NO_STATIC_REGISTRY
		status = dat_sr_provider_close(&info);
		if (DAT_SUCCESS != status) {
			dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
			    "DAT Registry: static registry unable to close "
			    "provider for IA name %s\n",
			    ia_name);
		}
#endif
	}

	return (status);
}


/*
 * Function: dat_registry_list_providers
 */

DAT_RETURN
dat_registry_list_providers(
	IN  DAT_COUNT   		max_to_return,
	    OUT DAT_COUNT   		*entries_returned,
	    OUT	DAT_PROVIDER_INFO 	*(dat_provider_list[]))
{
	DAT_RETURN	dat_status;

	dat_status = DAT_SUCCESS;
	dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
	    "DAT Registry: dat_registry_list_providers() called\n");

	if (DAT_FALSE == udat_check_state()) {
			return (DAT_ERROR(DAT_INVALID_STATE, 0));
	}

	if ((UDAT_IS_BAD_POINTER(entries_returned))) {
		return (DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG2));
	}

	if (0 != max_to_return && (UDAT_IS_BAD_POINTER(dat_provider_list))) {
		return (DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG3));
	}

	if (0 == max_to_return) {
		/*
		 * the user is allowed to call with max_to_return set to zero.
		 * in which case we simply return (in *entries_returned) the
		 * number of providers currently installed.  We must also
		 * (per spec) return an error
		 */
#ifndef DAT_NO_STATIC_REGISTRY
		(void) dat_sr_size(entries_returned);
#else
		(void) dat_dr_size(entries_returned);
#endif
		return (DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG1));
	} else {
#ifndef DAT_NO_STATIC_REGISTRY
		dat_status = dat_sr_list(max_to_return,
		    entries_returned,
		    dat_provider_list);
#else
		dat_status = dat_dr_list(max_to_return,
		    entries_returned,
		    dat_provider_list);
#endif
	}
	return (dat_status);
}


/*
 *
 * Internal Function Definitions
 *
 */


/*
 * Function: udat_check_state
 */

/*
 * This function returns TRUE if the DAT registry is in a state capable
 * of handling DAT API calls and false otherwise.
 */

DAT_BOOLEAN
udat_check_state(void)
{
	DAT_MODULE_STATE 		state;
	DAT_BOOLEAN 		status;

	state = dat_module_get_state();

	if (DAT_MODULE_STATE_UNINITIALIZED == state) {
		dat_init();
		status = DAT_TRUE;
	} else if (DAT_MODULE_STATE_DEINITIALIZED == state) {
		status = DAT_FALSE;
	} else {
		status = DAT_TRUE;
	}

	return (status);
}


/*
 * Local variables:
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 8
 * End:
 */