/*
 * 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 2003 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

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

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


#include "dat_dr.h"

#include "dat_dictionary.h"


/*
 *
 * Global Variables
 *
 */

static DAT_OS_LOCK 		g_dr_lock;
static DAT_DICTIONARY 		*g_dr_dictionary = NULL;


/*
 *
 * External Functions
 *
 */


/*
 * Function: dat_dr_init
 */

DAT_RETURN
dat_dr_init(void)
{
	DAT_RETURN 	status;

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

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

	return (DAT_SUCCESS);
}


/*
 * Function: dat_dr_fini
 */

DAT_RETURN
dat_dr_fini(void)
{
	DAT_RETURN 			status;

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

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

	return (DAT_SUCCESS);
}


/*
 * Function: dat_dr_insert
 */

extern DAT_RETURN
dat_dr_insert(
    IN  const DAT_PROVIDER_INFO *info,
    IN  DAT_DR_ENTRY 		*entry)
{
	DAT_RETURN 		status;
	DAT_DICTIONARY_ENTRY 	dict_entry;
	DAT_DR_ENTRY		*data;

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

	*data = *entry;

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

	dat_os_lock(&g_dr_lock);

	status = dat_dictionary_insert(g_dr_dictionary,
		dict_entry,
		info,
		(DAT_DICTIONARY_DATA *) data);

	dat_os_unlock(&g_dr_lock);

bail:
	if (DAT_SUCCESS != status) {
		if (NULL != data) {
			dat_os_free(data, sizeof (DAT_DR_ENTRY));
		}


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

	return (status);
}


/*
 * Function: dat_dr_remove
 */

extern DAT_RETURN
dat_dr_remove(
    IN  const DAT_PROVIDER_INFO *info)
{
	DAT_DR_ENTRY 		*data;
	DAT_DICTIONARY_ENTRY 	dict_entry;
	DAT_RETURN 		status;

	dat_os_lock(&g_dr_lock);

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

	if (DAT_SUCCESS != status) {
		/* return status from dat_dictionary_search() */
		goto bail;
	}

	if (0 != data->ref_count) {
		status = DAT_ERROR(DAT_PROVIDER_IN_USE, 0);
		goto bail;
	}

	dict_entry = NULL;
	status = dat_dictionary_remove(g_dr_dictionary,
	    &dict_entry,
	    info,
	    (DAT_DICTIONARY_DATA *) &data);

	if (DAT_SUCCESS != status) {
		/* return status from dat_dictionary_remove() */
		goto bail;
	}

	dat_os_free(data, sizeof (DAT_DR_ENTRY));

bail:
	dat_os_unlock(&g_dr_lock);

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

	return (status);
}


/*
 * Function: dat_dr_provider_open
 */

extern DAT_RETURN
dat_dr_provider_open(
    IN  const DAT_PROVIDER_INFO *info,
    OUT DAT_IA_OPEN_FUNC	*p_ia_open_func)
{
	DAT_RETURN 		status;
	DAT_DR_ENTRY 		*data;

	dat_os_lock(&g_dr_lock);

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

	dat_os_unlock(&g_dr_lock);

	if (DAT_SUCCESS == status) {
		data->ref_count++;
		*p_ia_open_func = data->ia_open_func;
	}

	return (status);
}


/*
 * Function: dat_dr_provider_close
 */

extern DAT_RETURN
dat_dr_provider_close(
    IN  const DAT_PROVIDER_INFO *info)
{
	DAT_RETURN 		status;
	DAT_DR_ENTRY 		*data;

	dat_os_lock(&g_dr_lock);

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

	dat_os_unlock(&g_dr_lock);

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

	return (status);
}


/*
 * Function: dat_dr_size
 */

DAT_RETURN
dat_dr_size(
    OUT	DAT_COUNT		*size)
{
	return (dat_dictionary_size(g_dr_dictionary, size));
}


/*
 * Function: dat_dr_list
 */

DAT_RETURN
dat_dr_list(
    IN  DAT_COUNT		max_to_return,
    OUT DAT_COUNT		*entries_returned,
    OUT DAT_PROVIDER_INFO	* (dat_provider_list[]))
{
	DAT_DR_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_dr_dictionary, &array_size);
		if (status != DAT_SUCCESS) {
			goto bail;
		}

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

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

		dat_os_lock(&g_dr_lock);

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

		dat_os_unlock(&g_dr_lock);

		if (DAT_SUCCESS == status) {
			break;
		} else {
			dat_os_free(array,
			    array_size * sizeof (DAT_DR_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_DR_ENTRY *));
	}

	return (status);
}

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