00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "db_config.h"
00011
00012 #ifndef lint
00013 static const char copyright[] =
00014 "Copyright (c) 1996-2005\nSleepycat Software Inc. All rights reserved.\n";
00015 #endif
00016
00017 #ifndef NO_SYSTEM_INCLUDES
00018 #include <sys/types.h>
00019 #include <sys/stat.h>
00020
00021 #include <fcntl.h>
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 #include <time.h>
00026 #include <unistd.h>
00027 #endif
00028
00029 #include "db_int.h"
00030 #include "dbinc/log.h"
00031
00032 enum which_open { OPEN_ORIGINAL, OPEN_HOT_BACKUP };
00033
00034 int db_hotbackup_backup_dir_clean __P((DB_ENV *, char *, int *, int, int));
00035 int db_hotbackup_data_copy __P((DB_ENV *, char *, char *, char *, int));
00036 int db_hotbackup_env_init __P((DB_ENV **, char *, char *, char *, enum which_open));
00037 int db_hotbackup_main __P((int, char *[]));
00038 int db_hotbackup_read_data_dir __P((DB_ENV *, char *, char *, int));
00039 int db_hotbackup_read_log_dir __P((DB_ENV *, char *, char *, int *, int, int));
00040 int db_hotbackup_usage __P((void));
00041 int db_hotbackup_version_check __P((void));
00042
00043 const char *progname;
00044
00045 int
00046 db_hotbackup(args)
00047 char *args;
00048 {
00049 int argc;
00050 char **argv;
00051
00052 __db_util_arg("db_hotbackup", args, &argc, &argv);
00053 return (db_hotbackup_main(argc, argv) ? EXIT_FAILURE : EXIT_SUCCESS);
00054 }
00055
00056 #include <stdio.h>
00057 #define ERROR_RETURN ERROR
00058
00059 int
00060 db_hotbackup_main(argc, argv)
00061 int argc;
00062 char *argv[];
00063 {
00064 extern char *optarg;
00065 extern int optind, __db_getopt_reset;
00066 time_t now;
00067 DB_ENV *dbenv;
00068 u_int data_cnt, data_next;
00069 int ch, checkpoint, copy_min, exitval, remove_max, ret, update, verbose;
00070 char *backup_dir, **data_dir, **dir, *home, *log_dir, *passwd;
00071
00072 if ((progname = strrchr(argv[0], '/')) == NULL)
00073 progname = argv[0];
00074 else
00075 ++progname;
00076
00077 if ((ret = db_hotbackup_version_check()) != 0)
00078 return (ret);
00079
00080 checkpoint = data_cnt = data_next = exitval = update = verbose = 0;
00081 data_dir = NULL;
00082 backup_dir = home = log_dir = passwd = NULL;
00083 copy_min = remove_max = 0;
00084 __db_getopt_reset = 1;
00085 while ((ch = getopt(argc, argv, "b:cd:h:l:P:uVv")) != EOF)
00086 switch (ch) {
00087 case 'b':
00088 backup_dir = optarg;
00089 break;
00090 case 'c':
00091 checkpoint = 1;
00092 break;
00093 case 'd':
00094
00095
00096
00097
00098 if (data_dir == NULL || data_next >= data_cnt - 2) {
00099 data_cnt = data_cnt == 0 ? 20 : data_cnt * 2;
00100 if ((data_dir = realloc(data_dir,
00101 data_cnt * sizeof(*data_dir))) == NULL) {
00102 fprintf(stderr, "%s: %s\n",
00103 progname, strerror(errno));
00104 return (EXIT_FAILURE);
00105 }
00106 }
00107 data_dir[data_next++] = optarg;
00108 break;
00109 case 'h':
00110 home = optarg;
00111 break;
00112 case 'l':
00113 log_dir = optarg;
00114 break;
00115 case 'P':
00116 passwd = strdup(optarg);
00117 memset(optarg, 0, strlen(optarg));
00118 if (passwd == NULL) {
00119 fprintf(stderr, "%s: strdup: %s\n",
00120 progname, strerror(errno));
00121 return (EXIT_FAILURE);
00122 }
00123 break;
00124 case 'u':
00125 update = 1;
00126 break;
00127 case 'V':
00128 printf("%s\n", db_version(NULL, NULL, NULL));
00129 return (EXIT_SUCCESS);
00130 case 'v':
00131 verbose = 1;
00132 break;
00133 case '?':
00134 default:
00135 return (db_hotbackup_usage());
00136 }
00137 argc -= optind;
00138 argv += optind;
00139
00140 if (argc != 0)
00141 return (db_hotbackup_usage());
00142
00143
00144 __db_util_siginit();
00145
00146
00147
00148
00149
00150
00151
00152
00153 if (home == NULL)
00154 home = getenv("DB_HOME");
00155 if (home == NULL) {
00156 fprintf(stderr,
00157 "%s: no source database environment specified\n", progname);
00158 return (db_hotbackup_usage());
00159 }
00160 if (log_dir == NULL)
00161 log_dir = home;
00162 if (backup_dir == NULL) {
00163 fprintf(stderr,
00164 "%s: no target backup directory specified\n", progname);
00165 return (db_hotbackup_usage());
00166 }
00167
00168
00169 if (data_dir != NULL)
00170 data_dir[data_next] = NULL;
00171
00172 if (verbose) {
00173 (void)time(&now);
00174 printf("%s: hot backup started at %s", progname, ctime(&now));
00175 }
00176
00177
00178 if ((ret = db_hotbackup_env_init(&dbenv, home, log_dir, passwd, OPEN_ORIGINAL)) != 0)
00179 goto shutdown;
00180
00181
00182
00183
00184
00185 if (checkpoint) {
00186 if (verbose)
00187 printf("%s: %s: force checkpoint\n", progname, home);
00188 if ((ret =
00189 dbenv->txn_checkpoint(dbenv, 0, 0, DB_FORCE)) != 0) {
00190 dbenv->err(dbenv, ret, "DB_ENV->txn_checkpoint");
00191 goto shutdown;
00192 }
00193 if (!update) {
00194 if (verbose)
00195 printf("%s: %s: remove unnecessary log files\n",
00196 progname, home);
00197 if ((ret = dbenv->log_archive(dbenv,
00198 NULL, DB_ARCH_REMOVE)) != 0) {
00199 dbenv->err(dbenv, ret, "DB_ENV->log_archive");
00200 goto shutdown;
00201 }
00202 }
00203 }
00204
00205
00206
00207
00208
00209
00210
00211 (void)__os_mkdir(NULL, backup_dir, __db_omode("rwx------"));
00212
00213
00214
00215
00216
00217
00218
00219 if ((ret = db_hotbackup_backup_dir_clean(
00220 dbenv, backup_dir, &remove_max, update, verbose)) != 0)
00221 goto shutdown;
00222
00223
00224
00225
00226
00227
00228 if (!update) {
00229 if (db_hotbackup_read_data_dir(dbenv, backup_dir, home, verbose) != 0)
00230 goto shutdown;
00231 if (data_dir != NULL)
00232 for (dir = &data_dir[0]; *dir != NULL; ++dir)
00233 if (db_hotbackup_read_data_dir(
00234 dbenv, backup_dir, *dir, verbose) != 0)
00235 goto shutdown;
00236 }
00237
00238
00239
00240
00241
00242
00243
00244
00245 if (db_hotbackup_read_log_dir(dbenv,
00246 backup_dir, log_dir, ©_min, update, verbose) != 0)
00247 goto shutdown;
00248
00249
00250
00251
00252
00253
00254
00255 if (update && remove_max < copy_min &&
00256 !(remove_max == 0 && copy_min == 1)) {
00257 fprintf(stderr,
00258 "%s: the largest log file removed (%d) must be greater\n",
00259 progname, remove_max);
00260 fprintf(stderr,
00261 "%s: than or equal the smallest log file copied (%d)\n",
00262 progname, copy_min);
00263 goto shutdown;
00264 }
00265
00266
00267 if ((ret = dbenv->close(dbenv, 0)) != 0) {
00268 fprintf(stderr,
00269 "%s: dbenv->close: %s\n", progname, db_strerror(ret));
00270 dbenv = NULL;
00271 goto shutdown;
00272 }
00273
00274 if (verbose)
00275 printf("%s: %s: run catastrophic recovery\n",
00276 progname, backup_dir);
00277 if ((ret = db_hotbackup_env_init(
00278 &dbenv, backup_dir, NULL, passwd, OPEN_HOT_BACKUP)) != 0)
00279 goto shutdown;
00280
00281
00282
00283
00284 if (verbose)
00285 printf("%s: %s: remove unnecessary log files\n",
00286 progname, backup_dir);
00287 if ((ret =
00288 dbenv->log_archive(dbenv, NULL, DB_ARCH_REMOVE)) != 0) {
00289 dbenv->err(dbenv, ret, "DB_ENV->log_archive");
00290 goto shutdown;
00291 }
00292
00293 if (0) {
00294 shutdown: exitval = 1;
00295 }
00296 if (dbenv != NULL && (ret = dbenv->close(dbenv, 0)) != 0) {
00297 exitval = 1;
00298 fprintf(stderr,
00299 "%s: dbenv->close: %s\n", progname, db_strerror(ret));
00300 }
00301
00302 if (data_dir != NULL)
00303 free(data_dir);
00304 if (passwd != NULL)
00305 free(passwd);
00306
00307 if (exitval == 0) {
00308 if (verbose) {
00309 (void)time(&now);
00310 printf("%s: hot backup completed at %s",
00311 progname, ctime(&now));
00312 }
00313 } else {
00314 fprintf(stderr, "%s: HOT BACKUP FAILED!\n", progname);
00315 }
00316
00317
00318 __db_util_sigresend();
00319
00320 return (exitval == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
00321
00322 }
00323
00324
00325
00326
00327
00328 int
00329 db_hotbackup_env_init(dbenvp, home, log_dir, passwd, which)
00330 DB_ENV **dbenvp;
00331 char *home, *log_dir, *passwd;
00332 enum which_open which;
00333 {
00334 DB_ENV *dbenv;
00335 int ret;
00336
00337 *dbenvp = NULL;
00338
00339
00340
00341
00342 if ((ret = db_env_create(&dbenv, 0)) != 0) {
00343 fprintf(stderr,
00344 "%s: db_env_create: %s\n", progname, db_strerror(ret));
00345 return (1);
00346 }
00347
00348 dbenv->set_errfile(dbenv, stderr);
00349 setbuf(stderr, NULL);
00350 dbenv->set_errpfx(dbenv, progname);
00351 setvbuf(stdout, NULL, _IOLBF, 0);
00352
00353
00354
00355
00356
00357 if (log_dir != NULL && log_dir != home &&
00358 (ret = dbenv->set_lg_dir(dbenv, log_dir)) != 0) {
00359 dbenv->err(dbenv, ret, "DB_ENV->set_lg_dir: %s", log_dir);
00360 return (1);
00361 }
00362
00363
00364 if (passwd != NULL &&
00365 (ret = dbenv->set_encrypt(dbenv, passwd, DB_ENCRYPT_AES)) != 0) {
00366 dbenv->err(dbenv, ret, "DB_ENV->set_encrypt");
00367 return (1);
00368 }
00369
00370 switch (which) {
00371 case OPEN_ORIGINAL:
00372
00373
00374
00375
00376
00377 if ((ret = dbenv->open(dbenv, home, DB_USE_ENVIRON, 0)) != 0 &&
00378 (ret == DB_VERSION_MISMATCH ||
00379 (ret = dbenv->open(dbenv, home, DB_CREATE |
00380 DB_INIT_LOG | DB_INIT_TXN | DB_PRIVATE | DB_USE_ENVIRON,
00381 0)) != 0)) {
00382 dbenv->err(dbenv, ret, "DB_ENV->open: %s", home);
00383 return (1);
00384 }
00385 break;
00386 case OPEN_HOT_BACKUP:
00387
00388
00389
00390
00391 if ((ret = dbenv->open(dbenv, home, DB_CREATE |
00392 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_PRIVATE |
00393 DB_RECOVER_FATAL | DB_USE_ENVIRON, 0)) != 0) {
00394 dbenv->err(dbenv, ret, "DB_ENV->open: %s", home);
00395 return (1);
00396 }
00397 break;
00398 }
00399
00400 *dbenvp = dbenv;
00401 return (0);
00402 }
00403
00404
00405
00406
00407
00408 int
00409 db_hotbackup_backup_dir_clean(dbenv, backup_dir, remove_maxp, update, verbose)
00410 DB_ENV *dbenv;
00411 char *backup_dir;
00412 int *remove_maxp, update, verbose;
00413 {
00414 int cnt, fcnt, ret, v;
00415 char **names;
00416 char buf[2048];
00417
00418
00419 if ((ret = __os_dirlist(dbenv, backup_dir, &names, &fcnt)) != 0) {
00420 dbenv->err(dbenv, ret, "%s: directory read", backup_dir);
00421 return (1);
00422 }
00423 for (cnt = fcnt; --cnt >= 0;) {
00424
00425
00426
00427 if (!strcmp(names[cnt], ".") || !strcmp(names[cnt], ".."))
00428 continue;
00429 if (strncmp(names[cnt], LFPREFIX, sizeof(LFPREFIX) - 1)) {
00430 if (update)
00431 continue;
00432 } else {
00433
00434 v = atoi(names[cnt] + sizeof(LFPREFIX) - 1);
00435 if (*remove_maxp < v)
00436 *remove_maxp = v;
00437 }
00438 if ((size_t)snprintf(buf, sizeof(buf),
00439 "%s/%s", backup_dir, names[cnt]) == sizeof(buf)) {
00440 dbenv->err(dbenv, ret,
00441 "%s/%s: path too long", backup_dir, names[cnt]);
00442 return (1);
00443 }
00444 if (verbose)
00445 printf("%s: removing %s\n", progname, buf);
00446 if ((ret = remove(buf)) != 0) {
00447 dbenv->err(dbenv, ret, "%s: remove", buf);
00448 return (1);
00449 }
00450 }
00451
00452 __os_dirfree(dbenv, names, fcnt);
00453
00454 if (verbose && *remove_maxp != 0)
00455 printf("%s: highest numbered log file removed: %d\n",
00456 progname, *remove_maxp);
00457
00458 return (0);
00459 }
00460
00461
00462
00463
00464
00465 int
00466 db_hotbackup_read_data_dir(dbenv, backup_dir, dir, verbose)
00467 DB_ENV *dbenv;
00468 char *backup_dir, *dir;
00469 int verbose;
00470 {
00471 int cnt, fcnt, ret;
00472 char **names;
00473 char buf[2048];
00474
00475
00476 if ((ret = __os_dirlist(dbenv, dir, &names, &fcnt)) != 0) {
00477 dbenv->err(dbenv, ret, "%s: directory read", dir);
00478 return (1);
00479 }
00480 for (cnt = fcnt; --cnt >= 0;) {
00481
00482
00483
00484
00485 if (!strcmp(names[cnt], ".") || !strcmp(names[cnt], ".."))
00486 continue;
00487 if (!strncmp(names[cnt], LFPREFIX, sizeof(LFPREFIX) - 1))
00488 continue;
00489 if (!strncmp(names[cnt],
00490 DB_REGION_PREFIX, sizeof(DB_REGION_PREFIX) - 1))
00491 continue;
00492
00493
00494 if ((size_t)snprintf(buf, sizeof(buf),
00495 "%s/%s", dir, names[cnt]) == sizeof(buf)) {
00496 dbenv->errx(dbenv,
00497 "%s/%s: path too long", dir, names[cnt]);
00498 return (1);
00499 }
00500
00501
00502 if ((ret = db_hotbackup_data_copy(
00503 dbenv, buf, backup_dir, names[cnt], verbose)) != 0)
00504 return (1);
00505 }
00506
00507 __os_dirfree(dbenv, names, fcnt);
00508
00509 return (0);
00510 }
00511
00512
00513
00514
00515
00516 int
00517 db_hotbackup_read_log_dir(dbenv, backup_dir, log_dir, copy_minp, update, verbose)
00518 DB_ENV *dbenv;
00519 char *backup_dir, *log_dir;
00520 int *copy_minp, update, verbose;
00521 {
00522 int aflag, ret, v;
00523 char **begin, **names;
00524 char from[2048], to[2048];
00525
00526 again: aflag = DB_ARCH_LOG;
00527
00528
00529
00530
00531
00532 if (update)
00533 aflag = 0;
00534
00535 if ((ret = dbenv->log_archive(dbenv, &names, aflag)) != 0) {
00536 dbenv->err(dbenv, ret, "%s: log_archive", log_dir);
00537 return (1);
00538 }
00539 if (names == NULL)
00540 goto done;
00541 begin = names;
00542 for (; *names != NULL; names++) {
00543
00544 v = atoi(*names + sizeof(LFPREFIX) - 1);
00545 if (*copy_minp == 0 || *copy_minp > v)
00546 *copy_minp = v;
00547
00548
00549 if ((size_t)snprintf(from, sizeof(from),
00550 "%s/%s", log_dir, *names) == sizeof(from)) {
00551 dbenv->errx(dbenv,
00552 "%s/%s: path too long", log_dir, *names);
00553 return (1);
00554 }
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564 if (update) {
00565 if ((size_t)snprintf(to, sizeof(to),
00566 "%s/%s", backup_dir, *names) == sizeof(to)) {
00567 dbenv->errx(dbenv,
00568 "%s/%s: path too long", backup_dir, *names);
00569 return (1);
00570 }
00571 if (rename(from, to) == 0) {
00572 if (verbose)
00573 printf("%s: moving %s to %s\n",
00574 progname, from, to);
00575 continue;
00576 }
00577 }
00578
00579
00580 if ((ret = db_hotbackup_data_copy(dbenv,
00581 from, backup_dir, *names, verbose)) != 0)
00582 return (1);
00583
00584 if (update) {
00585 if (verbose)
00586 printf("%s: removing %s\n", progname, from);
00587 if ((ret = __os_unlink(dbenv, from)) != 0) {
00588 dbenv->err(dbenv, ret,
00589 "unlink of %s failed", from);
00590 return (1);
00591 }
00592 }
00593
00594 }
00595
00596 free(begin);
00597 done: if (update) {
00598 update = 0;
00599 goto again;
00600 }
00601
00602 if (verbose && *copy_minp != 0)
00603 printf("%s: lowest numbered log file copied: %d\n",
00604 progname, *copy_minp);
00605
00606 return (0);
00607 }
00608
00609
00610
00611
00612
00613 int
00614 db_hotbackup_data_copy(dbenv, from, to_dir, to_file, verbose)
00615 DB_ENV *dbenv;
00616 char *from, *to_dir, *to_file;
00617 int verbose;
00618 {
00619 ssize_t nr, nw;
00620 size_t offset;
00621 int ret, rfd, wfd;
00622 char *buf, *taddr;
00623
00624 ret = 0;
00625 rfd = wfd = -1;
00626
00627 if (verbose)
00628 printf("%s: copying %s to %s/%s\n",
00629 progname, from, to_dir, to_file);
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641 if ((buf = malloc(MEGABYTE)) == NULL) {
00642 dbenv->err(dbenv,
00643 errno, "%lu buffer allocation", (u_long)MEGABYTE);
00644 return (1);
00645 }
00646
00647
00648 if ((rfd = open(from, O_RDONLY, 0)) == -1) {
00649 dbenv->err(dbenv, errno, "%s", from);
00650 goto err;
00651 }
00652
00653
00654 if ((u_int32_t)snprintf(
00655 buf, MEGABYTE, "%s/%s", to_dir, to_file) == MEGABYTE) {
00656 dbenv->errx(dbenv, "%s/%s: path too long", to_dir, to_file);
00657 goto err;
00658 }
00659 if ((wfd = open(
00660 buf, O_CREAT | O_TRUNC | O_WRONLY, __db_omode(OWNER_RW))) == -1)
00661 goto err;
00662
00663
00664 while ((nr = read(rfd, buf, MEGABYTE)) > 0)
00665 for (taddr = buf, offset = 0;
00666 offset < (size_t)nr; taddr += nw, offset += (size_t)nw) {
00667 RETRY_CHK(((nw = write(wfd,
00668 taddr, (u_int)(nr - offset))) < 0 ? 1 : 0), ret);
00669 if (ret != 0)
00670 break;
00671 }
00672 if (nr == -1) {
00673 dbenv->err(dbenv, errno, "%s: read", from);
00674 goto err;
00675 }
00676
00677 if (ret != 0) {
00678 dbenv->err(dbenv, errno, "%s: write %s/%s", to_dir, to_file);
00679 goto err;
00680 }
00681
00682 if (0) {
00683 err: ret = 1;
00684 }
00685 if (buf != NULL)
00686 free(buf);
00687
00688 if (rfd != -1)
00689 (void)close(rfd);
00690
00691
00692 if (wfd != -1 && (fsync(wfd) != 0 || close(wfd) != 0)) {
00693 dbenv->err(dbenv,
00694 errno, "%s: fsync %s/%s", to_dir, to_file);
00695 ret = 1;
00696 }
00697 return (ret);
00698 }
00699
00700 int
00701 db_hotbackup_usage()
00702 {
00703 (void)fprintf(stderr, "usage: %s [-cuVv]\n\t%s\n", progname,
00704 "[-d data_dir ...] [-h home] [-l log_dir] [-P password] -b backup_dir");
00705 return (EXIT_FAILURE);
00706 }
00707
00708 int
00709 db_hotbackup_version_check()
00710 {
00711 int v_major, v_minor, v_patch;
00712
00713
00714 (void)db_version(&v_major, &v_minor, &v_patch);
00715 if (v_major != DB_VERSION_MAJOR || v_minor != DB_VERSION_MINOR) {
00716 fprintf(stderr,
00717 "%s: version %d.%d doesn't match library version %d.%d\n",
00718 progname, DB_VERSION_MAJOR, DB_VERSION_MINOR,
00719 v_major, v_minor);
00720 return (EXIT_FAILURE);
00721 }
00722 return (0);
00723 }