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