if (ldb->transaction_active) {
ldb_debug(ldb, LDB_DEBUG_FATAL,
- "A transaction is still active in ldb context [%p]",
- ldb);
+ "A transaction is still active in ldb context [%p] on %s",
+ ldb, (const char *)ldb_get_opaque(ldb, "ldb_url"));
}
return 0;
}
ret = ldb_setup_wellknown_attributes(ldb);
- if (ret != 0) {
+ if (ret != LDB_SUCCESS) {
talloc_free(ldb);
return NULL;
}
unsigned int flags, const char *options[])
{
int ret;
- const char *url2;
+ char *url2;
/* We seem to need to do this here, or else some utilities don't
* get ldb backends */
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;
}
if (ldb_load_modules(ldb, options) != LDB_SUCCESS) {
ldb_debug(ldb, LDB_DEBUG_FATAL,
- "Unable to load modules for %s: %s\n",
+ "Unable to load modules for %s: %s",
url, ldb_errstring(ldb));
return LDB_ERR_OTHER;
}
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, ...)
}
}
-#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
*/
/* start a new transaction */
ldb->transaction_active++;
+ ldb->prepare_commit_done = false;
FIRST_OP(ldb, start_transaction);
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;
+}
+
+/*
+ prepare for transaction commit (first phase of two phase commit)
+*/
+int ldb_transaction_prepare_commit(struct ldb_context *ldb)
+{
+ struct ldb_module *module;
+ int status;
+
+ if (ldb->prepare_commit_done) {
+ return LDB_SUCCESS;
+ }
+
+ /* 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 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;
}
+
/*
commit a transaction
*/
struct ldb_module *module;
int status;
+ status = ldb_transaction_prepare_commit(ldb);
+ if (status != LDB_SUCCESS) {
+ return status;
+ }
+
ldb->transaction_active--;
ldb_debug(ldb, LDB_DEBUG_TRACE,
return LDB_ERR_OPERATIONS_ERROR;
}
- FIRST_OP(ldb, end_transaction);
-
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) {
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;
}
+
/*
cancel a transaction
*/
if (ldb->transaction_active < 0) {
ldb_debug(ldb, LDB_DEBUG_FATAL,
- "commit called but no ldb transactions are active!");
+ "cancel called but no ldb transactions are active!");
ldb->transaction_active = 0;
return LDB_ERR_OPERATIONS_ERROR;
}
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;
}
+/*
+ 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)
+{
+ 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)
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)?"<rootDSE>":
+ 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: <ALL>\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: <NONE>\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.
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:
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 */
+ ret = ldb_msg_canonicalize_ex(ldb, req->op.add.message,
+ (TALLOC_CTX*)req,
+ discard_const(&req->op.add.message));
+ if (ret != LDB_SUCCESS) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
FIRST_OP(ldb, add);
ret = module->ops->add(module, req);
break;
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;
struct ldb_reply *ares)
{
struct ldb_result *res;
- int n;
+ unsigned int n;
res = talloc_get_type(req->context, struct ldb_result);
/* 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);
+ }
+
+ talloc_free(ares);
+ return ldb_request_done(req, LDB_SUCCESS);
+}
+
int ldb_op_default_callback(struct ldb_request *req, struct ldb_reply *ares)
{
int ret;
return LDB_ERR_OPERATIONS_ERROR;
}
+ if (parent) {
+ req->handle->nesting++;
+ }
+
*ret_req = req;
return LDB_SUCCESS;
}
return LDB_ERR_OPERATIONS_ERROR;
}
+ if (parent) {
+ req->handle->nesting++;
+ }
+
*ret_req = req;
return LDB_SUCCESS;
return LDB_ERR_OPERATIONS_ERROR;
}
+ if (parent) {
+ req->handle->nesting++;
+ }
+
*ret_req = req;
return LDB_SUCCESS;
return LDB_ERR_OPERATIONS_ERROR;
}
+ if (parent) {
+ req->handle->nesting++;
+ }
+
*ret_req = req;
return LDB_SUCCESS;
return LDB_ERR_OPERATIONS_ERROR;
}
+ if (parent) {
+ req->handle->nesting++;
+ }
+
*ret_req = req;
return LDB_SUCCESS;
return LDB_ERR_OPERATIONS_ERROR;
}
+ if (parent) {
+ req->handle->nesting++;
+ }
+
*ret_req = req;
return LDB_SUCCESS;
}
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;
+}