Imported Upstream version 5.3.21
[platform/upstream/libdb.git] / util / db_dump.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 2012 Oracle and/or its affiliates.  All rights reserved.
5  *
6  * $Id$
7  */
8
9 #include "db_config.h"
10
11 #include "db_int.h"
12 #include "dbinc/db_page.h"
13 #include "dbinc/db_am.h"
14
15 #ifndef lint
16 static const char copyright[] =
17     "Copyright (c) 1996, 2012 Oracle and/or its affiliates.  All rights reserved.\n";
18 #endif
19
20 int      db_init __P((DB_ENV *, char *, int, u_int32_t, int *));
21 int      dump_sub __P((DB_ENV *, DB *, char *, int, int));
22 int      main __P((int, char *[]));
23 int      show_subs __P((DB *));
24 int      usage __P((void));
25 int      version_check __P((void));
26
27 const char *progname;
28
29 int
30 main(argc, argv)
31         int argc;
32         char *argv[];
33 {
34         extern char *optarg;
35         extern int optind;
36         DB_ENV  *dbenv;
37         DB *dbp;
38         db_pgno_t first, last;
39         u_int32_t cache;
40         int ch;
41         int exitval, keyflag, lflag, mflag, nflag, pflag, sflag, private;
42         int ret, Rflag, rflag, resize;
43         char *data_len, *dbname, *dopt, *filename, *home, *passwd;
44
45         if ((progname = __db_rpath(argv[0])) == NULL)
46                 progname = argv[0];
47         else
48                 ++progname;
49
50         if ((ret = version_check()) != 0)
51                 return (ret);
52
53         dbenv = NULL;
54         dbp = NULL;
55         exitval = lflag = mflag = nflag = pflag = rflag = Rflag = sflag = 0;
56         first = last = PGNO_INVALID;
57         keyflag = 0;
58         cache = MEGABYTE;
59         private = 0;
60         data_len = dbname = dopt = filename = home = passwd = NULL;
61         while ((ch = getopt(argc, argv, "d:D:f:F:h:klL:m:NpP:rRs:V")) != EOF)
62                 switch (ch) {
63                 case 'd':
64                         dopt = optarg;
65                         break;
66                 case 'D':
67                         data_len = optarg;
68                         break;
69                 case 'f':
70                         if (freopen(optarg, "w", stdout) == NULL) {
71                                 fprintf(stderr, DB_STR_A("5108",
72                                     "%s: %s: reopen: %s\n", "%s %s %s\n"),
73                                     progname, optarg, strerror(errno));
74                                 return (EXIT_FAILURE);
75                         }
76                         break;
77                 case 'F':
78                         first = (db_pgno_t)strtoul(optarg, NULL, 10);
79                         break;
80                 case 'h':
81                         home = optarg;
82                         break;
83                 case 'k':
84                         keyflag = 1;
85                         break;
86                 case 'l':
87                         lflag = 1;
88                         break;
89                 case 'L':
90                         last = (db_pgno_t)strtoul(optarg, NULL, 10);
91                         break;
92                 case 'm':
93                         mflag = 1;
94                         dbname = optarg;
95                         break;
96                 case 'N':
97                         nflag = 1;
98                         break;
99                 case 'P':
100                         if (passwd != NULL) {
101                                 fprintf(stderr, DB_STR("5130",
102                                         "Password may not be specified twice"));
103                                 free(passwd);
104                                 return (EXIT_FAILURE);
105                         }
106                         passwd = strdup(optarg);
107                         memset(optarg, 0, strlen(optarg));
108                         if (passwd == NULL) {
109                                 fprintf(stderr, DB_STR_A("5109",
110                                     "%s: strdup: %s\n", "%s %s\n"),
111                                     progname, strerror(errno));
112                                 return (EXIT_FAILURE);
113                         }
114                         break;
115                 case 'p':
116                         pflag = 1;
117                         break;
118                 case 's':
119                         sflag = 1;
120                         dbname = optarg;
121                         break;
122                 case 'R':
123                         Rflag = 1;
124                         /* DB_AGGRESSIVE requires DB_SALVAGE */
125                         /* FALLTHROUGH */
126                 case 'r':
127                         rflag = 1;
128                         break;
129                 case 'V':
130                         printf("%s\n", db_version(NULL, NULL, NULL));
131                         return (EXIT_SUCCESS);
132                 case '?':
133                 default:
134                         return (usage());
135                 }
136         argc -= optind;
137         argv += optind;
138
139         /*
140          * A file name must be specified, unless we're looking for an in-memory
141          * db,  in which case it must not.
142          */
143         if (argc == 0 && mflag)
144                 filename = NULL;
145         else if (argc == 1 && !mflag)
146                 filename = argv[0];
147         else
148                 return (usage());
149
150         if (dopt != NULL && pflag) {
151                 fprintf(stderr, DB_STR_A("5110",
152                     "%s: the -d and -p options may not both be specified\n",
153                     "%s\n"), progname);
154                 return (EXIT_FAILURE);
155         }
156         if (lflag && sflag) {
157                 fprintf(stderr, DB_STR_A("5111",
158                     "%s: the -l and -s options may not both be specified\n",
159                     "%s\n"), progname);
160                 return (EXIT_FAILURE);
161         }
162         if ((lflag || sflag) && mflag) {
163                 fprintf(stderr, DB_STR_A("5112",
164                     "%s: the -m option may not be specified with -l or -s\n",
165                     "%s\n"), progname);
166                 return (EXIT_FAILURE);
167         }
168
169         if (keyflag && rflag) {
170                 fprintf(stderr, DB_STR_A("5113",
171             "%s: the -k and -r or -R options may not both be specified\n",
172                     "%s\n"), progname);
173                 return (EXIT_FAILURE);
174         }
175
176         if (sflag && rflag) {
177                 fprintf(stderr, DB_STR_A("5114",
178             "%s: the -r or R options may not be specified with -s\n",
179                     "%s\n"), progname);
180                 return (EXIT_FAILURE);
181         }
182
183         /* Handle possible interruptions. */
184         __db_util_siginit();
185
186         /*
187          * Create an environment object and initialize it for error
188          * reporting.
189          */
190 retry:  if ((ret = db_env_create(&dbenv, 0)) != 0) {
191                 fprintf(stderr,
192                     "%s: db_env_create: %s\n", progname, db_strerror(ret));
193                 goto err;
194         }
195
196         dbenv->set_errfile(dbenv, stderr);
197         dbenv->set_errpfx(dbenv, progname);
198         if (data_len != NULL)
199                 (void)dbenv->set_data_len(dbenv, (u_int32_t)atol(data_len));
200
201         if (nflag) {
202                 if ((ret = dbenv->set_flags(dbenv, DB_NOLOCKING, 1)) != 0) {
203                         dbenv->err(dbenv, ret, "set_flags: DB_NOLOCKING");
204                         goto err;
205                 }
206                 if ((ret = dbenv->set_flags(dbenv, DB_NOPANIC, 1)) != 0) {
207                         dbenv->err(dbenv, ret, "set_flags: DB_NOPANIC");
208                         goto err;
209                 }
210         }
211         if (passwd != NULL && (ret = dbenv->set_encrypt(dbenv,
212             passwd, DB_ENCRYPT_AES)) != 0) {
213                 dbenv->err(dbenv, ret, "set_passwd");
214                 goto err;
215         }
216
217         /* Initialize the environment. */
218         if (db_init(dbenv, home, rflag, cache, &private) != 0)
219                 goto err;
220
221         /* Create the DB object and open the file. */
222         if ((ret = db_create(&dbp, dbenv, 0)) != 0) {
223                 dbenv->err(dbenv, ret, "db_create");
224                 goto err;
225         }
226
227 #if 0
228         Set application-specific btree compression functions here. For example:
229         if ((ret = dbp->set_bt_compress(
230             dbp, local_compress_func, local_decompress_func)) != 0) {
231                 dbp->err(dbp, ret, "DB->set_bt_compress");
232                 goto err;
233         }
234 #endif
235
236         /*
237          * If we're salvaging, don't do an open;  it might not be safe.
238          * Dispatch now into the salvager.
239          */
240         if (rflag) {
241                 /* The verify method is a destructor. */
242                 ret = dbp->verify(dbp, filename, dbname, stdout,
243                     DB_SALVAGE |
244                     (Rflag ? DB_AGGRESSIVE : 0) |
245                     (pflag ? DB_PRINTABLE : 0));
246                 dbp = NULL;
247                 if (ret != 0)
248                         goto err;
249                 goto done;
250         }
251
252         if ((ret = dbp->open(dbp, NULL,
253             filename, dbname, DB_UNKNOWN, DB_RDWRMASTER|DB_RDONLY, 0)) != 0) {
254                 dbp->err(dbp, ret, DB_STR_A("5115", "open: %s", "%s"),
255                     filename == NULL ? dbname : filename);
256                 goto err;
257         }
258         if (private != 0) {
259                 if ((ret = __db_util_cache(dbp, &cache, &resize)) != 0)
260                         goto err;
261                 if (resize) {
262                         (void)dbp->close(dbp, 0);
263                         dbp = NULL;
264
265                         (void)dbenv->close(dbenv, 0);
266                         dbenv = NULL;
267                         goto retry;
268                 }
269         }
270
271         if (dopt != NULL) {
272                 if ((ret =
273                     __db_dumptree(dbp, NULL, dopt, NULL, first, last)) != 0) {
274                         dbp->err(dbp, ret, "__db_dumptree: %s", filename);
275                         goto err;
276                 }
277         } else if (lflag) {
278                 if (dbp->get_multiple(dbp)) {
279                         if (show_subs(dbp))
280                                 goto err;
281                 } else {
282                         dbp->errx(dbp, DB_STR_A("5116",
283                             "%s: does not contain multiple databases", "%s"),
284                             filename);
285                         goto err;
286                 }
287         } else {
288                 if (dbname == NULL && dbp->get_multiple(dbp)) {
289                         if (dump_sub(dbenv, dbp, filename, pflag, keyflag))
290                                 goto err;
291                 } else
292                         if (dbp->dump(dbp, NULL,
293                             __db_pr_callback, stdout, pflag, keyflag))
294                                 goto err;
295         }
296
297         if (0) {
298 err:            exitval = 1;
299         }
300 done:   if (dbp != NULL && (ret = dbp->close(dbp, 0)) != 0) {
301                 exitval = 1;
302                 dbenv->err(dbenv, ret, DB_STR("5117", "close"));
303         }
304         if (dbenv != NULL && (ret = dbenv->close(dbenv, 0)) != 0) {
305                 exitval = 1;
306                 fprintf(stderr,
307                     "%s: dbenv->close: %s\n", progname, db_strerror(ret));
308         }
309
310         if (passwd != NULL)
311                 free(passwd);
312
313         /* Resend any caught signal. */
314         __db_util_sigresend();
315
316         return (exitval == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
317 }
318
319 /*
320  * db_init --
321  *      Initialize the environment.
322  */
323 int
324 db_init(dbenv, home, is_salvage, cache, is_privatep)
325         DB_ENV *dbenv;
326         char *home;
327         int is_salvage;
328         u_int32_t cache;
329         int *is_privatep;
330 {
331         int ret;
332
333         /*
334          * Try and use the underlying environment when opening a database.
335          * We wish to use the buffer pool so our information is as up-to-date
336          * as possible, even if the mpool cache hasn't been flushed.
337          *
338          * If we are not doing a salvage, we want to join the environment;
339          * if a locking system is present, this will let us use it and be
340          * safe to run concurrently with other threads of control.  (We never
341          * need to use transactions explicitly, as we're read-only.)  Note
342          * that in CDB, too, this will configure our environment
343          * appropriately, and our cursors will (correctly) do locking as CDB
344          * read cursors.
345          *
346          * If we are doing a salvage, the verification code will protest
347          * if we initialize transactions, logging, or locking;  do an
348          * explicit DB_INIT_MPOOL to try to join any existing environment
349          * before we create our own.
350          */
351         *is_privatep = 0;
352         if ((ret = dbenv->open(dbenv, home,
353             DB_USE_ENVIRON | (is_salvage ? DB_INIT_MPOOL : 0), 0)) == 0)
354                 return (0);
355         if (ret == DB_VERSION_MISMATCH)
356                 goto err;
357
358         /*
359          * An environment is required because we may be trying to look at
360          * databases in directories other than the current one.  We could
361          * avoid using an environment iff the -h option wasn't specified,
362          * but that seems like more work than it's worth.
363          *
364          * No environment exists (or, at least no environment that includes
365          * an mpool region exists).  Create one, but make it private so that
366          * no files are actually created.
367          */
368         *is_privatep = 1;
369         if ((ret = dbenv->set_cachesize(dbenv, 0, cache, 1)) == 0 &&
370             (ret = dbenv->open(dbenv, home,
371             DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_USE_ENVIRON, 0)) == 0)
372                 return (0);
373
374         /* An environment is required. */
375 err:    dbenv->err(dbenv, ret, "DB_ENV->open");
376         return (1);
377 }
378
379 /*
380  * dump_sub --
381  *      Dump out the records for a DB containing subdatabases.
382  */
383 int
384 dump_sub(dbenv, parent_dbp, parent_name, pflag, keyflag)
385         DB_ENV *dbenv;
386         DB *parent_dbp;
387         char *parent_name;
388         int pflag, keyflag;
389 {
390         DB *dbp;
391         DBC *dbcp;
392         DBT key, data;
393         int ret;
394         char *subdb;
395
396         /*
397          * Get a cursor and step through the database, dumping out each
398          * subdatabase.
399          */
400         if ((ret = parent_dbp->cursor(parent_dbp, NULL, &dbcp, 0)) != 0) {
401                 dbenv->err(dbenv, ret, "DB->cursor");
402                 return (1);
403         }
404
405         memset(&key, 0, sizeof(key));
406         memset(&data, 0, sizeof(data));
407         while ((ret = dbcp->get(dbcp, &key, &data,
408             DB_IGNORE_LEASE | DB_NEXT)) == 0) {
409                 /* Nul terminate the subdatabase name. */
410                 if ((subdb = malloc(key.size + 1)) == NULL) {
411                         dbenv->err(dbenv, ENOMEM, NULL);
412                         return (1);
413                 }
414                 memcpy(subdb, key.data, key.size);
415                 subdb[key.size] = '\0';
416
417                 /* Create the DB object and open the file. */
418                 if ((ret = db_create(&dbp, dbenv, 0)) != 0) {
419                         dbenv->err(dbenv, ret, "db_create");
420                         free(subdb);
421                         return (1);
422                 }
423
424 #if 0
425                 Set application-specific btree compression functions here.
426                 For example:
427
428                 if ((ret = dbp->set_bt_compress(
429                     dbp, local_compress_func, local_decompress_func)) != 0) {
430                         dbp->err(dbp, ret, "DB->set_bt_compress");
431                         goto err;
432                 }
433 #endif
434
435                 if ((ret = dbp->open(dbp, NULL,
436                     parent_name, subdb, DB_UNKNOWN, DB_RDONLY, 0)) != 0)
437                         dbp->err(dbp, ret,
438                             "DB->open: %s:%s", parent_name, subdb);
439                 if (ret == 0 && dbp->dump(
440                     dbp, subdb, __db_pr_callback, stdout, pflag, keyflag))
441                         ret = 1;
442                 (void)dbp->close(dbp, 0);
443                 free(subdb);
444                 if (ret != 0)
445                         return (1);
446         }
447         if (ret != DB_NOTFOUND) {
448                 parent_dbp->err(parent_dbp, ret, "DBcursor->get");
449                 return (1);
450         }
451
452         if ((ret = dbcp->close(dbcp)) != 0) {
453                 parent_dbp->err(parent_dbp, ret, "DBcursor->close");
454                 return (1);
455         }
456
457         return (0);
458 }
459
460 /*
461  * show_subs --
462  *      Display the subdatabases for a database.
463  */
464 int
465 show_subs(dbp)
466         DB *dbp;
467 {
468         DBC *dbcp;
469         DBT key, data;
470         int ret;
471
472         /*
473          * Get a cursor and step through the database, printing out the key
474          * of each key/data pair.
475          */
476         if ((ret = dbp->cursor(dbp, NULL, &dbcp, 0)) != 0) {
477                 dbp->err(dbp, ret, "DB->cursor");
478                 return (1);
479         }
480
481         memset(&key, 0, sizeof(key));
482         memset(&data, 0, sizeof(data));
483         while ((ret = dbcp->get(dbcp, &key, &data,
484             DB_IGNORE_LEASE | DB_NEXT)) == 0) {
485                 if ((ret = dbp->dbenv->prdbt(
486                     &key, 1, NULL, stdout, __db_pr_callback, 0, 0)) != 0) {
487                         dbp->errx(dbp, NULL);
488                         return (1);
489                 }
490         }
491         if (ret != DB_NOTFOUND) {
492                 dbp->err(dbp, ret, "DBcursor->get");
493                 return (1);
494         }
495
496         if ((ret = dbcp->close(dbcp)) != 0) {
497                 dbp->err(dbp, ret, "DBcursor->close");
498                 return (1);
499         }
500         return (0);
501 }
502
503 /*
504  * usage --
505  *      Display the usage message.
506  */
507 int
508 usage()
509 {
510         (void)fprintf(stderr, "usage: %s [-klNprRV]\n\t%s\n",
511             progname,
512     "[-d ahr] [-f output] [-h home] [-P password] [-s database] db_file");
513         (void)fprintf(stderr, "usage: %s [-kNpV] %s\n",
514             progname, "[-d ahr] [-f output] [-h home] -m database");
515         return (EXIT_FAILURE);
516 }
517
518 int
519 version_check()
520 {
521         int v_major, v_minor, v_patch;
522
523         /* Make sure we're loaded with the right version of the DB library. */
524         (void)db_version(&v_major, &v_minor, &v_patch);
525         if (v_major != DB_VERSION_MAJOR || v_minor != DB_VERSION_MINOR) {
526                 fprintf(stderr, DB_STR_A("5118",
527                     "%s: version %d.%d doesn't match library version %d.%d\n",
528                     "%s %d %d %d %d\n"), progname,
529                     DB_VERSION_MAJOR, DB_VERSION_MINOR,
530                     v_major, v_minor);
531                 return (EXIT_FAILURE);
532         }
533         return (0);
534 }