From a3d61e0485c70ec5215c34b6caf40e2e6c6c5338 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 24 May 2004 16:27:23 +0000 Subject: r848: convert lib/tdb into the same layout as lib/ldb metze (This used to be commit bacab322ce89979f0ad0811cd15b73d81eceb69d) --- source4/lib/tdb/tools/Makefile | 29 ++ source4/lib/tdb/tools/tdbbackup.c | 149 ++++++++++ source4/lib/tdb/tools/tdbdump.c | 89 ++++++ source4/lib/tdb/tools/tdbtest.c | 263 ++++++++++++++++++ source4/lib/tdb/tools/tdbtool.c | 547 +++++++++++++++++++++++++++++++++++++ source4/lib/tdb/tools/tdbtorture.c | 227 +++++++++++++++ 6 files changed, 1304 insertions(+) create mode 100644 source4/lib/tdb/tools/Makefile create mode 100644 source4/lib/tdb/tools/tdbbackup.c create mode 100644 source4/lib/tdb/tools/tdbdump.c create mode 100644 source4/lib/tdb/tools/tdbtest.c create mode 100644 source4/lib/tdb/tools/tdbtool.c create mode 100644 source4/lib/tdb/tools/tdbtorture.c (limited to 'source4/lib/tdb/tools') 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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] \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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 \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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tdb.h" +#include + +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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;i8) 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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