diff options
Diffstat (limited to 'ctdb/tools/ltdbtool.c')
-rw-r--r-- | ctdb/tools/ltdbtool.c | 395 |
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, <db_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); +} + |