view src/lib/module-dir.c @ 3988:ab1a0a377851 HEAD

Added module_dir_deinit().
author Timo Sirainen <tss@iki.fi>
date Sun, 05 Feb 2006 16:14:12 +0200
parents 928229f8b3e6
children 0c26c7f25b9c
line wrap: on
line source

/* Copyright (C) 2003 Timo Sirainen */

#include "lib.h"
#include "array.h"
#include "module-dir.h"

#ifdef HAVE_MODULES

#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <dlfcn.h>

#ifndef RTLD_GLOBAL
#  define RTLD_GLOBAL 0
#endif

#ifndef RTLD_NOW
#  define RTLD_NOW 0
#endif

void *module_get_symbol(struct module *module, const char *symbol)
{
	const char *error;
	void *ret;

	/* get our init func */
	ret = dlsym(module->handle, symbol);

	error = dlerror();
	if (error != NULL) {
		i_error("module %s: dlsym(%s) failed: %s",
			module->path, symbol, error);
		ret = NULL;
	}

	return ret;
}

static void *get_symbol(struct module *module, const char *symbol, bool quiet)
{
	if (quiet)
		return dlsym(module->handle, symbol);

	return module_get_symbol(module, symbol);
}

static void module_free(struct module *module)
{
	if (module->deinit != NULL)
		module->deinit();
	if (dlclose(module->handle) != 0)
		i_error("dlclose(%s) failed: %m", module->path);
	i_free(module->path);
	i_free(module->name);
	i_free(module);
}

static struct module *
module_load(const char *path, const char *name, bool require_init_funcs)
{
	void *handle;
	void (*init)(void);
	struct module *module;

	handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW);
	if (handle == NULL) {
		i_error("dlopen(%s) failed: %s", path, dlerror());
		return NULL;
	}

	module = i_new(struct module, 1);
	module->path = i_strdup(path);
	module->name = i_strdup(name);
	module->handle = handle;

	/* get our init func */
	init = (void (*)())
		get_symbol(module, t_strconcat(name, "_init", NULL),
			   !require_init_funcs);
	module->deinit = init == NULL ? NULL : (void (*)())
		get_symbol(module, t_strconcat(name, "_deinit", NULL),
			   !require_init_funcs);

	if ((init == NULL || module->deinit == NULL) && require_init_funcs) {
		module->deinit = NULL;
		module_free(module);
		return NULL;
	}

	if (getenv("DEBUG") != NULL)
		i_info("Module loaded: %s", path);

	if (init != NULL)
		init();
	return module;
}

static int module_name_cmp(const void *p1, const void *p2)
{
	const char *n1 = p1, *n2 = p2;

	if (strncmp(n1, "lib", 3) == 0)
		n1 += 3;
	if (strncmp(n2, "lib", 3) == 0)
		n1 += 3;

	return strcmp(n1, n2);
}

struct module *module_dir_load(const char *dir, bool require_init_funcs)
{
	DIR *dirp;
	struct dirent *d;
	const char *name, *path, *p, *stripped_name, **names_p;
	struct module *modules, *module;
	unsigned int i, count;
	array_t ARRAY_DEFINE(names, const char *);
	pool_t pool;

	if (getenv("DEBUG") != NULL)
		i_info("Loading modules from directory: %s", dir);

	dirp = opendir(dir);
	if (dirp == NULL) {
		if (errno != ENOENT)
			i_error("opendir(%s) failed: %m", dir);
		return NULL;
	}

	pool = pool_alloconly_create("module loader", 1024);
	ARRAY_CREATE(&names, pool, const char *, 32);

	modules = NULL;
	while ((d = readdir(dirp)) != NULL) {
		name = d->d_name;

		if (name[0] == '.')
			continue;

		p = strstr(name, ".so");
		if (p == NULL || strlen(p) != 3)
			continue;

		name = p_strdup(pool, d->d_name);
		array_append(&names, &name, 1);
	}

	names_p = array_get_modifyable(&names, NULL);
	count = array_count(&names);
	qsort(names_p, count, sizeof(const char *), module_name_cmp);

	for (i = 0; i < count; i++) {
		const char *name = names_p[i];

		/* [lib][nn_]name(.so) */
                stripped_name = name;
		if (strncmp(stripped_name, "lib", 3) == 0)
			stripped_name += 3;

		for (p = stripped_name; *p != '\0'; p++) {
			if (*p < '0' || *p > '9')
				break;
		}
		if (*p == '_')
			stripped_name = p + 1;

		p = strstr(stripped_name, ".so");
		i_assert(p != NULL);

		t_push();
		stripped_name = t_strdup_until(stripped_name, p);
		path = t_strconcat(dir, "/", name, NULL);
		module = module_load(path, stripped_name, require_init_funcs);
		t_pop();

		if (module != NULL) {
			module->next = modules;
			modules = module;
		}
	}
	pool_unref(pool);

	if (closedir(dirp) < 0)
		i_error("closedir(%s) failed: %m", dir);

	return modules;
}

void module_dir_deinit(struct module *modules)
{
	struct module *module;

	for (module = modules; module != NULL; module = module->next) {
		if (module->deinit != NULL) {
			module->deinit();
			module->deinit = NULL;
		}
	}
}

void module_dir_unload(struct module **modules)
{
	struct module *module, *next;

	for (module = *modules; module != NULL; module = next) {
		next = module->next;
		module_free(module);
	}

	*modules = NULL;
}

#else

struct module *module_dir_load(const char *dir __attr_unused__,
			       bool require_init_funcs __attr_unused__)
{
	i_error("Dynamically loadable module support not built in");
	return NULL;
}

void module_dir_deinit(struct module *modules __attr_unused__)
{
}

void module_dir_unload(struct module **modules __attr_unused__)
{
}

#endif