2017-02-21 20:16:54 +03:00
|
|
|
/* mdbx_dump.c - memory-mapped database dump tool */
|
|
|
|
|
|
|
|
/*
|
2019-02-03 12:28:01 +03:00
|
|
|
* Copyright 2015-2019 Leonid Yuriev <leo@yuriev.ru>
|
2017-03-16 18:09:27 +03:00
|
|
|
* and other libmdbx authors: please see AUTHORS file.
|
2017-02-21 20:16:54 +03:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted only as authorized by the OpenLDAP
|
|
|
|
* Public License.
|
|
|
|
*
|
|
|
|
* A copy of this license is available in the file LICENSE in the
|
|
|
|
* top-level directory of the distribution or, alternatively, at
|
2017-05-23 14:44:53 +03:00
|
|
|
* <http://www.OpenLDAP.org/license.html>. */
|
2017-02-21 20:16:54 +03:00
|
|
|
|
2017-07-10 20:45:24 +03:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#if _MSC_VER > 1800
|
|
|
|
#pragma warning(disable : 4464) /* relative include path contains '..' */
|
|
|
|
#endif
|
|
|
|
#pragma warning(disable : 4996) /* The POSIX name is deprecated... */
|
2017-07-26 18:32:46 +03:00
|
|
|
#endif /* _MSC_VER (warnings) */
|
2017-07-10 20:45:24 +03:00
|
|
|
|
2019-08-31 17:13:02 +03:00
|
|
|
#define MDBX_TOOLS /* Avoid using internal mdbx_assert() */
|
2019-08-31 17:10:04 +03:00
|
|
|
#include "../elements/internals.h"
|
2019-08-27 00:29:15 +03:00
|
|
|
|
2017-02-21 20:16:54 +03:00
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#define PRINT 1
|
|
|
|
static int mode;
|
|
|
|
|
|
|
|
typedef struct flagbit {
|
|
|
|
int bit;
|
|
|
|
char *name;
|
|
|
|
} flagbit;
|
|
|
|
|
2017-05-24 01:42:10 +03:00
|
|
|
flagbit dbflags[] = {{MDBX_REVERSEKEY, "reversekey"},
|
|
|
|
{MDBX_DUPSORT, "dupsort"},
|
|
|
|
{MDBX_INTEGERKEY, "integerkey"},
|
|
|
|
{MDBX_DUPFIXED, "dupfixed"},
|
|
|
|
{MDBX_INTEGERDUP, "integerdup"},
|
|
|
|
{MDBX_REVERSEDUP, "reversedup"},
|
2017-02-21 20:16:54 +03:00
|
|
|
{0, NULL}};
|
|
|
|
|
2017-07-10 20:45:24 +03:00
|
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
|
|
#include "wingetopt.h"
|
|
|
|
|
|
|
|
static volatile BOOL user_break;
|
|
|
|
static BOOL WINAPI ConsoleBreakHandlerRoutine(DWORD dwCtrlType) {
|
|
|
|
(void)dwCtrlType;
|
|
|
|
user_break = true;
|
|
|
|
return true;
|
|
|
|
}
|
2017-02-21 20:16:54 +03:00
|
|
|
|
2017-07-10 20:45:24 +03:00
|
|
|
#else /* WINDOWS */
|
|
|
|
|
|
|
|
static volatile sig_atomic_t user_break;
|
|
|
|
static void signal_handler(int sig) {
|
2017-02-21 20:16:54 +03:00
|
|
|
(void)sig;
|
2017-07-10 20:45:24 +03:00
|
|
|
user_break = 1;
|
2017-02-21 20:16:54 +03:00
|
|
|
}
|
|
|
|
|
2017-07-10 20:45:24 +03:00
|
|
|
#endif /* !WINDOWS */
|
|
|
|
|
2017-02-21 20:16:54 +03:00
|
|
|
static const char hexc[] = "0123456789abcdef";
|
|
|
|
|
2017-07-10 20:45:24 +03:00
|
|
|
static void dumpbyte(unsigned char c) {
|
2017-02-21 20:16:54 +03:00
|
|
|
putchar(hexc[c >> 4]);
|
|
|
|
putchar(hexc[c & 0xf]);
|
|
|
|
}
|
|
|
|
|
2017-05-23 21:05:54 +03:00
|
|
|
static void text(MDBX_val *v) {
|
2017-02-21 20:16:54 +03:00
|
|
|
unsigned char *c, *end;
|
|
|
|
|
|
|
|
putchar(' ');
|
2017-05-23 21:05:54 +03:00
|
|
|
c = v->iov_base;
|
|
|
|
end = c + v->iov_len;
|
2017-02-21 20:16:54 +03:00
|
|
|
while (c < end) {
|
2017-07-10 20:45:24 +03:00
|
|
|
if (isprint(*c) && *c != '\\') {
|
2017-02-21 20:16:54 +03:00
|
|
|
putchar(*c);
|
|
|
|
} else {
|
|
|
|
putchar('\\');
|
2017-07-10 20:45:24 +03:00
|
|
|
dumpbyte(*c);
|
2017-02-21 20:16:54 +03:00
|
|
|
}
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
putchar('\n');
|
|
|
|
}
|
|
|
|
|
2017-07-10 20:45:24 +03:00
|
|
|
static void dumpval(MDBX_val *v) {
|
2017-02-21 20:16:54 +03:00
|
|
|
unsigned char *c, *end;
|
|
|
|
|
|
|
|
putchar(' ');
|
2017-05-23 21:05:54 +03:00
|
|
|
c = v->iov_base;
|
|
|
|
end = c + v->iov_len;
|
2017-02-21 20:16:54 +03:00
|
|
|
while (c < end) {
|
2017-07-10 20:45:24 +03:00
|
|
|
dumpbyte(*c++);
|
2017-02-21 20:16:54 +03:00
|
|
|
}
|
|
|
|
putchar('\n');
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dump in BDB-compatible format */
|
2017-05-24 01:42:10 +03:00
|
|
|
static int dumpit(MDBX_txn *txn, MDBX_dbi dbi, char *name) {
|
|
|
|
MDBX_cursor *mc;
|
2017-02-21 20:16:54 +03:00
|
|
|
MDBX_stat ms;
|
2017-05-23 21:05:54 +03:00
|
|
|
MDBX_val key, data;
|
2017-02-21 20:16:54 +03:00
|
|
|
MDBX_envinfo info;
|
|
|
|
unsigned int flags;
|
|
|
|
int rc, i;
|
|
|
|
|
|
|
|
rc = mdbx_dbi_flags(txn, dbi, &flags);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
2017-03-29 18:51:22 +03:00
|
|
|
rc = mdbx_dbi_stat(txn, dbi, &ms, sizeof(ms));
|
2017-02-21 20:16:54 +03:00
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
2019-11-30 00:31:16 +03:00
|
|
|
rc = mdbx_env_info_ex(mdbx_txn_env(txn), txn, &info, sizeof(info));
|
2017-02-21 20:16:54 +03:00
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
printf("VERSION=3\n");
|
|
|
|
printf("format=%s\n", mode & PRINT ? "print" : "bytevalue");
|
|
|
|
if (name)
|
|
|
|
printf("database=%s\n", name);
|
|
|
|
printf("type=btree\n");
|
2019-11-30 00:31:16 +03:00
|
|
|
printf("mapsize=%" PRIu64 "\n", info.mi_geo.upper);
|
2017-07-12 21:13:17 +03:00
|
|
|
printf("maxreaders=%u\n", info.mi_maxreaders);
|
2017-02-21 20:16:54 +03:00
|
|
|
|
|
|
|
for (i = 0; dbflags[i].bit; i++)
|
|
|
|
if (flags & dbflags[i].bit)
|
|
|
|
printf("%s=1\n", dbflags[i].name);
|
|
|
|
|
|
|
|
printf("db_pagesize=%d\n", ms.ms_psize);
|
|
|
|
printf("HEADER=END\n");
|
|
|
|
|
|
|
|
rc = mdbx_cursor_open(txn, dbi, &mc);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
2017-05-24 01:42:10 +03:00
|
|
|
while ((rc = mdbx_cursor_get(mc, &key, &data, MDBX_NEXT)) == MDBX_SUCCESS) {
|
2017-07-10 20:45:24 +03:00
|
|
|
if (user_break) {
|
|
|
|
rc = MDBX_EINTR;
|
2017-02-21 20:16:54 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (mode & PRINT) {
|
|
|
|
text(&key);
|
|
|
|
text(&data);
|
|
|
|
} else {
|
2017-07-10 20:45:24 +03:00
|
|
|
dumpval(&key);
|
|
|
|
dumpval(&data);
|
2017-02-21 20:16:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
printf("DATA=END\n");
|
2017-05-24 01:42:10 +03:00
|
|
|
if (rc == MDBX_NOTFOUND)
|
|
|
|
rc = MDBX_SUCCESS;
|
2017-02-21 20:16:54 +03:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usage(char *prog) {
|
|
|
|
fprintf(stderr,
|
2019-12-05 01:43:57 +03:00
|
|
|
"usage: %s [-V] [-q] [-f file] [-l] [-p] [-a|-s subdb] [-r] [-n] "
|
2019-11-27 22:38:39 +03:00
|
|
|
"dbpath\n"
|
|
|
|
" -V\t\tprint version and exit\n"
|
|
|
|
" -q\t\tbe quiet\n"
|
|
|
|
" -f\t\twrite to file instead of stdout\n"
|
|
|
|
" -l\t\tlist subDBs and exit\n"
|
|
|
|
" -p\t\tuse printable characters\n"
|
|
|
|
" -a\t\tdump main DB and all subDBs,\n"
|
|
|
|
" \t\tby default dump only the main DB\n"
|
|
|
|
" -s\t\tdump only the named subDB\n"
|
|
|
|
" -r\t\trescure mode (ignore errors to dump corrupted DB)\n"
|
|
|
|
" -n\t\tNOSUBDIR mode for open\n",
|
2017-02-21 20:16:54 +03:00
|
|
|
prog);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
int i, rc;
|
2017-05-24 01:42:10 +03:00
|
|
|
MDBX_env *env;
|
2017-05-23 21:36:09 +03:00
|
|
|
MDBX_txn *txn;
|
2017-05-24 01:42:10 +03:00
|
|
|
MDBX_dbi dbi;
|
2017-02-21 20:16:54 +03:00
|
|
|
char *prog = argv[0];
|
|
|
|
char *envname;
|
|
|
|
char *subname = NULL;
|
2019-11-27 22:02:01 +03:00
|
|
|
int alldbs = 0, envflags = 0, list = 0, quiet = 0, rescue = 0;
|
2017-02-21 20:16:54 +03:00
|
|
|
|
2019-09-03 22:54:50 +03:00
|
|
|
if (argc < 2)
|
2017-02-21 20:16:54 +03:00
|
|
|
usage(prog);
|
|
|
|
|
2019-11-27 22:02:01 +03:00
|
|
|
while ((i = getopt(argc, argv, "af:lnps:Vrq")) != EOF) {
|
2017-02-21 20:16:54 +03:00
|
|
|
switch (i) {
|
|
|
|
case 'V':
|
2019-09-03 22:54:50 +03:00
|
|
|
printf("mdbx_dump version %d.%d.%d.%d\n"
|
|
|
|
" - source: %s %s, commit %s, tree %s\n"
|
2019-09-05 11:57:52 +03:00
|
|
|
" - anchor: %s\n"
|
2019-09-03 22:54:50 +03:00
|
|
|
" - build: %s for %s by %s\n"
|
|
|
|
" - flags: %s\n"
|
|
|
|
" - options: %s\n",
|
|
|
|
mdbx_version.major, mdbx_version.minor, mdbx_version.release,
|
|
|
|
mdbx_version.revision, mdbx_version.git.describe,
|
|
|
|
mdbx_version.git.datetime, mdbx_version.git.commit,
|
2019-09-05 11:57:52 +03:00
|
|
|
mdbx_version.git.tree, mdbx_sourcery_anchor, mdbx_build.datetime,
|
|
|
|
mdbx_build.target, mdbx_build.compiler, mdbx_build.flags,
|
|
|
|
mdbx_build.options);
|
2019-09-03 22:54:50 +03:00
|
|
|
return EXIT_SUCCESS;
|
2017-02-21 20:16:54 +03:00
|
|
|
case 'l':
|
|
|
|
list = 1;
|
|
|
|
/*FALLTHROUGH*/;
|
2017-10-28 10:35:01 +03:00
|
|
|
__fallthrough;
|
2017-02-21 20:16:54 +03:00
|
|
|
case 'a':
|
|
|
|
if (subname)
|
|
|
|
usage(prog);
|
|
|
|
alldbs++;
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
if (freopen(optarg, "w", stdout) == NULL) {
|
2018-10-14 10:32:37 +03:00
|
|
|
fprintf(stderr, "%s: %s: reopen: %s\n", prog, optarg,
|
|
|
|
mdbx_strerror(errno));
|
2017-02-21 20:16:54 +03:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'n':
|
2017-05-24 01:42:10 +03:00
|
|
|
envflags |= MDBX_NOSUBDIR;
|
2017-02-21 20:16:54 +03:00
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
mode |= PRINT;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
if (alldbs)
|
|
|
|
usage(prog);
|
|
|
|
subname = optarg;
|
|
|
|
break;
|
2019-11-27 22:02:01 +03:00
|
|
|
case 'q':
|
|
|
|
quiet = 1;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
rescue = 1;
|
|
|
|
break;
|
2017-02-21 20:16:54 +03:00
|
|
|
default:
|
|
|
|
usage(prog);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (optind != argc - 1)
|
|
|
|
usage(prog);
|
|
|
|
|
2017-07-10 20:45:24 +03:00
|
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
|
|
SetConsoleCtrlHandler(ConsoleBreakHandlerRoutine, true);
|
|
|
|
#else
|
2017-02-21 20:16:54 +03:00
|
|
|
#ifdef SIGPIPE
|
2017-07-10 20:45:24 +03:00
|
|
|
signal(SIGPIPE, signal_handler);
|
2017-02-21 20:16:54 +03:00
|
|
|
#endif
|
|
|
|
#ifdef SIGHUP
|
2017-07-10 20:45:24 +03:00
|
|
|
signal(SIGHUP, signal_handler);
|
2017-02-21 20:16:54 +03:00
|
|
|
#endif
|
2017-07-10 20:45:24 +03:00
|
|
|
signal(SIGINT, signal_handler);
|
|
|
|
signal(SIGTERM, signal_handler);
|
|
|
|
#endif /* !WINDOWS */
|
2017-02-21 20:16:54 +03:00
|
|
|
|
|
|
|
envname = argv[optind];
|
2019-11-27 22:02:01 +03:00
|
|
|
if (!quiet) {
|
|
|
|
fprintf(stderr, "mdbx_dump %s (%s, T-%s)\nRunning for %s...\n",
|
|
|
|
mdbx_version.git.describe, mdbx_version.git.datetime,
|
|
|
|
mdbx_version.git.tree, envname);
|
|
|
|
fflush(NULL);
|
|
|
|
}
|
2019-09-03 22:54:50 +03:00
|
|
|
|
2017-02-21 20:16:54 +03:00
|
|
|
rc = mdbx_env_create(&env);
|
|
|
|
if (rc) {
|
|
|
|
fprintf(stderr, "mdbx_env_create failed, error %d %s\n", rc,
|
|
|
|
mdbx_strerror(rc));
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (alldbs || subname) {
|
|
|
|
mdbx_env_set_maxdbs(env, 2);
|
|
|
|
}
|
|
|
|
|
2019-11-27 22:02:01 +03:00
|
|
|
rc = mdbx_env_open(
|
|
|
|
env, envname,
|
|
|
|
envflags | (rescue ? MDBX_RDONLY | MDBX_EXCLUSIVE : MDBX_RDONLY), 0);
|
2017-02-21 20:16:54 +03:00
|
|
|
if (rc) {
|
|
|
|
fprintf(stderr, "mdbx_env_open failed, error %d %s\n", rc,
|
|
|
|
mdbx_strerror(rc));
|
|
|
|
goto env_close;
|
|
|
|
}
|
|
|
|
|
2017-05-24 01:42:10 +03:00
|
|
|
rc = mdbx_txn_begin(env, NULL, MDBX_RDONLY, &txn);
|
2017-02-21 20:16:54 +03:00
|
|
|
if (rc) {
|
|
|
|
fprintf(stderr, "mdbx_txn_begin failed, error %d %s\n", rc,
|
|
|
|
mdbx_strerror(rc));
|
|
|
|
goto env_close;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = mdbx_dbi_open(txn, subname, 0, &dbi);
|
|
|
|
if (rc) {
|
|
|
|
fprintf(stderr, "mdbx_open failed, error %d %s\n", rc, mdbx_strerror(rc));
|
|
|
|
goto txn_abort;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (alldbs) {
|
2017-05-24 01:42:10 +03:00
|
|
|
MDBX_cursor *cursor;
|
2017-05-23 21:05:54 +03:00
|
|
|
MDBX_val key;
|
2017-02-21 20:16:54 +03:00
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
rc = mdbx_cursor_open(txn, dbi, &cursor);
|
|
|
|
if (rc) {
|
|
|
|
fprintf(stderr, "mdbx_cursor_open failed, error %d %s\n", rc,
|
|
|
|
mdbx_strerror(rc));
|
|
|
|
goto txn_abort;
|
|
|
|
}
|
2017-05-24 01:42:10 +03:00
|
|
|
while ((rc = mdbx_cursor_get(cursor, &key, NULL, MDBX_NEXT_NODUP)) == 0) {
|
2017-07-10 20:45:24 +03:00
|
|
|
if (user_break) {
|
|
|
|
rc = MDBX_EINTR;
|
|
|
|
break;
|
|
|
|
}
|
2017-02-21 20:16:54 +03:00
|
|
|
char *str;
|
2017-05-24 01:42:10 +03:00
|
|
|
MDBX_dbi db2;
|
2017-05-23 21:05:54 +03:00
|
|
|
if (memchr(key.iov_base, '\0', key.iov_len))
|
2017-02-21 20:16:54 +03:00
|
|
|
continue;
|
|
|
|
count++;
|
2018-10-12 22:01:36 +03:00
|
|
|
str = mdbx_malloc(key.iov_len + 1);
|
2017-05-23 21:05:54 +03:00
|
|
|
memcpy(str, key.iov_base, key.iov_len);
|
|
|
|
str[key.iov_len] = '\0';
|
2017-02-21 20:16:54 +03:00
|
|
|
rc = mdbx_dbi_open(txn, str, 0, &db2);
|
2017-05-24 01:42:10 +03:00
|
|
|
if (rc == MDBX_SUCCESS) {
|
2017-02-21 20:16:54 +03:00
|
|
|
if (list) {
|
|
|
|
printf("%s\n", str);
|
|
|
|
list++;
|
|
|
|
} else {
|
|
|
|
rc = dumpit(txn, db2, str);
|
2019-11-27 22:02:01 +03:00
|
|
|
if (rc) {
|
|
|
|
if (!rescue)
|
|
|
|
break;
|
|
|
|
fprintf(stderr, "%s: %s: ignore %s for `%s` and continue\n", prog,
|
|
|
|
envname, mdbx_strerror(rc), str);
|
|
|
|
/* Here is a hack for rescue mode, don't do that:
|
|
|
|
* - we should restart transaction in case error due
|
|
|
|
* database corruption;
|
|
|
|
* - but we won't close cursor, reopen and re-positioning it
|
|
|
|
* for new a transaction;
|
|
|
|
* - this is possible since DB is opened in read-only exclusive
|
|
|
|
* mode and transaction is the same, i.e. has the same address
|
|
|
|
* and so on. */
|
|
|
|
rc = mdbx_txn_reset(txn);
|
|
|
|
if (rc) {
|
|
|
|
fprintf(stderr, "mdbx_txn_reset failed, error %d %s\n", rc,
|
|
|
|
mdbx_strerror(rc));
|
|
|
|
goto env_close;
|
|
|
|
}
|
|
|
|
rc = mdbx_txn_renew(txn);
|
|
|
|
if (rc) {
|
|
|
|
fprintf(stderr, "mdbx_txn_renew failed, error %d %s\n", rc,
|
|
|
|
mdbx_strerror(rc));
|
|
|
|
goto env_close;
|
|
|
|
}
|
|
|
|
}
|
2017-02-21 20:16:54 +03:00
|
|
|
}
|
|
|
|
mdbx_dbi_close(env, db2);
|
|
|
|
}
|
2018-10-12 22:01:36 +03:00
|
|
|
mdbx_free(str);
|
2017-02-21 20:16:54 +03:00
|
|
|
if (rc)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
mdbx_cursor_close(cursor);
|
|
|
|
if (!count) {
|
|
|
|
fprintf(stderr, "%s: %s does not contain multiple databases\n", prog,
|
|
|
|
envname);
|
2017-05-24 01:42:10 +03:00
|
|
|
rc = MDBX_NOTFOUND;
|
|
|
|
} else if (rc == MDBX_INCOMPATIBLE) {
|
2017-02-21 20:16:54 +03:00
|
|
|
/* LY: the record it not a named sub-db. */
|
2017-05-24 01:42:10 +03:00
|
|
|
rc = MDBX_SUCCESS;
|
2017-02-21 20:16:54 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = dumpit(txn, dbi, subname);
|
|
|
|
}
|
2017-05-24 01:42:10 +03:00
|
|
|
if (rc && rc != MDBX_NOTFOUND)
|
2017-02-21 20:16:54 +03:00
|
|
|
fprintf(stderr, "%s: %s: %s\n", prog, envname, mdbx_strerror(rc));
|
|
|
|
|
|
|
|
mdbx_dbi_close(env, dbi);
|
|
|
|
txn_abort:
|
|
|
|
mdbx_txn_abort(txn);
|
|
|
|
env_close:
|
|
|
|
mdbx_env_close(env);
|
|
|
|
|
|
|
|
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
|
|
|
}
|