diff --git a/ctdb/libctdb/test/ctdb-test.c b/ctdb/libctdb/test/ctdb-test.c
index 1587a4f8c30..c1dc4b8bab5 100644
--- a/ctdb/libctdb/test/ctdb-test.c
+++ b/ctdb/libctdb/test/ctdb-test.c
@@ -447,6 +447,7 @@ int main(int argc, char *argv[])
"unfulfilled remaining. / "
"Testing blossoms fail.");
check_allocations();
+ check_databases();
dump_failinfo();
return EXIT_SUCCESS;
diff --git a/ctdb/libctdb/test/ctdb-test.h b/ctdb/libctdb/test/ctdb-test.h
index 68ab2ea8f39..ba8fbe147a0 100644
--- a/ctdb/libctdb/test/ctdb-test.h
+++ b/ctdb/libctdb/test/ctdb-test.h
@@ -16,4 +16,11 @@ struct ctdb_connection *get_ctdb(void);
/* Talloc bytes from an fd until EOF. Nul terminate. */
void *grab_fd(int fd, size_t *size);
+/* Check the databases are still ok. */
+void check_databases(void);
+
+/* Save and restore databases, in case children do damage. */
+void *save_databases(void);
+void restore_databases(void *);
+
#endif /* __HAVE_CTDB_TEST_H */
diff --git a/ctdb/libctdb/test/databases.c b/ctdb/libctdb/test/databases.c
new file mode 100644
index 00000000000..b0b9bbc20bf
--- /dev/null
+++ b/ctdb/libctdb/test/databases.c
@@ -0,0 +1,134 @@
+/*
+ database code for libctdb
+
+ Copyright (C) Rusty Russell 2010
+
+ 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 .
+*/
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "utils.h"
+#include "log.h"
+#include "ctdb-test.h"
+#include
+#include
+
+/* FIXME */
+#define DB_PATH "/tmp/ctdbd-test/dbs/"
+
+static int check_header(TDB_DATA key, TDB_DATA data, void *unused)
+{
+ struct ctdb_ltdb_header *hdr = (void *)data.dptr;
+ if (data.dsize < sizeof(*hdr)) {
+ log_line(LOG_ALWAYS, "tdb entry '%.*s' is truncated",
+ key.dsize, key.dptr);
+ return -1;
+ }
+ /* Currently a single-node cluster. */
+ if (hdr->dmaster != 0) {
+ log_line(LOG_ALWAYS, "tdb entry '%.*s' dmaster %u",
+ key.dsize, key.dptr, hdr->dmaster);
+ return -1;
+ }
+ /* Currently a single-node cluster. */
+ if (hdr->laccessor != 0) {
+ log_line(LOG_ALWAYS, "tdb entry '%.*s' laccessor %u",
+ key.dsize, key.dptr, hdr->laccessor);
+ return -1;
+ }
+ return 0;
+}
+
+static void check_database(const char *name)
+{
+ struct tdb_context *tdb = tdb_open(name, 0, TDB_DEFAULT, O_RDWR, 0);
+ if (!tdb)
+ err(1, "Opening tdb %s", name);
+
+ if (tdb_check(tdb, check_header, NULL) != 0) {
+ log_line(LOG_ALWAYS, "tdb %s is corrupt", name);
+ exit(EXIT_FAILURE);
+ }
+ tdb_close(tdb);
+}
+
+void check_databases(void)
+{
+ struct dirent *ent;
+ DIR *d = opendir(DB_PATH);
+ if (!d)
+ err(1, "Reading directory %s", DB_PATH);
+
+ while ((ent = readdir(d)) != NULL) {
+ if (strends(ent->d_name, ".tdb.0")) {
+ char *fullpath = talloc_asprintf(NULL, "%s/%s",
+ DB_PATH, ent->d_name);
+ check_database(fullpath);
+ talloc_free(fullpath);
+ }
+ }
+ closedir(d);
+}
+
+/* FIXME: We assume we don't need locks here. Not a solid assumption! */
+void *save_databases(void)
+{
+ struct tdb_context *tdb = tdb_open(NULL, 0, TDB_INTERNAL, 0, 0);
+ struct dirent *ent;
+ DIR *d = opendir(DB_PATH);
+ if (!d)
+ err(1, "Reading directory %s", DB_PATH);
+
+ while ((ent = readdir(d)) != NULL) {
+ if (strends(ent->d_name, ".tdb.0")) {
+ TDB_DATA data, key;
+ int fd;
+ char *fullpath = talloc_asprintf(NULL, "%s/%s",
+ DB_PATH, ent->d_name);
+ fd = open(fullpath, O_RDONLY);
+ if (fd < 0)
+ err(1, "Saving tdb %s", fullpath);
+ data.dptr = grab_fd(fd, &data.dsize);
+ key.dptr = (void *)fullpath;
+ key.dsize = strlen(fullpath) + 1;
+ tdb_store(tdb, key, data, TDB_INSERT);
+ talloc_free(fullpath);
+ close(fd);
+ }
+ }
+ closedir(d);
+ return tdb;
+}
+
+void restore_databases(void *_tdb)
+{
+ struct tdb_context *tdb = _tdb;
+ TDB_DATA key, data;
+
+ for (key = tdb_firstkey(tdb); key.dptr; key = tdb_nextkey(tdb, key)) {
+ int fd = open((char *)key.dptr, O_WRONLY);
+ if (fd < 0)
+ err(1, "Restoring tdb %s", (char *)key.dptr);
+ data = tdb_fetch(tdb, key);
+ write(fd, data.dptr, data.dsize);
+ free(data.dptr);
+ close(fd);
+ }
+ tdb_close(tdb);
+}
diff --git a/ctdb/libctdb/test/failtest.c b/ctdb/libctdb/test/failtest.c
index 3e80cb1f939..c43047d31f0 100644
--- a/ctdb/libctdb/test/failtest.c
+++ b/ctdb/libctdb/test/failtest.c
@@ -261,6 +261,7 @@ bool should_i_fail(const char *func, const char *caller)
size_t log_size;
char *log;
char *location = make_location(func, caller);
+ void *databases;
if (failpath)
return do_failpath(location);
@@ -286,6 +287,8 @@ bool should_i_fail(const char *func, const char *caller)
if (pipe(pfd) != 0)
err(1, "pipe failed for failtest!");
+ databases = save_databases();
+
fflush(stdout);
child = fork();
if (child == -1)
@@ -319,10 +322,11 @@ bool should_i_fail(const char *func, const char *caller)
if (WIFEXITED(status) && (WEXITSTATUS(status) == EXIT_SUCCESS
|| WEXITSTATUS(status) == EXIT_SCRIPTFAIL)) {
talloc_free(log);
+ restore_databases(databases);
return false;
}
- /* Reproduce child's path */
+ /* Reproduce child's path: leave databases for post-mortem. */
dec->failed = true;
log_line(LOG_ALWAYS, "Child %s %i on failure path: %s",
diff --git a/ctdb/libctdb/test/tui.c b/ctdb/libctdb/test/tui.c
index d411e6f0b41..1667de7846c 100644
--- a/ctdb/libctdb/test/tui.c
+++ b/ctdb/libctdb/test/tui.c
@@ -196,6 +196,7 @@ void script_fail(const char *fmt, ...)
talloc_free(str);
check_allocations();
+ check_databases();
exit(EXIT_SCRIPTFAIL);
}