r25084: Move samba-specific code out of lib/ldb directory.
[kai/samba.git] / source4 / lib / ldb / common / ldb_modules.c
index f83f0b06ef7ef7f5794d93e7516687f2ec30f133..9f94c90c92065408f7ff5eec27ab772a10440533 100644 (file)
@@ -1,4 +1,3 @@
-
 /* 
    ldb database library
 
@@ -11,7 +10,7 @@
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
-   version 2 of the License, or (at your option) any later version.
+   version 3 of the License, or (at your option) any later version.
 
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -19,8 +18,7 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with this library; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */
 
 /*
  *  Author: Simo Sorce
  */
 
+#include "ldb_includes.h"
+
+#if (_SAMBA_BUILD_ >= 4)
 #include "includes.h"
-#include "ldb/include/ldb.h"
-#include "ldb/include/ldb_errors.h"
-#include "ldb/include/ldb_private.h"
-#include "dlinklist.h"
-#include <sys/types.h> 
-#include <sys/stat.h> 
-#include <unistd.h> 
-
-#ifdef HAVE_DLOPEN_DISABLED
-#include <dlfcn.h>
+#include "build.h"
 #endif
 
 #define LDB_MODULE_PREFIX      "modules:"
 #define LDB_MODULE_PREFIX_LEN  8
 
-static char *talloc_strdup_no_spaces(struct ldb_context *ldb, const char *string)
+void ldb_set_modules_dir(struct ldb_context *ldb, const char *path)
+{
+       talloc_free(ldb->modules_dir);
+       ldb->modules_dir = talloc_strdup(ldb, path);
+}
+
+static char *ldb_modules_strdup_no_spaces(TALLOC_CTX *mem_ctx, const char *string)
 {
        int i, len;
        char *trimmed;
 
-       trimmed = talloc_strdup(ldb, string);
+       trimmed = talloc_strdup(mem_ctx, string);
        if (!trimmed) {
-               ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in talloc_strdup_trim_spaces()\n");
                return NULL;
        }
 
@@ -77,20 +74,22 @@ static char *talloc_strdup_no_spaces(struct ldb_context *ldb, const char *string
 
 /* modules are called in inverse order on the stack.
    Lets place them as an admin would think the right order is.
-   Modules order is imprtant */
-static char **ldb_modules_list_from_string(struct ldb_context *ldb, const char *string)
+   Modules order is important */
+const char **ldb_modules_list_from_string(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *string)
 {
        char **modules = NULL;
+       const char **m;
        char *modstr, *p;
        int i;
 
        /* spaces not admitted */
-       modstr = talloc_strdup_no_spaces(ldb, string);
+       modstr = ldb_modules_strdup_no_spaces(mem_ctx, string);
        if ( ! modstr) {
+               ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_strdup_no_spaces()\n");
                return NULL;
        }
 
-       modules = talloc_realloc(ldb, modules, char *, 2);
+       modules = talloc_realloc(mem_ctx, modules, char *, 2);
        if ( ! modules ) {
                ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()\n");
                talloc_free(modstr);
@@ -99,13 +98,14 @@ static char **ldb_modules_list_from_string(struct ldb_context *ldb, const char *
        talloc_steal(modules, modstr);
 
        i = 0;
+       /* The str*r*chr walks backwards:  This is how we get the inverse order mentioned above */
        while ((p = strrchr(modstr, ',')) != NULL) {
                *p = '\0';
                p++;
                modules[i] = p;
 
                i++;
-               modules = talloc_realloc(ldb, modules, char *, i + 2);
+               modules = talloc_realloc(mem_ctx, modules, char *, i + 2);
                if ( ! modules ) {
                        ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()\n");
                        return NULL;
@@ -116,35 +116,187 @@ static char **ldb_modules_list_from_string(struct ldb_context *ldb, const char *
 
        modules[i + 1] = NULL;
 
-       return modules;
+       m = (const char **)modules;
+
+       return m;
+}
+
+static struct ops_list_entry {
+       const struct ldb_module_ops *ops;
+       struct ops_list_entry *next;    
+} *registered_modules = NULL;
+
+static const struct ldb_module_ops *ldb_find_module_ops(const char *name)
+{
+       struct ops_list_entry *e;
+       for (e = registered_modules; e; e = e->next) {
+               if (strcmp(e->ops->name, name) == 0) 
+                       return e->ops;
+       }
+
+       return NULL;
+}
+
+#ifndef STATIC_LIBLDB_MODULES
+
+#ifdef HAVE_LDB_LDAP
+#define LDAP_INIT ldb_ldap_init,
+#else
+#define LDAP_INIT
+#endif
+
+#ifdef HAVE_LDB_SQLITE3
+#define SQLITE3_INIT ldb_sqlite3_init,
+#else
+#define SQLITE3_INIT
+#endif
+
+#define STATIC_LIBLDB_MODULES \
+       {       \
+               LDAP_INIT \
+               SQLITE3_INIT \
+               ldb_tdb_init,   \
+               ldb_operational_init,   \
+               ldb_rdn_name_init,      \
+               ldb_paged_results_init, \
+               ldb_sort_init,          \
+               ldb_asq_init,           \
+               NULL                    \
+       }
+#endif
+
+int ldb_global_init(void)
+{
+       static int (*static_init_fns[])(void) = STATIC_LIBLDB_MODULES;
+
+       static int initialized = 0;
+       int ret = 0, i;
+
+       if (initialized) 
+               return 0;
+
+       initialized = 1;
+       
+       for (i = 0; static_init_fns[i]; i++) {
+               if (static_init_fns[i]() == -1)
+                       ret = -1;
+       }
+
+       return ret;
+}
+
+int ldb_register_module(const struct ldb_module_ops *ops)
+{
+       struct ops_list_entry *entry = talloc(talloc_autofree_context(), struct ops_list_entry);
+
+       if (ldb_find_module_ops(ops->name) != NULL)
+               return -1;
+
+       if (entry == NULL)
+               return -1;
+
+       entry->ops = ops;
+       entry->next = registered_modules;
+       registered_modules = entry;
+
+       return 0;
+}
+
+int ldb_try_load_dso(struct ldb_context *ldb, const char *name)
+{
+       char *path;
+       void *handle;
+       int (*init_fn) (void);
+
+       if (ldb->modules_dir == NULL)
+               return -1;
+
+       path = talloc_asprintf(ldb, "%s/%s.%s", ldb->modules_dir, name, 
+                              SHLIBEXT);
+
+       ldb_debug(ldb, LDB_DEBUG_TRACE, "trying to load %s from %s\n", name, path);
+
+       handle = dlopen(path, RTLD_NOW);
+       if (handle == NULL) {
+               ldb_debug(ldb, LDB_DEBUG_WARNING, "unable to load %s from %s: %s\n", name, path, dlerror());
+               return -1;
+       }
+
+       init_fn = (int (*)(void))dlsym(handle, "init_module");
+
+       if (init_fn == NULL) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR, "no symbol `init_module' found in %s: %s\n", path, dlerror());
+               return -1;
+       }
+
+       talloc_free(path);
+
+       return init_fn();
+}
+
+int ldb_load_modules_list(struct ldb_context *ldb, const char **module_list, struct ldb_module *backend, struct ldb_module **out)
+{
+       struct ldb_module *module;
+       int i;
+       
+       module = backend;
+
+       for (i = 0; module_list[i] != NULL; i++) {
+               struct ldb_module *current;
+               const struct ldb_module_ops *ops;
+               
+               ops = ldb_find_module_ops(module_list[i]);
+               if (ops == NULL) {
+                       if (ldb_try_load_dso(ldb, module_list[i]) == 0) {
+                               ops = ldb_find_module_ops(module_list[i]);
+                       }
+               }
+               
+               if (ops == NULL) {
+                       ldb_debug(ldb, LDB_DEBUG_WARNING, "WARNING: Module [%s] not found\n", 
+                                 module_list[i]);
+                       continue;
+               }
+               
+               current = talloc_zero(ldb, struct ldb_module);
+               if (current == NULL) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               talloc_set_name(current, "ldb_module: %s", module_list[i]);
+               
+               current->ldb = ldb;
+               current->ops = ops;
+               
+               DLIST_ADD(module, current);
+       }
+       *out = module;
+       return LDB_SUCCESS;
+}
+
+int ldb_init_module_chain(struct ldb_context *ldb, struct ldb_module *module) 
+{
+       while (module && module->ops->init_context == NULL) 
+               module = module->next;
+
+       if (module && module->ops->init_context &&
+               module->ops->init_context(module) != LDB_SUCCESS) {
+               ldb_debug(ldb, LDB_DEBUG_FATAL, "module initialization failed\n");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       return LDB_SUCCESS;
 }
 
 int ldb_load_modules(struct ldb_context *ldb, const char *options[])
 {
-       char **modules = NULL;
+       const char **modules = NULL;
        int i;
-       struct {
-               const char *name;
-               ldb_module_init_t init;
-       } well_known_modules[] = {
-               { "schema", schema_module_init },
-               { "operational", operational_module_init },
-               { "rdn_name", rdn_name_module_init },
-               { "objectclass", objectclass_module_init },
-               { "paged_results", paged_results_module_init },
-               { "server_sort", server_sort_module_init },
-#ifdef _SAMBA_BUILD_
-               { "objectguid", objectguid_module_init },
-               { "samldb", samldb_module_init },
-               { "samba3sam", ldb_samba3sam_module_init },
-               { "proxy", proxy_module_init },
-               { "rootdse", rootdse_module_init },
-               { "extended_dn", extended_dn_module_init },
-               { "password_hash", password_hash_module_init },
-               { "kludge_acl", kludge_acl_module_init },
-#endif
-               { NULL, NULL }
-       };
+       int ret;
+       TALLOC_CTX *mem_ctx = talloc_new(ldb);
+       if (!mem_ctx) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 
        /* find out which modules we are requested to activate */
 
@@ -152,79 +304,58 @@ int ldb_load_modules(struct ldb_context *ldb, const char *options[])
        if (options) {
                for (i = 0; options[i] != NULL; i++) {
                        if (strncmp(options[i], LDB_MODULE_PREFIX, LDB_MODULE_PREFIX_LEN) == 0) {
-                               modules = ldb_modules_list_from_string(ldb, &options[i][LDB_MODULE_PREFIX_LEN]);
+                               modules = ldb_modules_list_from_string(ldb, mem_ctx, &options[i][LDB_MODULE_PREFIX_LEN]);
                        }
                }
        }
 
-       /* if not overloaded by options and the backend is not ldap try to load the modules list form ldb */
+       /* if not overloaded by options and the backend is not ldap try to load the modules list from ldb */
        if ((modules == NULL) && (strcmp("ldap", ldb->modules->ops->name) != 0)) { 
-               int ret;
                const char * const attrs[] = { "@LIST" , NULL};
                struct ldb_result *res = NULL;
-               struct ldb_dn *mods;
+               struct ldb_dn *mods_dn;
 
-               mods = ldb_dn_explode(ldb, "@MODULES");
-               if (mods == NULL) {
+               mods_dn = ldb_dn_new(mem_ctx, ldb, "@MODULES");
+               if (mods_dn == NULL) {
+                       talloc_free(mem_ctx);
                        return -1;
                }
 
-               ret = ldb_search(ldb, mods, LDB_SCOPE_BASE, "", attrs, &res);
-               talloc_free(mods);
+               ret = ldb_search(ldb, mods_dn, LDB_SCOPE_BASE, "", attrs, &res);
+               talloc_steal(mods_dn, res);
                if (ret == LDB_SUCCESS && (res->count == 0 || res->msgs[0]->num_elements == 0)) {
-                       ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db\n");
+                       ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db");
                } else {
                        if (ret != LDB_SUCCESS) {
                                ldb_debug(ldb, LDB_DEBUG_FATAL, "ldb error (%s) occurred searching for modules, bailing out\n", ldb_errstring(ldb));
+                               talloc_free(mem_ctx);
                                return -1;
                        }
                        if (res->count > 1) {
                                ldb_debug(ldb, LDB_DEBUG_FATAL, "Too many records found (%d), bailing out\n", res->count);
-                               talloc_free(res);
+                               talloc_free(mem_ctx);
                                return -1;
                        }
 
-                       modules = ldb_modules_list_from_string(ldb, 
+                       modules = ldb_modules_list_from_string(ldb, mem_ctx,
                                                               (const char *)res->msgs[0]->elements[0].values[0].data);
 
                }
 
-               talloc_free(res);
+               talloc_free(mods_dn);
        }
 
-       if (modules == NULL) {
-               ldb_debug(ldb, LDB_DEBUG_TRACE, "No modules specified for this database\n");
-               return 0;
-       }
-
-       for (i = 0; modules[i] != NULL; i++) {
-               struct ldb_module *current;
-               int m;
-               for (m=0;well_known_modules[m].name;m++) {
-                       if (strcmp(modules[i], well_known_modules[m].name) == 0) {
-                               current = well_known_modules[m].init(ldb, options);
-                               if (current == NULL) {
-                                       ldb_debug(ldb, LDB_DEBUG_FATAL, "function 'init_module' in %s fails\n", modules[i]);
-                                       return -1;
-                               }
-                               DLIST_ADD(ldb->modules, current);
-                               break;
-                       }
-               }
-               if (well_known_modules[m].name == NULL) {
-                       ldb_debug(ldb, LDB_DEBUG_WARNING, "WARNING: Module [%s] not found\n", 
-                                 modules[i]);
+       if (modules != NULL) {
+               ret = ldb_load_modules_list(ldb, modules, ldb->modules, &ldb->modules);
+               talloc_free(modules);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
                }
+       } else {
+               ldb_debug(ldb, LDB_DEBUG_TRACE, "No modules specified for this database");
        }
 
-       /* second stage init */
-       if (ldb_second_stage_init(ldb) != LDB_SUCCESS) {
-               ldb_debug(ldb, LDB_DEBUG_ERROR, "ERROR: Second stage init failed!\n");
-               return -1;
-       }
-
-       talloc_free(modules);
-       return 0; 
+       return ldb_init_module_chain(ldb, ldb->modules);
 }
 
 /*
@@ -233,25 +364,81 @@ int ldb_load_modules(struct ldb_context *ldb, const char *options[])
   when ldb is extended
 */
 #define FIND_OP(module, op) do { \
+       struct ldb_context *ldb = module->ldb; \
        module = module->next; \
        while (module && module->ops->op == NULL) module = module->next; \
-       if (module == NULL) return LDB_ERR_OTHER; \
+       if (module == NULL) { \
+               ldb_asprintf_errstring(ldb, "Unable to find backend operation for " #op ); \
+               return LDB_ERR_OPERATIONS_ERROR;        \
+       }                                               \
 } while (0)
 
 
 /*
    helper functions to call the next module in chain
 */
+
 int ldb_next_request(struct ldb_module *module, struct ldb_request *request)
 {
-       FIND_OP(module, request);
-       return module->ops->request(module, request);
+       int ret;
+       switch (request->operation) {
+       case LDB_SEARCH:
+               FIND_OP(module, search);
+               ret = module->ops->search(module, request);
+               break;
+       case LDB_ADD:
+               FIND_OP(module, add);
+               ret = module->ops->add(module, request);
+               break;
+       case LDB_MODIFY:
+               FIND_OP(module, modify);
+               ret = module->ops->modify(module, request);
+               break;
+       case LDB_DELETE:
+               FIND_OP(module, del);
+               ret = module->ops->del(module, request);
+               break;
+       case LDB_RENAME:
+               FIND_OP(module, rename);
+               ret = module->ops->rename(module, request);
+               break;
+       case LDB_EXTENDED:
+               FIND_OP(module, extended);
+               ret = module->ops->extended(module, request);
+               break;
+       case LDB_SEQUENCE_NUMBER:
+               FIND_OP(module, sequence_number);
+               ret = module->ops->sequence_number(module, request);
+               break;
+       default:
+               FIND_OP(module, request);
+               ret = module->ops->request(module, request);
+               break;
+       }
+       if (ret == LDB_SUCCESS) {
+               return ret;
+       }
+       if (!ldb_errstring(module->ldb)) {
+               /* Set a default error string, to place the blame somewhere */
+               ldb_asprintf_errstring(module->ldb, "error in module %s: %s (%d)", module->ops->name, ldb_strerror(ret), ret);
+       }
+       return ret;
 }
 
-int ldb_next_second_stage_init(struct ldb_module *module)
+int ldb_next_init(struct ldb_module *module)
 {
-       FIND_OP(module, second_stage_init);
-       return module->ops->second_stage_init(module);
+       /* init is different in that it is not an error if modules
+        * do not require initialization */
+
+       module = module->next;
+
+       while (module && module->ops->init_context == NULL) 
+               module = module->next;
+
+       if (module == NULL) 
+               return LDB_SUCCESS;
+
+       return module->ops->init_context(module);
 }
 
 int ldb_next_start_trans(struct ldb_module *module)
@@ -271,13 +458,3 @@ int ldb_next_del_trans(struct ldb_module *module)
        FIND_OP(module, del_transaction);
        return module->ops->del_transaction(module);
 }
-
-void ldb_set_errstring(struct ldb_module *module, char *err_string)
-{
-       if (module->ldb->err_string) {
-               talloc_free(module->ldb->err_string);
-       }
-
-       module->ldb->err_string = talloc_steal(module->ldb, err_string);
-}
-