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