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); }