Mercurial > dovecot > original-hg > dovecot-1.2
view src/lib/module-dir.c @ 9038:361b9a35e102 HEAD
Module loading: Increased initial memory pool size.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 17 May 2009 17:14:16 -0400 |
parents | 7cac67415704 |
children | 00cd9aacd03c |
line wrap: on
line source
/* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */ #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; /* clear out old errors */ (void)dlerror(); /* get our init func */ ret = dlsym(module->handle, symbol); if (ret == NULL) { error = dlerror(); if (error != NULL) { i_error("module %s: dlsym(%s) failed: %s", module->path, symbol, error); ret = NULL; } } return ret; } const char *module_file_get_name(const char *fname) { const char *p; /* [lib][nn_]name(.so) */ if (strncmp(fname, "lib", 3) == 0) fname += 3; for (p = fname; *p != '\0'; p++) { if (*p < '0' || *p > '9') break; } if (*p == '_') fname = p + 1; p = strstr(fname, MODULE_SUFFIX); if (p == NULL) return fname; return t_strdup_until(fname, p); } 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, const char *version) { void *handle; struct module *module; const char *const *module_version; 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; module_version = version == NULL ? NULL : get_symbol(module, t_strconcat(name, "_version", NULL), TRUE); if (module_version != NULL && strcmp(*module_version, version) != 0) { i_error("Module is for different version %s: %s", *module_version, path); module_free(module); return NULL; } /* get our init func */ module->init = (void (*)(void)) get_symbol(module, t_strconcat(name, "_init", NULL), !require_init_funcs); module->deinit = module->init == NULL ? NULL : (void (*)(void)) get_symbol(module, t_strconcat(name, "_deinit", NULL), !require_init_funcs); if ((module->init == NULL || module->deinit == NULL) && require_init_funcs) { i_error("Module doesn't have %s function: %s", module->init == NULL ? "init" : "deinit", path); module->deinit = NULL; module_free(module); return NULL; } if (getenv("DEBUG") != NULL) i_info("Module loaded: %s", path); return module; } static int module_name_cmp(const void *p1, const void *p2) { const char *const *n1 = p1, *const *n2 = p2; const char *s1 = *n1, *s2 = *n2; if (strncmp(s1, "lib", 3) == 0) s1 += 3; if (strncmp(s2, "lib", 3) == 0) s2 += 3; return strcmp(s1, s2); } static bool module_want_load(const char **names, const char *name) { size_t len; if (names == NULL) return TRUE; len = strlen(name); if (len > 7 && strcmp(name + len - 7, "_plugin") == 0) name = t_strndup(name, len - 7); for (; *names != NULL; names++) { if (strcmp(*names, name) == 0) { *names = ""; return TRUE; } } return FALSE; } static void check_duplicates(ARRAY_TYPE(const_string) *names, const char *name, const char *dir) { const char *const *names_p, *base_name, *tmp; unsigned int i, count; base_name = module_file_get_name(name); names_p = array_get(names, &count); for (i = 0; i < count; i++) { tmp = module_file_get_name(names_p[i]); if (strcmp(tmp, base_name) == 0) i_fatal("Multiple files for module %s: %s/%s, %s/%s", base_name, dir, name, dir, names_p[i]); } } static struct module * module_dir_load_real(const char *dir, const char *module_names, bool require_init_funcs, const char *version) { DIR *dirp; struct dirent *d; const char *name, *p, **names_p; const char **module_names_arr; struct module *modules, *module, **module_pos; unsigned int i, count; ARRAY_TYPE(const_string) names; pool_t pool; if (getenv("DEBUG") != NULL) i_info("Loading modules from directory: %s", dir); dirp = opendir(dir); if (dirp == NULL) { if (module_names != NULL) { /* we were given a list of modules to load. we can't fail. */ i_fatal("opendir(%s) failed: %m", dir); } if (errno != ENOENT) i_error("opendir(%s) failed: %m", dir); return NULL; } pool = pool_alloconly_create("module loader", 2048); p_array_init(&names, pool, 32); modules = NULL; while ((d = readdir(dirp)) != NULL) { name = d->d_name; if (name[0] == '.') continue; p = strstr(name, MODULE_SUFFIX); if (p == NULL || strlen(p) != 3) continue; T_BEGIN { check_duplicates(&names, name, dir); } T_END; name = p_strdup(pool, d->d_name); array_append(&names, &name, 1); } names_p = array_get_modifiable(&names, &count); qsort(names_p, count, sizeof(const char *), module_name_cmp); if (module_names == NULL) module_names_arr = NULL; else { module_names_arr = t_strsplit_spaces(module_names, ", "); /* allow giving the module names also in non-base form. convert them in here. */ for (i = 0; module_names_arr[i] != NULL; i++) { module_names_arr[i] = module_file_get_name(module_names_arr[i]); } } module_pos = &modules; for (i = 0; i < count; i++) T_BEGIN { const char *path, *stripped_name; name = names_p[i]; stripped_name = module_file_get_name(name); if (!module_want_load(module_names_arr, stripped_name)) module = NULL; else { path = t_strconcat(dir, "/", name, NULL); module = module_load(path, stripped_name, require_init_funcs, version); if (module == NULL && module_names_arr != NULL) i_fatal("Couldn't load required plugins"); } if (module != NULL) { *module_pos = module; module_pos = &module->next; } } T_END; if (module_names_arr != NULL) { /* make sure all modules were found */ for (; *module_names_arr != NULL; module_names_arr++) { if (**module_names_arr != '\0') { i_fatal("Plugin %s not found from directory %s", *module_names_arr, dir); } } } pool_unref(&pool); if (closedir(dirp) < 0) i_error("closedir(%s) failed: %m", dir); return modules; } struct module *module_dir_load(const char *dir, const char *module_names, bool require_init_funcs, const char *version) { struct module *modules; T_BEGIN { modules = module_dir_load_real(dir, module_names, require_init_funcs, version); } T_END; return modules; } void module_dir_init(struct module *modules) { struct module *module; for (module = modules; module != NULL; module = module->next) { if (module->init != NULL) { T_BEGIN { module->init(); } T_END; } } } void module_dir_deinit(struct module *modules) { struct module *module, **rev; unsigned int i, count = 0; for (module = modules; module != NULL; module = module->next) count++; if (count == 0) return; /* @UNSAFE: deinitialize in reverse order */ T_BEGIN { rev = t_new(struct module *, count); for (i = 0, module = modules; i < count; i++) { rev[count-i-1] = module; module = module->next; } for (i = 0; i < count; i++) { module = rev[i]; if (module->deinit != NULL) { module->deinit(); module->deinit = NULL; } } } T_END; } void module_dir_unload(struct module **modules) { struct module *module, *next; /* Call all modules' deinit() first, so that they may still call each others' functions. */ module_dir_deinit(*modules); 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, const char *module_names ATTR_UNUSED, bool require_init_funcs ATTR_UNUSED, const char *version 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