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