/*	$CoreSDI: resource.c,v 1.29 2001/10/05 23:14:58 claudio Exp $	*/

/*
 *   Copyright (c) 2000, 2001, Core SDI S.A., Argentina
 *   All rights reserved
 *  
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *  
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *   3. Neither name of the Core SDI S.A. nor the names of its contributors
 *      may be used to endorse or promote products derived from this software
 *      without specific prior written permission.
 *  
 *   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 *   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 *   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *   IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Resources Generic Interface
 * Author: Claudio Castiglia
 */

#include <sys/types.h>

#ifndef WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <dlfcn.h>
#endif	/* WIN32 */

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "sysdep.h"
#include "packet.h"
#include "version.h"
#include "audconf.h"
#include "resource.h"
#include "modtypes.h"
#include "modules.h"
#include "log.h"


/* Exported by resource modules */
static int (*_resmod_check) (const char *);
static int (*_resmod_load) (RESLIST *);
static int (*_resmod_save) (const RESLIST *);
static int (*_resmod_remove) (const char *);

/* Resources module handler */
static void *_res_handle = NULL;


/*
 * res_module_init():
 *	Init resource system: load specified resource module.
 *	Return 0 on success or -1 on error and set errno.
 */
int
res_module_init(const char *name)
{
	char fullname[MAXPATHLEN] = { "" };
	
	if (name == NULL)
	    	return (-1);

	full_module_pathname(fullname, "res", name);
	log_debug("Resources api: Initializing %s.", fullname);
	_res_handle = load_module(fullname);
	if (_res_handle == NULL)
		return (-1);

	_resmod_check = load_function(_res_handle, SYMBOL_PREFIX "check", 1);
	_resmod_load = load_function(_res_handle, SYMBOL_PREFIX "load", 1);
	_resmod_save = load_function(_res_handle, SYMBOL_PREFIX "save", 1);
	_resmod_remove = load_function(_res_handle, SYMBOL_PREFIX "remove", 1);

	if (_resmod_check == NULL || _resmod_load == NULL ||
	    _resmod_save == NULL || _resmod_remove == NULL) {
		res_module_release();
		return (-1);
	}
	return (0);
}


/*
 * res_module_release():
 *	Close resource module.
 */
void
res_module_release()
{
	if (_res_handle != NULL) {
		dlclose(_res_handle);
		_res_handle = NULL;
	}
}


/*
 * res_check_list():
 *	Check if list exists.
 *	Return 1 if specified list exists or 0 if not. This function
 *	does not load the list, just test its existense.
 */
int
res_check_list(const char *listname)
{
	if (listname != NULL)
		return (_resmod_check(listname));
	return (0);
}


/*
 * res_open_list():
 *	Create a resource list.
 *	Return a pointer to the new RESLIST structure;
 *	on error NULL is returned and errno set.
 *	mode values:
 *		RES_READ     : Create list, read existing data;
 *		RES_FORCEREAD: Create list only if data exists;
 *		RES_NOTREAD  : Create list, do not read data.
 */
RESLIST *
res_open_list(const char *listname, int mode)
{
	RESLIST *rlist;

	log_debug("Resources api: Opening %s, mode %i.", listname, mode);
	rlist = (RESLIST *) calloc(1, sizeof(RESLIST));
	if (rlist == NULL)
		return (NULL);
	strlcpy(rlist->lname, listname, sizeof(rlist->lname));

	switch (mode) {
	case RES_READ:
	case RES_FORCEREAD:
		if (_resmod_load(rlist) < 0) {
			if (mode == RES_FORCEREAD) {
				free(rlist);
				return (NULL);
			}
		}
		break;
	case RES_NOTREAD:
	default:
		break;
	}

	return (rlist);
}


/*
 * res_release_list():
 *	Release a list (flush pending data, close it, and
 *	free allocated mem).
 */
void
res_release_list(RESLIST *rlist)
{
	RESOURCE *r;
	RESLIST  *list, *pl;

	if (rlist != NULL) {
		if (_resmod_save(rlist) < 0)
			log_err("resource: save: %s: %s.",
			    rlist->lname, strerror(errno));
		for (list = rlist; list != NULL;) {
			for (r = list->first_res; r != NULL;) {
				if (r->name != NULL)
					free(r->name);
				if (r->data != NULL)
					free(r->data);
				if ( (r = r->next) != NULL)
					if (r->prev != NULL)
						free(r->prev);
			}
		pl = list;
		list = list->lnext;
		free(pl);
		}
	}
}


/*
 * res_remove_list():
 *	Remove a resource list.
 */
int
res_remove_list(const char *listname)
{
	if (listname == NULL) {
		errno = EINVAL;
		return (-1);
	}
	return (_resmod_remove(listname));
}


/*
 * res_save_list():
 *	Force the resource list to be saved (flushed);
 *	return 0 on success and -1 on error (in this case, the error
 *	code depends on the resource module used).
 */
int
res_save_list(const RESLIST *rlist)
{
	if (rlist == NULL) {
		errno = EINVAL;
		return (-1);
	}
	return (_resmod_save(rlist));
}


/*
 * res_add():
 *	Add a new resource into the list;
 *	return 0 on success and < 0 on error (and set errno).
 *	Returned errno values:
 *		EINVAL: Invalid parameters
 *		EEXIST: Resource exists
 *		and those possible returned by calloc(3).
 *	If you add a new and, you want this resource to be available
 *	on next program instances, you should save the entire
 *	resource list using the res_save() function.
 */
int
res_add(RESLIST *rlist, const char *rname, const void *data, ssize_t dsize)
{
	RESOURCE *r;

	if (rlist == NULL || rname == NULL || data == NULL || dsize <= 0) {
		errno = EINVAL;
		return (-1);
	}
	if (res_find(rlist, rname) != NULL) {
		errno = EEXIST;
		return (-1);
	}

	/* Allocate memory for a new resource */
	r = (RESOURCE *) calloc(1, sizeof(RESOURCE));
	if (r == NULL)
		return (-1);
	r->name = strdup(rname);
	r->data = malloc(dsize);
	if (r->data == NULL) {
		if (r->name != NULL)
			free(r->name);
		free(r);
		return (-1);
	}

	/* Copy data into the new resource */
	memcpy(r->data, data, dsize);
	r->dsize = dsize;

	/* Insert the new resource into the list */
	if (rlist->first_res == NULL)
		rlist->first_res = r;
	r->prev = rlist->last_res;
	rlist->last_res = r;
	if (r->prev != NULL)
		r->prev->next = r;

	return (0);
}


/*
 * res_remove():
 *	Remove a resource from the list (and from memory).
 *	If you want the changes to be available in next program
 *	instances, you should save the resource list using res_save()
 *	function.
 */
void
res_remove(RESLIST *rlist, const char *rname)
{
	RESOURCE *r;

	if (rlist != NULL && rname != NULL)
		if ( (r = res_find(rlist, rname)) != NULL) {
			if (r->next != NULL)
				r->next->prev = r->prev;
			if (r->prev != NULL)
				r->prev->next = r->next;
			if (rlist->first_res == r)
				rlist->first_res =
				    (r->prev != NULL) ? r->prev : r->next;
			if (rlist->last_res == r)
				rlist->last_res =
				    (r->next != NULL) ? r->next : r->prev;
			/*
			 * XXX modified rlist flag indicates to res_save()
			 * that _all_ list must be saved because any
			 * resources was deleted from the list.
			 */
			rlist->flags |= RF_MODIFIED;

			/* free resource */
			free(r->name);
			free(r->data);
			free(r);
		}
}


/*
 * res_replace():
 *	Replace an existing resource data;
 *	return 0 on success and < 0 on error (and set errno).
 *	Returned errno values:
 *		EINVAL: Invalid parameters
 *		and those possible returned by malloc(3).
 *	If you want the changes to be available on next program
 *	instances, you should save the resource list using res_save()
 *	function.
 *	NOTE: replaced data resource should belong to the list otherwise
 *	      changes will not be available on next program instances.
 */
int
res_replace(RESLIST *rlist, RESOURCE *r, const void *data, ssize_t dsize)
{
	void *new_data;

	if (rlist == NULL || r == NULL || data == NULL || dsize <= 0) {
		errno = EINVAL;
		return (-1);
	}
	if ( (new_data = malloc(dsize)) == NULL)
		return (-1);
	if (r->data)
		free(r->data);
	memcpy(new_data, data, dsize);
	r->data = new_data;
	r->dsize = dsize;
	rlist->flags |= RF_MODIFIED;	/* at least one resource was changed */
				/* XXX: wrong! with this, save will believe
					that all should be saved and not only
					those modified resources.
					Must be more specific.
				*/
	return (0);
}


/*
 * res_find():
 *	Search for a resource into the resource list;
 *	return NULL if no match.
 */
RESOURCE *
res_find(const RESLIST *rlist, const char *rname)
{
	RESOURCE *r;
	const RESLIST *list;

	if (rlist == NULL || rname == NULL)
		return (NULL);
	for (list = rlist; list != NULL; list = list->lnext)
		for (r = list->first_res; r != NULL; r = r->next)
			if (!strcmp(rname, r->name))
				return (r);
	return (NULL);
}

