s4-dsdb: call dsdb_make_schema_global() from ldb_wrap
[ira/wip.git] / source4 / lib / ldb_wrap.c
index f47d0d5d39202906126bc2c9da88bc988d1d8885..134c2271fde242ef22b6e42d3ab3bcbd86d85df9 100644 (file)
@@ -3,7 +3,7 @@
 
    LDB wrap functions
 
-   Copyright (C) Andrew Tridgell 2004
+   Copyright (C) Andrew Tridgell 2004-2009
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -34,6 +34,8 @@
 #include "ldb_wrap.h"
 #include "dsdb/samdb/samdb.h"
 #include "param/param.h"
+#include "dlinklist.h"
+#include "../tdb/include/tdb.h"
 
 /*
   this is used to catch debug messages from ldb
@@ -67,25 +69,53 @@ static void ldb_wrap_debug(void *context, enum ldb_debug_level level,
        free(s);
 }
 
-/* check for memory leaks on the ldb context */
-static int ldb_wrap_destructor(struct ldb_context *ldb)
+
+/*
+  connecting to a ldb can be a relatively expensive operation because
+  of the schema and partition loads. We keep a list of open ldb
+  contexts here, and try to re-use when possible. 
+
+  This means callers of ldb_wrap_connect() must use talloc_unlink() or
+  the free of a parent to destroy the context
+ */
+static struct ldb_wrap {
+       struct ldb_wrap *next, *prev;
+       struct ldb_wrap_context {
+               /* the context is what we use to tell if two ldb
+                * connections are exactly equivalent 
+                */              
+               const char *url;
+               struct tevent_context *ev;
+               struct loadparm_context *lp_ctx;
+               struct auth_session_info *session_info;
+               struct cli_credentials *credentials;
+               unsigned int flags;
+       } context;
+       struct ldb_context *ldb;
+} *ldb_wrap_list;
+
+/*
+  see if two database opens are equivalent
+ */
+static bool ldb_wrap_same_context(const struct ldb_wrap_context *c1,
+                                 const struct ldb_wrap_context *c2)
 {
-       size_t *startup_blocks = (size_t *)ldb_get_opaque(ldb, "startup_blocks");
-       if (startup_blocks &&
-           talloc_total_blocks(ldb) > *startup_blocks + 400) {
-               DEBUG(0,("WARNING: probable memory leak in ldb %s - %lu blocks (startup %lu) %lu bytes\n",
-                        (char *)ldb_get_opaque(ldb, "wrap_url"), 
-                        (unsigned long)talloc_total_blocks(ldb), 
-                        (unsigned long)*startup_blocks,
-                        (unsigned long)talloc_total_size(ldb)));
-#if 0
-               talloc_report_full(ldb, stdout);
-               call_backtrace();
-               smb_panic("probable memory leak in ldb");
-#endif
-       }
+       return (c1->ev == c2->ev &&
+               c1->lp_ctx == c2->lp_ctx &&
+               c1->session_info == c2->session_info &&
+               c1->credentials == c2->credentials &&
+               c1->flags == c2->flags &&
+               (c1->url == c2->url || strcmp(c1->url, c2->url) == 0));
+}
+
+/* 
+   free a ldb_wrap structure
+ */
+static int ldb_wrap_destructor(struct ldb_wrap *w)
+{
+       DLIST_REMOVE(ldb_wrap_list, w);
        return 0;
-}                               
+}
 
 /*
   wrapped connection to a ldb database
@@ -94,36 +124,50 @@ static int ldb_wrap_destructor(struct ldb_context *ldb)
   TODO:  We need an error_string parameter
  */
 struct ldb_context *ldb_wrap_connect(TALLOC_CTX *mem_ctx,
-                                    struct event_context *ev,
+                                    struct tevent_context *ev,
                                     struct loadparm_context *lp_ctx,
                                     const char *url,
                                     struct auth_session_info *session_info,
                                     struct cli_credentials *credentials,
-                                    unsigned int flags,
-                                    const char *options[])
+                                    unsigned int flags)
 {
        struct ldb_context *ldb;
        int ret;
        char *real_url = NULL;
-       size_t *startup_blocks;
+       struct ldb_wrap *w;
+       struct ldb_wrap_context c;
 
-       ldb = ldb_init(mem_ctx);
-       if (ldb == NULL) {
-               return NULL;
-       }
+       c.url          = url;
+       c.ev           = ev;
+       c.lp_ctx       = lp_ctx;
+       c.session_info = session_info;
+       c.credentials  = credentials;
+       c.flags        = flags;
 
-       ldb_set_modules_dir(ldb, 
-                           talloc_asprintf(ldb, "%s/ldb", lp_modulesdir(lp_ctx)));
+       /* see if we can re-use an existing ldb */
+       for (w=ldb_wrap_list; w; w=w->next) {
+               if (ldb_wrap_same_context(&c, &w->context)) {
+                       return talloc_reference(mem_ctx, w->ldb);
+               }
+       }
 
+       /* we want to use the existing event context if possible. This
+          relies on the fact that in smbd, everything is a child of
+          the main event_context */
        if (ev == NULL) {
                return NULL;
        }
 
-       if (ldb_set_opaque(ldb, "EventContext", ev)) {
-               talloc_free(ldb);
+       ldb = ldb_init(mem_ctx, ev);
+       if (ldb == NULL) {
                return NULL;
        }
 
+       ldb_set_modules_dir(ldb,
+                           talloc_asprintf(ldb,
+                                           "%s/ldb",
+                                           lp_modulesdir(lp_ctx)));
+
        if (ldb_set_opaque(ldb, "sessionInfo", session_info)) {
                talloc_free(ldb);
                return NULL;
@@ -138,17 +182,21 @@ struct ldb_context *ldb_wrap_connect(TALLOC_CTX *mem_ctx,
                talloc_free(ldb);
                return NULL;
        }
-       
-       if (lp_ctx != NULL && strcmp(lp_sam_url(lp_ctx), url) == 0) {
-               dsdb_set_global_schema(ldb);
-       }
 
+       /* This must be done before we load the schema, as these
+        * handlers for objectSid and objectGUID etc must take
+        * precedence over the 'binary attribute' declaration in the
+        * schema */
        ret = ldb_register_samba_handlers(ldb);
        if (ret == -1) {
                talloc_free(ldb);
                return NULL;
        }
 
+       if (lp_ctx != NULL && strcmp(lp_sam_url(lp_ctx), url) == 0) {
+               dsdb_set_global_schema(ldb);
+       }
+
        ldb_set_debug(ldb, ldb_wrap_debug, NULL);
 
        ldb_set_utf8_fns(ldb, NULL, wrap_casefold);
@@ -164,12 +212,16 @@ struct ldb_context *ldb_wrap_connect(TALLOC_CTX *mem_ctx,
                flags |= LDB_FLG_NOSYNC;
        }
 
+       if (DEBUGLVL(10)) {
+               flags |= LDB_FLG_ENABLE_TRACING;
+       }
+
        /* we usually want Samba databases to be private. If we later
           find we need one public, we will need to add a parameter to
           ldb_wrap_connect() */
        ldb_set_create_perms(ldb, 0600);
        
-       ret = ldb_connect(ldb, real_url, flags, options);
+       ret = ldb_connect(ldb, real_url, flags, NULL);
        if (ret != LDB_SUCCESS) {
                talloc_free(ldb);
                return NULL;
@@ -177,14 +229,53 @@ struct ldb_context *ldb_wrap_connect(TALLOC_CTX *mem_ctx,
 
        /* setup for leak detection */
        ldb_set_opaque(ldb, "wrap_url", real_url);
-       startup_blocks = talloc(ldb, size_t);
-       *startup_blocks = talloc_total_blocks(ldb);
-       ldb_set_opaque(ldb, "startup_blocks", startup_blocks);
        
-       talloc_set_destructor(ldb, ldb_wrap_destructor);
+       /* add to the list of open ldb contexts */
+       w = talloc(ldb, struct ldb_wrap);
+       if (w == NULL) {
+               talloc_free(ldb);
+               return NULL;
+       }
+
+       w->context = c;
+       w->context.url = talloc_strdup(w, url);
+       if (w->context.url == NULL) {
+               talloc_free(ldb);
+               return NULL;
+       }
+
+       w->ldb = ldb;
+
+       DLIST_ADD(ldb_wrap_list, w);
+
+       /* make the resulting schema global */
+       if (lp_ctx != NULL && strcmp(lp_sam_url(lp_ctx), url) == 0) {
+               dsdb_make_schema_global(ldb);
+       }
+
+       DEBUG(3,("ldb_wrap open of %s\n", url));
+
+       talloc_set_destructor(w, ldb_wrap_destructor);
 
        return ldb;
 }
 
+/*
+  when we fork() we need to make sure that any open ldb contexts have
+  any open transactions cancelled
+ */
+void ldb_wrap_fork_hook(void)
+{
+       struct ldb_wrap *w;
 
+       for (w=ldb_wrap_list; w; w=w->next) {
+               if (ldb_transaction_cancel_noerr(w->ldb) != LDB_SUCCESS) {
+                       smb_panic("Failed to cancel child transactions\n");
+               }
+       }       
+
+       if (tdb_reopen_all(1) == -1) {
+               smb_panic("tdb_reopen_all failed\n");
+       }
+}