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