Imported Upstream version 5.3.21
[platform/upstream/libdb.git] / util / db_printlog.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/btree.h"
14 #include "dbinc/fop.h"
15 #include "dbinc/hash.h"
16 #ifdef HAVE_HEAP
17 #include "dbinc/heap.h"
18 #endif
19 #include "dbinc/qam.h"
20 #include "dbinc/txn.h"
21
22 #ifndef lint
23 static const char copyright[] =
24     "Copyright (c) 1996, 2012 Oracle and/or its affiliates.  All rights reserved.\n";
25 #endif
26
27 int db_printlog_print_app_record __P((DB_ENV *, DBT *, DB_LSN *, db_recops));
28 int env_init_print __P((ENV *, u_int32_t, DB_DISTAB *));
29 int env_init_print_42 __P((ENV *, DB_DISTAB *));
30 int env_init_print_43 __P((ENV *, DB_DISTAB *));
31 int env_init_print_47 __P((ENV *, DB_DISTAB *));
32 int env_init_print_48 __P((ENV *, DB_DISTAB *));
33 int lsn_arg __P((char *, DB_LSN *));
34 int main __P((int, char *[]));
35 int open_rep_db __P((DB_ENV *, DB **, DBC **));
36 int usage __P((void));
37 int version_check __P((void));
38
39 const char *progname;
40
41 int
42 main(argc, argv)
43         int argc;
44         char *argv[];
45 {
46         extern char *optarg;
47         extern int optind;
48         DB *dbp;
49         DBC *dbc;
50         DBT data, keydbt;
51         DB_DISTAB dtab;
52         DB_ENV  *dbenv;
53         DB_LOG dblog;
54         DB_LOGC *logc;
55         DB_LSN key, start, stop, verslsn;
56         ENV *env;
57         u_int32_t logcflag, newversion, version;
58         int ch, cmp, exitval, i, nflag, rflag, ret, repflag;
59         char *data_len, *home, *passwd;
60
61         if ((progname = __db_rpath(argv[0])) == NULL)
62                 progname = argv[0];
63         else
64                 ++progname;
65
66         if ((ret = version_check()) != 0)
67                 return (ret);
68
69         dbp = NULL;
70         dbc = NULL;
71         dbenv = NULL;
72         env = NULL;
73         logc = NULL;
74         ZERO_LSN(start);
75         ZERO_LSN(stop);
76         exitval = nflag = rflag = repflag = 0;
77         data_len = home = passwd = NULL;
78
79         memset(&dtab, 0, sizeof(dtab));
80         memset(&dblog, 0, sizeof(dblog));
81
82         while ((ch = getopt(argc, argv, "b:D:e:h:NP:rRV")) != EOF)
83                 switch (ch) {
84                 case 'b':
85                         /* Don't use getsubopt(3), not all systems have it. */
86                         if (lsn_arg(optarg, &start))
87                                 return (usage());
88                         break;
89                 case 'D':
90                         data_len = optarg;
91                         break;
92                 case 'e':
93                         /* Don't use getsubopt(3), not all systems have it. */
94                         if (lsn_arg(optarg, &stop))
95                                 return (usage());
96                         break;
97                 case 'h':
98                         home = optarg;
99                         break;
100                 case 'N':
101                         nflag = 1;
102                         break;
103                 case 'P':
104                         if (passwd != NULL) {
105                                 fprintf(stderr, DB_STR("5138",
106                                         "Password may not be specified twice"));
107                                 free(passwd);
108                                 return (EXIT_FAILURE);
109                         }
110                         passwd = strdup(optarg);
111                         memset(optarg, 0, strlen(optarg));
112                         if (passwd == NULL) {
113                                 fprintf(stderr, DB_STR_A("5010",
114                                     "%s: strdup: %s\n", "%s %s\n"),
115                                     progname, strerror(errno));
116                                 return (EXIT_FAILURE);
117                         }
118                         break;
119                 case 'r':
120                         rflag = 1;
121                         break;
122                 case 'R':               /* Undocumented */
123                         repflag = 1;
124                         break;
125                 case 'V':
126                         printf("%s\n", db_version(NULL, NULL, NULL));
127                         return (EXIT_SUCCESS);
128                 case '?':
129                 default:
130                         return (usage());
131                 }
132         argc -= optind;
133         argv += optind;
134
135         if (argc > 0)
136                 return (usage());
137
138         /* Handle possible interruptions. */
139         __db_util_siginit();
140
141         /*
142          * Create an environment object and initialize it for error
143          * reporting.
144          */
145         if ((ret = db_env_create(&dbenv, 0)) != 0) {
146                 fprintf(stderr,
147                     "%s: db_env_create: %s\n", progname, db_strerror(ret));
148                 goto err;
149         }
150
151         dbenv->set_errfile(dbenv, stderr);
152         dbenv->set_errpfx(dbenv, progname);
153         dbenv->set_msgfile(dbenv, stdout);
154
155         if (data_len != NULL)
156                 (void)dbenv->set_data_len(dbenv, (u_int32_t)atol(data_len));
157
158         if (nflag) {
159                 if ((ret = dbenv->set_flags(dbenv, DB_NOLOCKING, 1)) != 0) {
160                         dbenv->err(dbenv, ret, "set_flags: DB_NOLOCKING");
161                         goto err;
162                 }
163                 if ((ret = dbenv->set_flags(dbenv, DB_NOPANIC, 1)) != 0) {
164                         dbenv->err(dbenv, ret, "set_flags: DB_NOPANIC");
165                         goto err;
166                 }
167         }
168
169         if (passwd != NULL && (ret = dbenv->set_encrypt(dbenv,
170             passwd, DB_ENCRYPT_AES)) != 0) {
171                 dbenv->err(dbenv, ret, "set_passwd");
172                 goto err;
173         }
174
175         /*
176          * Set up an app-specific dispatch function so that we can gracefully
177          * handle app-specific log records.
178          */
179         if ((ret = dbenv->set_app_dispatch(
180             dbenv, db_printlog_print_app_record)) != 0) {
181                 dbenv->err(dbenv, ret, "app_dispatch");
182                 goto err;
183         }
184
185         /*
186          * An environment is required, but as all we're doing is reading log
187          * files, we create one if it doesn't already exist.  If we create
188          * it, create it private so it automatically goes away when we're done.
189          * If we are reading the replication database, do not open the env
190          * with logging, because we don't want to log the opens.
191          */
192         if (repflag) {
193                 if ((ret = dbenv->open(dbenv, home,
194                     DB_INIT_MPOOL | DB_USE_ENVIRON, 0)) != 0 &&
195                     (ret == DB_VERSION_MISMATCH ||
196                     (ret = dbenv->open(dbenv, home,
197                     DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_USE_ENVIRON, 0))
198                     != 0)) {
199                         dbenv->err(dbenv, ret, "DB_ENV->open");
200                         goto err;
201                 }
202         } else if ((ret = dbenv->open(dbenv, home, DB_USE_ENVIRON, 0)) != 0 &&
203             (ret == DB_VERSION_MISMATCH ||
204             (ret = dbenv->open(dbenv, home,
205             DB_CREATE | DB_INIT_LOG | DB_PRIVATE | DB_USE_ENVIRON, 0)) != 0)) {
206                 dbenv->err(dbenv, ret, "DB_ENV->open");
207                 goto err;
208         }
209         env = dbenv->env;
210
211         /* Allocate a log cursor. */
212         if (repflag) {
213                 if ((ret = open_rep_db(dbenv, &dbp, &dbc)) != 0)
214                         goto err;
215         } else if ((ret = dbenv->log_cursor(dbenv, &logc, 0)) != 0) {
216                 dbenv->err(dbenv, ret, "DB_ENV->log_cursor");
217                 goto err;
218         }
219
220         if (IS_ZERO_LSN(start)) {
221                 memset(&keydbt, 0, sizeof(keydbt));
222                 logcflag = rflag ? DB_PREV : DB_NEXT;
223         } else {
224                 key = start;
225                 logcflag = DB_SET;
226         }
227         memset(&data, 0, sizeof(data));
228
229         /*
230          * If we're using the repflag, we're immediately initializing
231          * the print table.  Use the current version.  If we're printing
232          * the log then initialize version to 0 so that we get the
233          * correct version right away.
234          */
235         if (repflag)
236                 version = DB_LOGVERSION;
237         else
238                 version = 0;
239         ZERO_LSN(verslsn);
240
241         /* Initialize print callbacks if repflag. */
242         if (repflag &&
243             (ret = env_init_print(env, version, &dtab)) != 0) {
244                 dbenv->err(dbenv, ret, DB_STR("5011",
245                     "callback: initialization"));
246                 goto err;
247         }
248         for (; !__db_util_interrupted(); logcflag = rflag ? DB_PREV : DB_NEXT) {
249                 if (repflag) {
250                         ret = dbc->get(dbc, &keydbt, &data, logcflag);
251                         if (ret == 0)
252                                 key = ((__rep_control_args *)keydbt.data)->lsn;
253                 } else
254                         ret = logc->get(logc, &key, &data, logcflag);
255                 if (ret != 0) {
256                         if (ret == DB_NOTFOUND)
257                                 break;
258                         dbenv->err(dbenv,
259                             ret, repflag ? "DBC->get" : "DB_LOGC->get");
260                         goto err;
261                 }
262
263                 /*
264                  * We may have reached the end of the range we're displaying.
265                  */
266                 if (!IS_ZERO_LSN(stop)) {
267                         cmp = LOG_COMPARE(&key, &stop);
268                         if ((rflag && cmp < 0) || (!rflag && cmp > 0))
269                                 break;
270                 }
271                 if (!repflag && key.file != verslsn.file) {
272                         /*
273                          * If our log file changed, we need to see if the
274                          * version of the log file changed as well.
275                          * If it changed, reset the print table.
276                          */
277                         if ((ret = logc->version(logc, &newversion, 0)) != 0) {
278                                 dbenv->err(dbenv, ret, "DB_LOGC->version");
279                                 goto err;
280                         }
281                         if (version != newversion) {
282                                 version = newversion;
283                                 if ((ret = env_init_print(env, version,
284                                     &dtab)) != 0) {
285                                         dbenv->err(dbenv, ret, DB_STR("5012",
286                                             "callback: initialization"));
287                                         goto err;
288                                 }
289                         }
290                 }
291
292                 ret = __db_dispatch(env,
293                     &dtab, &data, &key, DB_TXN_PRINT, (void*)&dblog);
294
295                 /*
296                  * XXX
297                  * Just in case the underlying routines don't flush.
298                  */
299                 (void)fflush(stdout);
300
301                 if (ret != 0) {
302                         dbenv->err(dbenv, ret, DB_STR("5013",
303                             "tx: dispatch"));
304                         goto err;
305                 }
306         }
307
308         if (0) {
309 err:            exitval = 1;
310         }
311
312         if (dtab.int_dispatch != NULL)
313                 __os_free(env, dtab.int_dispatch);
314         if (dtab.ext_dispatch != NULL)
315                 __os_free(env, dtab.ext_dispatch);
316         /*
317          * Call __db_close to free the dummy DB handles that were used
318          * by the print routines.
319          */
320         for (i = 0; i < dblog.dbentry_cnt; i++)
321                 if (dblog.dbentry[i].dbp != NULL)
322                         (void)__db_close(dblog.dbentry[i].dbp, NULL, DB_NOSYNC);
323         if (env != NULL && dblog.dbentry != NULL)
324                 __os_free(env, dblog.dbentry);
325         if (logc != NULL && (ret = logc->close(logc, 0)) != 0)
326                 exitval = 1;
327
328         if (dbc != NULL && (ret = dbc->close(dbc)) != 0)
329                 exitval = 1;
330
331         if (dbp != NULL && (ret = dbp->close(dbp, 0)) != 0)
332                 exitval = 1;
333
334         if (dbenv != NULL && (ret = dbenv->close(dbenv, 0)) != 0) {
335                 exitval = 1;
336                 fprintf(stderr,
337                     "%s: dbenv->close: %s\n", progname, db_strerror(ret));
338         }
339
340         if (passwd != NULL)
341                 free(passwd);
342
343         /* Resend any caught signal. */
344         __db_util_sigresend();
345
346         return (exitval == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
347 }
348
349 /*
350  * env_init_print --
351  */
352 int
353 env_init_print(env, version, dtabp)
354         ENV *env;
355         u_int32_t version;
356         DB_DISTAB *dtabp;
357 {
358         int ret;
359
360         /*
361          * We need to prime the print table with the current print
362          * functions.  Then we overwrite only specific entries based on
363          * each previous version we support.
364          */
365         if ((ret = __bam_init_print(env, dtabp)) != 0)
366                 goto err;
367         if ((ret = __crdel_init_print(env, dtabp)) != 0)
368                 goto err;
369         if ((ret = __db_init_print(env, dtabp)) != 0)
370                 goto err;
371         if ((ret = __dbreg_init_print(env, dtabp)) != 0)
372                 goto err;
373         if ((ret = __fop_init_print(env, dtabp)) != 0)
374                 goto err;
375 #ifdef HAVE_HASH
376         if ((ret = __ham_init_print(env, dtabp)) != 0)
377                 goto err;
378 #endif
379 #ifdef HAVE_HEAP
380         if ((ret = __heap_init_print(env, dtabp)) != 0)
381                 goto err;
382 #endif
383 #ifdef HAVE_QUEUE
384         if ((ret = __qam_init_print(env, dtabp)) != 0)
385                 goto err;
386 #endif
387 #ifdef HAVE_REPLICATION_THREADS
388         if ((ret = __repmgr_init_print(env, dtabp)) != 0)
389                 goto err;
390 #endif
391         if ((ret = __txn_init_print(env, dtabp)) != 0)
392                 goto err;
393
394         /*
395          * There are no log differences between 5.0 and 5.2, but 5.2
396          * is a superset of 5.0.  Patch 2 of 4.8 added __db_pg_trunc
397          * but didn't alter any log records so we want the same
398          * override as 4.8
399          */
400         if (version > DB_LOGVERSION_48p2)
401                 goto done;
402         if ((ret = env_init_print_48(env, dtabp)) != 0)
403                 goto err;
404         if (version >= DB_LOGVERSION_48)
405                 goto done;
406         if ((ret = env_init_print_47(env, dtabp)) != 0)
407                 goto err;
408         if (version == DB_LOGVERSION_47)
409                 goto done;
410         /*
411          * There are no log record/recovery differences between 4.4 and 4.5.
412          * The log version changed due to checksum.  There are no log recovery
413          * differences between 4.5 and 4.6.  The name of the rep_gen in
414          * txn_checkpoint changed (to spare, since we don't use it anymore).
415          */
416         if (version >= DB_LOGVERSION_44)
417                 goto done;
418         if ((ret = env_init_print_43(env, dtabp)) != 0)
419                 goto err;
420         if (version == DB_LOGVERSION_43)
421                 goto done;
422         if (version != DB_LOGVERSION_42) {
423                 __db_errx(env, DB_STR_A("5014",
424                     "Unknown version %lu", "%lu"), (u_long)version);
425                 ret = EINVAL;
426                 goto err;
427         }
428         ret = env_init_print_42(env, dtabp);
429 done:
430 err:    return (ret);
431 }
432
433 int
434 env_init_print_42(env, dtabp)
435         ENV *env;
436         DB_DISTAB *dtabp;
437 {
438         int ret;
439
440         if ((ret = __db_add_recovery_int(env, dtabp,
441             __db_relink_42_print, DB___db_relink_42)) != 0)
442                 goto err;
443         if ((ret = __db_add_recovery_int(env, dtabp,
444             __db_pg_alloc_42_print, DB___db_pg_alloc_42)) != 0)
445                 goto err;
446         if ((ret = __db_add_recovery_int(env, dtabp,
447             __db_pg_free_42_print, DB___db_pg_free_42)) != 0)
448                 goto err;
449         if ((ret = __db_add_recovery_int(env, dtabp,
450             __db_pg_freedata_42_print, DB___db_pg_freedata_42)) != 0)
451                 goto err;
452 #if HAVE_HASH
453         if ((ret = __db_add_recovery_int(env, dtabp,
454             __ham_metagroup_42_print, DB___ham_metagroup_42)) != 0)
455                 goto err;
456         if ((ret = __db_add_recovery_int(env, dtabp,
457             __ham_groupalloc_42_print, DB___ham_groupalloc_42)) != 0)
458                 goto err;
459 #endif
460         if ((ret = __db_add_recovery_int(env, dtabp,
461             __txn_ckp_42_print, DB___txn_ckp_42)) != 0)
462                 goto err;
463 err:
464         return (ret);
465 }
466
467 int
468 env_init_print_43(env, dtabp)
469         ENV *env;
470         DB_DISTAB *dtabp;
471 {
472         int ret;
473
474         if ((ret = __db_add_recovery_int(env, dtabp,
475             __bam_relink_43_print, DB___bam_relink_43)) != 0)
476                 goto err;
477         /*
478          * We want to use the 4.2-based txn_regop record.
479          */
480         if ((ret = __db_add_recovery_int(env, dtabp,
481             __txn_regop_42_print, DB___txn_regop_42)) != 0)
482                 goto err;
483
484 err:
485         return (ret);
486 }
487
488 /*
489  * env_init_print_47 --
490  *
491  */
492 int
493 env_init_print_47(env, dtabp)
494         ENV *env;
495         DB_DISTAB *dtabp;
496 {
497         int ret;
498
499         if ((ret = __db_add_recovery_int(env, dtabp,
500            __bam_split_42_print, DB___bam_split_42)) != 0)
501                 goto err;
502         if ((ret = __db_add_recovery_int(env, dtabp,
503             __db_pg_sort_44_print, DB___db_pg_sort_44)) != 0)
504                 goto err;
505         if ((ret = __db_add_recovery_int(env, dtabp,
506             __db_pg_sort_44_print, DB___db_pg_sort_44)) != 0)
507                 goto err;
508         if ((ret = __db_add_recovery_int(env, dtabp,
509             __fop_create_42_print, DB___fop_create_42)) != 0)
510                 goto err;
511         if ((ret = __db_add_recovery_int(env, dtabp,
512             __fop_write_42_print, DB___fop_write_42)) != 0)
513                 goto err;
514         if ((ret = __db_add_recovery_int(env, dtabp,
515             __fop_rename_42_print, DB___fop_rename_42)) != 0)
516                 goto err;
517         if ((ret = __db_add_recovery_int(env, dtabp,
518             __fop_rename_42_print, DB___fop_rename_noundo_46)) != 0)
519                 goto err;
520         if ((ret = __db_add_recovery_int(env, dtabp,
521             __txn_xa_regop_42_print, DB___txn_xa_regop_42)) != 0)
522                 goto err;
523
524 err:
525         return (ret);
526 }
527
528 int
529 env_init_print_48(env, dtabp)
530         ENV *env;
531         DB_DISTAB *dtabp;
532 {
533         int ret;
534         if ((ret = __db_add_recovery_int(env, dtabp,
535             __db_pg_sort_44_print, DB___db_pg_sort_44)) != 0)
536                 goto err;
537         if ((ret = __db_add_recovery_int(env, dtabp,
538             __db_addrem_42_print, DB___db_addrem_42)) != 0)
539                 goto err;
540         if ((ret = __db_add_recovery_int(env, dtabp,
541             __db_big_42_print, DB___db_big_42)) != 0)
542                 goto err;
543         if ((ret = __db_add_recovery_int(env, dtabp,
544             __bam_split_48_print, DB___bam_split_48)) != 0)
545                 goto err;
546 #ifdef HAVE_HASH
547         if ((ret = __db_add_recovery_int(env, dtabp,
548             __ham_insdel_42_print, DB___ham_insdel_42)) != 0)
549                 goto err;
550         if ((ret = __db_add_recovery_int(env, dtabp,
551             __ham_replace_42_print, DB___ham_replace_42)) != 0)
552                 goto err;
553 #endif
554
555 err:
556         return (ret);
557 }
558
559 int
560 usage()
561 {
562         fprintf(stderr, "usage: %s %s\n", progname,
563             "[-NrV] [-b file/offset] [-e file/offset] [-h home] [-P password]");
564         return (EXIT_FAILURE);
565 }
566
567 int
568 version_check()
569 {
570         int v_major, v_minor, v_patch;
571
572         /* Make sure we're loaded with the right version of the DB library. */
573         (void)db_version(&v_major, &v_minor, &v_patch);
574         if (v_major != DB_VERSION_MAJOR || v_minor != DB_VERSION_MINOR) {
575                 fprintf(stderr, DB_STR_A("5015",
576                     "%s: version %d.%d doesn't match library version %d.%d\n",
577                     "%s %d %d %d %d\n"), progname,
578                     DB_VERSION_MAJOR, DB_VERSION_MINOR,
579                     v_major, v_minor);
580                 return (EXIT_FAILURE);
581         }
582         return (0);
583 }
584
585 /* Print an unknown, application-specific log record as best we can. */
586 int
587 db_printlog_print_app_record(dbenv, dbt, lsnp, op)
588         DB_ENV *dbenv;
589         DBT *dbt;
590         DB_LSN *lsnp;
591         db_recops op;
592 {
593         u_int32_t i, rectype;
594         int ch;
595
596         DB_ASSERT(dbenv->env, op == DB_TXN_PRINT);
597
598         COMPQUIET(dbenv, NULL);
599         COMPQUIET(op, DB_TXN_PRINT);
600
601         /*
602          * Fetch the rectype, which always must be at the beginning of the
603          * record (if dispatching is to work at all).
604          */
605         memcpy(&rectype, dbt->data, sizeof(rectype));
606
607         /*
608          * Applications may wish to customize the output here based on the
609          * rectype.  We just print the entire log record in the generic
610          * mixed-hex-and-printable format we use for binary data.
611          */
612         printf(DB_STR_A("5016",
613             "[%lu][%lu]application specific record: rec: %lu\n",
614             "%lu %lu %lu"), (u_long)lsnp->file, (u_long)lsnp->offset,
615             (u_long)rectype);
616         printf(DB_STR("5017", "\tdata: "));
617         for (i = 0; i < dbt->size; i++) {
618                 ch = ((u_int8_t *)dbt->data)[i];
619                 printf(isprint(ch) || ch == 0x0a ? "%c" : "%#x ", ch);
620         }
621         printf("\n\n");
622
623         return (0);
624 }
625
626 int
627 open_rep_db(dbenv, dbpp, dbcp)
628         DB_ENV *dbenv;
629         DB **dbpp;
630         DBC **dbcp;
631 {
632         int ret;
633
634         DB *dbp;
635         *dbpp = NULL;
636         *dbcp = NULL;
637
638         if ((ret = db_create(dbpp, dbenv, 0)) != 0) {
639                 dbenv->err(dbenv, ret, "db_create");
640                 return (ret);
641         }
642
643         dbp = *dbpp;
644         if ((ret = dbp->open(dbp, NULL,
645             REPDBNAME, NULL, DB_BTREE, DB_RDONLY, 0)) != 0) {
646                 dbenv->err(dbenv, ret, "DB->open");
647                 goto err;
648         }
649
650         if ((ret = dbp->cursor(dbp, NULL, dbcp, 0)) != 0) {
651                 dbenv->err(dbenv, ret, "DB->cursor");
652                 goto err;
653         }
654
655         return (0);
656
657 err:    if (*dbpp != NULL)
658                 (void)(*dbpp)->close(*dbpp, 0);
659         return (ret);
660 }
661
662 /*
663  * lsn_arg --
664  *      Parse a LSN argument.
665  */
666 int
667 lsn_arg(arg, lsnp)
668         char *arg;
669         DB_LSN *lsnp;
670 {
671         u_long uval;
672         char *p;
673
674         /*
675          * Expected format is: lsn.file/lsn.offset.
676          */
677         if ((p = strchr(arg, '/')) == NULL)
678                 return (1);
679         *p = '\0';
680
681         if (__db_getulong(NULL, progname, arg, 0, UINT32_MAX, &uval))
682                 return (1);
683         lsnp->file = uval;
684         if (__db_getulong(NULL, progname, p + 1, 0, UINT32_MAX, &uval))
685                 return (1);
686         lsnp->offset = uval;
687         return (0);
688 }