1 /* $NetBSD: mdb_dump.c,v 1.2 2020/08/11 13:15:38 christos Exp $ */ 2 3 /* mdb_dump.c - memory-mapped database dump tool */ 4 /* 5 * Copyright 2011-2020 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 if (*c == '\\') 74 putchar('\\'); 75 putchar(*c); 76 } else { 77 putchar('\\'); 78 hex(*c); 79 } 80 c++; 81 } 82 putchar('\n'); 83 } 84 85 static void byte(MDB_val *v) 86 { 87 unsigned char *c, *end; 88 89 putchar(' '); 90 c = v->mv_data; 91 end = c + v->mv_size; 92 while (c < end) { 93 hex(*c++); 94 } 95 putchar('\n'); 96 } 97 98 /* Dump in BDB-compatible format */ 99 static int dumpit(MDB_txn *txn, MDB_dbi dbi, char *name) 100 { 101 MDB_cursor *mc; 102 MDB_stat ms; 103 MDB_val key, data; 104 MDB_envinfo info; 105 unsigned int flags; 106 int rc, i; 107 108 rc = mdb_dbi_flags(txn, dbi, &flags); 109 if (rc) return rc; 110 111 rc = mdb_stat(txn, dbi, &ms); 112 if (rc) return rc; 113 114 rc = mdb_env_info(mdb_txn_env(txn), &info); 115 if (rc) return rc; 116 117 printf("VERSION=3\n"); 118 printf("format=%s\n", mode & PRINT ? "print" : "bytevalue"); 119 if (name) 120 printf("database=%s\n", name); 121 printf("type=btree\n"); 122 printf("mapsize=%" Z "u\n", info.me_mapsize); 123 if (info.me_mapaddr) 124 printf("mapaddr=%p\n", info.me_mapaddr); 125 printf("maxreaders=%u\n", info.me_maxreaders); 126 127 if (flags & MDB_DUPSORT) 128 printf("duplicates=1\n"); 129 130 for (i=0; dbflags[i].bit; i++) 131 if (flags & dbflags[i].bit) 132 printf("%s=1\n", dbflags[i].name); 133 134 printf("db_pagesize=%d\n", ms.ms_psize); 135 printf("HEADER=END\n"); 136 137 rc = mdb_cursor_open(txn, dbi, &mc); 138 if (rc) return rc; 139 140 while ((rc = mdb_cursor_get(mc, &key, &data, MDB_NEXT) == MDB_SUCCESS)) { 141 if (gotsig) { 142 rc = EINTR; 143 break; 144 } 145 if (mode & PRINT) { 146 text(&key); 147 text(&data); 148 } else { 149 byte(&key); 150 byte(&data); 151 } 152 } 153 printf("DATA=END\n"); 154 if (rc == MDB_NOTFOUND) 155 rc = MDB_SUCCESS; 156 157 return rc; 158 } 159 160 static void usage(char *prog) 161 { 162 fprintf(stderr, "usage: %s [-V] [-f output] [-l] [-n] [-p] [-a|-s subdb] dbpath\n", prog); 163 exit(EXIT_FAILURE); 164 } 165 166 int main(int argc, char *argv[]) 167 { 168 int i, rc; 169 MDB_env *env; 170 MDB_txn *txn; 171 MDB_dbi dbi; 172 char *prog = argv[0]; 173 char *envname; 174 char *subname = NULL; 175 int alldbs = 0, envflags = 0, list = 0; 176 177 if (argc < 2) { 178 usage(prog); 179 } 180 181 /* -a: dump main DB and all subDBs 182 * -s: dump only the named subDB 183 * -n: use NOSUBDIR flag on env_open 184 * -p: use printable characters 185 * -f: write to file instead of stdout 186 * -V: print version and exit 187 * (default) dump only the main DB 188 */ 189 while ((i = getopt(argc, argv, "af:lnps:V")) != EOF) { 190 switch(i) { 191 case 'V': 192 printf("%s\n", MDB_VERSION_STRING); 193 exit(0); 194 break; 195 case 'l': 196 list = 1; 197 /*FALLTHROUGH*/; 198 case 'a': 199 if (subname) 200 usage(prog); 201 alldbs++; 202 break; 203 case 'f': 204 if (freopen(optarg, "w", stdout) == NULL) { 205 fprintf(stderr, "%s: %s: reopen: %s\n", 206 prog, optarg, strerror(errno)); 207 exit(EXIT_FAILURE); 208 } 209 break; 210 case 'n': 211 envflags |= MDB_NOSUBDIR; 212 break; 213 case 'p': 214 mode |= PRINT; 215 break; 216 case 's': 217 if (alldbs) 218 usage(prog); 219 subname = optarg; 220 break; 221 default: 222 usage(prog); 223 } 224 } 225 226 if (optind != argc - 1) 227 usage(prog); 228 229 #ifdef SIGPIPE 230 signal(SIGPIPE, dumpsig); 231 #endif 232 #ifdef SIGHUP 233 signal(SIGHUP, dumpsig); 234 #endif 235 signal(SIGINT, dumpsig); 236 signal(SIGTERM, dumpsig); 237 238 envname = argv[optind]; 239 rc = mdb_env_create(&env); 240 if (rc) { 241 fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc)); 242 return EXIT_FAILURE; 243 } 244 245 if (alldbs || subname) { 246 mdb_env_set_maxdbs(env, 2); 247 } 248 249 rc = mdb_env_open(env, envname, envflags | MDB_RDONLY, 0664); 250 if (rc) { 251 fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc)); 252 goto env_close; 253 } 254 255 rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); 256 if (rc) { 257 fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc)); 258 goto env_close; 259 } 260 261 rc = mdb_open(txn, subname, 0, &dbi); 262 if (rc) { 263 fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc)); 264 goto txn_abort; 265 } 266 267 if (alldbs) { 268 MDB_cursor *cursor; 269 MDB_val key; 270 int count = 0; 271 272 rc = mdb_cursor_open(txn, dbi, &cursor); 273 if (rc) { 274 fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); 275 goto txn_abort; 276 } 277 while ((rc = mdb_cursor_get(cursor, &key, NULL, MDB_NEXT_NODUP)) == 0) { 278 char *str; 279 MDB_dbi db2; 280 if (memchr(key.mv_data, '\0', key.mv_size)) 281 continue; 282 count++; 283 str = malloc(key.mv_size+1); 284 memcpy(str, key.mv_data, key.mv_size); 285 str[key.mv_size] = '\0'; 286 rc = mdb_open(txn, str, 0, &db2); 287 if (rc == MDB_SUCCESS) { 288 if (list) { 289 printf("%s\n", str); 290 list++; 291 } else { 292 rc = dumpit(txn, db2, str); 293 if (rc) 294 break; 295 } 296 mdb_close(env, db2); 297 } 298 free(str); 299 if (rc) continue; 300 } 301 mdb_cursor_close(cursor); 302 if (!count) { 303 fprintf(stderr, "%s: %s does not contain multiple databases\n", prog, envname); 304 rc = MDB_NOTFOUND; 305 } else if (rc == MDB_NOTFOUND) { 306 rc = MDB_SUCCESS; 307 } 308 } else { 309 rc = dumpit(txn, dbi, subname); 310 } 311 if (rc && rc != MDB_NOTFOUND) 312 fprintf(stderr, "%s: %s: %s\n", prog, envname, mdb_strerror(rc)); 313 314 mdb_close(env, dbi); 315 txn_abort: 316 mdb_txn_abort(txn); 317 env_close: 318 mdb_env_close(env); 319 320 return rc ? EXIT_FAILURE : EXIT_SUCCESS; 321 } 322