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