1 /* $NetBSD: mdb_dump.c,v 1.1.1.3 2019/08/08 13:31:13 christos Exp $ */ 2 3 /* mdb_dump.c - memory-mapped database dump tool */ 4 /* 5 * Copyright 2011-2018 Howard Chu, Symas Corp. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted only as authorized by the OpenLDAP 10 * Public License. 11 * 12 * A copy of this license is available in the file LICENSE in the 13 * top-level directory of the distribution or, alternatively, at 14 * <http://www.OpenLDAP.org/license.html>. 15 */ 16 #include <stdio.h> 17 #include <errno.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <ctype.h> 21 #include <unistd.h> 22 #include <signal.h> 23 #include "lmdb.h" 24 25 #ifdef _WIN32 26 #define Z "I" 27 #else 28 #define Z "z" 29 #endif 30 31 #define PRINT 1 32 static int mode; 33 34 typedef struct flagbit { 35 int bit; 36 char *name; 37 } flagbit; 38 39 flagbit dbflags[] = { 40 { MDB_REVERSEKEY, "reversekey" }, 41 { MDB_DUPSORT, "dupsort" }, 42 { MDB_INTEGERKEY, "integerkey" }, 43 { MDB_DUPFIXED, "dupfixed" }, 44 { MDB_INTEGERDUP, "integerdup" }, 45 { MDB_REVERSEDUP, "reversedup" }, 46 { 0, NULL } 47 }; 48 49 static volatile sig_atomic_t gotsig; 50 51 static void dumpsig( int sig ) 52 { 53 gotsig=1; 54 } 55 56 static const char hexc[] = "0123456789abcdef"; 57 58 static void hex(unsigned char c) 59 { 60 putchar(hexc[c >> 4]); 61 putchar(hexc[c & 0xf]); 62 } 63 64 static void text(MDB_val *v) 65 { 66 unsigned char *c, *end; 67 68 putchar(' '); 69 c = v->mv_data; 70 end = c + v->mv_size; 71 while (c < end) { 72 if (isprint(*c)) { 73 putchar(*c); 74 } else { 75 putchar('\\'); 76 hex(*c); 77 } 78 c++; 79 } 80 putchar('\n'); 81 } 82 83 static void byte(MDB_val *v) 84 { 85 unsigned char *c, *end; 86 87 putchar(' '); 88 c = v->mv_data; 89 end = c + v->mv_size; 90 while (c < end) { 91 hex(*c++); 92 } 93 putchar('\n'); 94 } 95 96 /* Dump in BDB-compatible format */ 97 static int dumpit(MDB_txn *txn, MDB_dbi dbi, char *name) 98 { 99 MDB_cursor *mc; 100 MDB_stat ms; 101 MDB_val key, data; 102 MDB_envinfo info; 103 unsigned int flags; 104 int rc, i; 105 106 rc = mdb_dbi_flags(txn, dbi, &flags); 107 if (rc) return rc; 108 109 rc = mdb_stat(txn, dbi, &ms); 110 if (rc) return rc; 111 112 rc = mdb_env_info(mdb_txn_env(txn), &info); 113 if (rc) return rc; 114 115 printf("VERSION=3\n"); 116 printf("format=%s\n", mode & PRINT ? "print" : "bytevalue"); 117 if (name) 118 printf("database=%s\n", name); 119 printf("type=btree\n"); 120 printf("mapsize=%" Z "u\n", info.me_mapsize); 121 if (info.me_mapaddr) 122 printf("mapaddr=%p\n", info.me_mapaddr); 123 printf("maxreaders=%u\n", info.me_maxreaders); 124 125 if (flags & MDB_DUPSORT) 126 printf("duplicates=1\n"); 127 128 for (i=0; dbflags[i].bit; i++) 129 if (flags & dbflags[i].bit) 130 printf("%s=1\n", dbflags[i].name); 131 132 printf("db_pagesize=%d\n", ms.ms_psize); 133 printf("HEADER=END\n"); 134 135 rc = mdb_cursor_open(txn, dbi, &mc); 136 if (rc) return rc; 137 138 while ((rc = mdb_cursor_get(mc, &key, &data, MDB_NEXT) == MDB_SUCCESS)) { 139 if (gotsig) { 140 rc = EINTR; 141 break; 142 } 143 if (mode & PRINT) { 144 text(&key); 145 text(&data); 146 } else { 147 byte(&key); 148 byte(&data); 149 } 150 } 151 printf("DATA=END\n"); 152 if (rc == MDB_NOTFOUND) 153 rc = MDB_SUCCESS; 154 155 return rc; 156 } 157 158 static void usage(char *prog) 159 { 160 fprintf(stderr, "usage: %s [-V] [-f output] [-l] [-n] [-p] [-a|-s subdb] dbpath\n", prog); 161 exit(EXIT_FAILURE); 162 } 163 164 int main(int argc, char *argv[]) 165 { 166 int i, rc; 167 MDB_env *env; 168 MDB_txn *txn; 169 MDB_dbi dbi; 170 char *prog = argv[0]; 171 char *envname; 172 char *subname = NULL; 173 int alldbs = 0, envflags = 0, list = 0; 174 175 if (argc < 2) { 176 usage(prog); 177 } 178 179 /* -a: dump main DB and all subDBs 180 * -s: dump only the named subDB 181 * -n: use NOSUBDIR flag on env_open 182 * -p: use printable characters 183 * -f: write to file instead of stdout 184 * -V: print version and exit 185 * (default) dump only the main DB 186 */ 187 while ((i = getopt(argc, argv, "af:lnps:V")) != EOF) { 188 switch(i) { 189 case 'V': 190 printf("%s\n", MDB_VERSION_STRING); 191 exit(0); 192 break; 193 case 'l': 194 list = 1; 195 /*FALLTHROUGH*/; 196 case 'a': 197 if (subname) 198 usage(prog); 199 alldbs++; 200 break; 201 case 'f': 202 if (freopen(optarg, "w", stdout) == NULL) { 203 fprintf(stderr, "%s: %s: reopen: %s\n", 204 prog, optarg, strerror(errno)); 205 exit(EXIT_FAILURE); 206 } 207 break; 208 case 'n': 209 envflags |= MDB_NOSUBDIR; 210 break; 211 case 'p': 212 mode |= PRINT; 213 break; 214 case 's': 215 if (alldbs) 216 usage(prog); 217 subname = optarg; 218 break; 219 default: 220 usage(prog); 221 } 222 } 223 224 if (optind != argc - 1) 225 usage(prog); 226 227 #ifdef SIGPIPE 228 signal(SIGPIPE, dumpsig); 229 #endif 230 #ifdef SIGHUP 231 signal(SIGHUP, dumpsig); 232 #endif 233 signal(SIGINT, dumpsig); 234 signal(SIGTERM, dumpsig); 235 236 envname = argv[optind]; 237 rc = mdb_env_create(&env); 238 if (rc) { 239 fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc)); 240 return EXIT_FAILURE; 241 } 242 243 if (alldbs || subname) { 244 mdb_env_set_maxdbs(env, 2); 245 } 246 247 rc = mdb_env_open(env, envname, envflags | MDB_RDONLY, 0664); 248 if (rc) { 249 fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc)); 250 goto env_close; 251 } 252 253 rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); 254 if (rc) { 255 fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc)); 256 goto env_close; 257 } 258 259 rc = mdb_open(txn, subname, 0, &dbi); 260 if (rc) { 261 fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc)); 262 goto txn_abort; 263 } 264 265 if (alldbs) { 266 MDB_cursor *cursor; 267 MDB_val key; 268 int count = 0; 269 270 rc = mdb_cursor_open(txn, dbi, &cursor); 271 if (rc) { 272 fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); 273 goto txn_abort; 274 } 275 while ((rc = mdb_cursor_get(cursor, &key, NULL, MDB_NEXT_NODUP)) == 0) { 276 char *str; 277 MDB_dbi db2; 278 if (memchr(key.mv_data, '\0', key.mv_size)) 279 continue; 280 count++; 281 str = malloc(key.mv_size+1); 282 memcpy(str, key.mv_data, key.mv_size); 283 str[key.mv_size] = '\0'; 284 rc = mdb_open(txn, str, 0, &db2); 285 if (rc == MDB_SUCCESS) { 286 if (list) { 287 printf("%s\n", str); 288 list++; 289 } else { 290 rc = dumpit(txn, db2, str); 291 if (rc) 292 break; 293 } 294 mdb_close(env, db2); 295 } 296 free(str); 297 if (rc) continue; 298 } 299 mdb_cursor_close(cursor); 300 if (!count) { 301 fprintf(stderr, "%s: %s does not contain multiple databases\n", prog, envname); 302 rc = MDB_NOTFOUND; 303 } else if (rc == MDB_NOTFOUND) { 304 rc = MDB_SUCCESS; 305 } 306 } else { 307 rc = dumpit(txn, dbi, subname); 308 } 309 if (rc && rc != MDB_NOTFOUND) 310 fprintf(stderr, "%s: %s: %s\n", prog, envname, mdb_strerror(rc)); 311 312 mdb_close(env, dbi); 313 txn_abort: 314 mdb_txn_abort(txn); 315 env_close: 316 mdb_env_close(env); 317 318 return rc ? EXIT_FAILURE : EXIT_SUCCESS; 319 } 320