/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 *
 * MODULE: dat_sr.c
 *
 * PURPOSE: static registry implementation
 *
 * $Id: dat_sr.c,v 1.12 2003/08/20 14:28:40 hobie16 Exp $
 */


#include "dat_sr.h"

#include "dat_dictionary.h"
#include "udat_sr_parser.h"


/*
 *
 * Global Variables
 *
 */

static DAT_OS_LOCK 		g_sr_lock;
static DAT_DICTIONARY 		*g_sr_dictionary = NULL;


/*
 *
 * External Functions
 *
 */


/*
 * Function: dat_sr_init
 */

DAT_RETURN
dat_sr_init(void)
{
	DAT_RETURN 			status;

	status = dat_os_lock_init(&g_sr_lock);
	if (DAT_SUCCESS != status) {
		return (status);
	}

	status = dat_dictionary_create(&g_sr_dictionary);
	if (DAT_SUCCESS != status) {
		return (status);
	}

	/*
	 * 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.
	 */

	(void) dat_sr_load();

	return (DAT_SUCCESS);
}


/*
 * Function: dat_sr_fini
 */

extern DAT_RETURN
dat_sr_fini(void)
{
	DAT_RETURN 			status;

	status = dat_os_lock_destroy(&g_sr_lock);
	if (DAT_SUCCESS != status) {
		return (status);
	}

	status = dat_dictionary_destroy(g_sr_dictionary);
	if (DAT_SUCCESS != status) {
		return (status);
	}

	return (DAT_SUCCESS);
}


/*
 * Function: dat_sr_insert
 */

extern DAT_RETURN
dat_sr_insert(
    IN  const DAT_PROVIDER_INFO *info,
    IN  DAT_SR_ENTRY 		*entry)
{
	DAT_RETURN 		status;
	DAT_SR_ENTRY 		*data;
	DAT_OS_SIZE 		lib_path_size;
	DAT_OS_SIZE 		lib_path_len;
	DAT_OS_SIZE 		ia_params_size;
	DAT_OS_SIZE 		ia_params_len;
	DAT_DICTIONARY_ENTRY 	dict_entry;

	if (NULL == (data = dat_os_alloc(sizeof (DAT_SR_ENTRY)))) {
		status = DAT_ERROR(DAT_INSUFFICIENT_RESOURCES,
		    DAT_RESOURCE_MEMORY);
		goto bail;
	}

	lib_path_len = strlen(entry->lib_path);
	lib_path_size = (lib_path_len + 1) * sizeof (char);

	if (NULL == (data->lib_path = dat_os_alloc(lib_path_size))) {
		status = DAT_ERROR(DAT_INSUFFICIENT_RESOURCES,
		    DAT_RESOURCE_MEMORY);
		goto bail;
	}

	(void) dat_os_strncpy(data->lib_path, entry->lib_path, lib_path_len);
	data->lib_path[lib_path_len] = '\0';

	ia_params_len = strlen(entry->ia_params);
	ia_params_size = (ia_params_len + 1) * sizeof (char);

	if (NULL == (data->ia_params = dat_os_alloc(ia_params_size))) {
		status = DAT_ERROR(DAT_INSUFFICIENT_RESOURCES,
		    DAT_RESOURCE_MEMORY);
		goto bail;
	}

	(void) dat_os_strncpy(data->ia_params, entry->ia_params, ia_params_len);
	data->ia_params[ia_params_len] = '\0';

	data->info = entry->info;
	data->lib_handle = entry->lib_handle;
	data->ref_count = entry->ref_count;

	dict_entry = NULL;
	status = dat_dictionary_entry_create(&dict_entry);
	if (DAT_SUCCESS != status) {
		goto bail;
	}

	dat_os_lock(&g_sr_lock);

	status = dat_dictionary_insert(g_sr_dictionary,
				dict_entry,
				info,
				(DAT_DICTIONARY_DATA *)data);
	dat_os_unlock(&g_sr_lock);

bail:
	if (DAT_SUCCESS != status) {
		if (NULL != data) {
			if (NULL != data->lib_path) {
				dat_os_free(data->lib_path, lib_path_size);
			}

			if (NULL != data->ia_params) {
				dat_os_free(data->ia_params, ia_params_size);
			}

			dat_os_free(data, sizeof (DAT_SR_ENTRY));
		}

		if (NULL != dict_entry) {
			(void) dat_dictionary_entry_destroy(dict_entry);
		}
	}

	return (status);
}


/*
 * Function: dat_sr_size
 */

extern DAT_RETURN
dat_sr_size(
    OUT DAT_COUNT		*size)
{
	return (dat_dictionary_size(g_sr_dictionary, size));
}


/*
 * Function: dat_sr_list
 */

extern DAT_RETURN
dat_sr_list(
    IN  DAT_COUNT		max_to_return,
    OUT DAT_COUNT		*entries_returned,
    OUT DAT_PROVIDER_INFO	* (dat_provider_list[]))
{
	DAT_SR_ENTRY		**array;
	DAT_COUNT 		array_size;
	DAT_COUNT 		i;
	DAT_RETURN 		status;

	array = NULL;
	status = DAT_SUCCESS;

	/*
	 * The dictionary size may increase between the call to
	 * dat_dictionary_size() and dat_dictionary_enumerate().
	 * Therefore we loop until a successful enumeration is made.
	 */
	*entries_returned = 0;
	for (;;) {
		status = dat_dictionary_size(g_sr_dictionary, &array_size);
		if (DAT_SUCCESS != status) {
			goto bail;
		}

		if (array_size == 0) {
			status = DAT_SUCCESS;
			goto bail;
		}

		array = dat_os_alloc(array_size * sizeof (DAT_SR_ENTRY *));
		if (array == NULL) {
			status = DAT_ERROR(DAT_INSUFFICIENT_RESOURCES,
			    DAT_RESOURCE_MEMORY);
			goto bail;
		}

		dat_os_lock(&g_sr_lock);

		status = dat_dictionary_enumerate(g_sr_dictionary,
				(DAT_DICTIONARY_DATA *) array,
				array_size);

		dat_os_unlock(&g_sr_lock);

		if (DAT_SUCCESS == status) {
			break;
		} else {
			dat_os_free(array,
			    array_size * sizeof (DAT_SR_ENTRY *));
			array = NULL;
			continue;
		}
	}

	for (i = 0; (i < max_to_return) && (i < array_size); i++) {
		if (NULL == dat_provider_list[i]) {
			status = DAT_ERROR(DAT_INVALID_PARAMETER,
			    DAT_INVALID_ARG3);
			goto bail;
		}

		*dat_provider_list[i] = array[i]->info;
	}

	*entries_returned = i;

bail:
	if (NULL != array) {
		dat_os_free(array, array_size * sizeof (DAT_SR_ENTRY *));
	}

	return (status);
}



/*
 * Function: dat_sr_provider_open
 */

extern DAT_RETURN
dat_sr_provider_open(
    IN  const DAT_PROVIDER_INFO *info)
{
	DAT_RETURN 		status;
	DAT_SR_ENTRY 		*data;

	dat_os_lock(&g_sr_lock);

	status = dat_dictionary_search(g_sr_dictionary,
			info,
			(DAT_DICTIONARY_DATA *) &data);

	if (DAT_SUCCESS == status) {
		if (0 == data->ref_count) {
			status = dat_os_library_load(data->lib_path,
			    &data->lib_handle);
			if (status == DAT_SUCCESS) {
				data->ref_count++;
			} else {
				dat_os_dbg_print(DAT_OS_DBG_TYPE_SR,
				    "DAT Registry: static registry unable to "
				    "load library %s\n", data->lib_path);
				goto bail;
			}
			data->init_func = (DAT_PROVIDER_INIT_FUNC)
			    dat_os_library_sym(data->lib_handle,
				DAT_PROVIDER_INIT_FUNC_STR);
			data->fini_func = (DAT_PROVIDER_FINI_FUNC)
			    dat_os_library_sym(data->lib_handle,
				DAT_PROVIDER_FINI_FUNC_STR);

			if (NULL != data->init_func) {
				(*data->init_func)(&data->info,
				    data->ia_params);
			}
		} else {
			data->ref_count++;
		}
	}

bail:
	dat_os_unlock(&g_sr_lock);

	return (status);
}


/*
 * Function: dat_sr_provider_close
 */

extern DAT_RETURN
dat_sr_provider_close(
    IN  const DAT_PROVIDER_INFO *info)
{
	DAT_RETURN 		status;
	DAT_SR_ENTRY 		*data;

	dat_os_lock(&g_sr_lock);

	status = dat_dictionary_search(g_sr_dictionary,
			info,
			(DAT_DICTIONARY_DATA *)&data);

	if (DAT_SUCCESS == status) {
		if (1 == data->ref_count) {
			if (NULL != data->fini_func) {
				(*data->fini_func)(&data->info);
			}

			status = dat_os_library_unload(data->lib_handle);
			if (status == DAT_SUCCESS) {
				data->ref_count--;
			}
		} else {
			data->ref_count--;
		}
	}

	dat_os_unlock(&g_sr_lock);

	return (status);
}