static ex_t dbe_env_purgelogs(DB_ENV *dbe) { char **i, **list; ex_t e; /* figure redundant log files and nuke them */ e = BF_LOG_ARCHIVE(dbe, &list, DB_ARCH_ABS); if (e != 0) { print_error(__FILE__, __LINE__, "DB_ENV->log_archive failed: %s", db_strerror(e)); exit(EX_ERROR); } if (list != NULL) { if (DEBUG_DATABASE(0)) fprintf(dbgout, "removing inactive logfiles\n"); for (i = list; *i != NULL; i++) { if (DEBUG_DATABASE(1)) fprintf(dbgout, " removing logfile %s\n", *i); if (unlink(*i)) { if (errno != ENOENT) print_error(__FILE__, __LINE__, "cannot unlink \"%s\": %s", *i, strerror(errno)); /* proceed anyways */ } } xfree(list); } return EX_OK; }
/* dummy infrastructure, to be expanded by environment * or transactional initialization/shutdown */ static dbe_t *dbe_xinit(dbe_t *env, bfpath *bfp, u_int32_t flags) { int ret; env->magic = MAGIC_DBE; /* poor man's type checking */ ret = bf_dbenv_create(&env->dbe); if (db_cachesize != 0 && (ret = env->dbe->set_cachesize(env->dbe, db_cachesize/1024, (db_cachesize % 1024) * 1024*1024, 1)) != 0) { print_error(__FILE__, __LINE__, "DB_ENV->set_cachesize(%u), err: %d, %s", db_cachesize, ret, db_strerror(ret)); exit(EX_ERROR); } if (DEBUG_DATABASE(1)) fprintf(dbgout, "DB_ENV->set_cachesize(%u)\n", db_cachesize); dbe_config(env); flags |= DB_CREATE | dbenv_defflags; ret = env->dbe->open(env->dbe, bfp->dirname, flags, DS_MODE); if (ret != 0) { env->dbe->close(env->dbe, 0); print_error(__FILE__, __LINE__, "DB_ENV->open, err: %d, %s", ret, db_strerror(ret)); switch (ret) { case DB_RUNRECOVERY: diag_dbeopen(flags, bfp); break; case EINVAL: fprintf(stderr, "\n" "If you have just got a message that only private environments are supported,\n" "your Berkeley DB %d.%d was not configured properly.\n" "Bogofilter requires shared environments to support Berkeley DB transactions.\n", DB_VERSION_MAJOR, DB_VERSION_MINOR); fprintf(stderr, "Reconfigure and recompile Berkeley DB with the right mutex interface,\n" "see the docs/ref/build_unix/conf.html file that comes with your db source code.\n" "This can happen when the DB library was compiled with POSIX threads\n" "but your system does not support NPTL.\n"); break; } exit(EX_ERROR); } if (DEBUG_DATABASE(1)) fprintf(dbgout, "DB_ENV->open(home=%s)\n", bfp->dirname); return env; }
static int dbx_abort(void *vhandle) { int ret; dbh_t *dbh = vhandle; DB_TXN *t; assert(dbh); assert(dbh->magic == MAGIC_DBH); t = dbh->txn; assert(t); ret = BF_TXN_ABORT(t); if (ret) print_error(__FILE__, __LINE__, "DB_TXN->abort(%lx) error: %s", (unsigned long)BF_TXN_ID(t), db_strerror(ret)); else if (DEBUG_DATABASE(2)) fprintf(dbgout, "DB_TXN->abort(%lx)\n", (unsigned long)BF_TXN_ID(t)); dbh->txn = NULL; switch (ret) { case 0: return DST_OK; case DB_LOCK_DEADLOCK: return DST_TEMPFAIL; default: return DST_FAILURE; } }
static int dbx_begin(void *vhandle) { DB_TXN *t; int ret; dbh_t *dbh = vhandle; dbe_t *env = dbh->dbenv; assert(dbh); assert(dbh->magic == MAGIC_DBH); assert(dbh->txn == 0); assert(env); assert(env->dbe); ret = BF_TXN_BEGIN(env->dbe, NULL, &t, 0); if (ret) { print_error(__FILE__, __LINE__, "DB_ENV->txn_begin(%p), err: %d, %s", (void *)env->dbe, ret, db_strerror(ret)); return ret; } dbh->txn = t; if (DEBUG_DATABASE(2)) fprintf(dbgout, "DB_ENV->dbx_begin(%p), tid: %lx\n", (void *)env->dbe, (unsigned long)BF_TXN_ID(t)); return 0; }
/* close the environment, but do not release locks */ static void dbx_cleanup_lite(dbe_t *env) { if (env) { if (env->dbe) { int ret; /* checkpoint if more than 64 kB of logs have been written * or 120 min have passed since the previous checkpoint */ /* kB min flags */ ret = BF_TXN_CHECKPOINT(env->dbe, 64, 120, 0); ret = dbx_sync(env->dbe, ret); if (ret) print_error(__FILE__, __LINE__, "DBE->dbx_checkpoint err: %d, %s", ret, db_strerror(ret)); if (db_log_autoremove) dbe_env_purgelogs(env->dbe); ret = env->dbe->close(env->dbe, 0); if (DEBUG_DATABASE(1) || ret) fprintf(dbgout, "DB_ENV->close(%p): %s\n", (void *)env->dbe, db_strerror(ret)); clear_lock(); if (lockfd >= 0) close(lockfd); /* release locks */ } xfree(env->directory); xfree(env); } }
ex_t dbx_recover(bfpath *bfp, bool catastrophic, bool force) { dbe_t *env = xcalloc(1, sizeof(dbe_t)); /* set exclusive/write lock for recovery */ while ((force || needs_recovery()) && (db_try_glock(bfp, F_WRLCK, F_SETLKW) <= 0)) rand_sleep(10000,1000000); /* ok, when we have the lock, a concurrent process may have * proceeded with recovery */ if (!(force || needs_recovery())) return EX_OK; if (DEBUG_DATABASE(0)) fprintf(dbgout, "running %s data base recovery\n", catastrophic ? "catastrophic" : "regular"); env = dbe_xinit(env, bfp, catastrophic ? DB_RECOVER_FATAL : DB_RECOVER); if (env == NULL) { exit(EX_ERROR); } clear_lockfile(); dbx_cleanup_lite(env); return EX_OK; }
void dbx_log_flush(DB_ENV *dbe) { int ret; ret = BF_LOG_FLUSH(dbe, NULL); if (DEBUG_DATABASE(1)) fprintf(dbgout, "DB_ENV->log_flush(%p): %s\n", (void *)dbe, db_strerror(ret)); }
/** Create environment or exit with EX_ERROR */ static int bf_dbenv_create(DB_ENV **env) { int ret = db_env_create(env, 0); if (ret != 0) { print_error(__FILE__, __LINE__, "db_env_create, err: %d, %s", ret, db_strerror(ret)); exit(EX_ERROR); } if (DEBUG_DATABASE(1)) fprintf(dbgout, "db_env_create: %p\n", (void *)env); (*env)->set_errfile(*env, stderr); return ret; }
/** run recovery, open environment and keep the exclusive lock */ static DB_ENV *dbe_recover_open(bfpath *bfp, u_int32_t flags) { const u_int32_t local_flags = flags | DB_CREATE; DB_ENV *dbe; int e; if (DEBUG_DATABASE(0)) fprintf(dbgout, "trying to lock database directory\n"); db_try_glock(bfp, F_WRLCK, F_SETLKW); /* wait for exclusive lock */ /* run recovery */ bf_dbenv_create(&dbe); if (DEBUG_DATABASE(0)) fprintf(dbgout, "running regular data base recovery%s\n", flags & DB_PRIVATE ? " and removing environment" : ""); /* quirk: DB_RECOVER requires DB_CREATE and cannot work with DB_JOINENV */ /* * Hint from Keith Bostic, SleepyCat support, 2004-11-29, * we can use the DB_PRIVATE flag, that rebuilds the database * environment in heap memory, so we don't need to remove it. */ e = dbe->open(dbe, bfp->dirname, dbenv_defflags | local_flags | DB_RECOVER, DS_MODE); if (e != 0) { print_error(__FILE__, __LINE__, "Cannot recover environment \"%s\": %s", bfp->dirname, db_strerror(e)); if (e == DB_RUNRECOVERY) diag_dbeopen(flags, bfp); exit(EX_ERROR); } return dbe; }
/** checkpoint the open environment \a dbe once and unconditionally */ static ex_t dbe_env_checkpoint(DB_ENV *dbe) { int e; if (DEBUG_DATABASE(0)) fprintf(dbgout, "checkpoint database\n"); /* checkpoint the transactional system */ e = BF_TXN_CHECKPOINT(dbe, 0, 0, 0); e = dbx_sync(dbe, e); if (e != 0) { print_error(__FILE__, __LINE__, "DB_ENV->txn_checkpoint failed: %s", db_strerror(e)); exit(EX_ERROR); } return EX_OK; }
static void dbe_config(void *vhandle) { dbe_t *env = vhandle; int ret = 0; u_int32_t logsize = 1048576; /* 1 MByte (default in BDB 10 MByte) */ /* configure log file size */ ret = env->dbe->set_lg_max(env->dbe, logsize); if (ret) { print_error(__FILE__, __LINE__, "DB_ENV->set_lg_max(%lu) err: %d, %s", (unsigned long)logsize, ret, db_strerror(ret)); exit(EX_ERROR); } if (DEBUG_DATABASE(1)) fprintf(dbgout, "DB_ENV->set_lg_max(%lu)\n", (unsigned long)logsize); }
/* Initialize database. Returns: pointer to database handle on success, NULL otherwise. */ void *db_open(void * dummy, bfpath *bfp, dbmode_t open_mode) { dbh_t *handle; bool res; int open_flags; TCBDB *dbp; UNUSED(dummy); if (open_mode & DS_WRITE) open_flags = BDBOWRITER; else open_flags = BDBOREADER; handle = dbh_init(bfp); if (handle == NULL) return NULL; dbp = handle->dbp = tcbdbnew(); res = tcbdbopen(dbp, handle->name, open_flags); if (!res && (open_mode & DS_WRITE)) { res = tcbdbopen(dbp, handle->name, open_flags | BDBOCREAT); handle->created |= res; } if (!res) goto open_err; if (DEBUG_DATABASE(1)) fprintf(dbgout, "(tc) tcbdbopen( %s, %d )\n", handle->name, open_mode); return handle; open_err: print_error(__FILE__, __LINE__, "(tc) tcbdbopen(%s, %d), err: %d, %s", handle->name, open_flags, tcbdbecode(dbp), tcbdberrmsg(tcbdbecode(dbp))); dbh_free(handle); return NULL; }
/* Initialize database. Returns: pointer to database handle on success, NULL otherwise. */ void *db_open(void * dummy, bfpath *bfp, dbmode_t open_mode) { dbh_t *handle; int open_flags; VILLA *dbp; UNUSED(dummy); if (open_mode & DS_WRITE) open_flags = VL_OWRITER; else open_flags = VL_OREADER; handle = dbh_init(bfp); if (handle == NULL) return NULL; dbp = handle->dbp = vlopen(handle->name, open_flags, cmpkey); if ((dbp == NULL) && (open_mode & DS_WRITE)) { dbp = handle->dbp = vlopen(handle->name, open_flags|VL_OCREAT, cmpkey); if (dbp != NULL) handle->created = true; } if (dbp == NULL) goto open_err; if (DEBUG_DATABASE(1)) fprintf(dbgout, "(qdbm) vlopen( %s, %d )\n", handle->name, open_mode); return handle; open_err: print_error(__FILE__, __LINE__, "(qdbm) vlopen(%s, %d), err: %d, %s", handle->name, open_flags, dpecode, dperrmsg(dpecode)); dbh_free(handle); return NULL; }
static ex_t dbx_common_close(DB_ENV *dbe, bfpath *bfp) { int e; if (db_log_autoremove) dbe_env_purgelogs(dbe); if (DEBUG_DATABASE(0)) fprintf(dbgout, "closing environment\n"); e = dbe->close(dbe, 0); if (e != 0) { print_error(__FILE__, __LINE__, "Error closing environment \"%s\": %s", bfp->dirname, db_strerror(e)); exit(EX_ERROR); } clear_lock(); db_try_glock(bfp, F_UNLCK, F_SETLKW); /* release lock */ return EX_OK; }
static int dbx_commit(void *vhandle) { int ret; dbh_t *dbh = vhandle; DB_TXN *t; u_int32_t id; assert(dbh); assert(dbh->magic == MAGIC_DBH); t = dbh->txn; assert(t); id = BF_TXN_ID(t); ret = BF_TXN_COMMIT(t, 0); if (ret) print_error(__FILE__, __LINE__, "DB_TXN->commit(%lx) error: %s", (unsigned long)id, db_strerror(ret)); else if (DEBUG_DATABASE(2)) fprintf(dbgout, "DB_TXN->commit(%lx, 0)\n", (unsigned long)id); dbh->txn = NULL; switch (ret) { case 0: /* push out buffer pages so that >=15% are clean - we * can ignore errors here, as the log has all the data */ BF_MEMP_TRICKLE(dbh->dbenv->dbe, 15, NULL); return DST_OK; case DB_LOCK_DEADLOCK: return DST_TEMPFAIL; default: return DST_FAILURE; } }
/* Close files and clean up. */ void db_close(void *vhandle) { dbh_t *handle = vhandle; VILLA *dbp; if (handle == NULL) return; if (DEBUG_DATABASE(1)) fprintf(dbgout, "(qdbm) vlclose(%s)\n", handle->name); dbp = handle->dbp; db_optimize(dbp, handle->name); if (!vlclose(dbp)) print_error(__FILE__, __LINE__, "(qdbm) vlclose for %s err: %d, %s", handle->name, dpecode, dperrmsg(dpecode)); handle->dbp = NULL; dbh_free(handle); }
/* Close files and clean up. */ void db_close(void *vhandle) { dbh_t *handle = vhandle; TCBDB *dbp; if (handle == NULL) return; if (DEBUG_DATABASE(1)) fprintf(dbgout, "(tc) tcbdbclose(%s)\n", handle->name); dbp = handle->dbp; db_optimize(dbp, handle->name); if (!tcbdbclose(dbp)) print_error(__FILE__, __LINE__, "(tc) tcbdbclose for %s err: %d, %s", handle->name, tcbdbecode(dbp), tcbdberrmsg(tcbdbecode(dbp))); tcbdbdel(dbp); handle->dbp = NULL; dbh_free(handle); }