X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source4%2Flib%2Fldb%2Fcommon%2Fldb.c;h=dbb0089db495ec6762dacdbef7b5562d7cc6abed;hb=6a3aaa1197950d16bb623c26f910347bb1fceb83;hp=5f2e5e3ffc7a0cb9439b995271a4fa35495ba7b4;hpb=5f0fab780220045e04adb11843bcb0cd9a9b64d7;p=kamenim%2Fsamba.git diff --git a/source4/lib/ldb/common/ldb.c b/source4/lib/ldb/common/ldb.c index 5f2e5e3ffc..dbb0089db4 100644 --- a/source4/lib/ldb/common/ldb.c +++ b/source4/lib/ldb/common/ldb.c @@ -1,13 +1,13 @@ -/* +/* ldb database library Copyright (C) Andrew Tridgell 2004 - Copyright (C) Simo Sorce 2005-2006 + Copyright (C) Simo Sorce 2005-2008 ** NOTE! The following LGPL license applies to the ldb ** library. This does NOT imply that all of Samba is released ** under the LGPL - + 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 @@ -32,169 +32,92 @@ * Author: Andrew Tridgell */ -#include "ldb_includes.h" - -/* - initialise a ldb context - The mem_ctx is optional -*/ -struct ldb_context *ldb_init(void *mem_ctx) -{ - struct ldb_context *ldb = talloc_zero(mem_ctx, struct ldb_context); - int ret; - - ret = ldb_setup_wellknown_attributes(ldb); - if (ret != 0) { - talloc_free(ldb); - return NULL; - } - - ldb_set_utf8_default(ldb); - ldb_set_create_perms(ldb, 0666); - ldb_set_modules_dir(ldb, LDB_MODULESDIR); - - return ldb; -} +#define TEVENT_DEPRECATED 1 +#include "ldb_private.h" -static struct backends_list_entry { - struct ldb_backend_ops *ops; - struct backends_list_entry *prev, *next; -} *ldb_backends = NULL; - -#ifndef STATIC_LIBLDB_BACKENDS - -#ifdef HAVE_LDB_LDAP -#define LDAP_INIT &ldb_ldap_backend_ops, \ - &ldb_ildap_backend_ops, \ - &ldb_ldaps_backend_ops, -#else -#define LDAP_INIT -#endif - -#ifdef HAVE_LDB_SQLITE3 -#define SQLITE3_INIT &ldb_sqlite3_backend_ops, -#else -#define SQLITE3_INIT -#endif - -#define STATIC_LIBLDB_BACKENDS \ - LDAP_INIT \ - SQLITE3_INIT \ - &ldb_tdb_backend_ops, \ - NULL -#endif - -const static struct ldb_backend_ops *builtin_backends[] = { - STATIC_LIBLDB_BACKENDS -}; - -static ldb_connect_fn ldb_find_backend(const char *url) +static int ldb_context_destructor(void *ptr) { - struct backends_list_entry *backend; - int i; - - for (i = 0; builtin_backends[i]; i++) { - if (strncmp(builtin_backends[i]->name, url, strlen(builtin_backends[i]->name)) == 0) - return builtin_backends[i]->connect_fn; - } + struct ldb_context *ldb = talloc_get_type(ptr, struct ldb_context); - for (backend = ldb_backends; backend; backend = backend->next) { - if (strncmp(backend->ops->name, url, strlen(backend->ops->name)) == 0) { - return backend->ops->connect_fn; - } + if (ldb->transaction_active) { + ldb_debug(ldb, LDB_DEBUG_FATAL, + "A transaction is still active in ldb context [%p] on %s", + ldb, (const char *)ldb_get_opaque(ldb, "ldb_url")); } - return NULL; + return 0; } /* - register a new ldb backend + this is used to catch debug messages from events */ -int ldb_register_backend(const char *url_prefix, ldb_connect_fn connectfn) -{ - struct ldb_backend_ops *backend = talloc(talloc_autofree_context(), struct ldb_backend_ops); - struct backends_list_entry *entry = talloc(talloc_autofree_context(), struct backends_list_entry); +static void ldb_tevent_debug(void *context, enum tevent_debug_level level, + const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0); - if (ldb_find_backend(url_prefix)) { - return LDB_SUCCESS; - } - - /* Maybe check for duplicity here later on? */ +static void ldb_tevent_debug(void *context, enum tevent_debug_level level, + const char *fmt, va_list ap) +{ + struct ldb_context *ldb = talloc_get_type(context, struct ldb_context); + enum ldb_debug_level ldb_level = LDB_DEBUG_FATAL; + char *s = NULL; - backend->name = talloc_strdup(backend, url_prefix); - backend->connect_fn = connectfn; - entry->ops = backend; - DLIST_ADD(ldb_backends, entry); + switch (level) { + case TEVENT_DEBUG_FATAL: + ldb_level = LDB_DEBUG_FATAL; + break; + case TEVENT_DEBUG_ERROR: + ldb_level = LDB_DEBUG_ERROR; + break; + case TEVENT_DEBUG_WARNING: + ldb_level = LDB_DEBUG_WARNING; + break; + case TEVENT_DEBUG_TRACE: + ldb_level = LDB_DEBUG_TRACE; + break; + }; - return LDB_SUCCESS; + vasprintf(&s, fmt, ap); + if (!s) return; + ldb_debug(ldb, ldb_level, "tevent: %s", s); + free(s); } -/* - Return the ldb module form of a database. The URL can either be one of the following forms - ldb://path - ldapi://path - - flags is made up of LDB_FLG_* - - the options are passed uninterpreted to the backend, and are - backend specific. - - This allows modules to get at only the backend module, for example where a module - may wish to direct certain requests at a particular backend. +/* + initialise a ldb context + The mem_ctx is required + The event_ctx is required */ -int ldb_connect_backend(struct ldb_context *ldb, const char *url, const char *options[], - struct ldb_module **backend_module) +struct ldb_context *ldb_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx) { + struct ldb_context *ldb; int ret; - char *backend; - ldb_connect_fn fn; - if (strchr(url, ':') != NULL) { - backend = talloc_strndup(ldb, url, strchr(url, ':')-url); - } else { - /* Default to tdb */ - backend = talloc_strdup(ldb, "tdb"); + ldb = talloc_zero(mem_ctx, struct ldb_context); + /* FIXME: Hack a new event context so that CMD line utilities work + * until we have them all converted */ + if (ev_ctx == NULL) { + ev_ctx = tevent_context_init(talloc_autofree_context()); + tevent_set_debug(ev_ctx, ldb_tevent_debug, ldb); + tevent_loop_allow_nesting(ev_ctx); } - fn = ldb_find_backend(backend); - - if (fn == NULL) { - int (*init_fn) (void); - - init_fn = ldb_dso_load_symbol(ldb, backend, - "init_module"); - if (init_fn != NULL && init_fn() == 0) { - fn = ldb_find_backend(backend); - } - } - - if (fn == NULL) { - struct ldb_backend_ops *ops; - char *symbol_name = talloc_asprintf(ldb, "ldb_%s_backend_ops", backend); - if (symbol_name == NULL) { - return LDB_ERR_OPERATIONS_ERROR; - } - ops = ldb_dso_load_symbol(ldb, backend, symbol_name); - if (ops != NULL) { - fn = ops->connect_fn; - } - talloc_free(symbol_name); + ret = ldb_setup_wellknown_attributes(ldb); + if (ret != LDB_SUCCESS) { + talloc_free(ldb); + return NULL; } - talloc_free(backend); + ldb_set_utf8_default(ldb); + ldb_set_create_perms(ldb, 0666); + ldb_set_modules_dir(ldb, LDB_MODULESDIR); + ldb_set_event_context(ldb, ev_ctx); - if (fn == NULL) { - ldb_debug(ldb, LDB_DEBUG_FATAL, "Unable to find backend for '%s'\n", url); - return LDB_ERR_OTHER; - } + /* TODO: get timeout from options if available there */ + ldb->default_timeout = 300; /* set default to 5 minutes */ - ret = fn(ldb, url, ldb->flags, options, backend_module); + talloc_set_destructor((TALLOC_CTX *)ldb, ldb_context_destructor); - if (ret != LDB_SUCCESS) { - ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to connect to '%s'\n", url); - return ret; - } - return ret; + return ldb; } /* @@ -217,31 +140,40 @@ void ldb_set_default_dns(struct ldb_context *ldb) }; tmp_ctx = talloc_new(ldb); - ret = ldb_search(ldb, ldb_dn_new(tmp_ctx, ldb, NULL), LDB_SCOPE_BASE, - "(objectClass=*)", attrs, &res); - if (ret == LDB_SUCCESS) { - if (res->count == 1) { - if (!ldb_get_opaque(ldb, "rootDomainNamingContext")) { - tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0], "rootDomainNamingContext"); - ldb_set_opaque(ldb, "rootDomainNamingContext", tmp_dn); - } + ret = ldb_search(ldb, tmp_ctx, &res, ldb_dn_new(tmp_ctx, ldb, NULL), + LDB_SCOPE_BASE, attrs, "(objectClass=*)"); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return; + } - if (!ldb_get_opaque(ldb, "configurationNamingContext")) { - tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0], "configurationNamingContext"); - ldb_set_opaque(ldb, "configurationNamingContext", tmp_dn); - } + if (res->count != 1) { + talloc_free(tmp_ctx); + return; + } - if (!ldb_get_opaque(ldb, "schemaNamingContext")) { - tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0], "schemaNamingContext"); - ldb_set_opaque(ldb, "schemaNamingContext", tmp_dn); - } + if (!ldb_get_opaque(ldb, "rootDomainNamingContext")) { + tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0], + "rootDomainNamingContext"); + ldb_set_opaque(ldb, "rootDomainNamingContext", tmp_dn); + } - if (!ldb_get_opaque(ldb, "defaultNamingContext")) { - tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0], "defaultNamingContext"); - ldb_set_opaque(ldb, "defaultNamingContext", tmp_dn); - } - } - talloc_free(res); + if (!ldb_get_opaque(ldb, "configurationNamingContext")) { + tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0], + "configurationNamingContext"); + ldb_set_opaque(ldb, "configurationNamingContext", tmp_dn); + } + + if (!ldb_get_opaque(ldb, "schemaNamingContext")) { + tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0], + "schemaNamingContext"); + ldb_set_opaque(ldb, "schemaNamingContext", tmp_dn); + } + + if (!ldb_get_opaque(ldb, "defaultNamingContext")) { + tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0], + "defaultNamingContext"); + ldb_set_opaque(ldb, "defaultNamingContext", tmp_dn); } talloc_free(tmp_ctx); @@ -249,26 +181,30 @@ void ldb_set_default_dns(struct ldb_context *ldb) struct ldb_dn *ldb_get_root_basedn(struct ldb_context *ldb) { - return talloc_get_type(ldb_get_opaque(ldb, "rootDomainNamingContext"), struct ldb_dn); + void *opaque = ldb_get_opaque(ldb, "rootDomainNamingContext"); + return talloc_get_type(opaque, struct ldb_dn); } struct ldb_dn *ldb_get_config_basedn(struct ldb_context *ldb) { - return talloc_get_type(ldb_get_opaque(ldb, "configurationNamingContext"), struct ldb_dn); + void *opaque = ldb_get_opaque(ldb, "configurationNamingContext"); + return talloc_get_type(opaque, struct ldb_dn); } struct ldb_dn *ldb_get_schema_basedn(struct ldb_context *ldb) { - return talloc_get_type(ldb_get_opaque(ldb, "schemaNamingContext"), struct ldb_dn); + void *opaque = ldb_get_opaque(ldb, "schemaNamingContext"); + return talloc_get_type(opaque, struct ldb_dn); } struct ldb_dn *ldb_get_default_basedn(struct ldb_context *ldb) { - return talloc_get_type(ldb_get_opaque(ldb, "defaultNamingContext"), struct ldb_dn); + void *opaque = ldb_get_opaque(ldb, "defaultNamingContext"); + return talloc_get_type(opaque, struct ldb_dn); } -/* - connect to a database. The URL can either be one of the following forms +/* + connect to a database. The URL can either be one of the following forms ldb://path ldapi://path @@ -277,11 +213,13 @@ struct ldb_dn *ldb_get_default_basedn(struct ldb_context *ldb) the options are passed uninterpreted to the backend, and are backend specific */ -int ldb_connect(struct ldb_context *ldb, const char *url, unsigned int flags, const char *options[]) +int ldb_connect(struct ldb_context *ldb, const char *url, + unsigned int flags, const char *options[]) { int ret; - const char *url2; - /* We seem to need to do this here, or else some utilities don't get ldb backends */ + char *url2; + /* We seem to need to do this here, or else some utilities don't + * get ldb backends */ ldb->flags = flags; @@ -290,7 +228,7 @@ int ldb_connect(struct ldb_context *ldb, const char *url, unsigned int flags, co ldb_oom(ldb); return LDB_ERR_OPERATIONS_ERROR; } - ret = ldb_set_opaque(ldb, "ldb_url", talloc_strdup(ldb, url2)); + ret = ldb_set_opaque(ldb, "ldb_url", url2); if (ret != LDB_SUCCESS) { return ret; } @@ -301,14 +239,12 @@ int ldb_connect(struct ldb_context *ldb, const char *url, unsigned int flags, co } if (ldb_load_modules(ldb, options) != LDB_SUCCESS) { - ldb_debug(ldb, LDB_DEBUG_FATAL, "Unable to load modules for %s: %s\n", + ldb_debug(ldb, LDB_DEBUG_FATAL, + "Unable to load modules for %s: %s", url, ldb_errstring(ldb)); return LDB_ERR_OTHER; } - /* TODO: get timeout from options if available there */ - ldb->default_timeout = 300; /* set default to 5 minutes */ - /* set the default base dn */ ldb_set_default_dns(ldb); @@ -321,6 +257,9 @@ void ldb_set_errstring(struct ldb_context *ldb, const char *err_string) talloc_free(ldb->err_string); } ldb->err_string = talloc_strdup(ldb, err_string); + if (ldb->flags & LDB_FLG_ENABLE_TRACING) { + ldb_debug(ldb, LDB_DEBUG_TRACE, "ldb_set_errstring: %s", ldb->err_string); + } } void ldb_asprintf_errstring(struct ldb_context *ldb, const char *format, ...) @@ -346,22 +285,62 @@ void ldb_reset_err_string(struct ldb_context *ldb) } } -#define FIRST_OP(ldb, op) do { \ + + +/* + set an ldb error based on file:line +*/ +int ldb_error_at(struct ldb_context *ldb, int ecode, + const char *reason, const char *file, int line) +{ + if (reason == NULL) { + reason = ldb_strerror(ecode); + } + ldb_asprintf_errstring(ldb, "%s at %s:%d", reason, file, line); + return ecode; +} + + +#define FIRST_OP_NOERR(ldb, op) do { \ module = ldb->modules; \ while (module && module->ops->op == NULL) module = module->next; \ - if (module == NULL) { \ + if ((ldb->flags & LDB_FLG_ENABLE_TRACING) && module) { \ + ldb_debug(ldb, LDB_DEBUG_TRACE, "ldb_trace_request: (%s)->" #op, \ + module->ops->name); \ + } \ +} while (0) + +#define FIRST_OP(ldb, op) do { \ + FIRST_OP_NOERR(ldb, op); \ + if (module == NULL) { \ ldb_asprintf_errstring(ldb, "unable to find module or backend to handle operation: " #op); \ return LDB_ERR_OPERATIONS_ERROR; \ } \ } while (0) + /* start a transaction */ -static int ldb_transaction_start_internal(struct ldb_context *ldb) +int ldb_transaction_start(struct ldb_context *ldb) { struct ldb_module *module; int status; + + ldb_debug(ldb, LDB_DEBUG_TRACE, + "start ldb transaction (nesting: %d)", + ldb->transaction_active); + + /* explicit transaction active, count nested requests */ + if (ldb->transaction_active) { + ldb->transaction_active++; + return LDB_SUCCESS; + } + + /* start a new transaction */ + ldb->transaction_active++; + ldb->prepare_commit_done = false; + FIRST_OP(ldb, start_transaction); ldb_reset_err_string(ldb); @@ -371,119 +350,193 @@ static int ldb_transaction_start_internal(struct ldb_context *ldb) if (ldb->err_string == NULL) { /* no error string was setup by the backend */ ldb_asprintf_errstring(ldb, - "ldb transaction start: %s (%d)", - ldb_strerror(status), - status); + "ldb transaction start: %s (%d)", + ldb_strerror(status), + status); } } + if ((module && module->ldb->flags & LDB_FLG_ENABLE_TRACING)) { + ldb_debug(module->ldb, LDB_DEBUG_TRACE, "start ldb transaction error: %s", + ldb_errstring(module->ldb)); + } return status; } /* - commit a transaction + prepare for transaction commit (first phase of two phase commit) */ -static int ldb_transaction_commit_internal(struct ldb_context *ldb) +int ldb_transaction_prepare_commit(struct ldb_context *ldb) { struct ldb_module *module; int status; - FIRST_OP(ldb, end_transaction); - ldb_reset_err_string(ldb); + if (ldb->prepare_commit_done) { + return LDB_SUCCESS; + } - status = module->ops->end_transaction(module); + /* commit only when all nested transactions are complete */ + if (ldb->transaction_active > 1) { + return LDB_SUCCESS; + } + + ldb->prepare_commit_done = true; + + if (ldb->transaction_active < 0) { + ldb_debug(ldb, LDB_DEBUG_FATAL, + "prepare commit called but no ldb transactions are active!"); + ldb->transaction_active = 0; + return LDB_ERR_OPERATIONS_ERROR; + } + + /* call prepare transaction if available */ + FIRST_OP_NOERR(ldb, prepare_commit); + if (module == NULL) { + return LDB_SUCCESS; + } + + status = module->ops->prepare_commit(module); if (status != LDB_SUCCESS) { + /* if a module fails the prepare then we need + to call the end transaction for everyone */ + FIRST_OP(ldb, del_transaction); + module->ops->del_transaction(module); if (ldb->err_string == NULL) { /* no error string was setup by the backend */ - ldb_asprintf_errstring(ldb, - "ldb transaction commit: %s (%d)", - ldb_strerror(status), - status); + ldb_asprintf_errstring(ldb, + "ldb transaction prepare commit: %s (%d)", + ldb_strerror(status), + status); + } + if ((module && module->ldb->flags & LDB_FLG_ENABLE_TRACING)) { + ldb_debug(module->ldb, LDB_DEBUG_TRACE, "prepare commit transaction error: %s", + ldb_errstring(module->ldb)); } } + return status; } + /* - cancel a transaction + commit a transaction */ -static int ldb_transaction_cancel_internal(struct ldb_context *ldb) +int ldb_transaction_commit(struct ldb_context *ldb) { struct ldb_module *module; int status; - FIRST_OP(ldb, del_transaction); - status = module->ops->del_transaction(module); + status = ldb_transaction_prepare_commit(ldb); + if (status != LDB_SUCCESS) { + return status; + } + + ldb->transaction_active--; + + ldb_debug(ldb, LDB_DEBUG_TRACE, + "commit ldb transaction (nesting: %d)", + ldb->transaction_active); + + /* commit only when all nested transactions are complete */ + if (ldb->transaction_active > 0) { + return LDB_SUCCESS; + } + + if (ldb->transaction_active < 0) { + ldb_debug(ldb, LDB_DEBUG_FATAL, + "commit called but no ldb transactions are active!"); + ldb->transaction_active = 0; + return LDB_ERR_OPERATIONS_ERROR; + } + + ldb_reset_err_string(ldb); + + FIRST_OP(ldb, end_transaction); + status = module->ops->end_transaction(module); if (status != LDB_SUCCESS) { if (ldb->err_string == NULL) { /* no error string was setup by the backend */ - ldb_asprintf_errstring(ldb, - "ldb transaction cancel: %s (%d)", - ldb_strerror(status), - status); + ldb_asprintf_errstring(ldb, + "ldb transaction commit: %s (%d)", + ldb_strerror(status), + status); + } + if ((module && module->ldb->flags & LDB_FLG_ENABLE_TRACING)) { + ldb_debug(module->ldb, LDB_DEBUG_TRACE, "commit ldb transaction error: %s", + ldb_errstring(module->ldb)); } + /* cancel the transaction */ + FIRST_OP(ldb, del_transaction); + module->ops->del_transaction(module); } return status; } -int ldb_transaction_start(struct ldb_context *ldb) -{ - /* disable autotransactions */ - ldb->transaction_active++; - - return ldb_transaction_start_internal(ldb); -} - -int ldb_transaction_commit(struct ldb_context *ldb) -{ - /* renable autotransactions (when we reach 0) */ - if (ldb->transaction_active > 0) - ldb->transaction_active--; - - return ldb_transaction_commit_internal(ldb); -} +/* + cancel a transaction +*/ int ldb_transaction_cancel(struct ldb_context *ldb) { - /* renable autotransactions (when we reach 0) */ - if (ldb->transaction_active > 0) - ldb->transaction_active--; + struct ldb_module *module; + int status; - return ldb_transaction_cancel_internal(ldb); -} + ldb->transaction_active--; -static int ldb_autotransaction_start(struct ldb_context *ldb) -{ - /* explicit transaction active, ignore autotransaction request */ - if (ldb->transaction_active) + ldb_debug(ldb, LDB_DEBUG_TRACE, + "cancel ldb transaction (nesting: %d)", + ldb->transaction_active); + + /* really cancel only if all nested transactions are complete */ + if (ldb->transaction_active > 0) { return LDB_SUCCESS; + } - return ldb_transaction_start_internal(ldb); -} + if (ldb->transaction_active < 0) { + ldb_debug(ldb, LDB_DEBUG_FATAL, + "cancel called but no ldb transactions are active!"); + ldb->transaction_active = 0; + return LDB_ERR_OPERATIONS_ERROR; + } -static int ldb_autotransaction_commit(struct ldb_context *ldb) -{ - /* explicit transaction active, ignore autotransaction request */ - if (ldb->transaction_active) - return LDB_SUCCESS; + FIRST_OP(ldb, del_transaction); - return ldb_transaction_commit_internal(ldb); + status = module->ops->del_transaction(module); + if (status != LDB_SUCCESS) { + if (ldb->err_string == NULL) { + /* no error string was setup by the backend */ + ldb_asprintf_errstring(ldb, + "ldb transaction cancel: %s (%d)", + ldb_strerror(status), + status); + } + if ((module && module->ldb->flags & LDB_FLG_ENABLE_TRACING)) { + ldb_debug(module->ldb, LDB_DEBUG_TRACE, "cancel ldb transaction error: %s", + ldb_errstring(module->ldb)); + } + } + return status; } -static int ldb_autotransaction_cancel(struct ldb_context *ldb) +/* + cancel a transaction with no error if no transaction is pending + used when we fork() to clear any parent transactions +*/ +int ldb_transaction_cancel_noerr(struct ldb_context *ldb) { - /* explicit transaction active, ignore autotransaction request */ - if (ldb->transaction_active) - return LDB_SUCCESS; - - return ldb_transaction_cancel_internal(ldb); + if (ldb->transaction_active > 0) { + return ldb_transaction_cancel(ldb); + } + return LDB_SUCCESS; } + /* autostarts a transacion if none active */ -static int ldb_autotransaction_request(struct ldb_context *ldb, struct ldb_request *req) +static int ldb_autotransaction_request(struct ldb_context *ldb, + struct ldb_request *req) { int ret; - ret = ldb_autotransaction_start(ldb); + ret = ldb_transaction_start(ldb); if (ret != LDB_SUCCESS) { return ret; } @@ -494,9 +547,9 @@ static int ldb_autotransaction_request(struct ldb_context *ldb, struct ldb_reque } if (ret == LDB_SUCCESS) { - return ldb_autotransaction_commit(ldb); + return ldb_transaction_commit(ldb); } - ldb_autotransaction_cancel(ldb); + ldb_transaction_cancel(ldb); if (ldb->err_string == NULL) { /* no error string was setup by the backend */ @@ -508,19 +561,57 @@ static int ldb_autotransaction_request(struct ldb_context *ldb, struct ldb_reque int ldb_wait(struct ldb_handle *handle, enum ldb_wait_type type) { + struct tevent_context *ev; + int ret; + if (!handle) { - return LDB_SUCCESS; + return LDB_ERR_UNAVAILABLE; + } + + if (handle->state == LDB_ASYNC_DONE) { + return handle->status; } - return handle->module->ops->wait(handle, type); + ev = ldb_get_event_context(handle->ldb); + if (NULL == ev) { + return LDB_ERR_OPERATIONS_ERROR; + } + + switch (type) { + case LDB_WAIT_NONE: + ret = tevent_loop_once(ev); + if (ret != 0) { + return LDB_ERR_OPERATIONS_ERROR; + } + if (handle->state == LDB_ASYNC_DONE || + handle->status != LDB_SUCCESS) { + return handle->status; + } + break; + + case LDB_WAIT_ALL: + while (handle->state != LDB_ASYNC_DONE) { + ret = tevent_loop_once(ev); + if (ret != 0) { + return LDB_ERR_OPERATIONS_ERROR; + } + if (handle->status != LDB_SUCCESS) { + return handle->status; + } + } + return handle->status; + } + + return LDB_SUCCESS; } /* set the specified timeout or, if timeout is 0 set the default timeout */ -/* timeout == -1 means no timeout */ -int ldb_set_timeout(struct ldb_context *ldb, struct ldb_request *req, int timeout) +int ldb_set_timeout(struct ldb_context *ldb, + struct ldb_request *req, + int timeout) { if (req == NULL) return LDB_ERR_OPERATIONS_ERROR; - + if (timeout != 0) { req->timeout = timeout; } else { @@ -532,28 +623,24 @@ int ldb_set_timeout(struct ldb_context *ldb, struct ldb_request *req, int timeou } /* calculates the new timeout based on the previous starttime and timeout */ -int ldb_set_timeout_from_prev_req(struct ldb_context *ldb, struct ldb_request *oldreq, struct ldb_request *newreq) +int ldb_set_timeout_from_prev_req(struct ldb_context *ldb, + struct ldb_request *oldreq, + struct ldb_request *newreq) { - time_t now; - if (newreq == NULL) return LDB_ERR_OPERATIONS_ERROR; - now = time(NULL); - - if (oldreq == NULL) + if (oldreq == NULL) { return ldb_set_timeout(ldb, newreq, 0); - - if ((now - oldreq->starttime) > oldreq->timeout) { - return LDB_ERR_TIME_LIMIT_EXCEEDED; } + newreq->starttime = oldreq->starttime; - newreq->timeout = oldreq->timeout - (now - oldreq->starttime); + newreq->timeout = oldreq->timeout; return LDB_SUCCESS; } -/* +/* set the permissions for new files to be passed to open() in backends that use local files */ @@ -562,6 +649,124 @@ void ldb_set_create_perms(struct ldb_context *ldb, unsigned int perms) ldb->create_perms = perms; } +unsigned int ldb_get_create_perms(struct ldb_context *ldb) +{ + return ldb->create_perms; +} + +void ldb_set_event_context(struct ldb_context *ldb, struct tevent_context *ev) +{ + ldb->ev_ctx = ev; +} + +struct tevent_context * ldb_get_event_context(struct ldb_context *ldb) +{ + return ldb->ev_ctx; +} + +void ldb_request_set_state(struct ldb_request *req, int state) +{ + req->handle->state = state; +} + +int ldb_request_get_status(struct ldb_request *req) +{ + return req->handle->status; +} + + +/* + trace a ldb request +*/ +static void ldb_trace_request(struct ldb_context *ldb, struct ldb_request *req) +{ + TALLOC_CTX *tmp_ctx = talloc_new(req); + unsigned int i; + + switch (req->operation) { + case LDB_SEARCH: + ldb_debug_add(ldb, "ldb_trace_request: SEARCH\n"); + ldb_debug_add(ldb, " dn: %s\n", + ldb_dn_is_null(req->op.search.base)?"": + ldb_dn_get_linearized(req->op.search.base)); + ldb_debug_add(ldb, " scope: %s\n", + req->op.search.scope==LDB_SCOPE_BASE?"base": + req->op.search.scope==LDB_SCOPE_ONELEVEL?"one": + req->op.search.scope==LDB_SCOPE_SUBTREE?"sub":"UNKNOWN"); + ldb_debug_add(ldb, " expr: %s\n", + ldb_filter_from_tree(tmp_ctx, req->op.search.tree)); + if (req->op.search.attrs == NULL) { + ldb_debug_add(ldb, " attr: \n"); + } else { + for (i=0; req->op.search.attrs[i]; i++) { + ldb_debug_add(ldb, " attr: %s\n", req->op.search.attrs[i]); + } + } + break; + case LDB_DELETE: + ldb_debug_add(ldb, "ldb_trace_request: DELETE\n"); + ldb_debug_add(ldb, " dn: %s\n", + ldb_dn_get_linearized(req->op.del.dn)); + break; + case LDB_RENAME: + ldb_debug_add(ldb, "ldb_trace_request: RENAME\n"); + ldb_debug_add(ldb, " olddn: %s\n", + ldb_dn_get_linearized(req->op.rename.olddn)); + ldb_debug_add(ldb, " newdn: %s\n", + ldb_dn_get_linearized(req->op.rename.newdn)); + break; + case LDB_EXTENDED: + ldb_debug_add(ldb, "ldb_trace_request: EXTENDED\n"); + ldb_debug_add(ldb, " oid: %s\n", req->op.extended.oid); + ldb_debug_add(ldb, " data: %s\n", req->op.extended.data?"yes":"no"); + break; + case LDB_ADD: + ldb_debug_add(ldb, "ldb_trace_request: ADD\n"); + ldb_debug_add(req->handle->ldb, "%s\n", + ldb_ldif_message_string(req->handle->ldb, tmp_ctx, + LDB_CHANGETYPE_ADD, + req->op.add.message)); + break; + case LDB_MODIFY: + ldb_debug_add(ldb, "ldb_trace_request: MODIFY\n"); + ldb_debug_add(req->handle->ldb, "%s\n", + ldb_ldif_message_string(req->handle->ldb, tmp_ctx, + LDB_CHANGETYPE_ADD, + req->op.mod.message)); + break; + case LDB_REQ_REGISTER_CONTROL: + ldb_debug_add(ldb, "ldb_trace_request: REGISTER_CONTROL\n"); + ldb_debug_add(req->handle->ldb, "%s\n", + req->op.reg_control.oid); + break; + case LDB_REQ_REGISTER_PARTITION: + ldb_debug_add(ldb, "ldb_trace_request: REGISTER_PARTITION\n"); + ldb_debug_add(req->handle->ldb, "%s\n", + ldb_dn_get_linearized(req->op.reg_partition.dn)); + break; + default: + ldb_debug_add(ldb, "ldb_trace_request: UNKNOWN(%u)\n", + req->operation); + break; + } + + if (req->controls == NULL) { + ldb_debug_add(ldb, " control: \n"); + } else { + for (i=0; req->controls && req->controls[i]; i++) { + ldb_debug_add(ldb, " control: %s crit:%u data:%s\n", + req->controls[i]->oid, + req->controls[i]->critical, + req->controls[i]->data?"yes":"no"); + } + } + + ldb_debug_end(ldb, LDB_DEBUG_TRACE); + + talloc_free(tmp_ctx); +} + + /* start an ldb request NOTE: the request must be a talloc context. @@ -572,8 +777,17 @@ int ldb_request(struct ldb_context *ldb, struct ldb_request *req) struct ldb_module *module; int ret; + if (req->callback == NULL) { + ldb_set_errstring(ldb, "Requests MUST define callbacks"); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + ldb_reset_err_string(ldb); + if (ldb->flags & LDB_FLG_ENABLE_TRACING) { + ldb_trace_request(ldb, req); + } + /* call the first module in the chain */ switch (req->operation) { case LDB_SEARCH: @@ -581,6 +795,15 @@ int ldb_request(struct ldb_context *ldb, struct ldb_request *req) ret = module->ops->search(module, req); break; case LDB_ADD: + /* we have to canonicalise here, as so many places + * in modules and backends assume we don't have two + * elements with the same name */ + req->op.add.message = ldb_msg_canonicalize(ldb, req->op.add.message); + if (!req->op.add.message) { + ldb_oom(ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + talloc_steal(req, req->op.add.message); FIRST_OP(ldb, add); ret = module->ops->add(module, req); break; @@ -593,6 +816,16 @@ int ldb_request(struct ldb_context *ldb, struct ldb_request *req) ret = module->ops->del(module, req); break; case LDB_RENAME: + if (!ldb_dn_validate(req->op.rename.olddn)) { + ldb_asprintf_errstring(ldb, "ldb_rename: invalid olddn '%s'", + ldb_dn_get_linearized(req->op.rename.olddn)); + return LDB_ERR_INVALID_DN_SYNTAX; + } + if (!ldb_dn_validate(req->op.rename.newdn)) { + ldb_asprintf_errstring(ldb, "ldb_rename: invalid newdn '%s'", + ldb_dn_get_linearized(req->op.rename.newdn)); + return LDB_ERR_INVALID_DN_SYNTAX; + } FIRST_OP(ldb, rename); ret = module->ops->rename(module, req); break; @@ -600,10 +833,6 @@ int ldb_request(struct ldb_context *ldb, struct ldb_request *req) FIRST_OP(ldb, extended); ret = module->ops->extended(module, req); break; - case LDB_SEQUENCE_NUMBER: - FIRST_OP(ldb, sequence_number); - ret = module->ops->sequence_number(module, req); - break; default: FIRST_OP(ldb, request); ret = module->ops->request(module, req); @@ -613,6 +842,13 @@ int ldb_request(struct ldb_context *ldb, struct ldb_request *req) return ret; } +int ldb_request_done(struct ldb_request *req, int status) +{ + req->handle->state = LDB_ASYNC_DONE; + req->handle->status = status; + return status; +} + /* search the database given a LDAP-like search expression @@ -621,28 +857,27 @@ int ldb_request(struct ldb_context *ldb, struct ldb_request *req) Use talloc_free to free the ldb_message returned in 'res', if successful */ -int ldb_search_default_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) +int ldb_search_default_callback(struct ldb_request *req, + struct ldb_reply *ares) { struct ldb_result *res; - int n; - - if (!context) { - ldb_set_errstring(ldb, "NULL Context in callback"); - return LDB_ERR_OPERATIONS_ERROR; - } + unsigned int n; - res = talloc_get_type(context, struct ldb_result); + res = talloc_get_type(req->context, struct ldb_result); - if (!res || !ares) { - ldb_set_errstring(ldb, "NULL res or ares in callback"); - goto error; + if (!ares) { + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); + } + if (ares->error != LDB_SUCCESS) { + return ldb_request_done(req, ares->error); } switch (ares->type) { case LDB_REPLY_ENTRY: - res->msgs = talloc_realloc(res, res->msgs, struct ldb_message *, res->count + 2); + res->msgs = talloc_realloc(res, res->msgs, + struct ldb_message *, res->count + 2); if (! res->msgs) { - goto error; + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); } res->msgs[res->count + 1] = NULL; @@ -650,6 +885,7 @@ int ldb_search_default_callback(struct ldb_context *ldb, void *context, struct l res->msgs[res->count] = talloc_move(res->msgs, &ares->message); res->count++; break; + case LDB_REPLY_REFERRAL: if (res->refs) { for (n = 0; res->refs[n]; n++) /*noop*/ ; @@ -659,36 +895,113 @@ int ldb_search_default_callback(struct ldb_context *ldb, void *context, struct l res->refs = talloc_realloc(res, res->refs, char *, n + 2); if (! res->refs) { - goto error; + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); } res->refs[n] = talloc_move(res->refs, &ares->referral); res->refs[n + 1] = NULL; break; - case LDB_REPLY_EXTENDED: + case LDB_REPLY_DONE: - /* TODO: we should really support controls on entries and referrals too! */ + /* TODO: we should really support controls on entries + * and referrals too! */ res->controls = talloc_move(res, &ares->controls); - break; + + /* this is the last message, and means the request is done */ + /* we have to signal and eventual ldb_wait() waiting that the + * async request operation was completed */ + talloc_free(ares); + return ldb_request_done(req, LDB_SUCCESS); } + talloc_free(ares); + return LDB_SUCCESS; +} + +int ldb_modify_default_callback(struct ldb_request *req, struct ldb_reply *ares) +{ + struct ldb_result *res; + unsigned int n; + int ret; + + res = talloc_get_type(req->context, struct ldb_result); + + if (!ares) { + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); + } + + if (ares->error != LDB_SUCCESS) { + ret = ares->error; + talloc_free(ares); + return ldb_request_done(req, ret); + } + + switch (ares->type) { + case LDB_REPLY_REFERRAL: + if (res->refs) { + for (n = 0; res->refs[n]; n++) /*noop*/ ; + } else { + n = 0; + } + + res->refs = talloc_realloc(res, res->refs, char *, n + 2); + if (! res->refs) { + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); + } + + res->refs[n] = talloc_move(res->refs, &ares->referral); + res->refs[n + 1] = NULL; + break; + + case LDB_REPLY_DONE: + talloc_free(ares); + return ldb_request_done(req, LDB_SUCCESS); + default: + talloc_free(ares); + ldb_set_errstring(req->handle->ldb, "Invalid reply type!"); + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); + } -error: talloc_free(ares); - return LDB_ERR_OPERATIONS_ERROR; + return ldb_request_done(req, LDB_SUCCESS); } -int ldb_build_search_req(struct ldb_request **ret_req, +int ldb_op_default_callback(struct ldb_request *req, struct ldb_reply *ares) +{ + int ret; + + if (!ares) { + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); + } + + if (ares->error != LDB_SUCCESS) { + ret = ares->error; + talloc_free(ares); + return ldb_request_done(req, ret); + } + + if (ares->type != LDB_REPLY_DONE) { + talloc_free(ares); + ldb_set_errstring(req->handle->ldb, "Invalid reply type!"); + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); + } + + talloc_free(ares); + return ldb_request_done(req, LDB_SUCCESS); +} + +int ldb_build_search_req_ex(struct ldb_request **ret_req, struct ldb_context *ldb, - void *mem_ctx, + TALLOC_CTX *mem_ctx, struct ldb_dn *base, enum ldb_scope scope, - const char *expression, + struct ldb_parse_tree *tree, const char * const *attrs, struct ldb_control **controls, void *context, - ldb_request_callback_t callback) + ldb_request_callback_t callback, + struct ldb_request *parent) { struct ldb_request *req; @@ -696,7 +1009,7 @@ int ldb_build_search_req(struct ldb_request **ret_req, req = talloc(mem_ctx, struct ldb_request); if (req == NULL) { - ldb_set_errstring(ldb, "Out of Memory"); + ldb_oom(ldb); return LDB_ERR_OPERATIONS_ERROR; } @@ -708,9 +1021,9 @@ int ldb_build_search_req(struct ldb_request **ret_req, } req->op.search.scope = scope; - req->op.search.tree = ldb_parse_tree(req, expression); + req->op.search.tree = tree; if (req->op.search.tree == NULL) { - ldb_set_errstring(ldb, "Unable to parse search expression"); + ldb_set_errstring(ldb, "'tree' can't be NULL"); talloc_free(req); return LDB_ERR_OPERATIONS_ERROR; } @@ -720,17 +1033,60 @@ int ldb_build_search_req(struct ldb_request **ret_req, req->context = context; req->callback = callback; + ldb_set_timeout_from_prev_req(ldb, parent, req); + + req->handle = ldb_handle_new(req, ldb); + if (req->handle == NULL) { + ldb_oom(ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (parent) { + req->handle->nesting++; + } + *ret_req = req; return LDB_SUCCESS; } +int ldb_build_search_req(struct ldb_request **ret_req, + struct ldb_context *ldb, + TALLOC_CTX *mem_ctx, + struct ldb_dn *base, + enum ldb_scope scope, + const char *expression, + const char * const *attrs, + struct ldb_control **controls, + void *context, + ldb_request_callback_t callback, + struct ldb_request *parent) +{ + struct ldb_parse_tree *tree; + int ret; + + tree = ldb_parse_tree(mem_ctx, expression); + if (tree == NULL) { + ldb_set_errstring(ldb, "Unable to parse search expression"); + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ldb_build_search_req_ex(ret_req, ldb, mem_ctx, base, + scope, tree, attrs, controls, + context, callback, parent); + if (ret == LDB_SUCCESS) { + talloc_steal(*ret_req, tree); + } + return ret; +} + int ldb_build_add_req(struct ldb_request **ret_req, struct ldb_context *ldb, - void *mem_ctx, + TALLOC_CTX *mem_ctx, const struct ldb_message *message, struct ldb_control **controls, void *context, - ldb_request_callback_t callback) + ldb_request_callback_t callback, + struct ldb_request *parent) { struct ldb_request *req; @@ -748,6 +1104,18 @@ int ldb_build_add_req(struct ldb_request **ret_req, req->context = context; req->callback = callback; + ldb_set_timeout_from_prev_req(ldb, parent, req); + + req->handle = ldb_handle_new(req, ldb); + if (req->handle == NULL) { + ldb_oom(ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (parent) { + req->handle->nesting++; + } + *ret_req = req; return LDB_SUCCESS; @@ -755,11 +1123,12 @@ int ldb_build_add_req(struct ldb_request **ret_req, int ldb_build_mod_req(struct ldb_request **ret_req, struct ldb_context *ldb, - void *mem_ctx, + TALLOC_CTX *mem_ctx, const struct ldb_message *message, struct ldb_control **controls, void *context, - ldb_request_callback_t callback) + ldb_request_callback_t callback, + struct ldb_request *parent) { struct ldb_request *req; @@ -777,6 +1146,18 @@ int ldb_build_mod_req(struct ldb_request **ret_req, req->context = context; req->callback = callback; + ldb_set_timeout_from_prev_req(ldb, parent, req); + + req->handle = ldb_handle_new(req, ldb); + if (req->handle == NULL) { + ldb_oom(ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (parent) { + req->handle->nesting++; + } + *ret_req = req; return LDB_SUCCESS; @@ -784,11 +1165,12 @@ int ldb_build_mod_req(struct ldb_request **ret_req, int ldb_build_del_req(struct ldb_request **ret_req, struct ldb_context *ldb, - void *mem_ctx, + TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct ldb_control **controls, void *context, - ldb_request_callback_t callback) + ldb_request_callback_t callback, + struct ldb_request *parent) { struct ldb_request *req; @@ -806,6 +1188,18 @@ int ldb_build_del_req(struct ldb_request **ret_req, req->context = context; req->callback = callback; + ldb_set_timeout_from_prev_req(ldb, parent, req); + + req->handle = ldb_handle_new(req, ldb); + if (req->handle == NULL) { + ldb_oom(ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (parent) { + req->handle->nesting++; + } + *ret_req = req; return LDB_SUCCESS; @@ -813,12 +1207,13 @@ int ldb_build_del_req(struct ldb_request **ret_req, int ldb_build_rename_req(struct ldb_request **ret_req, struct ldb_context *ldb, - void *mem_ctx, + TALLOC_CTX *mem_ctx, struct ldb_dn *olddn, struct ldb_dn *newdn, struct ldb_control **controls, void *context, - ldb_request_callback_t callback) + ldb_request_callback_t callback, + struct ldb_request *parent) { struct ldb_request *req; @@ -837,54 +1232,61 @@ int ldb_build_rename_req(struct ldb_request **ret_req, req->context = context; req->callback = callback; + ldb_set_timeout_from_prev_req(ldb, parent, req); + + req->handle = ldb_handle_new(req, ldb); + if (req->handle == NULL) { + ldb_oom(ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (parent) { + req->handle->nesting++; + } + *ret_req = req; return LDB_SUCCESS; } -int ldb_extended_default_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) +int ldb_extended_default_callback(struct ldb_request *req, + struct ldb_reply *ares) { struct ldb_result *res; - - if (!context) { - ldb_set_errstring(ldb, "NULL Context in callback"); - return LDB_ERR_OPERATIONS_ERROR; - } - res = talloc_get_type(context, struct ldb_result); - if (!res || !ares) { - ldb_set_errstring(ldb, "NULL res or ares in callback"); - goto error; + res = talloc_get_type(req->context, struct ldb_result); + + if (!ares) { + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); + } + if (ares->error != LDB_SUCCESS) { + return ldb_request_done(req, ares->error); } - switch (ares->type) { - case LDB_REPLY_ENTRY: - case LDB_REPLY_REFERRAL: - case LDB_REPLY_DONE: - ldb_set_errstring(ldb, "invalid ares type in callback"); - goto error; - case LDB_REPLY_EXTENDED: + if (ares->type == LDB_REPLY_DONE) { + /* TODO: we should really support controls on entries and referrals too! */ res->extended = talloc_move(res, &ares->response); res->controls = talloc_move(res, &ares->controls); - break; + + talloc_free(ares); + return ldb_request_done(req, LDB_SUCCESS); } - talloc_free(ares); - return LDB_SUCCESS; -error: talloc_free(ares); - return LDB_ERR_OPERATIONS_ERROR; + ldb_set_errstring(req->handle->ldb, "Invalid reply type!"); + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); } int ldb_build_extended_req(struct ldb_request **ret_req, struct ldb_context *ldb, - void *mem_ctx, + TALLOC_CTX *mem_ctx, const char *oid, void *data, struct ldb_control **controls, void *context, - ldb_request_callback_t callback) + ldb_request_callback_t callback, + struct ldb_request *parent) { struct ldb_request *req; @@ -903,12 +1305,24 @@ int ldb_build_extended_req(struct ldb_request **ret_req, req->context = context; req->callback = callback; + ldb_set_timeout_from_prev_req(ldb, parent, req); + + req->handle = ldb_handle_new(req, ldb); + if (req->handle == NULL) { + ldb_oom(ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (parent) { + req->handle->nesting++; + } + *ret_req = req; return LDB_SUCCESS; } -int ldb_extended(struct ldb_context *ldb, +int ldb_extended(struct ldb_context *ldb, const char *oid, void *data, struct ldb_result **_res) @@ -926,13 +1340,14 @@ int ldb_extended(struct ldb_context *ldb, ret = ldb_build_extended_req(&req, ldb, ldb, oid, data, NULL, - res, ldb_extended_default_callback); + res, ldb_extended_default_callback, + NULL); if (ret != LDB_SUCCESS) goto done; ldb_set_timeout(ldb, req, 0); /* use default timeout */ ret = ldb_request(ldb, req); - + if (ret == LDB_SUCCESS) { ret = ldb_wait(req->handle, LDB_WAIT_ALL); } @@ -949,99 +1364,76 @@ done: } /* - note that ldb_search() will automatically replace a NULL 'base' value with the - defaultNamingContext from the rootDSE if available. + note that ldb_search() will automatically replace a NULL 'base' value + with the defaultNamingContext from the rootDSE if available. */ -int ldb_search(struct ldb_context *ldb, - struct ldb_dn *base, - enum ldb_scope scope, - const char *expression, - const char * const *attrs, - struct ldb_result **_res) +int ldb_search(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, + struct ldb_result **result, struct ldb_dn *base, + enum ldb_scope scope, const char * const *attrs, + const char *exp_fmt, ...) { struct ldb_request *req; - int ret; struct ldb_result *res; + char *expression; + va_list ap; + int ret; - *_res = NULL; + expression = NULL; + *result = NULL; + req = NULL; - res = talloc_zero(ldb, struct ldb_result); + res = talloc_zero(mem_ctx, struct ldb_result); if (!res) { return LDB_ERR_OPERATIONS_ERROR; } - ret = ldb_build_search_req(&req, ldb, ldb, + if (exp_fmt) { + va_start(ap, exp_fmt); + expression = talloc_vasprintf(mem_ctx, exp_fmt, ap); + va_end(ap); + + if (!expression) { + talloc_free(res); + return LDB_ERR_OPERATIONS_ERROR; + } + } + + ret = ldb_build_search_req(&req, ldb, mem_ctx, base?base:ldb_get_default_basedn(ldb), scope, expression, attrs, NULL, res, - ldb_search_default_callback); + ldb_search_default_callback, + NULL); if (ret != LDB_SUCCESS) goto done; - ldb_set_timeout(ldb, req, 0); /* use default timeout */ - ret = ldb_request(ldb, req); - + if (ret == LDB_SUCCESS) { ret = ldb_wait(req->handle, LDB_WAIT_ALL); } - talloc_free(req); - done: if (ret != LDB_SUCCESS) { talloc_free(res); - } - - *_res = res; - return ret; -} - -/* - a useful search function where you can easily define the expression and that - takes a memory context where results are allocated -*/ - -int ldb_search_exp_fmt(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_result **result, - struct ldb_dn *base, enum ldb_scope scope, const char * const *attrs, - const char *exp_fmt, ...) -{ - struct ldb_result *res; - char *expression; - va_list ap; - int ret; - - res = NULL; - *result = NULL; - - va_start(ap, exp_fmt); - expression = talloc_vasprintf(mem_ctx, exp_fmt, ap); - va_end(ap); - - if ( ! expression) { - return LDB_ERR_OPERATIONS_ERROR; - } - - ret = ldb_search(ldb, base, scope, expression, attrs, &res); - - if (ret == LDB_SUCCESS) { - talloc_steal(mem_ctx, res); - *result = res; + res = NULL; } talloc_free(expression); + talloc_free(req); + *result = res; return ret; } /* - add a record to the database. Will fail if a record with the given class and key - already exists + add a record to the database. Will fail if a record with the given class + and key already exists */ -int ldb_add(struct ldb_context *ldb, +int ldb_add(struct ldb_context *ldb, const struct ldb_message *message) { struct ldb_request *req; @@ -1056,12 +1448,11 @@ int ldb_add(struct ldb_context *ldb, message, NULL, NULL, + ldb_op_default_callback, NULL); if (ret != LDB_SUCCESS) return ret; - ldb_set_timeout(ldb, req, 0); /* use default timeout */ - /* do request and autostart a transaction */ ret = ldb_autotransaction_request(ldb, req); @@ -1072,7 +1463,7 @@ int ldb_add(struct ldb_context *ldb, /* modify the specified attributes of a record */ -int ldb_modify(struct ldb_context *ldb, +int ldb_modify(struct ldb_context *ldb, const struct ldb_message *message) { struct ldb_request *req; @@ -1087,12 +1478,11 @@ int ldb_modify(struct ldb_context *ldb, message, NULL, NULL, + ldb_op_default_callback, NULL); if (ret != LDB_SUCCESS) return ret; - ldb_set_timeout(ldb, req, 0); /* use default timeout */ - /* do request and autostart a transaction */ ret = ldb_autotransaction_request(ldb, req); @@ -1113,12 +1503,11 @@ int ldb_delete(struct ldb_context *ldb, struct ldb_dn *dn) dn, NULL, NULL, + ldb_op_default_callback, NULL); if (ret != LDB_SUCCESS) return ret; - ldb_set_timeout(ldb, req, 0); /* use default timeout */ - /* do request and autostart a transaction */ ret = ldb_autotransaction_request(ldb, req); @@ -1129,7 +1518,8 @@ int ldb_delete(struct ldb_context *ldb, struct ldb_dn *dn) /* rename a record in the database */ -int ldb_rename(struct ldb_context *ldb, struct ldb_dn *olddn, struct ldb_dn *newdn) +int ldb_rename(struct ldb_context *ldb, + struct ldb_dn *olddn, struct ldb_dn *newdn) { struct ldb_request *req; int ret; @@ -1139,12 +1529,11 @@ int ldb_rename(struct ldb_context *ldb, struct ldb_dn *olddn, struct ldb_dn *new newdn, NULL, NULL, + ldb_op_default_callback, NULL); if (ret != LDB_SUCCESS) return ret; - ldb_set_timeout(ldb, req, 0); /* use default timeout */ - /* do request and autostart a transaction */ ret = ldb_autotransaction_request(ldb, req); @@ -1156,39 +1545,52 @@ int ldb_rename(struct ldb_context *ldb, struct ldb_dn *olddn, struct ldb_dn *new /* return the global sequence number */ -int ldb_sequence_number(struct ldb_context *ldb, enum ldb_sequence_type type, uint64_t *seq_num) +int ldb_sequence_number(struct ldb_context *ldb, + enum ldb_sequence_type type, uint64_t *seq_num) { - struct ldb_request *req; + struct ldb_seqnum_request *seq; + struct ldb_seqnum_result *seqr; + struct ldb_result *res; + TALLOC_CTX *tmp_ctx; int ret; - req = talloc(ldb, struct ldb_request); - if (req == NULL) { + *seq_num = 0; + + tmp_ctx = talloc_zero(ldb, struct ldb_request); + if (tmp_ctx == NULL) { ldb_set_errstring(ldb, "Out of Memory"); return LDB_ERR_OPERATIONS_ERROR; } + seq = talloc_zero(tmp_ctx, struct ldb_seqnum_request); + if (seq == NULL) { + ldb_set_errstring(ldb, "Out of Memory"); + ret = LDB_ERR_OPERATIONS_ERROR; + goto done; + } + seq->type = type; - req->operation = LDB_SEQUENCE_NUMBER; - req->controls = NULL; - req->context = NULL; - req->callback = NULL; - ldb_set_timeout(ldb, req, 0); /* use default timeout */ + ret = ldb_extended(ldb, LDB_EXTENDED_SEQUENCE_NUMBER, seq, &res); + if (ret != LDB_SUCCESS) { + goto done; + } + talloc_steal(tmp_ctx, res); - req->op.seq_num.type = type; - /* do request and autostart a transaction */ - ret = ldb_request(ldb, req); - - if (ret == LDB_SUCCESS) { - *seq_num = req->op.seq_num.seq_num; + if (strcmp(LDB_EXTENDED_SEQUENCE_NUMBER, res->extended->oid) != 0) { + ldb_set_errstring(ldb, "Invalid OID in reply"); + ret = LDB_ERR_OPERATIONS_ERROR; + goto done; } + seqr = talloc_get_type(res->extended->data, + struct ldb_seqnum_result); + *seq_num = seqr->seq_num; - talloc_free(req); +done: + talloc_free(tmp_ctx); return ret; } - - /* - return extended error information + return extended error information */ const char *ldb_errstring(struct ldb_context *ldb) { @@ -1335,3 +1737,21 @@ void *ldb_get_opaque(struct ldb_context *ldb, const char *name) } return NULL; } + +int ldb_global_init(void) +{ + /* Provided for compatibility with some older versions of ldb */ + return 0; +} + +/* return the ldb flags */ +unsigned int ldb_get_flags(struct ldb_context *ldb) +{ + return ldb->flags; +} + +/* set the ldb flags */ +void ldb_set_flags(struct ldb_context *ldb, unsigned flags) +{ + ldb->flags = flags; +}