r25755: Fix a couple of memory leaks, in particular a new leak onto the NULL
[jra/samba/.git] / source4 / lib / ldb / common / ldb_modules.c
index 22d1ce112e15342f7e9dbf3e5c4ce54ec246cb12..87e5b2eeb35b1201f7cd22ec2d72d659a8275496 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_private.h"
-#include "dlinklist.h"
-#include <sys/types.h> 
-#include <sys/stat.h> 
-#include <unistd.h> 
+#include "build.h"
+#endif
 
-#define LDB_MODULE_PREFIX      "modules"
-#define LDB_MODULE_PREFIX_LEN  7
-#define LDB_MODULE_SEP         ':'
+#define LDB_MODULE_PREFIX      "modules:"
+#define LDB_MODULE_PREFIX_LEN  8
 
-int ldb_load_modules(struct ldb_context *ldb, const char *options[])
+void ldb_set_modules_dir(struct ldb_context *ldb, const char *path)
 {
-       struct ldb_module *current;
-       char **modules;
-       char *p, *q;
-       int pn, i;
+       talloc_free(ldb->modules_dir);
+       ldb->modules_dir = talloc_strdup(ldb, path);
+}
 
-       /* find out which modules we are requested to activate */
-       modules = NULL;
-       pn = 0;
+static char *ldb_modules_strdup_no_spaces(TALLOC_CTX *mem_ctx, const char *string)
+{
+       int i, len;
+       char *trimmed;
 
-       if (options) {
-               for (i = 0; options[i] != NULL; i++) {
-                       if (strncmp(options[i], LDB_MODULE_PREFIX, 
-                                   LDB_MODULE_PREFIX_LEN) == 0) {
-                               p = q = talloc_strdup(ldb, &options[i][LDB_MODULE_PREFIX_LEN]);
-                               if (*q != ':') {
-                                       talloc_free(q);
-                                       return -1;
-                               }
-                               do {
-                                       *p = '\0';
-                                       q = p + 1;
-                                       pn++;
-                                       modules = talloc_realloc(ldb, modules, char *, pn);
-                                       if (!modules) {
-                                               ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in register_modules()\n");
-                                               return -1;
-                                       }
-                                       modules[pn - 1] = q;
-                               } while ((p = strchr(q, LDB_MODULE_SEP)));
-                       }
+       trimmed = talloc_strdup(mem_ctx, string);
+       if (!trimmed) {
+               return NULL;
+       }
+
+       len = strlen(trimmed);
+       for (i = 0; trimmed[i] != '\0'; i++) {
+               switch (trimmed[i]) {
+               case ' ':
+               case '\t':
+               case '\n':
+                       memmove(&trimmed[i], &trimmed[i + 1], len -i -1);
+                       break;
                }
        }
 
-       if (!modules && strcmp("ldap", ldb->modules->ops->name)) { 
-               /* no modules in the options, look for @MODULES in the
-                  db (not for ldap) */
-               int ret, j, k;
-               const char * const attrs[] = { "@MODULE" , NULL};
-               struct ldb_message **msg = NULL;
+       return trimmed;
+}
 
-               ret = ldb_search(ldb, "", LDB_SCOPE_BASE, "dn=@MODULES", attrs, &msg);
-               if (ret == 0) {
-                       ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db\n");
-               } else {
-                       if (ret < 0) {
-                               ldb_debug(ldb, LDB_DEBUG_FATAL, "ldb error (%s) occurred searching for modules, bailing out\n", ldb_errstring(ldb));
-                               return -1;
-                       }
-                       if (ret > 1) {
-                               ldb_debug(ldb, LDB_DEBUG_FATAL, "Too many records found, bailing out\n");
-                               return -1;
-                       }
 
-                       for (j = 0; j < msg[0]->num_elements; j++) {
-                               for (k = 0; k < msg[0]->elements[j].num_values; k++) {
-                                       pn++;
-                                       modules = talloc_realloc(ldb, modules, char *, pn);
-                                       if (!modules) {
-                                               ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in register_modules()\n");
-                                               return -1;
-                                       }
-                                       modules[pn - 1] = talloc_strndup(modules, msg[0]->elements[j].values[k].data, msg[0]->elements[j].values[k].length);
-                                       if (!modules[pn - 1]) {
-                                               ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in register_modules()\n");
-                                               return -1;
-                                       }
-                               }
-                       }
+/* modules are called in inverse order on the stack.
+   Lets place them as an admin would think the right order is.
+   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 = 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(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);
+               return NULL;
+       }
+       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(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;
                }
-               talloc_free(msg);
+
        }
+       modules[i] = modstr;
 
-       if (modules) {
-               for (i = 0; i < pn; i++) {
-                       if (strcmp(modules[i], "timestamps") == 0) {
-                               current = timestamps_module_init(ldb, options);
-                               if (!current) {
-                                       ldb_debug(ldb, LDB_DEBUG_FATAL, "function 'init_module' in %s fails\n", modules[i]);
-                                       return -1;
-                               }
-                               DLIST_ADD(ldb->modules, current);
-                               continue;
-                       }
+       modules[i + 1] = NULL;
 
-                       if (strcmp(modules[i], "schema") == 0) {
-                               current = schema_module_init(ldb, options);
-                               if (!current) {
-                                       ldb_debug(ldb, LDB_DEBUG_FATAL, "function 'init_module' in %s fails\n", modules[i]);
-                                       return -1;
-                               }
-                               DLIST_ADD(ldb->modules, current);
-                               continue;
-                       }
+       m = (const char **)modules;
 
-#ifdef HAVE_DLOPEN_DISABLED
-               {
-                       void *handle;
-                       ldb_module_init_function init;
-                       struct stat st;
-                       const char *errstr;
+       return m;
+}
 
-                       if (stat(modules[i], &st) < 0) {
-                               ldb_debug(ldb, LDB_DEBUG_FATAL, "Required module [%s] not found, bailing out!\n", modules[i]);
-                               return -1;
-                       }
+static struct ops_list_entry {
+       const struct ldb_module_ops *ops;
+       struct ops_list_entry *next;    
+} *registered_modules = NULL;
 
-                       handle = dlopen(modules[i], RTLD_LAZY);
+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;
+       }
 
-                       if (!handle) {
-                               ldb_debug(ldb, LDB_DEBUG_FATAL, "Error loading module %s [%s]\n", modules[i], dlerror());
-                               return -1;
-                       }
+       return NULL;
+}
 
-                       init = (ldb_module_init_function)dlsym(handle, "init_module");
+#ifndef STATIC_LIBLDB_MODULES
 
-                       errstr = dlerror();
-                       if (errstr) {
-                               ldb_debug(ldb, LDB_DEBUG_FATAL, "Error trying to resolve symbol 'init_module' in %s [%s]\n", modules[i], errstr);
-                               return -1;
-                       }
+#ifdef HAVE_LDB_LDAP
+#define LDAP_INIT ldb_ldap_init,
+#else
+#define LDAP_INIT
+#endif
 
-                       current = init(ldb, options);
-                       if (!current) {
-                               ldb_debug(ldb, LDB_DEBUG_FATAL, "function 'init_module' in %s fails\n", modules[i]);
-                               return -1;
-                       }
-                       DLIST_ADD(ldb->modules, current);
-               }
+#ifdef HAVE_LDB_SQLITE3
+#define SQLITE3_INIT ldb_sqlite3_init,
 #else
-               ldb_debug(ldb, LDB_DEBUG_FATAL, "Required module [%s] not found, bailing out!\n", modules[i]);
-               return -1;
+#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;
 
-       return 0; 
+       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;
 }
 
-/*
-   helper functions to call the next module in chain
-*/
-int ldb_next_close(struct ldb_module *module)
+int ldb_register_module(const struct ldb_module_ops *ops)
 {
-       if (!module->next) {
+       struct ops_list_entry *entry = talloc(talloc_autofree_context(), struct ops_list_entry);
+
+       if (ldb_find_module_ops(ops->name) != NULL)
                return -1;
-       }
-       return module->next->ops->close(module->next);
+
+       if (entry == NULL)
+               return -1;
+
+       entry->ops = ops;
+       entry->next = registered_modules;
+       registered_modules = entry;
+
+       return 0;
 }
 
-int ldb_next_search(struct ldb_module *module, 
-              const char *base,
-              enum ldb_scope scope,
-              const char *expression,
-              const char * const *attrs, struct ldb_message ***res)
+int ldb_try_load_dso(struct ldb_context *ldb, const char *name)
 {
-       if (!module->next) {
+       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;
        }
-       return module->next->ops->search(module->next, base, scope, expression, attrs, res);
-}
 
-int ldb_next_search_free(struct ldb_module *module, struct ldb_message **msg)
-{
-       if (!module->next) {
+       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;
        }
-       return module->next->ops->search_free(module->next, msg);
+
+       talloc_free(path);
+
+       return init_fn();
 }
 
-int ldb_next_add_record(struct ldb_module *module, const struct ldb_message *message)
+int ldb_load_modules_list(struct ldb_context *ldb, const char **module_list, struct ldb_module *backend, struct ldb_module **out)
 {
-       if (!module->next) {
-               return -1;
+       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);
        }
-       return module->next->ops->add_record(module->next, message);
+       *out = module;
+       return LDB_SUCCESS;
 }
 
-int ldb_next_modify_record(struct ldb_module *module, const struct ldb_message *message)
+int ldb_init_module_chain(struct ldb_context *ldb, struct ldb_module *module) 
 {
-       if (!module->next) {
-               return -1;
+       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 module->next->ops->modify_record(module->next, message);
+
+       return LDB_SUCCESS;
 }
 
-int ldb_next_delete_record(struct ldb_module *module, const char *dn)
+int ldb_load_modules(struct ldb_context *ldb, const char *options[])
 {
-       if (!module->next) {
-               return -1;
+       const char **modules = NULL;
+       int i;
+       int ret;
+       TALLOC_CTX *mem_ctx = talloc_new(ldb);
+       if (!mem_ctx) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
-       return module->next->ops->delete_record(module->next, dn);
+
+       /* find out which modules we are requested to activate */
+
+       /* check if we have a custom module list passd as ldb option */
+       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, 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 from ldb */
+       if ((modules == NULL) && (strcmp("ldap", ldb->modules->ops->name) != 0)) { 
+               const char * const attrs[] = { "@LIST" , NULL};
+               struct ldb_result *res = NULL;
+               struct ldb_dn *mods_dn;
+
+               mods_dn = ldb_dn_new(mem_ctx, ldb, "@MODULES");
+               if (mods_dn == NULL) {
+                       talloc_free(mem_ctx);
+                       return -1;
+               }
+
+               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");
+               } 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(mem_ctx);
+                               return -1;
+                       }
+
+                       modules = ldb_modules_list_from_string(ldb, mem_ctx,
+                                                              (const char *)res->msgs[0]->elements[0].values[0].data);
+
+               }
+
+               talloc_free(mods_dn);
+       }
+
+       if (modules != NULL) {
+               ret = ldb_load_modules_list(ldb, modules, ldb->modules, &ldb->modules);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(mem_ctx);
+                       return ret;
+               }
+       } else {
+               ldb_debug(ldb, LDB_DEBUG_TRACE, "No modules specified for this database");
+       }
+
+       ret = ldb_init_module_chain(ldb, ldb->modules);
+       talloc_free(mem_ctx);
+       return ret;
 }
 
-int ldb_next_rename_record(struct ldb_module *module, const char *olddn, const char *newdn)
+/*
+  by using this we allow ldb modules to only implement the functions they care about,
+  which makes writing a module simpler, and makes it more likely to keep working
+  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) { \
+               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)
 {
-       if (!module->next) {
-               return -1;
+       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 module->next->ops->rename_record(module->next, olddn, newdn);
+       return ret;
 }
 
-int ldb_next_named_lock(struct ldb_module *module, const char *lockname)
+int ldb_next_init(struct ldb_module *module)
 {
-       if (!module->next) {
-               return -1;
-       }
-       return module->next->ops->named_lock(module->next, lockname);
+       /* 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_named_unlock(struct ldb_module *module, const char *lockname)
+int ldb_next_start_trans(struct ldb_module *module)
 {
-       if (!module->next) {
-               return -1;
-       }
-       return module->next->ops->named_unlock(module->next, lockname);
+       FIND_OP(module, start_transaction);
+       return module->ops->start_transaction(module);
 }
 
-const char *ldb_next_errstring(struct ldb_module *module)
+int ldb_next_end_trans(struct ldb_module *module)
 {
-       if (!module->next) {
-               return NULL;
-       }
-       return module->next->ops->errstring(module->next);
+       FIND_OP(module, end_transaction);
+       return module->ops->end_transaction(module);
 }
 
+int ldb_next_del_trans(struct ldb_module *module)
+{
+       FIND_OP(module, del_transaction);
+       return module->ops->del_transaction(module);
+}