From: Howard Chu Date: Wed, 13 Apr 2005 04:34:15 +0000 (+0000) Subject: ITS#3607 more enhancements X-Git-Tag: OPENLDAP_AC_BP~903 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=18e19a3a52ef507df6dc16ef6372a30c6dcfd983;p=openldap ITS#3607 more enhancements - recovers correctly, logs status - for Quick mode, where no transactional environment exists, refuses to start if the database is inconsistent - warns of poor performance if DB_CONFIG is not present - detects changes to DB_CONFIG and forces a recovery --- diff --git a/servers/slapd/back-bdb/init.c b/servers/slapd/back-bdb/init.c index e9ef03be63..b0ec7c65fa 100644 --- a/servers/slapd/back-bdb/init.c +++ b/servers/slapd/back-bdb/init.c @@ -20,7 +20,8 @@ #include #include #include - +#include +#include #include "back-bdb.h" #include #include @@ -89,15 +90,176 @@ bdb_checkpoint( void *ctx, void *arg ) return NULL; } +/* + * Unconditionally perform a database recovery. Only works on + * databases that were previously opened with transactions and + * logs enabled. + */ +static int +bdb_do_recovery( BackendDB *be ) +{ + struct bdb_info *bdb = (struct bdb_info *) be->be_private; + DB_ENV *re_dbenv; + u_int32_t flags; + int rc; + char path[MAXPATHLEN], *ptr; + + /* Create and init the recovery environment */ + rc = db_env_create( &re_dbenv, 0 ); + if( rc != 0 ) { + Debug( LDAP_DEBUG_ANY, + "bdb_do_recovery: db_env_create failed: %s (%d)\n", + db_strerror(rc), rc, 0 ); + return rc; + } + re_dbenv->set_errpfx( re_dbenv, be->be_suffix[0].bv_val ); + re_dbenv->set_errcall( re_dbenv, bdb_errcall ); + (void)re_dbenv->set_verbose(re_dbenv, DB_VERB_RECOVERY, 1); + (void)re_dbenv->set_verbose(re_dbenv, DB_VERB_CHKPOINT, 1); + + flags = DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | + DB_INIT_TXN | DB_USE_ENVIRON | DB_RECOVER; + + /* Open the environment, which will also perform the recovery */ +#ifdef HAVE_EBCDIC + strcpy( path, bdb->bi_dbenv_home ); + __atoe( path ); + rc = re_dbenv->open( re_dbenv, + path, + flags, + bdb->bi_dbenv_mode ); +#else + rc = re_dbenv->open( re_dbenv, + bdb->bi_dbenv_home, + flags, + bdb->bi_dbenv_mode ); +#endif + if( rc != 0 ) { + Debug( LDAP_DEBUG_ANY, + "bdb_do_recovery: dbenv_open failed: %s (%d)\n", + db_strerror(rc), rc, 0 ); + return rc; + } + (void) re_dbenv->close( re_dbenv, 0 ); + + /* By convention we reset the mtime for id2entry.bdb to the current time */ + ptr = lutil_strcopy( path, bdb->bi_dbenv_home); + *ptr++ = LDAP_DIRSEP[0]; + strcpy( ptr, bdbi_databases[0].file); + (void) utime( path, NULL); + + return 0; +} + +/* + * Database recovery logic: + * This function is called whenever the database appears to have been + * shut down uncleanly, as determined by the alock functions. + * Because of the -q function in slapadd, there is also the possibility + * that the shutdown happened when transactions weren't being used and + * the database is likely to be corrupt. The function checks for this + * condition by examining the environment to make sure it had previously + * been opened with transactions enabled. If this is the case, the + * database is recovered as usual. If transactions were not enabled, + * then this function will return a fail. + */ +static int +bdb_db_recover( BackendDB *be ) +{ + struct bdb_info *bdb = (struct bdb_info *) be->be_private; + DB_ENV *re_dbenv; + u_int32_t flags; + int rc; +#ifdef HAVE_EBCDIC + char path[MAXPATHLEN]; +#endif + + /* Create the recovery environment, then open it. + * We use the DB_JOIN in combination with a flags value of + * zero so we join an existing environment and can read the + * value of the flags that were used the last time the + * environment was opened. DB_CREATE is added because the + * open would fail if the only thing that had been done + * was an open with transactions and logs disabled. + */ + rc = db_env_create( &re_dbenv, 0 ); + if( rc != 0 ) { + Debug( LDAP_DEBUG_ANY, + "bdb_db_recover: db_env_create failed: %s (%d)\n", + db_strerror(rc), rc, 0 ); + return rc; + } + re_dbenv->set_errpfx( re_dbenv, be->be_suffix[0].bv_val ); + re_dbenv->set_errcall( re_dbenv, bdb_errcall ); + + Debug( LDAP_DEBUG_TRACE, + "bdb_db_recover: dbenv_open(%s)\n", + bdb->bi_dbenv_home, 0, 0); + +#ifdef HAVE_EBCDIC + strcpy( path, bdb->bi_dbenv_home ); + __atoe( path ); + rc = re_dbenv->open( re_dbenv, + path, + DB_JOINENV, + bdb->bi_dbenv_mode ); +#else + rc = re_dbenv->open( re_dbenv, + bdb->bi_dbenv_home, + DB_JOINENV, + bdb->bi_dbenv_mode ); +#endif + + if( rc == ENOENT ) { + goto re_exit; + } + else if( rc != 0 ) { + Debug( LDAP_DEBUG_ANY, + "bdb_db_recover: dbenv_open failed: %s (%d)\n", + db_strerror(rc), rc, 0 ); + return rc; + } + + /* + * Check the flags that had been used in the previous open. + * The environment needed to have had both + * DB_INIT_LOG and DB_INIT_TXN set for us to be willing to + * recover the database. Otherwise the an app failed while running + * without transactions and logs enabled and the dn2id and id2entry + * mapping is likely to be corrupt. + */ + rc = re_dbenv->get_open_flags( re_dbenv, &flags ); + if( rc != 0 ) { + Debug( LDAP_DEBUG_ANY, + "bdb_db_recover: get_open_flags failed: %s (%d)\n", + db_strerror(rc), rc, 0 ); + return rc; + } + + (void) re_dbenv->close( re_dbenv, 0 ); + + if( (flags & DB_INIT_LOG) && (flags & DB_INIT_TXN) ) { + return bdb_do_recovery( be ); + } + +re_exit: + Debug( LDAP_DEBUG_ANY, + "bdb_db_recover: Database cannot be recovered. "\ + "Restore from backup!\n", 0, 0, 0); + return -1; + +} + + static int bdb_db_open( BackendDB *be ) { int rc, i; struct bdb_info *bdb = (struct bdb_info *) be->be_private; + struct stat stat1, stat2; u_int32_t flags; -#ifdef HAVE_EBCDIC char path[MAXPATHLEN]; -#endif + char *ptr; Debug( LDAP_DEBUG_ARGS, "bdb_db_open: %s\n", @@ -110,36 +272,97 @@ bdb_db_open( BackendDB *be ) return -1; } #endif - /* we should check existance of dbenv_home and db_directory */ - rc = db_env_create( &bdb->bi_dbenv, 0 ); - if( rc != 0 ) { + /* Check existence of dbenv_home. Any error means trouble */ + rc = stat( bdb->bi_dbenv_home, &stat1 ); + if( rc !=0 ) { Debug( LDAP_DEBUG_ANY, - "bdb_db_open: db_env_create failed: %s (%d)\n", - db_strerror(rc), rc, 0 ); - return rc; + "bdb_db_open: Cannot access database directory %s (%d)\n", + bdb->bi_dbenv_home, errno, 0 ); + return -1; } - - flags = DB_INIT_MPOOL | DB_THREAD | DB_CREATE; - - if ( !( slapMode & SLAP_TOOL_QUICK )) - flags |= DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN; - rc = alock_open( &bdb->bi_alock_info, "slapd", bdb->bi_dbenv_home, - slapMode & SLAP_TOOL_READONLY ? ALOCK_LOCKED : ALOCK_UNIQUE ); + /* Perform database use arbitration/recovery logic */ + rc = alock_open( &bdb->bi_alock_info, + "slapd", + bdb->bi_dbenv_home, + slapMode & SLAP_TOOL_READONLY ? + ALOCK_LOCKED : ALOCK_UNIQUE ); + if( rc == ALOCK_RECOVER ) { Debug( LDAP_DEBUG_ANY, - "bdb_db_open: alock_open: recovery required\n", 0, 0, 0 ); - flags |= DB_RECOVER; + "bdb_db_open: unclean shutdown detected;" + " attempting recovery.\n", + 0, 0, 0 ); + if( bdb_db_recover( be ) != 0 ) { + Debug( LDAP_DEBUG_ANY, + "bdb_db_open: DB recovery failed.\n", + 0, 0, 0 ); + return -1; + } + if( alock_recover (&bdb->bi_alock_info) != 0 ) { + Debug( LDAP_DEBUG_ANY, + "bdb_db_open: alock_recover failed\n", + 0, 0, 0 ); + return -1; + } + } else if( rc == ALOCK_BUSY ) { Debug( LDAP_DEBUG_ANY, - "bdb_db_open: alock_open: database in use\n", 0, 0, 0 ); + "bdb_db_open: database already in use\n", + 0, 0, 0 ); return -1; } else if( rc != ALOCK_CLEAN ) { Debug( LDAP_DEBUG_ANY, - "bdb_db_open: alock_open: database unstable\n", 0, 0, 0 ); + "bdb_db_open: alock package is unstable\n", + 0, 0, 0 ); return -1; } + + /* + * The DB_CONFIG file may have changed. If so, recover the + * database so that new settings are put into effect. Also + * note the possible absence of DB_CONFIG in the log. + */ + if( stat( bdb->bi_db_config_path, &stat1 ) == 0 ) { + ptr = lutil_strcopy(path, bdb->bi_dbenv_home); + *ptr++ = LDAP_DIRSEP[0]; + strcpy( ptr, bdbi_databases[0].file); + if( stat( path, &stat2 ) == 0 ) { + if( stat2.st_mtime <= stat1.st_mtime ) { + Debug( LDAP_DEBUG_ANY, + "bdb_db_open: DB_CONFIG for suffix %s has changed.\n" + "Performing database recovery to activate new settings.\n", + be->be_suffix[0].bv_val, 0, 0 ); + if( bdb_do_recovery( be ) != 0) { + Debug( LDAP_DEBUG_ANY, + "bdb_db_open: db recovery failed.\n", + 0, 0, 0 ); + return -1; + } + } + + } + } + else { + Debug( LDAP_DEBUG_ANY, + "bdb_db_open: Warning - No DB_CONFIG file found " + "in directory %s: (%d)\n" + "Expect poor performance for suffix %s.\n", + bdb->bi_dbenv_home, errno, be->be_suffix[0].bv_val ); + } + + flags = DB_INIT_MPOOL | DB_THREAD | DB_CREATE; + if ( !( slapMode & SLAP_TOOL_QUICK )) + flags |= DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN; + + rc = db_env_create( &bdb->bi_dbenv, 0 ); + if( rc != 0 ) { + Debug( LDAP_DEBUG_ANY, + "bdb_db_open: db_env_create failed: %s (%d)\n", + db_strerror(rc), rc, 0 ); + return rc; + } /* If a key was set, use shared memory for the BDB environment */ if ( bdb->bi_shm_key ) { @@ -205,14 +428,6 @@ bdb_db_open( BackendDB *be ) db_strerror(rc), rc, 0 ); return rc; } - if( flags & DB_RECOVER ) { - rc = alock_recover (&bdb->bi_alock_info); - if( rc != 0 ) { - Debug( LDAP_DEBUG_ANY, - "bdb_db_open: unable to alock_recover\n", 0, 0, 0 ); - return -1; - } - } flags = DB_THREAD | bdb->bi_db_opflags; @@ -398,7 +613,7 @@ bdb_db_close( BackendDB *be ) ldap_pvt_thread_rdwr_wunlock ( &bdb->bi_idl_tree_rwlock ); } - if ( !( slapMode & SLAP_TOOL_QUICK )) { + if ( !( slapMode & SLAP_TOOL_QUICK ) && bdb->bi_dbenv ) { XLOCK_ID_FREE(bdb->bi_dbenv, bdb->bi_cache.c_locker); }