summaryrefslogtreecommitdiff
path: root/source4/lib/tdb/tools
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2004-05-24 16:27:23 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:56:02 -0500
commita3d61e0485c70ec5215c34b6caf40e2e6c6c5338 (patch)
treec6dd1cdffceadf0e88f5ddbde8507c49478f55fa /source4/lib/tdb/tools
parent29a93d26cfc17dad96c74da9b05535b38552e4c0 (diff)
downloadsamba-a3d61e0485c70ec5215c34b6caf40e2e6c6c5338.tar.gz
samba-a3d61e0485c70ec5215c34b6caf40e2e6c6c5338.tar.bz2
samba-a3d61e0485c70ec5215c34b6caf40e2e6c6c5338.zip
r848: convert lib/tdb into the same layout as lib/ldb
metze (This used to be commit bacab322ce89979f0ad0811cd15b73d81eceb69d)
Diffstat (limited to 'source4/lib/tdb/tools')
-rw-r--r--source4/lib/tdb/tools/Makefile29
-rw-r--r--source4/lib/tdb/tools/tdbbackup.c149
-rw-r--r--source4/lib/tdb/tools/tdbdump.c89
-rw-r--r--source4/lib/tdb/tools/tdbtest.c263
-rw-r--r--source4/lib/tdb/tools/tdbtool.c547
-rw-r--r--source4/lib/tdb/tools/tdbtorture.c227
6 files changed, 1304 insertions, 0 deletions
diff --git a/source4/lib/tdb/tools/Makefile b/source4/lib/tdb/tools/Makefile
new file mode 100644
index 0000000000..59fbb079bd
--- /dev/null
+++ b/source4/lib/tdb/tools/Makefile
@@ -0,0 +1,29 @@
+#
+# Makefile for tdb directory
+#
+
+CFLAGS = -DSTANDALONE -DTDB_DEBUG -g -DHAVE_MMAP=1
+CC = gcc
+
+PROGS = tdbtest tdbtool tdbtorture
+TDB_OBJ = tdb.o spinlock.o
+
+default: $(PROGS)
+
+tdbtest: tdbtest.o $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o tdbtest tdbtest.o $(TDB_OBJ) -lgdbm
+
+tdbtool: tdbtool.o $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o tdbtool tdbtool.o $(TDB_OBJ)
+
+tdbtorture: tdbtorture.o $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o tdbtorture tdbtorture.o $(TDB_OBJ)
+
+tdbdump: tdbdump.o $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o tdbdump tdbdump.o $(TDB_OBJ)
+
+tdbbackup: tdbbackup.o $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o tdbbackup tdbbackup.o $(TDB_OBJ)
+
+clean:
+ rm -f $(PROGS) *.o *~ *% core test.db test.tdb test.gdbm
diff --git a/source4/lib/tdb/tools/tdbbackup.c b/source4/lib/tdb/tools/tdbbackup.c
new file mode 100644
index 0000000000..1a0e1c1588
--- /dev/null
+++ b/source4/lib/tdb/tools/tdbbackup.c
@@ -0,0 +1,149 @@
+/*
+ Unix SMB/CIFS implementation.
+ low level tdb backup and restore utility
+ Copyright (C) Andrew Tridgell 2002
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+
+ This program is meant for backup/restore of tdb databases. Typical usage would be:
+ tdbbackup *.tdb
+ when Samba shuts down cleanly, which will make a backup of all the local databases
+ to *.bak files. Then on Samba startup you would use:
+ tdbbackup -v *.tdb
+ and this will check the databases for corruption and if corruption is detected then
+ the backup will be restored.
+
+ You may also like to do a backup on a regular basis while Samba is
+ running, perhaps using cron.
+
+ The reason this program is needed is to cope with power failures
+ while Samba is running. A power failure could lead to database
+ corruption and Samba will then not start correctly.
+
+ Note that many of the databases in Samba are transient and thus
+ don't need to be backed up, so you can optimise the above a little
+ by only running the backup on the critical databases.
+
+ */
+
+#ifdef STANDALONE
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+
+#else
+
+#include "includes.h"
+
+#endif
+
+#include "tdb.h"
+#include "tdbback.h"
+
+/*
+ see if one file is newer than another
+*/
+static int file_newer(const char *fname1, const char *fname2)
+{
+ struct stat st1, st2;
+ if (stat(fname1, &st1) != 0) {
+ return 0;
+ }
+ if (stat(fname2, &st2) != 0) {
+ return 1;
+ }
+ return (st1.st_mtime > st2.st_mtime);
+}
+
+static void usage(void)
+{
+ printf("Usage: tdbbackup [options] <fname...>\n\n");
+ printf(" -h this help message\n");
+ printf(" -s suffix set the backup suffix\n");
+ printf(" -v verify mode (restore if corrupt)\n");
+}
+
+
+ int main(int argc, char *argv[])
+{
+ int i;
+ int ret = 0;
+ int c;
+ int verify = 0;
+ const char *suffix = ".bak";
+ extern int optind;
+ extern char *optarg;
+
+ while ((c = getopt(argc, argv, "vhs:")) != -1) {
+ switch (c) {
+ case 'h':
+ usage();
+ exit(0);
+ case 'v':
+ verify = 1;
+ break;
+ case 's':
+ suffix = optarg;
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ for (i=0; i<argc; i++) {
+ const char *fname = argv[i];
+ char *bak_name;
+
+ bak_name = add_suffix(fname, suffix);
+
+ if (verify) {
+ if (verify_tdb(fname, bak_name) != 0) {
+ ret = 1;
+ }
+ } else {
+ if (file_newer(fname, bak_name) &&
+ backup_tdb(fname, bak_name) != 0) {
+ ret = 1;
+ }
+ }
+
+ free(bak_name);
+ }
+
+ return ret;
+}
diff --git a/source4/lib/tdb/tools/tdbdump.c b/source4/lib/tdb/tools/tdbdump.c
new file mode 100644
index 0000000000..9c1dc2761b
--- /dev/null
+++ b/source4/lib/tdb/tools/tdbdump.c
@@ -0,0 +1,89 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple tdb dump util
+ Copyright (C) Andrew Tridgell 2001
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+#include "tdb.h"
+
+static void print_data(TDB_DATA d)
+{
+ unsigned char *p = d.dptr;
+ int len = d.dsize;
+ while (len--) {
+ if (isprint(*p) && !strchr("\"\\", *p)) {
+ fputc(*p, stdout);
+ } else {
+ printf("\\%02X", *p);
+ }
+ p++;
+ }
+}
+
+static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ printf("{\n");
+ printf("key = \"");
+ print_data(key);
+ printf("\"\n");
+ printf("data = \"");
+ print_data(dbuf);
+ printf("\"\n");
+ printf("}\n");
+ return 0;
+}
+
+static int dump_tdb(const char *fname)
+{
+ TDB_CONTEXT *tdb;
+
+ tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
+ if (!tdb) {
+ printf("Failed to open %s\n", fname);
+ return 1;
+ }
+
+ tdb_traverse(tdb, traverse_fn, NULL);
+ return 0;
+}
+
+ int main(int argc, char *argv[])
+{
+ char *fname;
+
+ if (argc < 2) {
+ printf("Usage: tdbdump <fname>\n");
+ exit(1);
+ }
+
+ fname = argv[1];
+
+ return dump_tdb(fname);
+}
diff --git a/source4/lib/tdb/tools/tdbtest.c b/source4/lib/tdb/tools/tdbtest.c
new file mode 100644
index 0000000000..89295a3291
--- /dev/null
+++ b/source4/lib/tdb/tools/tdbtest.c
@@ -0,0 +1,263 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <signal.h>
+#include "tdb.h"
+#include <gdbm.h>
+
+/* a test program for tdb - the trivial database */
+
+
+
+#define DELETE_PROB 7
+#define STORE_PROB 5
+
+static TDB_CONTEXT *db;
+static GDBM_FILE gdbm;
+
+struct timeval tp1,tp2;
+
+static void start_timer(void)
+{
+ gettimeofday(&tp1,NULL);
+}
+
+static double end_timer(void)
+{
+ gettimeofday(&tp2,NULL);
+ return((tp2.tv_sec - tp1.tv_sec) +
+ (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
+}
+
+static void fatal(char *why)
+{
+ perror(why);
+ exit(1);
+}
+
+static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(stdout, format, ap);
+ va_end(ap);
+ fflush(stdout);
+}
+
+static void compare_db(void)
+{
+ TDB_DATA d, key, nextkey;
+ datum gd, gkey, gnextkey;
+
+ key = tdb_firstkey(db);
+ while (key.dptr) {
+ d = tdb_fetch(db, key);
+ gkey.dptr = key.dptr;
+ gkey.dsize = key.dsize;
+
+ gd = gdbm_fetch(gdbm, gkey);
+
+ if (!gd.dptr) fatal("key not in gdbm");
+ if (gd.dsize != d.dsize) fatal("data sizes differ");
+ if (memcmp(gd.dptr, d.dptr, d.dsize)) {
+ fatal("data differs");
+ }
+
+ nextkey = tdb_nextkey(db, key);
+ free(key.dptr);
+ free(d.dptr);
+ free(gd.dptr);
+ key = nextkey;
+ }
+
+ gkey = gdbm_firstkey(gdbm);
+ while (gkey.dptr) {
+ gd = gdbm_fetch(gdbm, gkey);
+ key.dptr = gkey.dptr;
+ key.dsize = gkey.dsize;
+
+ d = tdb_fetch(db, key);
+
+ if (!d.dptr) fatal("key not in db");
+ if (d.dsize != gd.dsize) fatal("data sizes differ");
+ if (memcmp(d.dptr, gd.dptr, gd.dsize)) {
+ fatal("data differs");
+ }
+
+ gnextkey = gdbm_nextkey(gdbm, gkey);
+ free(gkey.dptr);
+ free(gd.dptr);
+ free(d.dptr);
+ gkey = gnextkey;
+ }
+}
+
+static char *randbuf(int len)
+{
+ char *buf;
+ int i;
+ buf = (char *)malloc(len+1);
+
+ for (i=0;i<len;i++) {
+ buf[i] = 'a' + (rand() % 26);
+ }
+ buf[i] = 0;
+ return buf;
+}
+
+static void addrec_db(void)
+{
+ int klen, dlen;
+ char *k, *d;
+ TDB_DATA key, data;
+
+ klen = 1 + (rand() % 4);
+ dlen = 1 + (rand() % 100);
+
+ k = randbuf(klen);
+ d = randbuf(dlen);
+
+ key.dptr = k;
+ key.dsize = klen+1;
+
+ data.dptr = d;
+ data.dsize = dlen+1;
+
+ if (rand() % DELETE_PROB == 0) {
+ tdb_delete(db, key);
+ } else if (rand() % STORE_PROB == 0) {
+ if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+ fatal("tdb_store failed");
+ }
+ } else {
+ data = tdb_fetch(db, key);
+ if (data.dptr) free(data.dptr);
+ }
+
+ free(k);
+ free(d);
+}
+
+static void addrec_gdbm(void)
+{
+ int klen, dlen;
+ char *k, *d;
+ datum key, data;
+
+ klen = 1 + (rand() % 4);
+ dlen = 1 + (rand() % 100);
+
+ k = randbuf(klen);
+ d = randbuf(dlen);
+
+ key.dptr = k;
+ key.dsize = klen+1;
+
+ data.dptr = d;
+ data.dsize = dlen+1;
+
+ if (rand() % DELETE_PROB == 0) {
+ gdbm_delete(gdbm, key);
+ } else if (rand() % STORE_PROB == 0) {
+ if (gdbm_store(gdbm, key, data, GDBM_REPLACE) != 0) {
+ fatal("gdbm_store failed");
+ }
+ } else {
+ data = gdbm_fetch(gdbm, key);
+ if (data.dptr) free(data.dptr);
+ }
+
+ free(k);
+ free(d);
+}
+
+static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+#if 0
+ printf("[%s] [%s]\n", key.dptr, dbuf.dptr);
+#endif
+ tdb_delete(tdb, key);
+ return 0;
+}
+
+static void merge_test(void)
+{
+ int i;
+ char keys[5][2];
+ TDB_DATA key, data;
+
+ for (i = 0; i < 5; i++) {
+ sprintf(keys[i], "%d", i);
+ key.dptr = keys[i];
+ key.dsize = 2;
+
+ data.dptr = "test";
+ data.dsize = 4;
+
+ if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+ fatal("tdb_store failed");
+ }
+ }
+
+ key.dptr = keys[0];
+ tdb_delete(db, key);
+ key.dptr = keys[4];
+ tdb_delete(db, key);
+ key.dptr = keys[2];
+ tdb_delete(db, key);
+ key.dptr = keys[1];
+ tdb_delete(db, key);
+ key.dptr = keys[3];
+ tdb_delete(db, key);
+}
+
+int main(int argc, char *argv[])
+{
+ int i, seed=0;
+ int loops = 10000;
+
+ unlink("test.gdbm");
+
+ db = tdb_open("test.tdb", 0, TDB_CLEAR_IF_FIRST,
+ O_RDWR | O_CREAT | O_TRUNC, 0600);
+ gdbm = gdbm_open("test.gdbm", 512, GDBM_WRITER|GDBM_NEWDB|GDBM_FAST,
+ 0600, NULL);
+
+ if (!db || !gdbm) {
+ fatal("db open failed");
+ }
+
+ tdb_logging_function(db, tdb_log);
+
+#if 1
+ srand(seed);
+ start_timer();
+ for (i=0;i<loops;i++) addrec_gdbm();
+ printf("gdbm got %.2f ops/sec\n", i/end_timer());
+#endif
+
+ merge_test();
+
+ srand(seed);
+ start_timer();
+ for (i=0;i<loops;i++) addrec_db();
+ printf("tdb got %.2f ops/sec\n", i/end_timer());
+
+ compare_db();
+
+ printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL));
+ printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL));
+
+ tdb_close(db);
+ gdbm_close(gdbm);
+
+ return 0;
+}
diff --git a/source4/lib/tdb/tools/tdbtool.c b/source4/lib/tdb/tools/tdbtool.c
new file mode 100644
index 0000000000..92009dcef4
--- /dev/null
+++ b/source4/lib/tdb/tools/tdbtool.c
@@ -0,0 +1,547 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba database functions
+ Copyright (C) Andrew Tridgell 1999-2000
+ Copyright (C) Paul `Rusty' Russell 2000
+ Copyright (C) Jeremy Allison 2000
+ Copyright (C) Andrew Esh 2001
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+#include "tdb.h"
+
+/* a tdb tool for manipulating a tdb database */
+
+#define FSTRING_LEN 256
+typedef char fstring[FSTRING_LEN];
+
+typedef struct connections_key {
+ pid_t pid;
+ int cnum;
+ fstring name;
+} connections_key;
+
+typedef struct connections_data {
+ int magic;
+ pid_t pid;
+ int cnum;
+ uid_t uid;
+ gid_t gid;
+ char name[24];
+ char addr[24];
+ char machine[128];
+ time_t start;
+} connections_data;
+
+static TDB_CONTEXT *tdb;
+
+static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
+
+static void print_asc(unsigned char *buf,int len)
+{
+ int i;
+
+ /* We're probably printing ASCII strings so don't try to display
+ the trailing NULL character. */
+
+ if (buf[len - 1] == 0)
+ len--;
+
+ for (i=0;i<len;i++)
+ printf("%c",isprint(buf[i])?buf[i]:'.');
+}
+
+static void print_data(unsigned char *buf,int len)
+{
+ int i=0;
+ if (len<=0) return;
+ printf("[%03X] ",i);
+ for (i=0;i<len;) {
+ printf("%02X ",(int)buf[i]);
+ i++;
+ if (i%8 == 0) printf(" ");
+ if (i%16 == 0) {
+ print_asc(&buf[i-16],8); printf(" ");
+ print_asc(&buf[i-8],8); printf("\n");
+ if (i<len) printf("[%03X] ",i);
+ }
+ }
+ if (i%16) {
+ int n;
+
+ n = 16 - (i%16);
+ printf(" ");
+ if (n>8) printf(" ");
+ while (n--) printf(" ");
+
+ n = i%16;
+ if (n > 8) n = 8;
+ print_asc(&buf[i-(i%16)],n); printf(" ");
+ n = (i%16) - n;
+ if (n>0) print_asc(&buf[i-n],n);
+ printf("\n");
+ }
+}
+
+static void help(void)
+{
+ printf("\n"
+"tdbtool: \n"
+" create dbname : create a database\n"
+" open dbname : open an existing database\n"
+" erase : erase the database\n"
+" dump : dump the database as strings\n"
+" insert key data : insert a record\n"
+" move key file : move a record to a destination tdb\n"
+" store key data : store a record (replace)\n"
+" show key : show a record by key\n"
+" delete key : delete a record by key\n"
+" list : print the database hash table and freelist\n"
+" free : print the database freelist\n"
+" 1 | first : print the first record\n"
+" n | next : print the next record\n"
+" q | quit : terminate\n"
+" \\n : repeat 'next' command\n"
+"\n");
+}
+
+static void terror(char *why)
+{
+ printf("%s\n", why);
+}
+
+static char *get_token(int startover)
+{
+ static char tmp[1024];
+ static char *cont = NULL;
+ char *insert, *start;
+ char *k = strtok(NULL, " ");
+
+ if (!k)
+ return NULL;
+
+ if (startover)
+ start = tmp;
+ else
+ start = cont;
+
+ strcpy(start, k);
+ insert = start + strlen(start) - 1;
+ while (*insert == '\\') {
+ *insert++ = ' ';
+ k = strtok(NULL, " ");
+ if (!k)
+ break;
+ strcpy(insert, k);
+ insert = start + strlen(start) - 1;
+ }
+
+ /* Get ready for next call */
+ cont = start + strlen(start) + 1;
+ return start;
+}
+
+static void create_tdb(void)
+{
+ char *tok = get_token(1);
+ if (!tok) {
+ help();
+ return;
+ }
+ if (tdb) tdb_close(tdb);
+ tdb = tdb_open(tok, 0, TDB_CLEAR_IF_FIRST,
+ O_RDWR | O_CREAT | O_TRUNC, 0600);
+ if (!tdb) {
+ printf("Could not create %s: %s\n", tok, strerror(errno));
+ }
+}
+
+static void open_tdb(void)
+{
+ char *tok = get_token(1);
+ if (!tok) {
+ help();
+ return;
+ }
+ if (tdb) tdb_close(tdb);
+ tdb = tdb_open(tok, 0, 0, O_RDWR, 0600);
+ if (!tdb) {
+ printf("Could not open %s: %s\n", tok, strerror(errno));
+ }
+}
+
+static void insert_tdb(void)
+{
+ char *k = get_token(1);
+ char *d = get_token(0);
+ TDB_DATA key, dbuf;
+
+ if (!k || !d) {
+ help();
+ return;
+ }
+
+ key.dptr = k;
+ key.dsize = strlen(k)+1;
+ dbuf.dptr = d;
+ dbuf.dsize = strlen(d)+1;
+
+ if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
+ terror("insert failed");
+ }
+}
+
+static void store_tdb(void)
+{
+ char *k = get_token(1);
+ char *d = get_token(0);
+ TDB_DATA key, dbuf;
+
+ if (!k || !d) {
+ help();
+ return;
+ }
+
+ key.dptr = k;
+ key.dsize = strlen(k)+1;
+ dbuf.dptr = d;
+ dbuf.dsize = strlen(d)+1;
+
+ printf("Storing key:\n");
+ print_rec(tdb, key, dbuf, NULL);
+
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
+ terror("store failed");
+ }
+}
+
+static void show_tdb(void)
+{
+ char *k = get_token(1);
+ TDB_DATA key, dbuf;
+
+ if (!k) {
+ help();
+ return;
+ }
+
+ key.dptr = k;
+ key.dsize = strlen(k)+1;
+
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr) {
+ /* maybe it is non-NULL terminated key? */
+ key.dsize = strlen(k);
+ dbuf = tdb_fetch(tdb, key);
+
+ if ( !dbuf.dptr ) {
+ terror("fetch failed");
+ return;
+ }
+ }
+
+ /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
+ print_rec(tdb, key, dbuf, NULL);
+
+ free( dbuf.dptr );
+
+ return;
+}
+
+static void delete_tdb(void)
+{
+ char *k = get_token(1);
+ TDB_DATA key;
+
+ if (!k) {
+ help();
+ return;
+ }
+
+ key.dptr = k;
+ key.dsize = strlen(k)+1;
+
+ if (tdb_delete(tdb, key) != 0) {
+ terror("delete failed");
+ }
+}
+
+static void move_rec(void)
+{
+ char *k = get_token(1);
+ char *file = get_token(0);
+ TDB_DATA key, dbuf;
+ TDB_CONTEXT *dst_tdb;
+
+ if (!k) {
+ help();
+ return;
+ }
+
+ if ( !file ) {
+ terror("need destination tdb name");
+ return;
+ }
+
+ key.dptr = k;
+ key.dsize = strlen(k)+1;
+
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr) {
+ /* maybe it is non-NULL terminated key? */
+ key.dsize = strlen(k);
+ dbuf = tdb_fetch(tdb, key);
+
+ if ( !dbuf.dptr ) {
+ terror("fetch failed");
+ return;
+ }
+ }
+
+ print_rec(tdb, key, dbuf, NULL);
+
+ dst_tdb = tdb_open(file, 0, 0, O_RDWR, 0600);
+ if ( !dst_tdb ) {
+ terror("unable to open destination tdb");
+ return;
+ }
+
+ if ( tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) == -1 ) {
+ terror("failed to move record");
+ }
+ else
+ printf("record moved\n");
+
+ tdb_close( dst_tdb );
+
+ return;
+}
+
+#if 0
+static int print_conn_key(TDB_DATA key)
+{
+ printf( "pid =%5d ", ((connections_key*)key.dptr)->pid);
+ printf( "cnum =%10d ", ((connections_key*)key.dptr)->cnum);
+ printf( "name =[%s]\n", ((connections_key*)key.dptr)->name);
+ return 0;
+}
+
+static int print_conn_data(TDB_DATA dbuf)
+{
+ printf( "pid =%5d ", ((connections_data*)dbuf.dptr)->pid);
+ printf( "cnum =%10d ", ((connections_data*)dbuf.dptr)->cnum);
+ printf( "name =[%s]\n", ((connections_data*)dbuf.dptr)->name);
+
+ printf( "uid =%5d ", ((connections_data*)dbuf.dptr)->uid);
+ printf( "addr =[%s]\n", ((connections_data*)dbuf.dptr)->addr);
+ printf( "gid =%5d ", ((connections_data*)dbuf.dptr)->gid);
+ printf( "machine=[%s]\n", ((connections_data*)dbuf.dptr)->machine);
+ printf( "start = %s\n", ctime(&((connections_data*)dbuf.dptr)->start));
+ return 0;
+}
+#endif
+
+static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+#if 0
+ print_conn_key(key);
+ print_conn_data(dbuf);
+ return 0;
+#else
+ printf("\nkey %d bytes\n", key.dsize);
+ print_asc(key.dptr, key.dsize);
+ printf("\ndata %d bytes\n", dbuf.dsize);
+ print_data(dbuf.dptr, dbuf.dsize);
+ return 0;
+#endif
+}
+
+static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ print_asc(key.dptr, key.dsize);
+ printf("\n");
+ return 0;
+}
+
+static int total_bytes;
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ total_bytes += dbuf.dsize;
+ return 0;
+}
+
+static void info_tdb(void)
+{
+ int count;
+ total_bytes = 0;
+ if ((count = tdb_traverse(tdb, traverse_fn, NULL) == -1))
+ printf("Error = %s\n", tdb_errorstr(tdb));
+ else
+ printf("%d records totalling %d bytes\n", count, total_bytes);
+}
+
+static char *tdb_getline(char *prompt)
+{
+ static char line[1024];
+ char *p;
+ fputs(prompt, stdout);
+ line[0] = 0;
+ p = fgets(line, sizeof(line)-1, stdin);
+ if (p) p = strchr(p, '\n');
+ if (p) *p = 0;
+ return p?line:NULL;
+}
+
+static int do_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
+ void *state)
+{
+ return tdb_delete(the_tdb, key);
+}
+
+static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
+{
+ TDB_DATA dbuf;
+ *pkey = tdb_firstkey(the_tdb);
+
+ dbuf = tdb_fetch(the_tdb, *pkey);
+ if (!dbuf.dptr) terror("fetch failed");
+ else {
+ /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
+ print_rec(the_tdb, *pkey, dbuf, NULL);
+ }
+}
+
+static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
+{
+ TDB_DATA dbuf;
+ *pkey = tdb_nextkey(the_tdb, *pkey);
+
+ dbuf = tdb_fetch(the_tdb, *pkey);
+ if (!dbuf.dptr)
+ terror("fetch failed");
+ else
+ /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
+ print_rec(the_tdb, *pkey, dbuf, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+ int bIterate = 0;
+ char *line;
+ char *tok;
+ TDB_DATA iterate_kbuf;
+
+ if (argv[1]) {
+ static char tmp[1024];
+ sprintf(tmp, "open %s", argv[1]);
+ tok=strtok(tmp," ");
+ open_tdb();
+ }
+
+ while ((line = tdb_getline("tdb> "))) {
+
+ /* Shell command */
+
+ if (line[0] == '!') {
+ system(line + 1);
+ continue;
+ }
+
+ if ((tok = strtok(line," "))==NULL) {
+ if (bIterate)
+ next_record(tdb, &iterate_kbuf);
+ continue;
+ }
+ if (strcmp(tok,"create") == 0) {
+ bIterate = 0;
+ create_tdb();
+ continue;
+ } else if (strcmp(tok,"open") == 0) {
+ open_tdb();
+ continue;
+ } else if ((strcmp(tok, "q") == 0) ||
+ (strcmp(tok, "quit") == 0)) {
+ break;
+ }
+
+ /* all the rest require a open database */
+ if (!tdb) {
+ bIterate = 0;
+ terror("database not open");
+ help();
+ continue;
+ }
+
+ if (strcmp(tok,"insert") == 0) {
+ bIterate = 0;
+ insert_tdb();
+ } else if (strcmp(tok,"store") == 0) {
+ bIterate = 0;
+ store_tdb();
+ } else if (strcmp(tok,"show") == 0) {
+ bIterate = 0;
+ show_tdb();
+ } else if (strcmp(tok,"erase") == 0) {
+ bIterate = 0;
+ tdb_traverse(tdb, do_delete_fn, NULL);
+ } else if (strcmp(tok,"delete") == 0) {
+ bIterate = 0;
+ delete_tdb();
+ } else if (strcmp(tok,"dump") == 0) {
+ bIterate = 0;
+ tdb_traverse(tdb, print_rec, NULL);
+ } else if (strcmp(tok,"move") == 0) {
+ bIterate = 0;
+ move_rec();
+ } else if (strcmp(tok,"list") == 0) {
+ tdb_dump_all(tdb);
+ } else if (strcmp(tok, "free") == 0) {
+ tdb_printfreelist(tdb);
+ } else if (strcmp(tok,"info") == 0) {
+ info_tdb();
+ } else if ( (strcmp(tok, "1") == 0) ||
+ (strcmp(tok, "first") == 0)) {
+ bIterate = 1;
+ first_record(tdb, &iterate_kbuf);
+ } else if ((strcmp(tok, "n") == 0) ||
+ (strcmp(tok, "next") == 0)) {
+ next_record(tdb, &iterate_kbuf);
+ } else if ((strcmp(tok, "keys") == 0)) {
+ bIterate = 0;
+ tdb_traverse(tdb, print_key, NULL);
+ } else {
+ help();
+ }
+ }
+
+ if (tdb) tdb_close(tdb);
+
+ return 0;
+}
diff --git a/source4/lib/tdb/tools/tdbtorture.c b/source4/lib/tdb/tools/tdbtorture.c
new file mode 100644
index 0000000000..3f704e537e
--- /dev/null
+++ b/source4/lib/tdb/tools/tdbtorture.c
@@ -0,0 +1,227 @@
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include "tdb.h"
+
+/* this tests tdb by doing lots of ops from several simultaneous
+ writers - that stresses the locking code. Build with TDB_DEBUG=1
+ for best effect */
+
+
+
+#define REOPEN_PROB 30
+#define DELETE_PROB 8
+#define STORE_PROB 4
+#define APPEND_PROB 6
+#define LOCKSTORE_PROB 0
+#define TRAVERSE_PROB 20
+#define CULL_PROB 100
+#define KEYLEN 3
+#define DATALEN 100
+#define LOCKLEN 20
+
+static TDB_CONTEXT *db;
+
+static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(stdout, format, ap);
+ va_end(ap);
+ fflush(stdout);
+#if 0
+ {
+ char *ptr;
+ asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid());
+ system(ptr);
+ free(ptr);
+ }
+#endif
+}
+
+static void fatal(char *why)
+{
+ perror(why);
+ exit(1);
+}
+
+static char *randbuf(int len)
+{
+ char *buf;
+ int i;
+ buf = (char *)malloc(len+1);
+
+ for (i=0;i<len;i++) {
+ buf[i] = 'a' + (rand() % 26);
+ }
+ buf[i] = 0;
+ return buf;
+}
+
+static int cull_traverse(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf,
+ void *state)
+{
+ if (random() % CULL_PROB == 0) {
+ tdb_delete(tdb, key);
+ }
+ return 0;
+}
+
+static void addrec_db(void)
+{
+ int klen, dlen, slen;
+ char *k, *d, *s;
+ TDB_DATA key, data, lockkey;
+
+ klen = 1 + (rand() % KEYLEN);
+ dlen = 1 + (rand() % DATALEN);
+ slen = 1 + (rand() % LOCKLEN);
+
+ k = randbuf(klen);
+ d = randbuf(dlen);
+ s = randbuf(slen);
+
+ key.dptr = k;
+ key.dsize = klen+1;
+
+ data.dptr = d;
+ data.dsize = dlen+1;
+
+ lockkey.dptr = s;
+ lockkey.dsize = slen+1;
+
+#if REOPEN_PROB
+ if (random() % REOPEN_PROB == 0) {
+ tdb_reopen_all();
+ goto next;
+ }
+#endif
+
+#if DELETE_PROB
+ if (random() % DELETE_PROB == 0) {
+ tdb_delete(db, key);
+ goto next;
+ }
+#endif
+
+#if STORE_PROB
+ if (random() % STORE_PROB == 0) {
+ if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+ fatal("tdb_store failed");
+ }
+ goto next;
+ }
+#endif
+
+#if APPEND_PROB
+ if (random() % APPEND_PROB == 0) {
+ if (tdb_append(db, key, data) != 0) {
+ fatal("tdb_append failed");
+ }
+ goto next;
+ }
+#endif
+
+#if LOCKSTORE_PROB
+ if (random() % LOCKSTORE_PROB == 0) {
+ tdb_chainlock(db, lockkey);
+ data = tdb_fetch(db, key);
+ if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+ fatal("tdb_store failed");
+ }
+ if (data.dptr) free(data.dptr);
+ tdb_chainunlock(db, lockkey);
+ goto next;
+ }
+#endif
+
+#if TRAVERSE_PROB
+ if (random() % TRAVERSE_PROB == 0) {
+ tdb_traverse(db, cull_traverse, NULL);
+ goto next;
+ }
+#endif
+
+ data = tdb_fetch(db, key);
+ if (data.dptr) free(data.dptr);
+
+next:
+ free(k);
+ free(d);
+ free(s);
+}
+
+static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf,
+ void *state)
+{
+ tdb_delete(tdb, key);
+ return 0;
+}
+
+#ifndef NPROC
+#define NPROC 6
+#endif
+
+#ifndef NLOOPS
+#define NLOOPS 200000
+#endif
+
+int main(int argc, char *argv[])
+{
+ int i, seed=0;
+ int loops = NLOOPS;
+ pid_t pids[NPROC];
+
+ pids[0] = getpid();
+
+ for (i=0;i<NPROC-1;i++) {
+ if ((pids[i+1]=fork()) == 0) break;
+ }
+
+ db = tdb_open("torture.tdb", 2, TDB_CLEAR_IF_FIRST,
+ O_RDWR | O_CREAT, 0600);
+ if (!db) {
+ fatal("db open failed");
+ }
+ tdb_logging_function(db, tdb_log);
+
+ srand(seed + getpid());
+ srandom(seed + getpid() + time(NULL));
+ for (i=0;i<loops;i++) addrec_db();
+
+ tdb_traverse(db, NULL, NULL);
+ tdb_traverse(db, traverse_fn, NULL);
+ tdb_traverse(db, traverse_fn, NULL);
+
+ tdb_close(db);
+
+ if (getpid() == pids[0]) {
+ for (i=0;i<NPROC-1;i++) {
+ int status;
+ if (waitpid(pids[i+1], &status, 0) != pids[i+1]) {
+ printf("failed to wait for %d\n",
+ (int)pids[i+1]);
+ exit(1);
+ }
+ if (WEXITSTATUS(status) != 0) {
+ printf("child %d exited with status %d\n",
+ (int)pids[i+1], WEXITSTATUS(status));
+ exit(1);
+ }
+ }
+ printf("OK\n");
+ }
+
+ return 0;
+}