diff options
author | Andrew Tridgell <tridge@samba.org> | 2010-11-01 12:32:18 +1100 |
---|---|---|
committer | Andrew Tridgell <tridge@samba.org> | 2010-11-01 18:55:18 +1100 |
commit | 8b63ff52e2442d457b5221f51cb9e85e4b94e298 (patch) | |
tree | 582abea1338458934880dab68bcececbcd371e10 /source4 | |
parent | fd6d29bc08cacbd5270599aab52e0006bea2002f (diff) | |
download | samba-8b63ff52e2442d457b5221f51cb9e85e4b94e298.tar.gz samba-8b63ff52e2442d457b5221f51cb9e85e4b94e298.tar.bz2 samba-8b63ff52e2442d457b5221f51cb9e85e4b94e298.zip |
s4-ldb: support a new type of ldb module loading
this supports module loading in ldb which uses the approach of "load
all modules in a directory". This is much more flexible than the
current module loading, as it will allow us to load modules for
command line parsing and authentication.
Modules are loaded from a colon separated path, in the environment
variable LDB_MODULES_PATH. If unset, it defaults to LDB_MODULESDIR.
Within each directory modules are loaded recursively (traversing down
the directory tree). The device/inode number of each module is
remembered to prevent us loading a module twice.
Each module is checked for a ldb_init_module() function with
dlsym(). If found, it is called with the ldb module version as an
argument.
Diffstat (limited to 'source4')
-rw-r--r-- | source4/lib/ldb/common/ldb.c | 10 | ||||
-rw-r--r-- | source4/lib/ldb/common/ldb_modules.c | 185 | ||||
-rw-r--r-- | source4/lib/ldb/include/ldb_module.h | 3 | ||||
-rw-r--r-- | source4/lib/ldb/wscript | 2 |
4 files changed, 199 insertions, 1 deletions
diff --git a/source4/lib/ldb/common/ldb.c b/source4/lib/ldb/common/ldb.c index e2a3e603ce..f6189debe6 100644 --- a/source4/lib/ldb/common/ldb.c +++ b/source4/lib/ldb/common/ldb.c @@ -92,6 +92,16 @@ struct ldb_context *ldb_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx) { struct ldb_context *ldb; int ret; + const char *modules_path = getenv("LDB_MODULES_PATH"); + + if (modules_path == NULL) { + modules_path = LDB_MODULESDIR; + } + + ret = ldb_modules_load(modules_path, LDB_VERSION); + if (ret != LDB_SUCCESS) { + return NULL; + } ldb = talloc_zero(mem_ctx, struct ldb_context); /* A new event context so that callers who don't want ldb diff --git a/source4/lib/ldb/common/ldb_modules.c b/source4/lib/ldb/common/ldb_modules.c index 1b0f6f74f8..57389e8c9a 100644 --- a/source4/lib/ldb/common/ldb_modules.c +++ b/source4/lib/ldb/common/ldb_modules.c @@ -33,6 +33,7 @@ #include "ldb_private.h" #include "dlinklist.h" +#include "system/dir.h" #define LDB_MODULE_PREFIX "modules:" #define LDB_MODULE_PREFIX_LEN 8 @@ -867,6 +868,190 @@ int ldb_mod_register_control(struct ldb_module *module, const char *oid) return ret; } +static int ldb_modules_load_dir(const char *modules_dir, const char *version); + + +/* + load one module. A static list of loaded module inode numbers is + used to prevent a module being loaded twice + + dlopen() is used on the module, and dlsym() is then used to look for + a ldb_init_module() function. If present, that function is called + with the ldb version number as an argument. + + The ldb_init_module() function will typically call + ldb_register_module() and ldb_register_backend() to register a + module or backend, but it may also be used to register command line + handling functions, ldif handlers or any other local + modififications. + + The ldb_init_module() function does not get a ldb_context passed in, + as modules will be used for multiple ldb context handles. The call + from the first ldb_init() is just a convenient way to ensure it is + called early enough. + */ +static int ldb_modules_load_one(const char *path, const char *version) +{ + void *handle; + int (*init_fn)(const char *); + int ret; + struct stat st; + static struct loaded { + struct loaded *next, *prev; + ino_t st_ino; + dev_t st_dev; + } *loaded; + struct loaded *le; + + ret = stat(path, &st); + if (ret != 0) { + fprintf(stderr, "ldb: unable to stat module %s : %s\n", path, strerror(errno)); + return LDB_ERR_UNAVAILABLE; + } + + for (le=loaded; le; le=le->next) { + if (le->st_ino == st.st_ino && + le->st_dev == st.st_dev) { + /* its already loaded */ + return LDB_SUCCESS; + } + } + + le = talloc(loaded, struct loaded); + if (le == NULL) { + fprintf(stderr, "ldb: unable to allocated loaded entry\n"); + return LDB_ERR_UNAVAILABLE; + } + + le->st_ino = st.st_ino; + le->st_dev = st.st_dev; + + DLIST_ADD_END(loaded, le, struct loaded); + + /* if it is a directory, recurse */ + if (S_ISDIR(st.st_mode)) { + return ldb_modules_load_dir(path, version); + } + + handle = dlopen(path, RTLD_NOW); + if (handle == NULL) { + fprintf(stderr, "ldb: unable to dlopen %s : %s", path, dlerror()); + return LDB_SUCCESS; + } + + init_fn = dlsym(handle, "ldb_init_module"); + if (init_fn == NULL) { + /* ignore it, it could be an old-style + * module. Once we've converted all modules we + * could consider this an error */ + dlclose(handle); + return LDB_SUCCESS; + } + + ret = init_fn(version); + return ret; +} + + +/* + load all modules from the given ldb modules directory. This is run once + during the first ldb_init() call. + + Modules are loaded in alphabetical order to ensure that any module + load ordering dependencies are reproducible. Modules should avoid + relying on load order + */ +static int ldb_modules_load_dir(const char *modules_dir, const char *version) +{ + DIR *dir; + struct dirent *de; + const char **modlist = NULL; + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + unsigned i, num_modules = 0; + + dir = opendir(modules_dir); + if (dir == NULL) { + talloc_free(tmp_ctx); + fprintf(stderr, "ldb: unable to open modules directory '%s' - %s\n", + modules_dir, strerror(errno)); + return LDB_ERR_UNAVAILABLE; + } + + + while ((de = readdir(dir))) { + if (ISDOT(de->d_name) || ISDOTDOT(de->d_name)) + continue; + + modlist = talloc_realloc(tmp_ctx, modlist, const char *, num_modules+1); + if (modlist == NULL) { + talloc_free(tmp_ctx); + closedir(dir); + fprintf(stderr, "ldb: unable to allocate modules list\n"); + return LDB_ERR_UNAVAILABLE; + } + modlist[num_modules] = talloc_asprintf(modlist, "%s/%s", modules_dir, de->d_name); + if (modlist[num_modules] == NULL) { + talloc_free(tmp_ctx); + closedir(dir); + fprintf(stderr, "ldb: unable to allocate module list entry\n"); + return LDB_ERR_UNAVAILABLE; + } + num_modules++; + } + + closedir(dir); + + /* sort the directory, so we get consistent load ordering */ + qsort(modlist, num_modules, sizeof(modlist[0]), QSORT_CAST strcmp); + + for (i=0; i<num_modules; i++) { + int ret = ldb_modules_load_one(modlist[i], version); + if (ret != LDB_SUCCESS) { + fprintf(stderr, "ldb: failed to initialise module %s : %s", + modlist[i], ldb_strerror(ret)); + talloc_free(tmp_ctx); + return ret; + } + } + + talloc_free(tmp_ctx); + + return LDB_SUCCESS; +} + +/* + load all modules from the given ldb modules path, colon + separated. + + modules are loaded recursively for all subdirectories in the paths + */ +int ldb_modules_load(const char *modules_path, const char *version) +{ + char *tok, *path, *tok_ptr=NULL; + + path = talloc_strdup(NULL, modules_path); + if (path == NULL) { + fprintf(stderr, "ldb: failed to allocate modules_path"); + return LDB_ERR_UNAVAILABLE; + } + + for (tok=strtok_r(path, ":", &tok_ptr); + tok; + tok=strtok_r(NULL, ":", &tok_ptr)) { + int ret; + + ret = ldb_modules_load_dir(tok, version); + if (ret != LDB_SUCCESS) { + talloc_free(path); + return ret; + } + } + talloc_free(path); + + return LDB_SUCCESS; +} + + #ifdef STATIC_ldb_MODULES #define STATIC_LIBLDB_MODULES STATIC_ldb_MODULES #endif diff --git a/source4/lib/ldb/include/ldb_module.h b/source4/lib/ldb/include/ldb_module.h index c1d668222f..dc6d19dee7 100644 --- a/source4/lib/ldb/include/ldb_module.h +++ b/source4/lib/ldb/include/ldb_module.h @@ -227,4 +227,7 @@ void ldb_req_mark_untrusted(struct ldb_request *req); */ bool ldb_req_is_untrusted(struct ldb_request *req); +/* load all modules from the given directory */ +int ldb_modules_load(const char *modules_path, const char *version); + #endif diff --git a/source4/lib/ldb/wscript b/source4/lib/ldb/wscript index 69b5c453f3..04671352bf 100644 --- a/source4/lib/ldb/wscript +++ b/source4/lib/ldb/wscript @@ -219,7 +219,7 @@ def build(bld): 'common/ldb.c', deps='tevent', includes='include', - cflags='-DLDB_MODULESDIR=\"%s\"' % modules_dir) + cflags='-DLDB_MODULESDIR=\"%s\" -DLDB_VERSION=\"%s\"' % (modules_dir, VERSION)) if s4_build: extra_cmdline_deps = ' LDBSAMBA POPT_SAMBA POPT_CREDENTIALS ' \ |