/*
 * dsyslog - a dumb syslog (e.g. syslog for people who have a clue)
 * Copyright (c) 2008 William Pitcock <nenolod@sacredspiral.co.uk>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice is present in all copies.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
 */

#include "dsyslog.h"

#include <dlfcn.h>

static GList *module_list_ = NULL;

void
dsyslog_module_load(const gchar *path)
{
	dsyslog_module_t *module;

	_ENTER;

	_DEBUG("filespec = %s", path);

	module = g_slice_new0(dsyslog_module_t);

	module->handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
	if (!module->handle) {
		_ERROR("Loading module %s failed: %s", path, dlerror());
		g_slice_free(dsyslog_module_t, module);
		_LEAVE;
	}
	_DEBUG("libdl handle [%p]", module->handle);

	module->modinit = dlsym(module->handle, "_modinit");
	if (!module->modinit) {
		_ERROR("Module %s is invalid", path);
		dlclose(module->handle);
		g_slice_free(dsyslog_module_t, module);
		_LEAVE;
	}

	module->moddeinit = dlsym(module->handle, "_modfini");
	if (!module->moddeinit) {
		_ERROR("Module %s is invalid", path);
		dlclose(module->handle);
		g_slice_free(dsyslog_module_t, module);
		_LEAVE;
	}

	module->modinit();
	module_list_ = g_list_append(module_list_, module);

	_LEAVE;
}

void
dsyslog_module_unload(dsyslog_module_t *module)
{
	_ENTER;

	module_list_ = g_list_remove_all(module_list_, module);
	module->moddeinit();
	dlclose(module->handle);
	g_slice_free(dsyslog_module_t, module);

	_LEAVE;
}

void
dsyslog_module_clear(void)
{
	GList *n, *n2;

	_ENTER;

	for (n = module_list_, n2 = g_list_next(module_list_); n != NULL; n = n2, n2 = g_list_next(n2)) {
		dsyslog_module_t *module = (dsyslog_module_t *) n->data;

		dsyslog_module_unload(module);
	}

	module_list_ = NULL;

	_LEAVE;
}
