mirror of
https://github.com/samba-team/samba.git
synced 2025-03-20 22:50:26 +03:00
add ltdbtool - a standalone ltdb tool
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> (This used to be ctdb commit efcf2815711cd5371633614fb91273bd0a786da0)
This commit is contained in:
parent
14cfbcbef3
commit
082de99f87
@ -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)
|
||||
|
377
ctdb/tools/ltdbtool.c
Normal file
377
ctdb/tools/ltdbtool.c
Normal file
@ -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, <db_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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user