1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2000,2008 Oracle. All rights reserved. 5 * 6 * $Id: db_server_util.c,v 12.16 2008/03/14 20:00:18 mbrey Exp $ 7 */ 8 9#include "db_config.h" 10 11#include "db_int.h" 12#ifdef HAVE_SYSTEM_INCLUDE_FILES 13#include <rpc/rpc.h> 14#endif 15#include "db_server.h" 16#include "dbinc_auto/clib_ext.h" 17#include "dbinc/db_server_int.h" 18#include "dbinc_auto/common_ext.h" 19#include "dbinc_auto/rpc_server_ext.h" 20 21static int add_home __P((char *)); 22static int add_passwd __P((char *)); 23static int env_recover __P((char *)); 24static void __dbclear_child __P((ct_entry *)); 25 26static LIST_HEAD(cthead, ct_entry) __dbsrv_head; 27static LIST_HEAD(homehead, home_entry) __dbsrv_home; 28static long __dbsrv_defto = DB_SERVER_TIMEOUT; 29static long __dbsrv_maxto = DB_SERVER_MAXTIMEOUT; 30static long __dbsrv_idleto = DB_SERVER_IDLETIMEOUT; 31static char *logfile = NULL; 32static char *prog; 33 34static void usage __P((void)); 35static void version_check __P((void)); 36 37int __dbsrv_verbose = 0; 38 39int 40main(argc, argv) 41 int argc; 42 char **argv; 43{ 44 extern int __dbsrv_main(); 45 extern char *optarg; 46 CLIENT *cl; 47 int ch, ret; 48 char *passwd; 49 50 prog = argv[0]; 51 52 version_check(); 53 54 ret = 0; 55 /* 56 * Check whether another server is running or not. There 57 * is a race condition where two servers could be racing to 58 * register with the portmapper. The goal of this check is to 59 * forbid running additional servers (like those started from 60 * the test suite) if the user is already running one. 61 * 62 * XXX 63 * This does not solve nor prevent two servers from being 64 * started at the same time and running recovery at the same 65 * time on the same environments. 66 */ 67 if ((cl = clnt_create("localhost", 68 DB_RPC_SERVERPROG, DB_RPC_SERVERVERS, "tcp")) != NULL) { 69 fprintf(stderr, 70 "%s: Berkeley DB RPC server already running.\n", prog); 71 clnt_destroy(cl); 72 return (EXIT_FAILURE); 73 } 74 75 LIST_INIT(&__dbsrv_home); 76 while ((ch = getopt(argc, argv, "h:I:L:P:t:T:Vv")) != EOF) 77 switch (ch) { 78 case 'h': 79 (void)add_home(optarg); 80 break; 81 case 'I': 82 if (__db_getlong(NULL, prog, 83 optarg, 1, LONG_MAX, &__dbsrv_idleto)) 84 return (EXIT_FAILURE); 85 break; 86 case 'L': 87 logfile = optarg; 88 break; 89 case 'P': 90 passwd = strdup(optarg); 91 memset(optarg, 0, strlen(optarg)); 92 if (passwd == NULL) { 93 fprintf(stderr, "%s: strdup: %s\n", 94 prog, strerror(errno)); 95 return (EXIT_FAILURE); 96 } 97 if ((ret = add_passwd(passwd)) != 0) { 98 fprintf(stderr, "%s: strdup: %s\n", 99 prog, strerror(ret)); 100 return (EXIT_FAILURE); 101 } 102 break; 103 case 't': 104 if (__db_getlong(NULL, prog, 105 optarg, 1, LONG_MAX, &__dbsrv_defto)) 106 return (EXIT_FAILURE); 107 break; 108 case 'T': 109 if (__db_getlong(NULL, prog, 110 optarg, 1, LONG_MAX, &__dbsrv_maxto)) 111 return (EXIT_FAILURE); 112 break; 113 case 'V': 114 printf("%s\n", db_version(NULL, NULL, NULL)); 115 return (EXIT_SUCCESS); 116 case 'v': 117 __dbsrv_verbose = 1; 118 break; 119 default: 120 usage(); 121 } 122 /* 123 * Check default timeout against maximum timeout 124 */ 125 if (__dbsrv_defto > __dbsrv_maxto) 126 __dbsrv_defto = __dbsrv_maxto; 127 128 /* 129 * Check default timeout against idle timeout 130 * It would be bad to timeout environments sooner than txns. 131 */ 132 if (__dbsrv_defto > __dbsrv_idleto) 133 fprintf(stderr, 134 "%s: WARNING: Idle timeout %ld is less than resource timeout %ld\n", 135 prog, __dbsrv_idleto, __dbsrv_defto); 136 137 LIST_INIT(&__dbsrv_head); 138 139 /* 140 * If a client crashes during an RPC, our reply to it 141 * generates a SIGPIPE. Ignore SIGPIPE so we don't exit unnecessarily. 142 */ 143#ifdef SIGPIPE 144 signal(SIGPIPE, SIG_IGN); 145#endif 146 147 if (logfile != NULL && __db_util_logset("berkeley_db_svc", logfile)) 148 return (EXIT_FAILURE); 149 150 /* 151 * Now that we are ready to start, run recovery on all the 152 * environments specified. 153 */ 154 if (env_recover(prog) != 0) 155 return (EXIT_FAILURE); 156 157 /* 158 * We've done our setup, now call the generated server loop 159 */ 160 if (__dbsrv_verbose) 161 printf("%s: Ready to receive requests\n", prog); 162 __dbsrv_main(); 163 164 abort(); 165 166 /* NOTREACHED */ 167 return (0); 168} 169 170static void 171usage() 172{ 173 fprintf(stderr, "usage: %s %s\n\t%s\n", prog, 174 "[-Vv] [-h home] [-P passwd]", 175 "[-I idletimeout] [-L logfile] [-t def_timeout] [-T maxtimeout]"); 176 exit(EXIT_FAILURE); 177} 178 179static void 180version_check() 181{ 182 int v_major, v_minor, v_patch; 183 184 /* Make sure we're loaded with the right version of the DB library. */ 185 (void)db_version(&v_major, &v_minor, &v_patch); 186 if (v_major != DB_VERSION_MAJOR || 187 v_minor != DB_VERSION_MINOR || v_patch != DB_VERSION_PATCH) { 188 fprintf(stderr, 189 "%s: version %d.%d.%d doesn't match library version %d.%d.%d\n", 190 prog, DB_VERSION_MAJOR, DB_VERSION_MINOR, 191 DB_VERSION_PATCH, v_major, v_minor, v_patch); 192 exit(EXIT_FAILURE); 193 } 194} 195 196/* 197 * PUBLIC: void __dbsrv_settimeout __P((ct_entry *, u_int32_t)); 198 */ 199void 200__dbsrv_settimeout(ctp, to) 201 ct_entry *ctp; 202 u_int32_t to; 203{ 204 if (to > (u_int32_t)__dbsrv_maxto) 205 ctp->ct_timeout = __dbsrv_maxto; 206 else if (to <= 0) 207 ctp->ct_timeout = __dbsrv_defto; 208 else 209 ctp->ct_timeout = to; 210} 211 212/* 213 * PUBLIC: void __dbsrv_timeout __P((int)); 214 */ 215void 216__dbsrv_timeout(force) 217 int force; 218{ 219 static long to_hint = -1; 220 time_t t; 221 long to; 222 ct_entry *ctp, *nextctp; 223 224 if ((t = time(NULL)) == -1) 225 return; 226 227 /* 228 * Check hint. If hint is further in the future 229 * than now, no work to do. 230 */ 231 if (!force && to_hint > 0 && t < to_hint) 232 return; 233 to_hint = -1; 234 /* 235 * Timeout transactions or cursors holding DB resources. 236 * Do this before timing out envs to properly release resources. 237 * 238 * !!! 239 * We can just loop through this list looking for cursors and txns. 240 * We do not need to verify txn and cursor relationships at this 241 * point because we maintain the list in LIFO order *and* we 242 * maintain activity in the ultimate txn parent of any cursor 243 * so either everything in a txn is timing out, or nothing. 244 * So, since we are LIFO, we will correctly close/abort all the 245 * appropriate handles, in the correct order. 246 */ 247 for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; ctp = nextctp) { 248 nextctp = LIST_NEXT(ctp, entries); 249 switch (ctp->ct_type) { 250 case CT_TXN: 251 to = *(ctp->ct_activep) + ctp->ct_timeout; 252 /* TIMEOUT */ 253 if (to < t) { 254 if (__dbsrv_verbose) 255 printf("Timing out txn id %ld\n", 256 ctp->ct_id); 257 (void)((DB_TXN *)ctp->ct_anyp)-> 258 abort((DB_TXN *)ctp->ct_anyp); 259 __dbdel_ctp(ctp); 260 /* 261 * If we timed out an txn, we may have closed 262 * all sorts of ctp's. 263 * So start over with a guaranteed good ctp. 264 */ 265 nextctp = LIST_FIRST(&__dbsrv_head); 266 } else if ((to_hint > 0 && to_hint > to) || 267 to_hint == -1) 268 to_hint = to; 269 break; 270 case CT_CURSOR: 271 case (CT_JOINCUR | CT_CURSOR): 272 to = *(ctp->ct_activep) + ctp->ct_timeout; 273 /* TIMEOUT */ 274 if (to < t) { 275 if (__dbsrv_verbose) 276 printf("Timing out cursor %ld\n", 277 ctp->ct_id); 278 (void)__dbc_close_int(ctp); 279 /* 280 * Start over with a guaranteed good ctp. 281 */ 282 nextctp = LIST_FIRST(&__dbsrv_head); 283 } else if ((to_hint > 0 && to_hint > to) || 284 to_hint == -1) 285 to_hint = to; 286 break; 287 default: 288 break; 289 } 290 } 291 /* 292 * Timeout idle handles. 293 * If we are forcing a timeout, we'll close all env handles. 294 */ 295 for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; ctp = nextctp) { 296 nextctp = LIST_NEXT(ctp, entries); 297 if (ctp->ct_type != CT_ENV) 298 continue; 299 to = *(ctp->ct_activep) + ctp->ct_idle; 300 /* TIMEOUT */ 301 if (to < t || force) { 302 if (__dbsrv_verbose) 303 printf("Timing out env id %ld\n", ctp->ct_id); 304 (void)__env_close_int(ctp->ct_id, 0, 1); 305 /* 306 * If we timed out an env, we may have closed 307 * all sorts of ctp's (maybe even all of them. 308 * So start over with a guaranteed good ctp. 309 */ 310 nextctp = LIST_FIRST(&__dbsrv_head); 311 } 312 } 313} 314 315/* 316 * RECURSIVE FUNCTION. We need to clear/free any number of levels of nested 317 * layers. 318 */ 319static void 320__dbclear_child(parent) 321 ct_entry *parent; 322{ 323 ct_entry *ctp, *nextctp; 324 325 for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; 326 ctp = nextctp) { 327 nextctp = LIST_NEXT(ctp, entries); 328 if (ctp->ct_type == 0) 329 continue; 330 if (ctp->ct_parent == parent) { 331 __dbclear_child(ctp); 332 /* 333 * Need to do this here because le_next may 334 * have changed with the recursive call and we 335 * don't want to point to a removed entry. 336 */ 337 nextctp = LIST_NEXT(ctp, entries); 338 __dbclear_ctp(ctp); 339 } 340 } 341} 342 343/* 344 * PUBLIC: void __dbclear_ctp __P((ct_entry *)); 345 */ 346void 347__dbclear_ctp(ctp) 348 ct_entry *ctp; 349{ 350 LIST_REMOVE(ctp, entries); 351 __os_free(NULL, ctp); 352} 353 354/* 355 * PUBLIC: void __dbdel_ctp __P((ct_entry *)); 356 */ 357void 358__dbdel_ctp(parent) 359 ct_entry *parent; 360{ 361 __dbclear_child(parent); 362 __dbclear_ctp(parent); 363} 364 365/* 366 * PUBLIC: ct_entry *new_ct_ent __P((int *)); 367 */ 368ct_entry * 369new_ct_ent(errp) 370 int *errp; 371{ 372 time_t t; 373 ct_entry *ctp, *octp; 374 int ret; 375 376 if ((ret = __os_malloc(NULL, sizeof(ct_entry), &ctp)) != 0) { 377 *errp = ret; 378 return (NULL); 379 } 380 memset(ctp, 0, sizeof(ct_entry)); 381 /* 382 * Get the time as ID. We may service more than one request per 383 * second however. If we are, then increment id value until we 384 * find an unused one. We insert entries in LRU fashion at the 385 * head of the list. So, if the first entry doesn't match, then 386 * we know for certain that we can use our entry. 387 */ 388 if ((t = time(NULL)) == -1) { 389 *errp = __os_get_errno(); 390 __os_free(NULL, ctp); 391 return (NULL); 392 } 393 octp = LIST_FIRST(&__dbsrv_head); 394 if (octp != NULL && octp->ct_id >= t) 395 t = octp->ct_id + 1; 396 ctp->ct_id = (long)t; 397 ctp->ct_idle = __dbsrv_idleto; 398 ctp->ct_activep = &ctp->ct_active; 399 ctp->ct_origp = NULL; 400 ctp->ct_refcount = 1; 401 402 LIST_INSERT_HEAD(&__dbsrv_head, ctp, entries); 403 return (ctp); 404} 405 406/* 407 * PUBLIC: ct_entry *get_tableent __P((long)); 408 */ 409ct_entry * 410get_tableent(id) 411 long id; 412{ 413 ct_entry *ctp; 414 415 for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; 416 ctp = LIST_NEXT(ctp, entries)) 417 if (ctp->ct_id == id) 418 return (ctp); 419 return (NULL); 420} 421 422/* 423 * PUBLIC: ct_entry *__dbsrv_sharedb __P((ct_entry *, const char *, 424 * PUBLIC: const char *, DBTYPE, u_int32_t)); 425 */ 426ct_entry * 427__dbsrv_sharedb(db_ctp, name, subdb, type, flags) 428 ct_entry *db_ctp; 429 const char *name, *subdb; 430 DBTYPE type; 431 u_int32_t flags; 432{ 433 ct_entry *ctp; 434 435 /* 436 * Check if we can share a db handle. Criteria for sharing are: 437 * If any of the non-sharable flags are set, we cannot share. 438 * Must be a db ctp, obviously. 439 * Must share the same env parent. 440 * Must be the same type, or current one DB_UNKNOWN. 441 * Must be same byteorder, or current one must not care. 442 * All flags must match. 443 * Must be same name, but don't share in-memory databases. 444 * Must be same subdb name. 445 */ 446 if (flags & DB_SERVER_DBNOSHARE) 447 return (NULL); 448 for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; 449 ctp = LIST_NEXT(ctp, entries)) { 450 /* 451 * Skip ourselves. 452 */ 453 if (ctp == db_ctp) 454 continue; 455 if (ctp->ct_type != CT_DB) 456 continue; 457 if (ctp->ct_envparent != db_ctp->ct_envparent) 458 continue; 459 if (type != DB_UNKNOWN && ctp->ct_dbdp.type != type) 460 continue; 461 if (ctp->ct_dbdp.dbflags != LF_ISSET(DB_SERVER_DBFLAGS)) 462 continue; 463 if (db_ctp->ct_dbdp.setflags != 0 && 464 ctp->ct_dbdp.setflags != db_ctp->ct_dbdp.setflags) 465 continue; 466 if (name == NULL || ctp->ct_dbdp.db == NULL || 467 strcmp(name, ctp->ct_dbdp.db) != 0) 468 continue; 469 if (subdb != ctp->ct_dbdp.subdb && 470 (subdb == NULL || ctp->ct_dbdp.subdb == NULL || 471 strcmp(subdb, ctp->ct_dbdp.subdb) != 0)) 472 continue; 473 /* 474 * If we get here, then we match. 475 */ 476 ctp->ct_refcount++; 477 return (ctp); 478 } 479 480 return (NULL); 481} 482 483/* 484 * PUBLIC: ct_entry *__dbsrv_shareenv 485 * PUBLIC: __P((ct_entry *, home_entry *, u_int32_t)); 486 */ 487ct_entry * 488__dbsrv_shareenv(env_ctp, home, flags) 489 ct_entry *env_ctp; 490 home_entry *home; 491 u_int32_t flags; 492{ 493 ct_entry *ctp; 494 495 /* 496 * Check if we can share an env. Criteria for sharing are: 497 * Must be an env ctp, obviously. 498 * Must share the same home env. 499 * All flags must match. 500 */ 501 for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; 502 ctp = LIST_NEXT(ctp, entries)) { 503 /* 504 * Skip ourselves. 505 */ 506 if (ctp == env_ctp) 507 continue; 508 if (ctp->ct_type != CT_ENV) 509 continue; 510 if (ctp->ct_envdp.home != home) 511 continue; 512 if (ctp->ct_envdp.envflags != flags) 513 continue; 514 if (ctp->ct_envdp.onflags != env_ctp->ct_envdp.onflags) 515 continue; 516 if (ctp->ct_envdp.offflags != env_ctp->ct_envdp.offflags) 517 continue; 518 /* 519 * If we get here, then we match. The only thing left to 520 * check is the timeout. Since the server timeout set by 521 * the client is a hint, for sharing we'll give them the 522 * benefit of the doubt and grant them the longer timeout. 523 */ 524 if (ctp->ct_timeout < env_ctp->ct_timeout) 525 ctp->ct_timeout = env_ctp->ct_timeout; 526 ctp->ct_refcount++; 527 return (ctp); 528 } 529 530 return (NULL); 531} 532 533/* 534 * PUBLIC: void __dbsrv_active __P((ct_entry *)); 535 */ 536void 537__dbsrv_active(ctp) 538 ct_entry *ctp; 539{ 540 time_t t; 541 ct_entry *envctp; 542 543 if (ctp == NULL) 544 return; 545 if ((t = time(NULL)) == -1) 546 return; 547 *(ctp->ct_activep) = t; 548 if ((envctp = ctp->ct_envparent) == NULL) 549 return; 550 *(envctp->ct_activep) = t; 551 return; 552} 553 554/* 555 * PUBLIC: int __db_close_int __P((long, u_int32_t)); 556 */ 557int 558__db_close_int(id, flags) 559 long id; 560 u_int32_t flags; 561{ 562 DB *dbp; 563 int ret; 564 ct_entry *ctp; 565 566 ctp = get_tableent(id); 567 if (ctp == NULL) 568 return (DB_NOSERVER_ID); 569 570 if (__dbsrv_verbose && ctp->ct_refcount != 1) 571 printf("Deref'ing dbp id %ld, refcount %d\n", 572 id, ctp->ct_refcount); 573 574 if (--ctp->ct_refcount != 0) 575 return (0); 576 577 if (__dbsrv_verbose) 578 printf("Closing dbp id %ld\n", id); 579 580 dbp = ctp->ct_dbp; 581 ret = dbp->close(dbp, flags); 582 if (ctp->ct_dbdp.db != NULL) 583 __os_free(NULL, ctp->ct_dbdp.db); 584 if (ctp->ct_dbdp.subdb != NULL) 585 __os_free(NULL, ctp->ct_dbdp.subdb); 586 __dbdel_ctp(ctp); 587 return (ret); 588} 589 590/* 591 * PUBLIC: int __dbc_close_int __P((ct_entry *)); 592 */ 593int 594__dbc_close_int(dbc_ctp) 595 ct_entry *dbc_ctp; 596{ 597 DBC *dbc; 598 int ret; 599 ct_entry *ctp; 600 601 dbc = (DBC *)dbc_ctp->ct_anyp; 602 603 ret = dbc->close(dbc); 604 /* 605 * If this cursor is a join cursor then we need to fix up the 606 * cursors that it was joined from so that they are independent again. 607 */ 608 if (dbc_ctp->ct_type & CT_JOINCUR) 609 for (ctp = LIST_FIRST(&__dbsrv_head); ctp != NULL; 610 ctp = LIST_NEXT(ctp, entries)) { 611 /* 612 * Test if it is a join cursor, and if it is part 613 * of this one. 614 */ 615 if ((ctp->ct_type & CT_JOIN) && 616 ctp->ct_activep == &dbc_ctp->ct_active) { 617 ctp->ct_type &= ~CT_JOIN; 618 ctp->ct_activep = ctp->ct_origp; 619 __dbsrv_active(ctp); 620 } 621 } 622 __dbclear_ctp(dbc_ctp); 623 return (ret); 624 625} 626 627/* 628 * PUBLIC: int __env_close_int __P((long, u_int32_t, int)); 629 */ 630int 631__env_close_int(id, flags, force) 632 long id; 633 u_int32_t flags; 634 int force; 635{ 636 DB_ENV *dbenv; 637 int ret; 638 ct_entry *ctp, *dbctp, *nextctp; 639 640 ctp = get_tableent(id); 641 if (ctp == NULL) 642 return (DB_NOSERVER_ID); 643 644 if (__dbsrv_verbose && ctp->ct_refcount != 1) 645 printf("Deref'ing env id %ld, refcount %d\n", 646 id, ctp->ct_refcount); 647 /* 648 * If we are timing out, we need to force the close, no matter 649 * what the refcount. 650 */ 651 if (--ctp->ct_refcount != 0 && !force) 652 return (0); 653 dbenv = ctp->ct_envp; 654 if (__dbsrv_verbose) 655 printf("Closing env id %ld\n", id); 656 657 /* 658 * If we're timing out an env, we want to close all of its 659 * database handles as well. All of the txns and cursors 660 * must have been timed out prior to timing out the env. 661 */ 662 if (force) 663 for (dbctp = LIST_FIRST(&__dbsrv_head); 664 dbctp != NULL; dbctp = nextctp) { 665 nextctp = LIST_NEXT(dbctp, entries); 666 if (dbctp->ct_type != CT_DB) 667 continue; 668 if (dbctp->ct_envparent != ctp) 669 continue; 670 /* 671 * We found a DB handle that is part of this 672 * environment. Close it. 673 */ 674 __db_close_int(dbctp->ct_id, 0); 675 /* 676 * If we timed out a dbp, we may have removed 677 * multiple ctp entries. Start over with a 678 * guaranteed good ctp. 679 */ 680 nextctp = LIST_FIRST(&__dbsrv_head); 681 } 682 ret = dbenv->close(dbenv, flags); 683 __dbdel_ctp(ctp); 684 return (ret); 685} 686 687static int 688add_home(home) 689 char *home; 690{ 691 home_entry *hp, *homep; 692 int ret; 693 694 if ((ret = __os_malloc(NULL, sizeof(home_entry), &hp)) != 0) 695 return (ret); 696 if ((ret = __os_malloc(NULL, strlen(home)+1, &hp->home)) != 0) 697 return (ret); 698 memcpy(hp->home, home, strlen(home)+1); 699 hp->dir = home; 700 hp->passwd = NULL; 701 /* 702 * This loop is to remove any trailing path separators, 703 * to assure hp->name points to the last component. 704 */ 705 hp->name = __db_rpath(home); 706 if (hp->name != NULL) { 707 *(hp->name) = '\0'; 708 hp->name++; 709 } else 710 hp->name = home; 711 while (*(hp->name) == '\0') { 712 hp->name = __db_rpath(home); 713 *(hp->name) = '\0'; 714 hp->name++; 715 } 716 /* 717 * Now we have successfully added it. Make sure there are no 718 * identical names. 719 */ 720 for (homep = LIST_FIRST(&__dbsrv_home); homep != NULL; 721 homep = LIST_NEXT(homep, entries)) 722 if (strcmp(homep->name, hp->name) == 0) { 723 printf("Already added home name %s, at directory %s\n", 724 hp->name, homep->dir); 725 __os_free(NULL, hp->home); 726 __os_free(NULL, hp); 727 return (-1); 728 } 729 LIST_INSERT_HEAD(&__dbsrv_home, hp, entries); 730 if (__dbsrv_verbose) 731 printf("Added home %s in dir %s\n", hp->name, hp->dir); 732 return (0); 733} 734 735static int 736add_passwd(passwd) 737 char *passwd; 738{ 739 home_entry *hp; 740 741 /* 742 * We add the passwd to the last given home dir. If there 743 * isn't a home dir, or the most recent one already has a 744 * passwd, then there is a user error. 745 */ 746 hp = LIST_FIRST(&__dbsrv_home); 747 if (hp == NULL || hp->passwd != NULL) 748 return (EINVAL); 749 /* 750 * We've already strdup'ed the passwd above, so we don't need 751 * to malloc new space, just point to it. 752 */ 753 hp->passwd = passwd; 754 return (0); 755} 756 757/* 758 * PUBLIC: home_entry *get_fullhome __P((char *)); 759 */ 760home_entry * 761get_fullhome(name) 762 char *name; 763{ 764 home_entry *hp; 765 766 if (name == NULL) 767 return (NULL); 768 for (hp = LIST_FIRST(&__dbsrv_home); hp != NULL; 769 hp = LIST_NEXT(hp, entries)) 770 if (strcmp(name, hp->name) == 0) 771 return (hp); 772 return (NULL); 773} 774 775static int 776env_recover(progname) 777 char *progname; 778{ 779 DB_ENV *dbenv; 780 home_entry *hp; 781 u_int32_t flags; 782 int exitval, ret; 783 784 for (hp = LIST_FIRST(&__dbsrv_home); hp != NULL; 785 hp = LIST_NEXT(hp, entries)) { 786 exitval = 0; 787 if ((ret = db_env_create(&dbenv, 0)) != 0) { 788 fprintf(stderr, "%s: db_env_create: %s\n", 789 progname, db_strerror(ret)); 790 exit(EXIT_FAILURE); 791 } 792 if (__dbsrv_verbose == 1) 793 (void)dbenv->set_verbose(dbenv, DB_VERB_RECOVERY, 1); 794 dbenv->set_errfile(dbenv, stderr); 795 dbenv->set_errpfx(dbenv, progname); 796 if (hp->passwd != NULL) 797 (void)dbenv->set_encrypt(dbenv, hp->passwd, 798 DB_ENCRYPT_AES); 799 800 /* 801 * Initialize the env with DB_RECOVER. That is all we 802 * have to do to run recovery. 803 */ 804 if (__dbsrv_verbose) 805 printf("Running recovery on %s\n", hp->home); 806 flags = DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | 807 DB_INIT_TXN | DB_USE_ENVIRON | DB_RECOVER; 808 if ((ret = dbenv->open(dbenv, hp->home, flags, 0)) != 0) { 809 dbenv->err(dbenv, ret, "DB_ENV->open"); 810 goto error; 811 } 812 813 if (0) { 814error: exitval = 1; 815 } 816 if ((ret = dbenv->close(dbenv, 0)) != 0) { 817 exitval = 1; 818 fprintf(stderr, "%s: dbenv->close: %s\n", 819 progname, db_strerror(ret)); 820 } 821 if (exitval) 822 return (exitval); 823 } 824 return (0); 825} 826