mdb_dump.c (6398B)
1 /* mdb_dump.c - memory-mapped database dump tool */ 2 /* 3 * Copyright 2011-2021 Howard Chu, Symas Corp. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted only as authorized by the OpenLDAP 8 * Public License. 9 * 10 * A copy of this license is available in the file LICENSE in the 11 * top-level directory of the distribution or, alternatively, at 12 * <http://www.OpenLDAP.org/license.html>. 13 */ 14 #include <stdio.h> 15 #include <errno.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <ctype.h> 19 #include <unistd.h> 20 #include <signal.h> 21 #include "lmdb.h" 22 23 #ifdef _WIN32 24 #define Z "I" 25 #else 26 #define Z "z" 27 #endif 28 29 #define PRINT 1 30 static int mode; 31 32 typedef struct flagbit { 33 int bit; 34 char *name; 35 } flagbit; 36 37 flagbit dbflags[] = { 38 { MDB_REVERSEKEY, "reversekey" }, 39 { MDB_DUPSORT, "dupsort" }, 40 { MDB_INTEGERKEY, "integerkey" }, 41 { MDB_DUPFIXED, "dupfixed" }, 42 { MDB_INTEGERDUP, "integerdup" }, 43 { MDB_REVERSEDUP, "reversedup" }, 44 { 0, NULL } 45 }; 46 47 static volatile sig_atomic_t gotsig; 48 49 static void dumpsig( int sig ) 50 { 51 gotsig=1; 52 } 53 54 static const char hexc[] = "0123456789abcdef"; 55 56 static void hex(unsigned char c) 57 { 58 putchar(hexc[c >> 4]); 59 putchar(hexc[c & 0xf]); 60 } 61 62 static void text(MDB_val *v) 63 { 64 unsigned char *c, *end; 65 66 putchar(' '); 67 c = v->mv_data; 68 end = c + v->mv_size; 69 while (c < end) { 70 if (isprint(*c)) { 71 if (*c == '\\') 72 putchar('\\'); 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 }