summaryrefslogtreecommitdiffstats
path: root/ctdb/tools/ltdbtool.c
diff options
context:
space:
mode:
Diffstat (limited to 'ctdb/tools/ltdbtool.c')
-rw-r--r--ctdb/tools/ltdbtool.c395
1 files changed, 395 insertions, 0 deletions
diff --git a/ctdb/tools/ltdbtool.c b/ctdb/tools/ltdbtool.c
new file mode 100644
index 0000000..98a1b51
--- /dev/null
+++ b/ctdb/tools/ltdbtool.c
@@ -0,0 +1,395 @@
+/*
+ * ctdb local tdb tool
+ *
+ * Copyright (C) Gregor Beck 2011
+ * Copyright (C) Michael Adam 2011
+ *
+ * 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/filesys.h"
+#include "system/network.h"
+#include "system/locale.h"
+
+#include <tdb.h>
+
+#include "protocol/protocol.h"
+
+enum {
+ MAX_HEADER_SIZE=24,
+ OUT_MODE = S_IRUSR | S_IWUSR,
+ OUT_FLAGS = O_EXCL|O_CREAT|O_RDWR,
+};
+
+union ltdb_header {
+ struct ctdb_ltdb_header hdr;
+ uint32_t uints[MAX_HEADER_SIZE/4];
+};
+
+static const union ltdb_header DEFAULT_HDR = {
+ .hdr = {
+ .dmaster = -1,
+ }
+};
+
+static int help(const char* cmd)
+{
+ fprintf(stdout, ""
+"Usage: %s [options] <command>\n"
+"\n"
+"Options:\n"
+" -s {0|32|64} specify how to determine the ctdb record header size\n"
+" for the input database:\n"
+" 0: no ctdb header\n"
+" 32: ctdb header size of a 32 bit system (20 bytes)\n"
+" 64: ctdb header size of a 64 bit system (24 bytes)\n"
+" default: 32 or 64 depending on the system architecture\n"
+"\n"
+" -S <num> the number of bytes to interpret as ctdb record header\n"
+" for the input database (beware!)\n"
+"\n"
+" -o {0|32|64} specify how to determine the ctdb record header size\n"
+" for the output database\n"
+" 0: no ctdb header\n"
+" 32: ctdb header size of a 32 bit system (20 bytes)\n"
+" 64: ctdb header size of a 64 bit system (24 bytes)\n"
+" default: 32 or 64 depending on the system architecture\n"
+"\n"
+" -O <num> the number of bytes to interpret as ctdb record header\n"
+" for the output database (beware!)\n"
+"\n"
+" -e Include empty records, defaults to off\n"
+"\n"
+" -p print header (for the dump command), defaults to off\n"
+"\n"
+" -h print this help\n"
+"\n"
+"Commands:\n"
+" help print this help\n"
+" dump <db> dump the db to stdout\n"
+" convert <in_db> <out_db> convert the db\n\n", cmd);
+ return 0;
+}
+
+static int usage(const char* cmd)
+{
+ fprintf(stderr,
+ "Usage: %s dump [-e] [-p] [-s{0|32|64}] <idb>\n"
+ " %s convert [-e] [-s{0|32|64}] [-o{0|32|64}] <idb> <odb>\n"
+ " %s {help|-h}\n"
+ , cmd, cmd, cmd);
+ return -1;
+}
+
+static int
+ltdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT*, TDB_DATA, TDB_DATA,
+ struct ctdb_ltdb_header*, void *),
+ void *state, size_t hsize, bool skip_empty);
+
+struct write_record_ctx {
+ TDB_CONTEXT* tdb;
+ size_t hsize;
+ int tdb_store_flags;
+};
+
+static int
+write_record(TDB_CONTEXT* tdb, TDB_DATA key, TDB_DATA val,
+ struct ctdb_ltdb_header* hdr,
+ void* write_record_ctx);
+
+
+struct dump_record_ctx {
+ FILE* file;
+ void (*print_data)(FILE*, TDB_DATA);
+ void (*dump_header)(struct dump_record_ctx*, struct ctdb_ltdb_header*);
+};
+
+static int dump_record(TDB_CONTEXT* tdb, TDB_DATA key, TDB_DATA val,
+ struct ctdb_ltdb_header* hdr,
+ void* dump_record_ctx);
+static void print_data_tdbdump(FILE* file, TDB_DATA data);
+static void dump_header_full(struct dump_record_ctx*, struct ctdb_ltdb_header*);
+static void dump_header_nop(struct dump_record_ctx* c,
+ struct ctdb_ltdb_header* h)
+{}
+
+static int dump_db(const char* iname,
+ FILE* ofile,
+ size_t hsize,
+ bool dump_header,
+ bool empty)
+{
+ int ret = -1;
+ TDB_CONTEXT* idb = tdb_open(iname, 0, TDB_DEFAULT, O_RDONLY, 0);
+ if (!idb) {
+ perror("tdbopen in");
+ } else {
+ struct dump_record_ctx dump_ctx = {
+ .file = ofile,
+ .print_data = &print_data_tdbdump,
+ .dump_header = dump_header ? &dump_header_full
+ : &dump_header_nop,
+ };
+ ret = ltdb_traverse(idb, &dump_record, &dump_ctx, hsize, !empty);
+ tdb_close(idb);
+ }
+ return ret;
+}
+
+static int conv_db(const char* iname, const char* oname, size_t isize,
+ size_t osize, bool keep_empty)
+{
+ int ret = -1;
+ TDB_CONTEXT* idb = tdb_open(iname, 0, TDB_DEFAULT, O_RDONLY, 0);
+ if (!idb) {
+ perror("tdbopen in");
+ } else {
+ TDB_CONTEXT* odb = tdb_open(oname, 0, TDB_DEFAULT, OUT_FLAGS, OUT_MODE);
+ if (!odb) {
+ perror("tdbopen out");
+ } else {
+ struct write_record_ctx ctx = {
+ .tdb = odb,
+ .hsize = osize,
+ .tdb_store_flags = TDB_REPLACE,
+ };
+ ret = ltdb_traverse(idb, &write_record, &ctx, isize, !keep_empty);
+ tdb_close(odb);
+ }
+ tdb_close(idb);
+ }
+ return ret;
+}
+
+static bool parse_size(size_t* size, const char* arg, bool raw) {
+ long val;
+ errno = 0;
+ val = strtol(arg, (char **) NULL, 10);
+ if (errno != 0) {
+ return false;
+ }
+ if (!raw) {
+ switch(val) {
+ case 0:
+ break;
+ case 32:
+ val = 20;
+ break;
+ case 64:
+ val = 24;
+ break;
+ default:
+ return false;
+ }
+ }
+ *size = MIN(val, MAX_HEADER_SIZE);
+ return true;
+}
+
+
+int main(int argc, char* argv[])
+{
+ size_t isize = sizeof(struct ctdb_ltdb_header);
+ size_t osize = sizeof(struct ctdb_ltdb_header);
+ bool print_header = false;
+ bool keep_empty = false;
+ int opt;
+ const char *cmd, *idb, *odb;
+
+ while ((opt = getopt(argc, argv, "s:o:S:O:phe")) != -1) {
+ switch (opt) {
+ case 's':
+ case 'S':
+ if (!parse_size(&isize, optarg, isupper(opt))) {
+ return usage(argv[0]);
+ }
+ break;
+ case 'o':
+ case 'O':
+ if (!parse_size(&osize, optarg, isupper(opt))) {
+ return usage(argv[0]);
+ }
+ break;
+ case 'p':
+ print_header = true;
+ break;
+ case 'e':
+ keep_empty = true;
+ break;
+ case 'h':
+ return help(argv[0]);
+ default:
+ return usage(argv[0]);
+ }
+ }
+
+ if (argc - optind < 1) {
+ return usage(argv[0]);
+ }
+
+ cmd = argv[optind];
+
+ if (strcmp(cmd, "help") == 0) {
+ return help(argv[0]);
+ }
+ else if (strcmp(cmd, "dump") == 0) {
+ int ret;
+ if (argc - optind != 2) {
+ return usage(argv[0]);
+ }
+ idb = argv[optind+1];
+ ret = dump_db(idb, stdout, isize, print_header, keep_empty);
+ return (ret >= 0) ? 0 : ret;
+ }
+ else if (strcmp(cmd, "convert") == 0) {
+ int ret;
+ if (argc - optind != 3) {
+ return usage(argv[0]);
+ }
+ idb = argv[optind+1];
+ odb = argv[optind+2];
+ ret = conv_db(idb, odb, isize, osize, keep_empty);
+ return (ret >= 0) ? 0 : ret;
+ }
+
+ return usage(argv[0]);
+}
+
+struct ltdb_traverse_ctx {
+ int (*fn)(TDB_CONTEXT*,TDB_DATA,TDB_DATA,struct ctdb_ltdb_header*,void *);
+ void* state;
+ size_t hsize;
+ bool skip_empty;
+ int nempty;
+};
+
+static int
+ltdb_traverse_fn(TDB_CONTEXT* tdb, TDB_DATA key, TDB_DATA val,
+ void* ltdb_traverse_ctx)
+{
+ struct ltdb_traverse_ctx* ctx =
+ (struct ltdb_traverse_ctx*)ltdb_traverse_ctx;
+ union ltdb_header hdr = DEFAULT_HDR;
+
+ const size_t hsize = MIN(sizeof(hdr), ctx->hsize);
+ if (val.dsize < hsize) {
+ fprintf(stderr, "Value too short to contain a ctdb header: ");
+ print_data_tdbdump(stderr, key);
+ fprintf(stderr, " = ");
+ print_data_tdbdump(stderr, val);
+ fputc('\n', stderr);
+ return -1;
+ }
+ if (val.dsize == hsize && ctx->skip_empty) {
+ ctx->nempty++;
+ return 0;
+ }
+
+ memcpy(&hdr, val.dptr, hsize);
+
+ if (hdr.uints[5] != 0) {
+ fprintf(stderr, "Warning: header padding isn't zero! Wrong header size?\n");
+ }
+ val.dptr += ctx->hsize;
+ val.dsize -= ctx->hsize;
+ return ctx->fn(tdb, key, val, &hdr.hdr, ctx->state);
+}
+
+static int ltdb_traverse(TDB_CONTEXT *tdb,
+ int (*fn)(TDB_CONTEXT*, TDB_DATA, TDB_DATA,
+ struct ctdb_ltdb_header*, void *),
+ void *state, size_t hsize, bool skip_empty)
+{
+ struct ltdb_traverse_ctx ctx = {
+ .fn = fn,
+ .state = state,
+ .hsize = hsize,
+ .skip_empty = skip_empty,
+ .nempty = 0,
+ };
+ int ret = tdb_traverse(tdb, &ltdb_traverse_fn, &ctx);
+
+ return (ret < 0) ? ret : (ret - ctx.nempty);
+}
+
+static int write_record(TDB_CONTEXT* tdb, TDB_DATA key, TDB_DATA val,
+ struct ctdb_ltdb_header* hdr,
+ void* write_record_ctx)
+{
+ struct write_record_ctx* ctx
+ = (struct write_record_ctx*)write_record_ctx;
+ int ret;
+
+ if (ctx->hsize == 0) {
+ ret = tdb_store(ctx->tdb, key, val, ctx->tdb_store_flags);
+ } else {
+ TDB_DATA rec[2];
+
+ rec[0].dsize = ctx->hsize;
+ rec[0].dptr = (uint8_t *)hdr;
+
+ rec[1].dsize = val.dsize;
+ rec[1].dptr = val.dptr;
+
+ ret = tdb_storev(ctx->tdb, key, rec, 2, ctx->tdb_store_flags);
+ }
+
+ if (ret == -1) {
+ fprintf(stderr, "tdb_store: %s\n", tdb_errorstr(ctx->tdb));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int dump_record(TDB_CONTEXT* tdb, TDB_DATA key, TDB_DATA val,
+ struct ctdb_ltdb_header* hdr,
+ void* dump_record_ctx)
+{
+ struct dump_record_ctx* ctx = (struct dump_record_ctx*)dump_record_ctx;
+
+ fprintf(ctx->file, "{\nkey(%d) = ", (int)key.dsize);
+ ctx->print_data(ctx->file, key);
+ fputc('\n', ctx->file);
+ ctx->dump_header(ctx, hdr);
+ fprintf(ctx->file, "data(%d) = ", (int)val.dsize);
+ ctx->print_data(ctx->file, val);
+ fprintf(ctx->file, "\n}\n");
+ return 0;
+}
+
+static void dump_header_full(struct dump_record_ctx* c,
+ struct ctdb_ltdb_header* h)
+{
+ fprintf(c->file, "dmaster: %d\nrsn: %llu\nflags: 0x%X\n",
+ (int)h->dmaster,
+ (unsigned long long)h->rsn, h->flags);
+}
+
+static void print_data_tdbdump(FILE* file, TDB_DATA data)
+{
+ unsigned char *ptr = data.dptr;
+ fputc('"', file);
+ while (data.dsize--) {
+ if (isprint(*ptr) && !strchr("\"\\", *ptr)) {
+ fputc(*ptr, file);
+ } else {
+ fprintf(file, "\\%02X", *ptr);
+ }
+ ptr++;
+ }
+ fputc('"',file);
+}
+