diff options
Diffstat (limited to 'lib/ldb/tools')
-rw-r--r-- | lib/ldb/tools/cmdline.c | 766 | ||||
-rw-r--r-- | lib/ldb/tools/cmdline.h | 62 | ||||
-rw-r--r-- | lib/ldb/tools/ldbadd.c | 186 | ||||
-rw-r--r-- | lib/ldb/tools/ldbdel.c | 135 | ||||
-rw-r--r-- | lib/ldb/tools/ldbdump.c | 383 | ||||
-rw-r--r-- | lib/ldb/tools/ldbedit.c | 383 | ||||
-rw-r--r-- | lib/ldb/tools/ldbmodify.c | 187 | ||||
-rw-r--r-- | lib/ldb/tools/ldbrename.c | 85 | ||||
-rw-r--r-- | lib/ldb/tools/ldbsearch.c | 342 | ||||
-rw-r--r-- | lib/ldb/tools/ldbtest.c | 438 | ||||
-rw-r--r-- | lib/ldb/tools/ldbutil.c | 220 | ||||
-rw-r--r-- | lib/ldb/tools/ldbutil.h | 46 |
12 files changed, 3233 insertions, 0 deletions
diff --git a/lib/ldb/tools/cmdline.c b/lib/ldb/tools/cmdline.c new file mode 100644 index 0000000..ff25fe0 --- /dev/null +++ b/lib/ldb/tools/cmdline.c @@ -0,0 +1,766 @@ +/* + ldb database library - command line handling for ldb tools + + Copyright (C) Andrew Tridgell 2005 + + ** 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 + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "system/time.h" +#include "ldb.h" +#include "ldb_module.h" +#include "tools/cmdline.h" + +static struct ldb_cmdline options; /* needs to be static for older compilers */ + +enum ldb_cmdline_options { CMDLINE_RELAX=1 }; + +static struct poptOption builtin_popt_options[] = { + POPT_AUTOHELP + { + .longName = "url", + .shortName = 'H', + .argInfo = POPT_ARG_STRING, + .arg = &options.url, + .val = 0, + .descrip = "database URL", + .argDescrip = "URL" + }, + { + .longName = "basedn", + .shortName = 'b', + .argInfo = POPT_ARG_STRING, + .arg = &options.basedn, + .val = 0, + .descrip = "base DN", + .argDescrip = "DN" + }, + { + .longName = "editor", + .shortName = 'e', + .argInfo = POPT_ARG_STRING, + .arg = &options.editor, + .val = 0, + .descrip = "external editor", + .argDescrip = "PROGRAM" + }, + { + .longName = "scope", + .shortName = 's', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 's', + .descrip = "search scope", + .argDescrip = "SCOPE" + }, + { + .longName = "verbose", + .shortName = 'v', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'v', + .descrip = "increase verbosity", + .argDescrip = NULL + }, + { + .longName = "trace", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &options.tracing, + .val = 0, + .descrip = "enable tracing", + .argDescrip = NULL + }, + { + .longName = "interactive", + .shortName = 'i', + .argInfo = POPT_ARG_NONE, + .arg = &options.interactive, + .val = 0, + .descrip = "input from stdin", + .argDescrip = NULL + }, + { + .longName = "recursive", + .shortName = 'r', + .argInfo = POPT_ARG_NONE, + .arg = &options.recursive, + .val = 0, + .descrip = "recursive delete", + .argDescrip = NULL + }, + { + .longName = "modules-path", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &options.modules_path, + .val = 0, + .descrip = "modules path", + .argDescrip = "PATH" + }, + { + .longName = "num-searches", + .shortName = 0, + .argInfo = POPT_ARG_INT, + .arg = &options.num_searches, + .val = 0, + .descrip = "number of test searches", + .argDescrip = NULL + }, + { + .longName = "num-records", + .shortName = 0, + .argInfo = POPT_ARG_INT, + .arg = &options.num_records, + .val = 0, + .descrip = "number of test records", + .argDescrip = NULL + }, + { + .longName = "all", + .shortName = 'a', + .argInfo = POPT_ARG_NONE, + .arg = &options.all_records, + .val = 0, + .descrip = "(|(objectClass=*)(distinguishedName=*))", + .argDescrip = NULL + }, + { + .longName = "nosync", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &options.nosync, + .val = 0, + .descrip = "non-synchronous transactions", + .argDescrip = NULL + }, + { + .longName = "sorted", + .shortName = 'S', + .argInfo = POPT_ARG_NONE, + .arg = &options.sorted, + .val = 0, + .descrip = "sort attributes", + .argDescrip = NULL + }, + { + .longName = NULL, + .shortName = 'o', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'o', + .descrip = "ldb_connect option", + .argDescrip = "OPTION" + }, + { + .longName = "controls", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'c', + .descrip = "controls", + .argDescrip = NULL + }, + { + .longName = "show-binary", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &options.show_binary, + .val = 0, + .descrip = "display binary LDIF", + .argDescrip = NULL + }, + { + .longName = "paged", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'P', + .descrip = "use a paged search", + .argDescrip = NULL + }, + { + .longName = "show-deleted", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'D', + .descrip = "show deleted objects", + .argDescrip = NULL + }, + { + .longName = "show-recycled", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'R', + .descrip = "show recycled objects", + .argDescrip = NULL + }, + { + .longName = "show-deactivated-link", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'd', + .descrip = "show deactivated links", + .argDescrip = NULL + }, + { + .longName = "reveal", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'r', + .descrip = "reveal ldb internals", + .argDescrip = NULL + }, + { + .longName = "relax", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = CMDLINE_RELAX, + .descrip = "pass relax control", + .argDescrip = NULL + }, + { + .longName = "cross-ncs", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'N', + .descrip = "search across NC boundaries", + .argDescrip = NULL + }, + { + .longName = "extended-dn", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'E', + .descrip = "show extended DNs", + .argDescrip = NULL + }, + POPT_TABLEEND +}; + +void ldb_cmdline_help(struct ldb_context *ldb, const char *cmdname, FILE *f) +{ + poptContext pc; + struct poptOption **popt_options = ldb_module_popt_options(ldb); + pc = poptGetContext(cmdname, 0, NULL, *popt_options, + POPT_CONTEXT_KEEP_FIRST); + poptPrintHelp(pc, f, 0); +} + +/* + add a control to the options structure + */ +static bool add_control(TALLOC_CTX *mem_ctx, const char *control) +{ + unsigned int i; + + /* count how many controls we already have */ + for (i=0; options.controls && options.controls[i]; i++) ; + + options.controls = talloc_realloc(mem_ctx, options.controls, const char *, i + 2); + if (options.controls == NULL) { + return false; + } + options.controls[i] = control; + options.controls[i+1] = NULL; + return true; +} + +/** + process command line options +*/ +static struct ldb_cmdline *ldb_cmdline_process_internal(struct ldb_context *ldb, + int argc, const char **argv, + void (*usage)(struct ldb_context *), + bool dont_create, + bool search) +{ + struct ldb_cmdline *ret=NULL; + poptContext pc; + int num_options = 0; + int opt; + unsigned int flags = 0; + int rc; + struct poptOption **popt_options; + + /* make the ldb utilities line buffered */ + setlinebuf(stdout); + + ret = talloc_zero(ldb, struct ldb_cmdline); + if (ret == NULL) { + fprintf(stderr, "Out of memory!\n"); + goto failed; + } + + options = *ret; + + /* pull in URL */ + options.url = getenv("LDB_URL"); + + /* and editor (used by ldbedit) */ + options.editor = getenv("VISUAL"); + if (!options.editor) { + options.editor = getenv("EDITOR"); + } + if (!options.editor) { + options.editor = "vi"; + } + + options.scope = LDB_SCOPE_DEFAULT; + + popt_options = ldb_module_popt_options(ldb); + (*popt_options) = builtin_popt_options; + + rc = ldb_modules_hook(ldb, LDB_MODULE_HOOK_CMDLINE_OPTIONS); + if (rc != LDB_SUCCESS) { + fprintf(stderr, "ldb: failed to run command line hooks : %s\n", ldb_strerror(rc)); + goto failed; + } + + pc = poptGetContext(argv[0], argc, argv, *popt_options, + POPT_CONTEXT_KEEP_FIRST); + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 's': { + const char *arg = poptGetOptArg(pc); + if (strcmp(arg, "base") == 0) { + options.scope = LDB_SCOPE_BASE; + } else if (strcmp(arg, "sub") == 0) { + options.scope = LDB_SCOPE_SUBTREE; + } else if (strcmp(arg, "one") == 0) { + options.scope = LDB_SCOPE_ONELEVEL; + } else { + fprintf(stderr, "Invalid scope '%s'\n", arg); + goto failed; + } + break; + } + + case 'v': + options.verbose++; + break; + + case 'o': + options.options = talloc_realloc(ret, options.options, + const char *, num_options+3); + if (options.options == NULL) { + fprintf(stderr, "Out of memory!\n"); + goto failed; + } + options.options[num_options] = poptGetOptArg(pc); + options.options[num_options+1] = NULL; + num_options++; + break; + + case 'c': { + const char *cs = poptGetOptArg(pc); + const char *p; + + for (p = cs; p != NULL; ) { + const char *t, *c; + + t = strchr(p, ','); + if (t == NULL) { + c = talloc_strdup(options.controls, p); + p = NULL; + } else { + c = talloc_strndup(options.controls, p, t-p); + p = t + 1; + } + if (c == NULL || !add_control(ret, c)) { + fprintf(stderr, __location__ ": out of memory\n"); + goto failed; + } + } + + break; + } + case 'P': + if (!add_control(ret, "paged_results:1:1024")) { + fprintf(stderr, __location__ ": out of memory\n"); + goto failed; + } + break; + case 'D': + if (!add_control(ret, "show_deleted:1")) { + fprintf(stderr, __location__ ": out of memory\n"); + goto failed; + } + break; + case 'R': + if (!add_control(ret, "show_recycled:0")) { + fprintf(stderr, __location__ ": out of memory\n"); + goto failed; + } + break; + case 'd': + if (!add_control(ret, "show_deactivated_link:0")) { + fprintf(stderr, __location__ ": out of memory\n"); + goto failed; + } + break; + case 'r': + if (!add_control(ret, "reveal_internals:0")) { + fprintf(stderr, __location__ ": out of memory\n"); + goto failed; + } + break; + case CMDLINE_RELAX: + if (!add_control(ret, "relax:0")) { + fprintf(stderr, __location__ ": out of memory\n"); + goto failed; + } + break; + case 'N': + if (!add_control(ret, "search_options:1:2")) { + fprintf(stderr, __location__ ": out of memory\n"); + goto failed; + } + break; + case 'E': + if (!add_control(ret, "extended_dn:1:1")) { + fprintf(stderr, __location__ ": out of memory\n"); + goto failed; + } + break; + default: + fprintf(stderr, "Invalid option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + if (usage) usage(ldb); + goto failed; + } + } + + /* setup the remaining options for the main program to use */ + options.argv = poptGetArgs(pc); + if (options.argv) { + options.argv++; + while (options.argv[options.argc]) options.argc++; + } + + *ret = options; + + /* all utils need some option */ + if (ret->url == NULL) { + fprintf(stderr, "You must supply a url with -H or with $LDB_URL\n"); + if (usage) usage(ldb); + goto failed; + } + + if (strcmp(ret->url, "NONE") == 0) { + return ret; + } + + if (options.nosync) { + flags |= LDB_FLG_NOSYNC; + } + + if (search) { + flags |= LDB_FLG_DONT_CREATE_DB; + + if (options.show_binary) { + flags |= LDB_FLG_SHOW_BINARY; + } + } + + if (options.tracing) { + flags |= LDB_FLG_ENABLE_TRACING; + } + + if (options.modules_path != NULL) { + ldb_set_modules_dir(ldb, options.modules_path); + } + + rc = ldb_modules_hook(ldb, LDB_MODULE_HOOK_CMDLINE_PRECONNECT); + if (rc != LDB_SUCCESS) { + fprintf(stderr, "ldb: failed to run preconnect hooks : %s\n", ldb_strerror(rc)); + goto failed; + } + + /* now connect to the ldb */ + if (ldb_connect(ldb, ret->url, flags, ret->options) != LDB_SUCCESS) { + fprintf(stderr, "Failed to connect to %s - %s\n", + ret->url, ldb_errstring(ldb)); + goto failed; + } + + rc = ldb_modules_hook(ldb, LDB_MODULE_HOOK_CMDLINE_POSTCONNECT); + if (rc != LDB_SUCCESS) { + fprintf(stderr, "ldb: failed to run post connect hooks : %s\n", ldb_strerror(rc)); + goto failed; + } + + return ret; + +failed: + talloc_free(ret); + exit(LDB_ERR_OPERATIONS_ERROR); + return NULL; +} + +struct ldb_cmdline *ldb_cmdline_process_search(struct ldb_context *ldb, + int argc, const char **argv, + void (*usage)(struct ldb_context *)) +{ + return ldb_cmdline_process_internal(ldb, argc, argv, usage, true, true); +} + +struct ldb_cmdline *ldb_cmdline_process_edit(struct ldb_context *ldb, + int argc, const char **argv, + void (*usage)(struct ldb_context *)) +{ + return ldb_cmdline_process_internal(ldb, argc, argv, usage, false, true); +} + +struct ldb_cmdline *ldb_cmdline_process(struct ldb_context *ldb, + int argc, const char **argv, + void (*usage)(struct ldb_context *)) +{ + return ldb_cmdline_process_internal(ldb, argc, argv, usage, false, false); +} + +/* this function check controls reply and determines if more + * processing is needed setting up the request controls correctly + * + * returns: + * -1 error + * 0 all ok + * 1 all ok, more processing required + */ +int handle_controls_reply(struct ldb_control **reply, struct ldb_control **request) +{ + unsigned int i, j; + int ret = 0; + + if (reply == NULL || request == NULL) return -1; + + for (i = 0; reply[i]; i++) { + if (strcmp(LDB_CONTROL_VLV_RESP_OID, reply[i]->oid) == 0) { + struct ldb_vlv_resp_control *rep_control; + + rep_control = talloc_get_type(reply[i]->data, struct ldb_vlv_resp_control); + if (rep_control == NULL) { + fprintf(stderr, + "Warning VLV reply OID received " + "with no VLV data\n"); + continue; + } + + /* check we have a matching control in the request */ + for (j = 0; request[j]; j++) { + if (strcmp(LDB_CONTROL_VLV_REQ_OID, request[j]->oid) == 0) + break; + } + if (! request[j]) { + fprintf(stderr, "Warning VLV reply received but no request have been made\n"); + continue; + } + + /* check the result */ + if (rep_control->vlv_result != 0) { + fprintf(stderr, "Warning: VLV not performed with error: %d\n", rep_control->vlv_result); + } else { + fprintf(stderr, "VLV Info: target position = %d, content count = %d\n", rep_control->targetPosition, rep_control->contentCount); + } + + continue; + } + + if (strcmp(LDB_CONTROL_ASQ_OID, reply[i]->oid) == 0) { + struct ldb_asq_control *rep_control; + + rep_control = talloc_get_type(reply[i]->data, struct ldb_asq_control); + if (rep_control == NULL) { + fprintf(stderr, + "Warning ASQ reply OID received " + "with no ASQ data\n"); + continue; + } + + /* check the result */ + if (rep_control->result != 0) { + fprintf(stderr, "Warning: ASQ not performed with error: %d\n", rep_control->result); + } + + continue; + } + + if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, reply[i]->oid) == 0) { + struct ldb_paged_control *rep_control, *req_control; + + rep_control = talloc_get_type(reply[i]->data, struct ldb_paged_control); + if (rep_control == NULL) { + fprintf(stderr, + "Warning PAGED_RESULTS reply OID " + "received with no data\n"); + continue; + } + + if (rep_control->cookie_len == 0) { /* we are done */ + break; + } + + /* more processing required */ + /* let's fill in the request control with the new cookie */ + + for (j = 0; request[j]; j++) { + if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, request[j]->oid) == 0) + break; + } + /* if there's a reply control we must find a request + * control matching it */ + if (! request[j]) return -1; + + req_control = talloc_get_type(request[j]->data, struct ldb_paged_control); + + if (req_control->cookie) + talloc_free(req_control->cookie); + req_control->cookie = (char *)talloc_memdup( + req_control, rep_control->cookie, + rep_control->cookie_len); + req_control->cookie_len = rep_control->cookie_len; + + ret = 1; + + continue; + } + + if (strcmp(LDB_CONTROL_SORT_RESP_OID, reply[i]->oid) == 0) { + struct ldb_sort_resp_control *rep_control; + + rep_control = talloc_get_type(reply[i]->data, struct ldb_sort_resp_control); + if (rep_control == NULL) { + fprintf(stderr, + "Warning SORT reply OID " + "received with no data\n"); + continue; + } + + /* check we have a matching control in the request */ + for (j = 0; request[j]; j++) { + if (strcmp(LDB_CONTROL_SERVER_SORT_OID, request[j]->oid) == 0) + break; + } + if (! request[j]) { + fprintf(stderr, "Warning Server Sort reply received but no request found\n"); + continue; + } + + /* check the result */ + if (rep_control->result != 0) { + fprintf(stderr, "Warning: Sorting not performed with error: %d\n", rep_control->result); + } + + continue; + } + + if (strcmp(LDB_CONTROL_DIRSYNC_OID, reply[i]->oid) == 0) { + struct ldb_dirsync_control *rep_control, *req_control; + char *cookie; + + rep_control = talloc_get_type(reply[i]->data, struct ldb_dirsync_control); + if (rep_control == NULL) { + fprintf(stderr, + "Warning DIRSYNC reply OID " + "received with no data\n"); + continue; + } + if (rep_control->cookie_len == 0) /* we are done */ + break; + + /* more processing required */ + /* let's fill in the request control with the new cookie */ + + for (j = 0; request[j]; j++) { + if (strcmp(LDB_CONTROL_DIRSYNC_OID, request[j]->oid) == 0) + break; + } + /* if there's a reply control we must find a request + * control matching it */ + if (! request[j]) return -1; + + req_control = talloc_get_type(request[j]->data, struct ldb_dirsync_control); + + if (req_control->cookie) + talloc_free(req_control->cookie); + req_control->cookie = (char *)talloc_memdup( + req_control, rep_control->cookie, + rep_control->cookie_len); + req_control->cookie_len = rep_control->cookie_len; + + cookie = ldb_base64_encode(req_control, rep_control->cookie, rep_control->cookie_len); + printf("# DIRSYNC cookie returned was:\n# %s\n", cookie); + + continue; + } + if (strcmp(LDB_CONTROL_DIRSYNC_EX_OID, reply[i]->oid) == 0) { + struct ldb_dirsync_control *rep_control, *req_control; + char *cookie; + + rep_control = talloc_get_type(reply[i]->data, struct ldb_dirsync_control); + if (rep_control == NULL) { + fprintf(stderr, + "Warning DIRSYNC_EX reply OID " + "received with no data\n"); + continue; + } + if (rep_control->cookie_len == 0) /* we are done */ + break; + + /* more processing required */ + /* let's fill in the request control with the new cookie */ + + for (j = 0; request[j]; j++) { + if (strcmp(LDB_CONTROL_DIRSYNC_EX_OID, request[j]->oid) == 0) + break; + } + /* if there's a reply control we must find a request + * control matching it */ + if (! request[j]) return -1; + + req_control = talloc_get_type(request[j]->data, struct ldb_dirsync_control); + + if (req_control->cookie) + talloc_free(req_control->cookie); + req_control->cookie = (char *)talloc_memdup( + req_control, rep_control->cookie, + rep_control->cookie_len); + req_control->cookie_len = rep_control->cookie_len; + + cookie = ldb_base64_encode(req_control, rep_control->cookie, rep_control->cookie_len); + printf("# DIRSYNC_EX cookie returned was:\n# %s\n", cookie); + + continue; + } + + /* no controls matched, throw a warning */ + fprintf(stderr, "Unknown reply control oid: %s\n", reply[i]->oid); + } + + return ret; +} diff --git a/lib/ldb/tools/cmdline.h b/lib/ldb/tools/cmdline.h new file mode 100644 index 0000000..dbc216a --- /dev/null +++ b/lib/ldb/tools/cmdline.h @@ -0,0 +1,62 @@ +/* + ldb database library - command line handling for ldb tools + + Copyright (C) Andrew Tridgell 2005 + + ** 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 + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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, see <http://www.gnu.org/licenses/>. +*/ + +#include <popt.h> + +struct ldb_cmdline { + const char *url; + enum ldb_scope scope; + const char *basedn; + const char *modules_path; + int interactive; + int sorted; + const char *editor; + int verbose; + int recursive; + int all_records; + int nosync; + const char **options; + int argc; + const char **argv; + int num_records; + int num_searches; + const char *sasl_mechanism; + const char **controls; + int show_binary; + int tracing; +}; + +struct ldb_cmdline *ldb_cmdline_process_search(struct ldb_context *ldb, + int argc, const char **argv, + void (*usage)(struct ldb_context *)); +struct ldb_cmdline *ldb_cmdline_process_edit(struct ldb_context *ldb, + int argc, const char **argv, + void (*usage)(struct ldb_context *)); +struct ldb_cmdline *ldb_cmdline_process(struct ldb_context *ldb, int argc, + const char **argv, + void (*usage)(struct ldb_context *)); + + +int handle_controls_reply(struct ldb_control **reply, struct ldb_control **request); +void ldb_cmdline_help(struct ldb_context *ldb, const char *cmdname, FILE *f); + diff --git a/lib/ldb/tools/ldbadd.c b/lib/ldb/tools/ldbadd.c new file mode 100644 index 0000000..e6cea29 --- /dev/null +++ b/lib/ldb/tools/ldbadd.c @@ -0,0 +1,186 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2004 + + ** 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 + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldbadd + * + * Description: utility to add records - modelled on ldapadd + * + * Author: Andrew Tridgell + */ + +#include "replace.h" +#include "ldb.h" +#include "tools/cmdline.h" +#include "ldbutil.h" +#include "include/ldb_private.h" + +static struct ldb_cmdline *options; + +static void usage(struct ldb_context *ldb) +{ + printf("Usage: ldbadd <options> <ldif...>\n"); + printf("Adds records to a ldb, reading ldif the specified list of files\n\n"); + ldb_cmdline_help(ldb, "ldbadd", stdout); + exit(LDB_ERR_OPERATIONS_ERROR); +} + + +/* + add records from an opened file +*/ +static int process_file(struct ldb_context *ldb, FILE *f, unsigned int *count) +{ + struct ldb_ldif *ldif; + int fun_ret = LDB_SUCCESS, ret; + struct ldb_control **req_ctrls = ldb_parse_control_strings(ldb, ldb, (const char **)options->controls); + struct ldif_read_file_state state = { + .f = f + }; + + if (options->controls != NULL && req_ctrls== NULL) { + printf("parsing controls failed: %s\n", ldb_errstring(ldb)); + return LDB_ERR_OPERATIONS_ERROR; + } + + fun_ret = ldb_transaction_start(ldb); + if (fun_ret != LDB_SUCCESS) { + fprintf(stderr, "ERR: (%s) on transaction start\n", + ldb_errstring(ldb)); + return fun_ret; + } + + while ((ldif = ldb_ldif_read_file_state(ldb, &state))) { + if (ldif->changetype != LDB_CHANGETYPE_ADD && + ldif->changetype != LDB_CHANGETYPE_NONE) { + fprintf(stderr, "Only CHANGETYPE_ADD records allowed\n"); + break; + } + + ret = ldb_msg_normalize(ldb, ldif, ldif->msg, &ldif->msg); + if (ret != LDB_SUCCESS) { + fprintf(stderr, + "ERR: Message canonicalize failed - %s\n", + ldb_strerror(ret)); + fun_ret = ret; + ldb_ldif_read_free(ldb, ldif); + continue; + } + + ret = ldb_add_ctrl(ldb, ldif->msg,req_ctrls); + if (ret != LDB_SUCCESS) { + fprintf(stderr, "ERR: %s : \"%s\" on DN %s at block before line %llu\n", + ldb_strerror(ret), ldb_errstring(ldb), + ldb_dn_get_linearized(ldif->msg->dn), + (unsigned long long)state.line_no); + fun_ret = ret; + } else { + (*count)++; + if (options->verbose) { + printf("Added %s\n", ldb_dn_get_linearized(ldif->msg->dn)); + } + } + ldb_ldif_read_free(ldb, ldif); + if (ret) { + break; + } + } + + if (fun_ret == LDB_SUCCESS && !feof(f)) { + fprintf(stderr, "Failed to parse ldif\n"); + fun_ret = LDB_ERR_OPERATIONS_ERROR; + } + + if (fun_ret == LDB_SUCCESS) { + fun_ret = ldb_transaction_commit(ldb); + if (fun_ret != LDB_SUCCESS) { + fprintf(stderr, "ERR: (%s) on transaction commit\n", + ldb_errstring(ldb)); + } + } else { + ldb_transaction_cancel(ldb); + } + + return fun_ret; +} + + + +int main(int argc, const char **argv) +{ + struct ldb_context *ldb; + unsigned int i, count = 0; + int ret = LDB_SUCCESS; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + ldb = ldb_init(mem_ctx, NULL); + if (ldb == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + options = ldb_cmdline_process(ldb, argc, argv, usage); + + ret = ldb_transaction_start(ldb); + if (ret != LDB_SUCCESS) { + printf("Failed to start transaction: %s\n", ldb_errstring(ldb)); + return ret; + } + + if (options->argc == 0) { + ret = process_file(ldb, stdin, &count); + } else { + for (i=0;i<options->argc;i++) { + const char *fname = options->argv[i]; + FILE *f; + f = fopen(fname, "r"); + if (!f) { + perror(fname); + return LDB_ERR_OPERATIONS_ERROR; + } + ret = process_file(ldb, f, &count); + fclose(f); + } + } + + if (count != 0) { + ret = ldb_transaction_commit(ldb); + if (ret != LDB_SUCCESS) { + printf("Failed to commit transaction: %s\n", ldb_errstring(ldb)); + return ret; + } + } else { + ldb_transaction_cancel(ldb); + } + + talloc_free(mem_ctx); + + if (ret) { + printf("Add failed after processing %u records\n", count); + } else { + printf("Added %u records successfully\n", count); + } + + return ret; +} diff --git a/lib/ldb/tools/ldbdel.c b/lib/ldb/tools/ldbdel.c new file mode 100644 index 0000000..8036d09 --- /dev/null +++ b/lib/ldb/tools/ldbdel.c @@ -0,0 +1,135 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2004 + + ** 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 + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldbdel + * + * Description: utility to delete records - modelled on ldapdelete + * + * Author: Andrew Tridgell + */ + +#include "replace.h" +#include "ldb.h" +#include "tools/cmdline.h" +#include "ldbutil.h" + +static int dn_cmp(struct ldb_message **msg1, struct ldb_message **msg2) +{ + return ldb_dn_compare((*msg1)->dn, (*msg2)->dn); +} + +static int ldb_delete_recursive(struct ldb_context *ldb, struct ldb_dn *dn,struct ldb_control **req_ctrls) +{ + int ret; + unsigned int i, total=0; + const char *attrs[] = { NULL }; + struct ldb_result *res; + + ret = ldb_search(ldb, ldb, &res, dn, LDB_SCOPE_SUBTREE, attrs, "distinguishedName=*"); + if (ret != LDB_SUCCESS) return ret; + + /* sort the DNs, deepest first */ + TYPESAFE_QSORT(res->msgs, res->count, dn_cmp); + + for (i = 0; i < res->count; i++) { + if (ldb_delete_ctrl(ldb, res->msgs[i]->dn,req_ctrls) == LDB_SUCCESS) { + total++; + } else { + printf("Failed to delete '%s' - %s\n", + ldb_dn_get_linearized(res->msgs[i]->dn), + ldb_errstring(ldb)); + } + } + + talloc_free(res); + + if (total == 0) { + return LDB_ERR_OPERATIONS_ERROR; + } + printf("Deleted %u records\n", total); + return LDB_SUCCESS; +} + +static void usage(struct ldb_context *ldb) +{ + printf("Usage: ldbdel <options> <DN...>\n"); + printf("Deletes records from a ldb\n\n"); + ldb_cmdline_help(ldb, "ldbdel", stdout); + exit(LDB_ERR_OPERATIONS_ERROR); +} + +int main(int argc, const char **argv) +{ + struct ldb_control **req_ctrls; + struct ldb_cmdline *options; + struct ldb_context *ldb; + int ret = 0, i; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + ldb = ldb_init(mem_ctx, NULL); + if (ldb == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + options = ldb_cmdline_process(ldb, argc, argv, usage); + + if (options->argc < 1) { + usage(ldb); + } + + req_ctrls = ldb_parse_control_strings(ldb, ldb, (const char **)options->controls); + if (options->controls != NULL && req_ctrls== NULL) { + printf("parsing controls failed: %s\n", ldb_errstring(ldb)); + return LDB_ERR_OPERATIONS_ERROR; + } + + for (i=0;i<options->argc;i++) { + struct ldb_dn *dn; + + dn = ldb_dn_new(ldb, ldb, options->argv[i]); + if (dn == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + if (options->recursive) { + ret = ldb_delete_recursive(ldb, dn,req_ctrls); + } else { + ret = ldb_delete_ctrl(ldb, dn,req_ctrls); + if (ret == LDB_SUCCESS) { + printf("Deleted 1 record\n"); + } + } + if (ret != LDB_SUCCESS) { + printf("delete of '%s' failed - (%s) %s\n", + ldb_dn_get_linearized(dn), + ldb_strerror(ret), + ldb_errstring(ldb)); + } + } + + talloc_free(mem_ctx); + + return ret; +} diff --git a/lib/ldb/tools/ldbdump.c b/lib/ldb/tools/ldbdump.c new file mode 100644 index 0000000..5cdb7d8 --- /dev/null +++ b/lib/ldb/tools/ldbdump.c @@ -0,0 +1,383 @@ +/* + Unix SMB/CIFS implementation. + simple ldb tdb dump util + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Andrew Bartlett 2012 + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "system/locale.h" +#include "system/time.h" +#include "system/filesys.h" +#include "system/wait.h" +#include <tdb.h> +#include <ldb.h> +#include <ldb_private.h> + +#ifdef HAVE_LMDB +#include <lmdb.h> +#endif /* ifdef HAVE_LMDB */ + + +static struct ldb_context *ldb; +bool show_index = false; +bool validate_contents = false; + +static void print_data(TDB_DATA d) +{ + unsigned char *p = (unsigned char *)d.dptr; + int len = d.dsize; + while (len--) { + if (isprint(*p) && !strchr("\"\\", *p)) { + fputc(*p, stdout); + } else { + printf("\\%02X", *p); + } + p++; + } +} + +static unsigned int pull_uint32(uint8_t *p) +{ + return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); +} + + +static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA _dbuf, void *state) +{ + int ret, i, j; + struct ldb_dn *dn = state; + struct ldb_message *msg = ldb_msg_new(NULL); + struct ldb_val dbuf = { + .data = _dbuf.dptr, + .length = _dbuf.dsize, + }; + struct ldb_ldif ldif = { + .msg = msg, + .changetype = LDB_CHANGETYPE_NONE + }; + if (!msg) { + return -1; + } + + ret = ldb_unpack_data(ldb, &dbuf, msg); + if (ret != 0) { + fprintf(stderr, "Failed to parse record %*.*s as an LDB record\n", (int)key.dsize, (int)key.dsize, (char *)key.dptr); + TALLOC_FREE(msg); + return 0; + } + + if (dn && ldb_dn_compare(msg->dn, dn) != 0) { + TALLOC_FREE(msg); + return 0; + } + + if (!show_index && ldb_dn_is_special(msg->dn)) { + const char *dn_lin = ldb_dn_get_linearized(msg->dn); + if ((strcmp(dn_lin, "@BASEINFO") == 0) || (strncmp(dn_lin, "@INDEX:", strlen("@INDEX:")) == 0)) { + /* + the user has asked not to show index + records. Also exclude BASEINFO as it + contains meta-data which will be re-created + if this database is restored + */ + TALLOC_FREE(msg); + return 0; + } + } + + printf("# key: "); + print_data(key); + printf("\n# pack format: %#010x\n", pull_uint32(_dbuf.dptr)); + + if (!validate_contents || ldb_dn_is_special(msg->dn)) { + ldb_ldif_write_file(ldb, stdout, &ldif); + TALLOC_FREE(msg); + return 0; + } + + for (i=0;i<msg->num_elements;i++) { + const struct ldb_schema_attribute *a; + + a = ldb_schema_attribute_by_name(ldb, msg->elements[i].name); + for (j=0;j<msg->elements[i].num_values;j++) { + struct ldb_val v; + ret = a->syntax->ldif_write_fn(ldb, msg, &msg->elements[i].values[j], &v); + if (ret != 0) { + v = msg->elements[i].values[j]; + if (ldb_should_b64_encode(ldb, &v)) { + v.data = (uint8_t *)ldb_base64_encode(ldb, (char *)v.data, v.length); + v.length = strlen((char *)v.data); + } + fprintf(stderr, "On %s element %s value %d (%*.*s) failed to convert to LDIF correctly, skipping possibly corrupt record\n", + ldb_dn_get_linearized(msg->dn), + msg->elements[i].name, + j, (int)v.length, (int)v.length, + v.data); + TALLOC_FREE(msg); + return 0; + } + } + } + ldb_ldif_write_file(ldb, stdout, &ldif); + TALLOC_FREE(msg); + + return 0; +} + +static void log_stderr(struct tdb_context *tdb, enum tdb_debug_level level, + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); + +static void log_stderr(struct tdb_context *tdb, enum tdb_debug_level level, + const char *fmt, ...) +{ + va_list ap; + const char *name = tdb_name(tdb); + const char *prefix = ""; + + if (!name) + name = "unnamed"; + + switch (level) { + case TDB_DEBUG_ERROR: + prefix = "ERROR: "; + break; + case TDB_DEBUG_WARNING: + prefix = "WARNING: "; + break; + case TDB_DEBUG_TRACE: + return; + + default: + case TDB_DEBUG_FATAL: + prefix = "FATAL: "; + break; + } + + va_start(ap, fmt); + fprintf(stderr, "tdb(%s): %s", name, prefix); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static void emergency_walk(TDB_DATA key, TDB_DATA dbuf, void *keyname) +{ + traverse_fn(NULL, key, dbuf, keyname); +} + +static int dump_tdb(const char *fname, struct ldb_dn *dn, bool emergency) +{ + TDB_CONTEXT *tdb; + struct tdb_logging_context logfn = { + .log_fn = log_stderr, + }; + + tdb = tdb_open_ex(fname, 0, 0, O_RDONLY, 0, &logfn, NULL); + if (!tdb) { + fprintf(stderr, "Failed to open %s\n", fname); + return 1; + } + + if (emergency) { + return tdb_rescue(tdb, emergency_walk, dn) == 0; + } + return tdb_traverse(tdb, traverse_fn, dn) == -1 ? 1 : 0; +} + +#ifdef HAVE_LMDB +static int dump_lmdb(const char *fname, struct ldb_dn *dn, bool emergency) +{ + int ret; + struct MDB_env *env = NULL; + struct MDB_txn *txn = NULL; + MDB_dbi dbi; + struct MDB_cursor *cursor = NULL; + struct MDB_val key; + struct MDB_val data; + + ret = mdb_env_create(&env); + if (ret != 0) { + fprintf(stderr, + "Could not create MDB environment: (%d) %s\n", + ret, + mdb_strerror(ret)); + goto close_env; + } + + ret = mdb_env_open(env, + fname, + MDB_NOSUBDIR|MDB_NOTLS|MDB_RDONLY, + 0600); + if (ret != 0) { + fprintf(stderr, + "Could not open environment for %s: (%d) %s\n", + fname, + ret, + mdb_strerror(ret)); + goto close_env; + } + + ret = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); + if (ret != 0) { + fprintf(stderr, + "Could not start transaction: (%d) %s\n", + ret, + mdb_strerror(ret)); + goto close_env; + } + + ret = mdb_dbi_open(txn, NULL, 0, &dbi); + if (ret != 0) { + fprintf(stderr, + "Could not open database: (%d) %s\n", + ret, + mdb_strerror(ret)); + goto close_txn; + } + + ret = mdb_cursor_open(txn, dbi, &cursor); + if (ret != 0) { + fprintf(stderr, + "Could not open cursor: (%d) %s\n", + ret, + mdb_strerror(ret)); + goto close_txn; + } + + ret = mdb_cursor_get(cursor, &key, &data, MDB_FIRST); + if (ret != 0 && ret != MDB_NOTFOUND) { + fprintf(stderr, + "Could not find first record: (%d) %s\n", + ret, + mdb_strerror(ret)); + goto close_cursor; + } + while (ret != MDB_NOTFOUND) { + struct TDB_DATA tkey = { + .dptr = key.mv_data, + .dsize = key.mv_size + }; + struct TDB_DATA tdata = { + .dptr = data.mv_data, + .dsize = data.mv_size + }; + traverse_fn(NULL, tkey, tdata, dn); + ret = mdb_cursor_get(cursor, &key, &data, MDB_NEXT); + if (ret != 0 && ret != MDB_NOTFOUND) { + fprintf(stderr, + "Could not read next record: (%d) %s\n", + ret, + mdb_strerror(ret)); + goto close_cursor; + } + } + ret = 0; + +close_cursor: + mdb_cursor_close(cursor); +close_txn: + mdb_txn_commit(txn); +close_env: + mdb_env_close(env); + + if (ret != 0) { + return 1; + } + return 0; + +} +#else +static int dump_lmdb(const char *fname, struct ldb_dn *dn, bool emergency) +{ + /* not built with lmdb support */ + return 1; +} +#endif /* #ifdef HAVE_LMDB */ + +static void usage( void) +{ + printf( "Usage: ldbdump [options] <filename>\n\n"); + printf( " -h this help message\n"); + printf( " -d DN dumps DN only\n"); + printf( " -e emergency dump, for corrupt databases\n"); + printf( " -i include index and @BASEINFO records in dump\n"); + printf( " -c validate contents of the records\n"); +} + + int main(int argc, char *argv[]) +{ + bool emergency = false; + int c, rc; + char *fname; + struct ldb_dn *dn = NULL; + + ldb = ldb_init(NULL, NULL); + if (ldb == NULL) { + fprintf(stderr, "ldb: ldb_init failed()"); + exit(1); + } + + rc = ldb_modules_hook(ldb, LDB_MODULE_HOOK_CMDLINE_PRECONNECT); + if (rc != LDB_SUCCESS) { + fprintf(stderr, "ldb: failed to run preconnect hooks (needed to get Samba LDIF handlers): %s\n", ldb_strerror(rc)); + exit(1); + } + + if (argc < 2) { + printf("Usage: ldbdump <fname>\n"); + exit(1); + } + + while ((c = getopt( argc, argv, "hd:eic")) != -1) { + switch (c) { + case 'h': + usage(); + exit( 0); + case 'd': + dn = ldb_dn_new(ldb, ldb, optarg); + if (!dn) { + fprintf(stderr, "ldb failed to parse %s as a DN\n", optarg); + exit(1); + } + break; + case 'e': + emergency = true; + break; + case 'i': + show_index = true; + break; + case 'c': + validate_contents = true; + break; + default: + usage(); + exit( 1); + } + } + + fname = argv[optind]; + + rc = dump_lmdb(fname, dn, emergency); + if (rc != 0) { + rc = dump_tdb(fname, dn, emergency); + if (rc != 0) { + fprintf(stderr, "Failed to open %s\n", fname); + return 1; + } + } + return 0; + +} diff --git a/lib/ldb/tools/ldbedit.c b/lib/ldb/tools/ldbedit.c new file mode 100644 index 0000000..497ef97 --- /dev/null +++ b/lib/ldb/tools/ldbedit.c @@ -0,0 +1,383 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2004 + + ** 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 + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldbedit + * + * Description: utility for ldb database editing + * + * Author: Andrew Tridgell + */ + +#include "replace.h" +#include "system/filesys.h" +#include "system/time.h" +#include "system/filesys.h" +#include "ldb.h" +#include "tools/cmdline.h" +#include "tools/ldbutil.h" + +static struct ldb_cmdline *options; + +/* + debug routine +*/ +static void ldif_write_msg(struct ldb_context *ldb, + FILE *f, + enum ldb_changetype changetype, + struct ldb_message *msg) +{ + struct ldb_ldif ldif; + ldif.changetype = changetype; + ldif.msg = msg; + ldb_ldif_write_file(ldb, f, &ldif); +} + +/* + modify a database record so msg1 becomes msg2 + returns the number of modified elements +*/ +static int modify_record(struct ldb_context *ldb, + struct ldb_message *msg1, + struct ldb_message *msg2, + struct ldb_control **req_ctrls) +{ + int ret; + struct ldb_message *mod; + + if (ldb_msg_difference(ldb, ldb, msg1, msg2, &mod) != LDB_SUCCESS) { + fprintf(stderr, "Failed to calculate message differences\n"); + return -1; + } + + ret = mod->num_elements; + if (ret == 0) { + goto done; + } + + if (options->verbose > 0) { + ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_MODIFY, mod); + } + + if (ldb_modify_ctrl(ldb, mod, req_ctrls) != LDB_SUCCESS) { + fprintf(stderr, "failed to modify %s - %s\n", + ldb_dn_get_linearized(msg1->dn), ldb_errstring(ldb)); + ret = -1; + goto done; + } + +done: + talloc_free(mod); + return ret; +} + +/* + find dn in msgs[] +*/ +static struct ldb_message *msg_find(struct ldb_context *ldb, + struct ldb_message **msgs, + unsigned int count, + struct ldb_dn *dn) +{ + unsigned int i; + for (i=0;i<count;i++) { + if (ldb_dn_compare(dn, msgs[i]->dn) == 0) { + return msgs[i]; + } + } + return NULL; +} + +/* + merge the changes in msgs2 into the messages from msgs1 +*/ +static int merge_edits(struct ldb_context *ldb, + struct ldb_message **msgs1, unsigned int count1, + struct ldb_message **msgs2, unsigned int count2) +{ + unsigned int i; + struct ldb_message *msg; + int ret; + unsigned int adds=0, modifies=0, deletes=0; + struct ldb_control **req_ctrls = ldb_parse_control_strings(ldb, ldb, (const char **)options->controls); + if (options->controls != NULL && req_ctrls == NULL) { + fprintf(stderr, "parsing controls failed: %s\n", ldb_errstring(ldb)); + return -1; + } + + if (ldb_transaction_start(ldb) != LDB_SUCCESS) { + fprintf(stderr, "Failed to start transaction: %s\n", ldb_errstring(ldb)); + return -1; + } + + /* do the adds and modifies */ + for (i=0;i<count2;i++) { + msg = msg_find(ldb, msgs1, count1, msgs2[i]->dn); + if (!msg) { + if (options->verbose > 0) { + ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_ADD, msgs2[i]); + } + if (ldb_add_ctrl(ldb, msgs2[i], req_ctrls) != LDB_SUCCESS) { + fprintf(stderr, "failed to add %s - %s\n", + ldb_dn_get_linearized(msgs2[i]->dn), + ldb_errstring(ldb)); + ldb_transaction_cancel(ldb); + return -1; + } + adds++; + } else { + ret = modify_record(ldb, msg, msgs2[i], req_ctrls); + if (ret != -1) { + modifies += (unsigned int) ret; + } else { + ldb_transaction_cancel(ldb); + return -1; + } + } + } + + /* do the deletes */ + for (i=0;i<count1;i++) { + msg = msg_find(ldb, msgs2, count2, msgs1[i]->dn); + if (!msg) { + if (options->verbose > 0) { + ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_DELETE, msgs1[i]); + } + if (ldb_delete_ctrl(ldb, msgs1[i]->dn, req_ctrls) != LDB_SUCCESS) { + fprintf(stderr, "failed to delete %s - %s\n", + ldb_dn_get_linearized(msgs1[i]->dn), + ldb_errstring(ldb)); + ldb_transaction_cancel(ldb); + return -1; + } + deletes++; + } + } + + if (ldb_transaction_commit(ldb) != LDB_SUCCESS) { + fprintf(stderr, "Failed to commit transaction: %s\n", ldb_errstring(ldb)); + return -1; + } + + printf("# %u adds %u modifies %u deletes\n", adds, modifies, deletes); + + return 0; +} + +/* + save a set of messages as ldif to a file +*/ +static int save_ldif(struct ldb_context *ldb, + FILE *f, struct ldb_message **msgs, unsigned int count) +{ + unsigned int i; + + fprintf(f, "# editing %u records\n", count); + + for (i=0;i<count;i++) { + struct ldb_ldif ldif; + fprintf(f, "# record %u\n", i+1); + + ldif.changetype = LDB_CHANGETYPE_NONE; + ldif.msg = msgs[i]; + + ldb_ldif_write_file(ldb, f, &ldif); + } + + return 0; +} + + +/* + edit the ldb search results in msgs using the user selected editor +*/ +static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1, + unsigned int count1, const char *editor) +{ + int fd, ret; + FILE *f; + char file_template[] = "/tmp/ldbedit.XXXXXX"; + char *cmd; + struct ldb_ldif *ldif; + struct ldb_message **msgs2 = NULL; + unsigned int count2 = 0; + + /* write out the original set of messages to a temporary + file */ + fd = mkstemp(file_template); + + if (fd == -1) { + perror(file_template); + return -1; + } + + f = fdopen(fd, "r+"); + + if (!f) { + perror("fopen"); + close(fd); + unlink(file_template); + return -1; + } + + if (save_ldif(ldb, f, msgs1, count1) != 0) { + return -1; + } + + fclose(f); + + cmd = talloc_asprintf(ldb, "%s %s", editor, file_template); + + if (!cmd) { + unlink(file_template); + fprintf(stderr, "out of memory\n"); + return -1; + } + + /* run the editor */ + ret = system(cmd); + talloc_free(cmd); + + if (ret != 0) { + unlink(file_template); + fprintf(stderr, "edit with %s failed\n", editor); + return -1; + } + + /* read the resulting ldif into msgs2 */ + f = fopen(file_template, "r"); + if (!f) { + perror(file_template); + return -1; + } + + while ((ldif = ldb_ldif_read_file(ldb, f))) { + msgs2 = talloc_realloc(ldb, msgs2, struct ldb_message *, count2+1); + if (!msgs2) { + fprintf(stderr, "out of memory"); + return -1; + } + msgs2[count2++] = ldif->msg; + } + + /* the feof() test works here, even for the last line of the + * file, as we parse ldif files character by character, and + * feof() is only true if we have failed to read a character + * from the file. So if the last line is bad, we don't get + * feof() set, so we know the record was bad. Only if we + * attempt to go to the next record will we get feof() and + * thus consider that the ldif has ended without errors + */ + if (!feof(f)) { + fprintf(stderr, "Error parsing ldif - aborting\n"); + fclose(f); + unlink(file_template); + return -1; + } + + fclose(f); + unlink(file_template); + + return merge_edits(ldb, msgs1, count1, msgs2, count2); +} + +static void usage(struct ldb_context *ldb) +{ + printf("Usage: ldbedit <options> <expression> <attributes ...>\n"); + ldb_cmdline_help(ldb, "ldbedit", stdout); + exit(LDB_ERR_OPERATIONS_ERROR); +} + +int main(int argc, const char **argv) +{ + struct ldb_context *ldb; + struct ldb_result *result = NULL; + struct ldb_dn *basedn = NULL; + int ret; + const char *expression = "(|(objectClass=*)(distinguishedName=*))"; + const char * const * attrs = NULL; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + struct ldb_control **req_ctrls; + unsigned int i; + + ldb = ldb_init(mem_ctx, NULL); + if (ldb == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + options = ldb_cmdline_process_edit(ldb, argc, argv, usage); + + /* the check for '=' is for compatibility with ldapsearch */ + if (options->argc > 0 && + strchr(options->argv[0], '=')) { + expression = options->argv[0]; + options->argv++; + options->argc--; + } + + if (options->argc > 0) { + attrs = (const char * const *)(options->argv); + } + + if (options->basedn != NULL) { + basedn = ldb_dn_new(ldb, ldb, options->basedn); + if (basedn == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + } + + for (i = 0; options->controls != NULL && options->controls[i] != NULL; i++) { + if (strncmp(options->controls[i], "reveal_internals:", 17) == 0) { + printf("Using reveal internals has unintended consequences.\n"); + printf("If this is your intent, manually perform the search," + " and use ldbmodify directly.\n"); + return LDB_ERR_OPERATIONS_ERROR; + } + } + + req_ctrls = ldb_parse_control_strings(ldb, ldb, (const char **)options->controls); + if (options->controls != NULL && req_ctrls== NULL) { + printf("parsing controls failed: %s\n", ldb_errstring(ldb)); + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ldb_search_ctrl(ldb, ldb, &result, basedn, options->scope, attrs, req_ctrls, "%s", expression); + if (ret != LDB_SUCCESS) { + printf("search failed - %s\n", ldb_errstring(ldb)); + return ret; + } + + if (result->count == 0) { + printf("no matching records - cannot edit\n"); + talloc_free(mem_ctx); + return LDB_SUCCESS; + } + + ret = do_edit(ldb, result->msgs, result->count, options->editor); + + talloc_free(mem_ctx); + + return ret == 0 ? LDB_SUCCESS : LDB_ERR_OPERATIONS_ERROR; +} diff --git a/lib/ldb/tools/ldbmodify.c b/lib/ldb/tools/ldbmodify.c new file mode 100644 index 0000000..73df417 --- /dev/null +++ b/lib/ldb/tools/ldbmodify.c @@ -0,0 +1,187 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2004 + + ** 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 + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldbmodify + * + * Description: utility to modify records - modelled on ldapmodify + * + * Author: Andrew Tridgell + */ + +#include "replace.h" +#include "ldb.h" +#include "tools/cmdline.h" +#include "ldbutil.h" +#include "include/ldb_private.h" + +static struct ldb_cmdline *options; + +static void usage(struct ldb_context *ldb) +{ + printf("Usage: ldbmodify <options> <ldif...>\n"); + printf("Modifies a ldb based upon ldif change records\n\n"); + ldb_cmdline_help(ldb, "ldbmodify", stdout); + exit(LDB_ERR_OPERATIONS_ERROR); +} + +/* + process modifies for one file +*/ +static int process_file(struct ldb_context *ldb, FILE *f, unsigned int *count) +{ + struct ldb_ldif *ldif; + int fun_ret = LDB_SUCCESS, ret; + struct ldb_control **req_ctrls = ldb_parse_control_strings(ldb, ldb, (const char **)options->controls); + struct ldif_read_file_state state = { + .f = f + }; + + if (options->controls != NULL && req_ctrls== NULL) { + printf("parsing controls failed: %s\n", ldb_errstring(ldb)); + exit(LDB_ERR_OPERATIONS_ERROR); + } + + fun_ret = ldb_transaction_start(ldb); + if (fun_ret != LDB_SUCCESS) { + fprintf(stderr, "ERR: (%s) on transaction start\n", + ldb_errstring(ldb)); + return fun_ret; + } + + while ((ldif = ldb_ldif_read_file_state(ldb, &state))) { + struct ldb_dn *olddn; + bool deleteoldrdn = false; + struct ldb_dn *newdn; + const char *errstr = NULL; + + switch (ldif->changetype) { + case LDB_CHANGETYPE_NONE: + case LDB_CHANGETYPE_ADD: + ret = ldb_add_ctrl(ldb, ldif->msg,req_ctrls); + break; + case LDB_CHANGETYPE_DELETE: + ret = ldb_delete_ctrl(ldb, ldif->msg->dn,req_ctrls); + break; + case LDB_CHANGETYPE_MODIFY: + ret = ldb_modify_ctrl(ldb, ldif->msg,req_ctrls); + break; + case LDB_CHANGETYPE_MODRDN: + ret = ldb_ldif_parse_modrdn(ldb, ldif, ldif, &olddn, + NULL, &deleteoldrdn, + NULL, &newdn); + if (ret == LDB_SUCCESS) { + if (deleteoldrdn) { + ret = ldb_rename(ldb, olddn, newdn); + } else { + errstr = "modrdn: deleteoldrdn=0 " + "not supported."; + ret = LDB_ERR_CONSTRAINT_VIOLATION; + } + } + break; + } + if (ret != LDB_SUCCESS) { + if (errstr == NULL) { + errstr = ldb_errstring(ldb); + } + fprintf(stderr, + "ERR: (%s) \"%s\" on DN %s at block before " + "line %zu\n", + ldb_strerror(ret), + errstr, + ldb_dn_get_linearized(ldif->msg->dn), + state.line_no); + fun_ret = ret; + } else { + (*count)++; + if (options->verbose) { + printf("Modified %s\n", ldb_dn_get_linearized(ldif->msg->dn)); + } + } + ldb_ldif_read_free(ldb, ldif); + if (ret) { + break; + } + } + + if (fun_ret == LDB_SUCCESS && !feof(f)) { + fprintf(stderr, "Failed to parse ldif\n"); + fun_ret = LDB_ERR_OPERATIONS_ERROR; + } + + if (fun_ret == LDB_SUCCESS) { + fun_ret = ldb_transaction_commit(ldb); + if (fun_ret != LDB_SUCCESS) { + fprintf(stderr, "ERR: (%s) on transaction commit\n", + ldb_errstring(ldb)); + } + } else { + ldb_transaction_cancel(ldb); + } + + return fun_ret; +} + +int main(int argc, const char **argv) +{ + struct ldb_context *ldb; + unsigned int i, count = 0; + int ret = LDB_SUCCESS; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + ldb = ldb_init(mem_ctx, NULL); + if (ldb == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + options = ldb_cmdline_process(ldb, argc, argv, usage); + + if (options->argc == 0) { + ret = process_file(ldb, stdin, &count); + } else { + for (i=0;i<options->argc;i++) { + const char *fname = options->argv[i]; + FILE *f; + f = fopen(fname, "r"); + if (!f) { + perror(fname); + return LDB_ERR_OPERATIONS_ERROR; + } + ret = process_file(ldb, f, &count); + fclose(f); + } + } + + talloc_free(mem_ctx); + + if (ret) { + printf("Modify failed after processing %u records\n", count); + } else { + printf("Modified %u records successfully\n", count); + } + + return ret; +} diff --git a/lib/ldb/tools/ldbrename.c b/lib/ldb/tools/ldbrename.c new file mode 100644 index 0000000..ab2be4d --- /dev/null +++ b/lib/ldb/tools/ldbrename.c @@ -0,0 +1,85 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Stefan Metzmacher 2004 + + ** 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 + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldbrename + * + * Description: utility to rename records - modelled on ldapmodrdn + * + * Author: Andrew Tridgell + * Author: Stefan Metzmacher + */ + +#include "replace.h" +#include "ldb.h" +#include "tools/cmdline.h" + +static void usage(struct ldb_context *ldb) +{ + printf("Usage: ldbrename [<options>] <olddn> <newdn>\n"); + printf("Renames records in a ldb\n\n"); + ldb_cmdline_help(ldb, "ldbmodify", stdout); + exit(LDB_ERR_OPERATIONS_ERROR); +} + + +int main(int argc, const char **argv) +{ + struct ldb_context *ldb; + int ret; + struct ldb_cmdline *options; + struct ldb_dn *dn1, *dn2; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + ldb = ldb_init(mem_ctx, NULL); + if (ldb == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + options = ldb_cmdline_process(ldb, argc, argv, usage); + + if (options->argc < 2) { + usage(ldb); + } + + dn1 = ldb_dn_new(ldb, ldb, options->argv[0]); + dn2 = ldb_dn_new(ldb, ldb, options->argv[1]); + if ((dn1 == NULL) || (dn2 == NULL)) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ldb_rename(ldb, dn1, dn2); + if (ret == LDB_SUCCESS) { + printf("Renamed 1 record\n"); + } else { + printf("rename of '%s' to '%s' failed - %s\n", + options->argv[0], options->argv[1], ldb_errstring(ldb)); + } + + talloc_free(mem_ctx); + + return ret; +} diff --git a/lib/ldb/tools/ldbsearch.c b/lib/ldb/tools/ldbsearch.c new file mode 100644 index 0000000..374f240 --- /dev/null +++ b/lib/ldb/tools/ldbsearch.c @@ -0,0 +1,342 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2004 + + ** 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 + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldbsearch + * + * Description: utility for ldb search - modelled on ldapsearch + * + * Author: Andrew Tridgell + */ + +#include "replace.h" +#include "system/filesys.h" +#include "system/time.h" +#include "ldb.h" +#include "tools/cmdline.h" + +static void usage(struct ldb_context *ldb) +{ + printf("Usage: ldbsearch <options> <expression> <attrs...>\n"); + ldb_cmdline_help(ldb, "ldbsearch", stdout); + exit(LDB_ERR_OPERATIONS_ERROR); +} + +static int do_compare_msg(struct ldb_message **el1, + struct ldb_message **el2, + void *opaque) +{ + return ldb_dn_compare((*el1)->dn, (*el2)->dn); +} + +struct search_context { + struct ldb_context *ldb; + struct ldb_control **req_ctrls; + + int sort; + unsigned int num_stored; + struct ldb_message **store; + unsigned int refs_stored; + char **refs_store; + + unsigned int entries; + unsigned int refs; + + unsigned int pending; + int status; +}; + +static int store_message(struct ldb_message *msg, struct search_context *sctx) { + + sctx->store = talloc_realloc(sctx, sctx->store, struct ldb_message *, sctx->num_stored + 2); + if (!sctx->store) { + fprintf(stderr, "talloc_realloc failed while storing messages\n"); + return -1; + } + + sctx->store[sctx->num_stored] = talloc_move(sctx->store, &msg); + sctx->num_stored++; + sctx->store[sctx->num_stored] = NULL; + + return 0; +} + +static int store_referral(char *referral, struct search_context *sctx) { + + sctx->refs_store = talloc_realloc(sctx, sctx->refs_store, char *, sctx->refs_stored + 2); + if (!sctx->refs_store) { + fprintf(stderr, "talloc_realloc failed while storing referrals\n"); + return -1; + } + + sctx->refs_store[sctx->refs_stored] = talloc_move(sctx->refs_store, &referral); + sctx->refs_stored++; + sctx->refs_store[sctx->refs_stored] = NULL; + + return 0; +} + +static int display_message(struct ldb_message *msg, struct search_context *sctx) { + struct ldb_ldif ldif; + + sctx->entries++; + printf("# record %d\n", sctx->entries); + + ldif.changetype = LDB_CHANGETYPE_NONE; + ldif.msg = msg; + + if (sctx->sort) { + /* + * Ensure attributes are always returned in the same + * order. For testing, this makes comparison of old + * vs. new much easier. + */ + ldb_msg_sort_elements(ldif.msg); + } + + ldb_ldif_write_file(sctx->ldb, stdout, &ldif); + + return 0; +} + +static int display_referral(char *referral, struct search_context *sctx) +{ + + sctx->refs++; + printf("# Referral\nref: %s\n\n", referral); + + return 0; +} + +static int search_callback(struct ldb_request *req, struct ldb_reply *ares) +{ + struct search_context *sctx; + int ret = LDB_SUCCESS; + + sctx = talloc_get_type(req->context, struct search_context); + + 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: + if (sctx->sort) { + ret = store_message(ares->message, sctx); + } else { + ret = display_message(ares->message, sctx); + } + break; + + case LDB_REPLY_REFERRAL: + if (sctx->sort) { + ret = store_referral(ares->referral, sctx); + } else { + ret = display_referral(ares->referral, sctx); + } + if (ret) { + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); + } + break; + + case LDB_REPLY_DONE: + if (ares->controls) { + if (handle_controls_reply(ares->controls, sctx->req_ctrls) == 1) + sctx->pending = 1; + } + talloc_free(ares); + return ldb_request_done(req, LDB_SUCCESS); + } + + talloc_free(ares); + if (ret != LDB_SUCCESS) { + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); + } + + return LDB_SUCCESS; +} + +static int do_search(struct ldb_context *ldb, + struct ldb_dn *basedn, + struct ldb_cmdline *options, + const char *expression, + const char * const *attrs) +{ + struct ldb_request *req; + struct search_context *sctx; + int ret; + + req = NULL; + + sctx = talloc_zero(ldb, struct search_context); + if (!sctx) return LDB_ERR_OPERATIONS_ERROR; + + sctx->ldb = ldb; + sctx->sort = options->sorted; + sctx->req_ctrls = ldb_parse_control_strings(ldb, sctx, (const char **)options->controls); + if (options->controls != NULL && sctx->req_ctrls== NULL) { + printf("parsing controls failed: %s\n", ldb_errstring(ldb)); + return LDB_ERR_OPERATIONS_ERROR; + } + +again: + /* free any previous requests */ + if (req) talloc_free(req); + + ret = ldb_build_search_req(&req, ldb, ldb, + basedn, options->scope, + expression, attrs, + sctx->req_ctrls, + sctx, search_callback, + NULL); + if (ret != LDB_SUCCESS) { + talloc_free(sctx); + printf("allocating request failed: %s\n", ldb_errstring(ldb)); + return ret; + } + + if (basedn == NULL) { + /* + we need to use a NULL base DN when doing a cross-ncs + search so we find results on all partitions in a + forest. When doing a domain-local search, default to + the default basedn + */ + struct ldb_control *ctrl; + struct ldb_search_options_control *search_options = NULL; + + ctrl = ldb_request_get_control(req, LDB_CONTROL_SEARCH_OPTIONS_OID); + if (ctrl) { + search_options = talloc_get_type(ctrl->data, struct ldb_search_options_control); + } + + if (ctrl == NULL || search_options == NULL || + !(search_options->search_options & LDB_SEARCH_OPTION_PHANTOM_ROOT)) { + struct ldb_dn *base = ldb_get_default_basedn(ldb); + if (base != NULL) { + req->op.search.base = base; + } + } + } + + sctx->pending = 0; + + ret = ldb_request(ldb, req); + if (ret != LDB_SUCCESS) { + talloc_free(sctx); + talloc_free(req); + printf("search failed - %s\n", ldb_errstring(ldb)); + return ret; + } + + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + if (ret != LDB_SUCCESS) { + talloc_free(sctx); + talloc_free(req); + printf("search error - %s\n", ldb_errstring(ldb)); + return ret; + } + + if (sctx->pending) + goto again; + + if (sctx->sort && (sctx->num_stored != 0 || sctx->refs != 0)) { + unsigned int i; + + if (sctx->num_stored) { + LDB_TYPESAFE_QSORT(sctx->store, sctx->num_stored, ldb, do_compare_msg); + } + for (i = 0; i < sctx->num_stored; i++) { + display_message(sctx->store[i], sctx); + } + + for (i = 0; i < sctx->refs_stored; i++) { + display_referral(sctx->refs_store[i], sctx); + } + } + + printf("# returned %u records\n# %u entries\n# %u referrals\n", + sctx->entries + sctx->refs, sctx->entries, sctx->refs); + + talloc_free(sctx); + talloc_free(req); + + return LDB_SUCCESS; +} + +int main(int argc, const char **argv) +{ + struct ldb_context *ldb; + struct ldb_dn *basedn = NULL; + const char * const * attrs = NULL; + struct ldb_cmdline *options; + int ret = -1; + const char *expression = "(|(objectClass=*)(distinguishedName=*))"; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + ldb = ldb_init(mem_ctx, NULL); + if (ldb == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + options = ldb_cmdline_process_search(ldb, argc, argv, usage); + + /* the check for '=' is for compatibility with ldapsearch */ + if (!options->interactive && + options->argc > 0 && + strpbrk(options->argv[0], "=<>~:")) { + expression = options->argv[0]; + options->argv++; + options->argc--; + } + + if (options->argc > 0) { + attrs = (const char * const *)(options->argv); + } + + if (options->basedn != NULL) { + basedn = ldb_dn_new(ldb, ldb, options->basedn); + if (basedn == NULL) { + talloc_free(mem_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + } + + if (options->interactive) { + char line[1024]; + while (fgets(line, sizeof(line), stdin)) { + ret = do_search(ldb, basedn, options, line, attrs); + } + } else { + ret = do_search(ldb, basedn, options, expression, attrs); + } + + talloc_free(mem_ctx); + + return ret; +} diff --git a/lib/ldb/tools/ldbtest.c b/lib/ldb/tools/ldbtest.c new file mode 100644 index 0000000..64b6baa --- /dev/null +++ b/lib/ldb/tools/ldbtest.c @@ -0,0 +1,438 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2004 + + ** 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 + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Component: ldbtest + * + * Description: utility to test ldb + * + * Author: Andrew Tridgell + */ + +#include "replace.h" +#include "system/filesys.h" +#include "system/time.h" +#include "ldb.h" +#include "tools/cmdline.h" + +static struct timespec tp1,tp2; +static struct ldb_cmdline *options; + +static void _start_timer(void) +{ + if (clock_gettime(CUSTOM_CLOCK_MONOTONIC, &tp1) != 0) { + clock_gettime(CLOCK_REALTIME, &tp1); + } +} + +static double _end_timer(void) +{ + if (clock_gettime(CUSTOM_CLOCK_MONOTONIC, &tp2) != 0) { + clock_gettime(CLOCK_REALTIME, &tp2); + } + return((tp2.tv_sec - tp1.tv_sec) + + (tp2.tv_nsec - tp1.tv_nsec)*1.0e-9); +} + +static void add_records(struct ldb_context *ldb, + struct ldb_dn *basedn, + unsigned int count) +{ + struct ldb_message msg = {0}; + unsigned int i; + +#if 0 + if (ldb_lock(ldb, "transaction") != 0) { + printf("transaction lock failed\n"); + exit(LDB_ERR_OPERATIONS_ERROR); + } +#endif + for (i=0;i<count;i++) { + struct ldb_message_element el[6]; + struct ldb_val vals[6][1]; + char *name; + TALLOC_CTX *tmp_ctx = talloc_new(ldb); + + name = talloc_asprintf(tmp_ctx, "Test%d", i); + + msg.dn = ldb_dn_copy(tmp_ctx, basedn); + ldb_dn_add_child_fmt(msg.dn, "cn=%s", name); + msg.num_elements = 6; + msg.elements = el; + + el[0].flags = 0; + el[0].name = talloc_strdup(tmp_ctx, "cn"); + el[0].num_values = 1; + el[0].values = vals[0]; + vals[0][0].data = (uint8_t *)name; + vals[0][0].length = strlen(name); + + el[1].flags = 0; + el[1].name = "title"; + el[1].num_values = 1; + el[1].values = vals[1]; + vals[1][0].data = (uint8_t *)talloc_asprintf(tmp_ctx, "The title of %s", name); + vals[1][0].length = strlen((char *)vals[1][0].data); + + el[2].flags = 0; + el[2].name = talloc_strdup(tmp_ctx, "uid"); + el[2].num_values = 1; + el[2].values = vals[2]; + vals[2][0].data = (uint8_t *)ldb_casefold(ldb, tmp_ctx, name, strlen(name)); + vals[2][0].length = strlen((char *)vals[2][0].data); + + el[3].flags = 0; + el[3].name = talloc_strdup(tmp_ctx, "mail"); + el[3].num_values = 1; + el[3].values = vals[3]; + vals[3][0].data = (uint8_t *)talloc_asprintf(tmp_ctx, "%s@example.com", name); + vals[3][0].length = strlen((char *)vals[3][0].data); + + el[4].flags = 0; + el[4].name = talloc_strdup(tmp_ctx, "objectClass"); + el[4].num_values = 1; + el[4].values = vals[4]; + vals[4][0].data = (uint8_t *)talloc_strdup(tmp_ctx, "OpenLDAPperson"); + vals[4][0].length = strlen((char *)vals[4][0].data); + + el[5].flags = 0; + el[5].name = talloc_strdup(tmp_ctx, "sn"); + el[5].num_values = 1; + el[5].values = vals[5]; + vals[5][0].data = (uint8_t *)name; + vals[5][0].length = strlen((char *)vals[5][0].data); + + ldb_delete(ldb, msg.dn); + + if (ldb_add(ldb, &msg) != LDB_SUCCESS) { + printf("Add of %s failed - %s\n", name, ldb_errstring(ldb)); + exit(LDB_ERR_OPERATIONS_ERROR); + } + + printf("adding uid %s\r", name); + fflush(stdout); + + talloc_free(tmp_ctx); + } +#if 0 + if (ldb_unlock(ldb, "transaction") != 0) { + printf("transaction unlock failed\n"); + exit(LDB_ERR_OPERATIONS_ERROR); + } +#endif + printf("\n"); +} + +static void modify_records(struct ldb_context *ldb, + struct ldb_dn *basedn, + unsigned int count) +{ + struct ldb_message msg = {0}; + unsigned int i; + + for (i=0;i<count;i++) { + struct ldb_message_element el[3]; + struct ldb_val vals[3]; + char *name; + TALLOC_CTX *tmp_ctx = talloc_new(ldb); + + name = talloc_asprintf(tmp_ctx, "Test%d", i); + msg.dn = ldb_dn_copy(tmp_ctx, basedn); + ldb_dn_add_child_fmt(msg.dn, "cn=%s", name); + + msg.num_elements = 3; + msg.elements = el; + + el[0].flags = LDB_FLAG_MOD_DELETE; + el[0].name = talloc_strdup(tmp_ctx, "mail"); + el[0].num_values = 0; + + el[1].flags = LDB_FLAG_MOD_ADD; + el[1].name = talloc_strdup(tmp_ctx, "mail"); + el[1].num_values = 1; + el[1].values = &vals[1]; + vals[1].data = (uint8_t *)talloc_asprintf(tmp_ctx, "%s@other.example.com", name); + vals[1].length = strlen((char *)vals[1].data); + + el[2].flags = LDB_FLAG_MOD_REPLACE; + el[2].name = talloc_strdup(tmp_ctx, "mail"); + el[2].num_values = 1; + el[2].values = &vals[2]; + vals[2].data = (uint8_t *)talloc_asprintf(tmp_ctx, "%s@other2.example.com", name); + vals[2].length = strlen((char *)vals[2].data); + + if (ldb_modify(ldb, &msg) != LDB_SUCCESS) { + printf("Modify of %s failed - %s\n", name, ldb_errstring(ldb)); + exit(LDB_ERR_OPERATIONS_ERROR); + } + + printf("Modifying uid %s\r", name); + fflush(stdout); + + talloc_free(tmp_ctx); + } + + printf("\n"); +} + + +static void delete_records(struct ldb_context *ldb, + struct ldb_dn *basedn, + unsigned int count) +{ + unsigned int i; + + for (i=0;i<count;i++) { + struct ldb_dn *dn; + char *name = talloc_asprintf(ldb, "Test%d", i); + dn = ldb_dn_copy(name, basedn); + ldb_dn_add_child_fmt(dn, "cn=%s", name); + + printf("Deleting uid Test%d\r", i); + fflush(stdout); + + if (ldb_delete(ldb, dn) != LDB_SUCCESS) { + printf("Delete of %s failed - %s\n", ldb_dn_get_linearized(dn), ldb_errstring(ldb)); + exit(LDB_ERR_OPERATIONS_ERROR); + } + talloc_free(name); + } + + printf("\n"); +} + +static void search_uid(struct ldb_context *ldb, struct ldb_dn *basedn, + unsigned int nrecords, unsigned int nsearches) +{ + unsigned int i; + + for (i=0;i<nsearches;i++) { + int uid = (i * 700 + 17) % (nrecords * 2); + char *expr; + struct ldb_result *res = NULL; + int ret; + + expr = talloc_asprintf(ldb, "(uid=TEST%d)", uid); + ret = ldb_search(ldb, ldb, &res, basedn, LDB_SCOPE_SUBTREE, NULL, "%s", expr); + + if (ret != LDB_SUCCESS || (uid < nrecords && res->count != 1)) { + printf("Failed to find %s - %s\n", expr, ldb_errstring(ldb)); + exit(LDB_ERR_OPERATIONS_ERROR); + } + + if (uid >= nrecords && res->count > 0) { + printf("Found %s !? - %d\n", expr, ret); + exit(LDB_ERR_OPERATIONS_ERROR); + } + + printf("Testing uid %d/%d - %d \r", i, uid, res->count); + fflush(stdout); + + talloc_free(res); + talloc_free(expr); + } + + printf("\n"); +} + +static void start_test(struct ldb_context *ldb, unsigned int nrecords, + unsigned int nsearches) +{ + struct ldb_dn *basedn; + + basedn = ldb_dn_new(ldb, ldb, options->basedn); + if ( ! ldb_dn_validate(basedn)) { + printf("Invalid base DN format\n"); + exit(LDB_ERR_INVALID_DN_SYNTAX); + } + + printf("Adding %d records\n", nrecords); + add_records(ldb, basedn, nrecords); + + printf("Starting search on uid\n"); + _start_timer(); + search_uid(ldb, basedn, nrecords, nsearches); + printf("uid search took %.2f seconds\n", _end_timer()); + + printf("Modifying records\n"); + modify_records(ldb, basedn, nrecords); + + printf("Deleting records\n"); + delete_records(ldb, basedn, nrecords); +} + + +/* + 2) Store an @indexlist record + + 3) Store a record that contains fields that should be index according +to @index + + 4) disconnection from database + + 5) connect to same database + + 6) search for record added in step 3 using a search key that should +be indexed +*/ +static void start_test_index(struct ldb_context **ldb) +{ + struct ldb_message *msg; + struct ldb_result *res = NULL; + struct ldb_dn *indexlist; + struct ldb_dn *basedn; + int ret; + unsigned int flags = 0; + const char *specials; + + specials = getenv("LDB_SPECIALS"); + if (specials && atoi(specials) == 0) { + printf("LDB_SPECIALS disabled - skipping index test\n"); + return; + } + + if (options->nosync) { + flags |= LDB_FLG_NOSYNC; + } + + printf("Starting index test\n"); + + indexlist = ldb_dn_new(*ldb, *ldb, "@INDEXLIST"); + + ldb_delete(*ldb, indexlist); + + msg = ldb_msg_new(NULL); + if (msg == NULL) { + printf("ldb_msg_new failed\n"); + exit(LDB_ERR_OPERATIONS_ERROR); + } + + msg->dn = indexlist; + ldb_msg_add_string(msg, "@IDXATTR", strdup("uid")); + + if (ldb_add(*ldb, msg) != 0) { + printf("Add of %s failed - %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(*ldb)); + exit(LDB_ERR_OPERATIONS_ERROR); + } + + basedn = ldb_dn_new(*ldb, *ldb, options->basedn); + + memset(msg, 0, sizeof(*msg)); + msg->dn = ldb_dn_copy(msg, basedn); + ldb_dn_add_child_fmt(msg->dn, "cn=test"); + ldb_msg_add_string(msg, "cn", strdup("test")); + ldb_msg_add_string(msg, "sn", strdup("test")); + ldb_msg_add_string(msg, "uid", strdup("test")); + ldb_msg_add_string(msg, "objectClass", strdup("OpenLDAPperson")); + + if (ldb_add(*ldb, msg) != LDB_SUCCESS) { + printf("Add of %s failed - %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(*ldb)); + exit(LDB_ERR_OPERATIONS_ERROR); + } + + if (talloc_free(*ldb) != 0) { + printf("failed to free/close ldb database"); + exit(LDB_ERR_OPERATIONS_ERROR); + } + + (*ldb) = ldb_init(options, NULL); + + ret = ldb_connect(*ldb, options->url, flags, NULL); + if (ret != LDB_SUCCESS) { + printf("failed to connect to %s\n", options->url); + exit(LDB_ERR_OPERATIONS_ERROR); + } + + basedn = ldb_dn_new(*ldb, *ldb, options->basedn); + msg->dn = basedn; + ldb_dn_add_child_fmt(msg->dn, "cn=test"); + + ret = ldb_search(*ldb, *ldb, &res, basedn, LDB_SCOPE_SUBTREE, NULL, "uid=test"); + if (ret != LDB_SUCCESS) { + printf("Search with (uid=test) filter failed!\n"); + exit(LDB_ERR_OPERATIONS_ERROR); + } + if(res->count != 1) { + printf("Should have found 1 record - found %d\n", res->count); + exit(LDB_ERR_OPERATIONS_ERROR); + } + + indexlist = ldb_dn_new(*ldb, *ldb, "@INDEXLIST"); + + if (ldb_delete(*ldb, msg->dn) != 0 || + ldb_delete(*ldb, indexlist) != 0) { + printf("cleanup failed - %s\n", ldb_errstring(*ldb)); + exit(LDB_ERR_OPERATIONS_ERROR); + } + + printf("Finished index test\n"); +} + + +static void usage(struct ldb_context *ldb) +{ + printf("Usage: ldbtest <options>\n"); + printf("Options:\n"); + printf(" -H ldb_url choose the database (or $LDB_URL)\n"); + printf(" --num-records nrecords database size to use\n"); + printf(" --num-searches nsearches number of searches to do\n"); + printf("\n"); + printf("tests ldb API\n\n"); + exit(LDB_ERR_OPERATIONS_ERROR); +} + +int main(int argc, const char **argv) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + struct ldb_context *ldb; + + ldb = ldb_init(mem_ctx, NULL); + if (ldb == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + options = ldb_cmdline_process(ldb, argc, argv, usage); + + talloc_steal(mem_ctx, options); + + if (options->basedn == NULL) { + options->basedn = "ou=Ldb Test,ou=People,o=University of Michigan,c=TEST"; + } + + srandom(1); + + printf("Testing with num-records=%d and num-searches=%d\n", + options->num_records, options->num_searches); + + start_test(ldb, + (unsigned int) options->num_records, + (unsigned int) options->num_searches); + + start_test_index(&ldb); + + talloc_free(mem_ctx); + + return LDB_SUCCESS; +} diff --git a/lib/ldb/tools/ldbutil.c b/lib/ldb/tools/ldbutil.c new file mode 100644 index 0000000..9ff7fad --- /dev/null +++ b/lib/ldb/tools/ldbutil.c @@ -0,0 +1,220 @@ +/* + ldb database library utility + + Copyright (C) Matthieu Patou 2009 + + ** 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 + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Description: Common function used by ldb_add/ldb_modify/ldb_delete + * + * Author: Matthieu Patou + */ + +#include "replace.h" +#include "ldb.h" +#include "ldb_module.h" +#include "ldbutil.h" + + +/* autostarts a transacion if none active */ +static int ldb_do_autotransaction(struct ldb_context *ldb, + struct ldb_request *req) +{ + int ret; + + ret = ldb_transaction_start(ldb); + if (ret != LDB_SUCCESS) { + return ret; + } + + ret = ldb_request(ldb, req); + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + + if (ret == LDB_SUCCESS) { + return ldb_transaction_commit(ldb); + } + ldb_transaction_cancel(ldb); + + if (ldb_errstring(ldb) == NULL) { + /* no error string was setup by the backend */ + ldb_asprintf_errstring(ldb, "%s (%d)", ldb_strerror(ret), ret); + } + + return ret; +} +/* + Same as ldb_add but accept control +*/ +int ldb_add_ctrl(struct ldb_context *ldb, + const struct ldb_message *message, + struct ldb_control **controls) +{ + struct ldb_request *req; + int ret; + + ret = ldb_msg_sanity_check(ldb, message); + if (ret != LDB_SUCCESS) { + return ret; + } + + ret = ldb_build_add_req(&req, ldb, ldb, + message, + controls, + NULL, + ldb_op_default_callback, + NULL); + + if (ret != LDB_SUCCESS) return ret; + + /* do request and autostart a transaction */ + ret = ldb_do_autotransaction(ldb, req); + + talloc_free(req); + return ret; +} + +/* + same as ldb_delete but accept control +*/ +int ldb_delete_ctrl(struct ldb_context *ldb, struct ldb_dn *dn, + struct ldb_control **controls) +{ + struct ldb_request *req; + int ret; + + ret = ldb_build_del_req(&req, ldb, ldb, + dn, + controls, + NULL, + ldb_op_default_callback, + NULL); + + if (ret != LDB_SUCCESS) return ret; + + /* do request and autostart a transaction */ + ret = ldb_do_autotransaction(ldb, req); + + talloc_free(req); + return ret; +} + + +/* + same as ldb_modify, but accepts controls +*/ +int ldb_modify_ctrl(struct ldb_context *ldb, + const struct ldb_message *message, + struct ldb_control **controls) +{ + struct ldb_request *req; + int ret; + + ret = ldb_msg_sanity_check(ldb, message); + if (ret != LDB_SUCCESS) { + return ret; + } + + ret = ldb_build_mod_req(&req, ldb, ldb, + message, + controls, + NULL, + ldb_op_default_callback, + NULL); + + if (ret != LDB_SUCCESS) return ret; + + /* do request and autostart a transaction */ + ret = ldb_do_autotransaction(ldb, req); + + talloc_free(req); + return ret; +} + + +/* + ldb_search with controls +*/ +int ldb_search_ctrl(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, + struct ldb_result **result, struct ldb_dn *base, + enum ldb_scope scope, const char * const *attrs, + struct ldb_control **controls, + const char *exp_fmt, ...) +{ + struct ldb_request *req; + struct ldb_result *res; + char *expression; + va_list ap; + int ret; + + expression = NULL; + *result = NULL; + req = NULL; + + res = talloc_zero(mem_ctx, struct ldb_result); + if (!res) { + return LDB_ERR_OPERATIONS_ERROR; + } + + 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, + controls, + res, + ldb_search_default_callback, + NULL); + ldb_req_set_location(req, "ldb_search_ctrl"); + + if (ret != LDB_SUCCESS) goto done; + + ret = ldb_request(ldb, req); + + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + +done: + if (ret != LDB_SUCCESS) { + talloc_free(res); + res = NULL; + } + + talloc_free(expression); + talloc_free(req); + + *result = res; + return ret; +} diff --git a/lib/ldb/tools/ldbutil.h b/lib/ldb/tools/ldbutil.h new file mode 100644 index 0000000..6723863 --- /dev/null +++ b/lib/ldb/tools/ldbutil.h @@ -0,0 +1,46 @@ +/* + ldb database library utility header file + + Copyright (C) Matthieu Patou 2009 + + ** 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 + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb + * + * Description: Common function used by ldb_add/ldb_modify/ldb_delete + * + * Author: Matthieu Patou + */ + +#include "ldb.h" + +int ldb_add_ctrl(struct ldb_context *ldb, + const struct ldb_message *message, + struct ldb_control **controls); +int ldb_delete_ctrl(struct ldb_context *ldb, struct ldb_dn *dn, + struct ldb_control **controls); +int ldb_modify_ctrl(struct ldb_context *ldb, + const struct ldb_message *message, + struct ldb_control **controls); +int ldb_search_ctrl(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, + struct ldb_result **result, struct ldb_dn *base, + enum ldb_scope scope, const char * const *attrs, + struct ldb_control **controls, + const char *exp_fmt, ...) PRINTF_ATTRIBUTE(8,9); |