add ltdbtool - a standalone ltdb tool
authorGregor Beck <gbeck@sernet.de>
Thu, 14 Apr 2011 10:51:59 +0000 (12:51 +0200)
committerRonnie Sahlberg <ronniesahlberg@gmail.com>
Thu, 12 May 2011 08:55:52 +0000 (18:55 +1000)
This this is a tool to handle (dump and convert) ctdb's local tdb
copies (ltdbs) without connecting to a ctdb daemon.

It can be used to

* dump the contents of a ltdb, printing
  the ctdb record header information

* dump a non-clustered tdb database (like tdbdump)

* convert between an ltdb and a non-clustered tdb
  (adding or removing ctdb headers)

* convert between 64 and 32 bit ltdbs
  (the ctdb record headers differ by 4 bytes of padding)

usage: bin/ltdbtool dump [-p] [-s{0|32|64}] <idb>
       bin/ltdbtool convert [-s{0|32|64}] [-o{0|32|64}] <idb> <odb>

Pair-Programmed-With: Michael Adam <obnox@samba.org>

Makefile.in
tools/ltdbtool.c [new file with mode: 0644]

index 6bbf61686461df1cfe969fcc68b6a26a13f24f31..3c75e2d430bb59b89a1dfb43e0d98aef3d115804 100755 (executable)
@@ -123,6 +123,10 @@ bin/ctdb: $(CTDB_CLIENT_OBJ) tools/ctdb.o tools/ctdb_vacuum.o libctdb/libctdb.a
        @echo Linking $@
        @$(CC) $(CFLAGS) -o $@ tools/ctdb.o tools/ctdb_vacuum.o $(CTDB_CLIENT_OBJ) $(LIB_FLAGS) libctdb/libctdb.a
 
+bin/ltdbtool: tools/ltdbtool.o @TDB_OBJ@
+       @echo Linking $@
+       @$(CC) $(CFLAGS) -o $@ $+
+
 bin/smnotify: utils/smnotify/gen_xdr.o utils/smnotify/gen_smnotify.o utils/smnotify/smnotify.o $(POPT_OBJ)
        @echo Linking $@
        @$(CC) $(CFLAGS) -o $@ utils/smnotify/smnotify.o utils/smnotify/gen_xdr.o utils/smnotify/gen_smnotify.o $(POPT_OBJ) $(LIB_FLAGS)
diff --git a/tools/ltdbtool.c b/tools/ltdbtool.c
new file mode 100644 (file)
index 0000000..cdde72d
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ * 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 <stdio.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <ctype.h> /* isprint */
+#include <string.h> /* strstr */
+#include <fcntl.h> /* mode_t */
+#include <sys/stat.h> /* S_IRUSR */
+#include <stdint.h> /* uint32_t */
+#include <netinet/in.h> /* struct sockaddr_in */
+#include <sys/socket.h> /* struct sockaddr */
+#include <sys/param.h>  /* MIN */
+#include <tdb.h>
+#include <unistd.h> /* getopt */
+#include <errno.h>
+
+#include "ctdb_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"
+"   -p              print header (for the dump command), defaults ot 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 [-p] [-s{0|32|64}] <idb>\n"
+               "       %s convert [-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, int hsize);
+
+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, int hsize, bool dump_header)
+{
+       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);
+               tdb_close(idb);
+       }
+       return ret;
+}
+
+static int conv_db(const char* iname, const char* oname, size_t isize, size_t osize)
+{
+       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);
+                       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;
+       int opt;
+       const char *cmd, *idb, *odb;
+
+       while ((opt = getopt(argc, argv, "s:o:S:O:ph")) != -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 '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);
+               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);
+               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;
+};
+
+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;
+       }
+
+       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);
+}
+
+int ltdb_traverse(TDB_CONTEXT *tdb,
+                 int (*fn)(TDB_CONTEXT *,TDB_DATA,TDB_DATA,struct ctdb_ltdb_header*,void *),
+                 void *state, int hsize)
+{
+       struct ltdb_traverse_ctx ctx = {
+               .fn = fn,
+               .state = state,
+               .hsize = hsize < 0 ? sizeof(struct ctdb_ltdb_header) : hsize,
+       };
+       return tdb_traverse(tdb, &ltdb_traverse_fn, &ctx);
+}
+
+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;
+
+       if (ctx->hsize == 0) {
+               if (tdb_store(ctx->tdb, key, val, ctx->tdb_store_flags) == -1) {
+                       fprintf(stderr, "tdb_store: %s\n", tdb_errorstr(ctx->tdb));
+                       return -1;
+               }
+       } else {
+               TDB_DATA h = {
+                       .dptr = (void*)hdr,
+                       .dsize = ctx->hsize,
+               };
+               if(tdb_store(ctx->tdb, key, h, ctx->tdb_store_flags) == -1) {
+                       fprintf(stderr, "tdb_store: %s\n", tdb_errorstr(ctx->tdb));
+                       return -1;
+               }
+               if(tdb_append(ctx->tdb, key, val) == -1) {
+                       fprintf(stderr, "tdb_append: %s\n", tdb_errorstr(ctx->tdb));
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+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;
+}
+
+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);
+}
+
+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);
+}
+