#endif
-#include <pthread.h>
+#include <libcitadel.h>
#include "citadel.h"
#include "server.h"
-#include "serv_extensions.h"
#include "citserver.h"
#include "database.h"
#include "msgbase.h"
#include "sysdep_decls.h"
+#include "threads.h"
#include "config.h"
+#include "control.h"
+
+#include "ctdl_module.h"
+
static DB *dbp[MAXCDB]; /* One DB handle for each Citadel database */
static DB_ENV *dbenv; /* The DB environment (global) */
-struct cdbtsd { /* Thread-specific DB stuff */
- DB_TXN *tid; /* Transaction handle */
- DBC *cursors[MAXCDB]; /* Cursors, for traversals... */
-};
#ifdef HAVE_ZLIB
#include <zlib.h>
#endif
-static pthread_key_t tsdkey;
-
-#define MYCURSORS (((struct cdbtsd*)pthread_getspecific(tsdkey))->cursors)
-#define MYTID (((struct cdbtsd*)pthread_getspecific(tsdkey))->tid)
/* Verbose logging callback */
void cdb_verbose_log(const DB_ENV *dbenv, const char *msg)
{
- lprintf(CTDL_DEBUG, "BDB: %s\n", msg);
+ CtdlLogPrintf(CTDL_DEBUG, "BDB: %s\n", msg);
+}
+
+
+/* Verbose logging callback */
+void cdb_verbose_err(const DB_ENV *dbenv, const char *errpfx, const char *msg)
+{
+ CtdlLogPrintf(CTDL_ALERT, "BDB: %s\n", msg);
}
ret = tid->abort(tid);
if (ret) {
- lprintf(CTDL_EMERG, "cdb_*: txn_abort: %s\n",
+ CtdlLogPrintf(CTDL_EMERG, "cdb_*: txn_abort: %s\n",
db_strerror(ret));
abort();
}
ret = tid->commit(tid, 0);
if (ret) {
- lprintf(CTDL_EMERG, "cdb_*: txn_commit: %s\n",
+ CtdlLogPrintf(CTDL_EMERG, "cdb_*: txn_commit: %s\n",
db_strerror(ret));
abort();
}
ret = dbenv->txn_begin(dbenv, NULL, tid, 0);
if (ret) {
- lprintf(CTDL_EMERG, "cdb_*: txn_begin: %s\n",
+ CtdlLogPrintf(CTDL_EMERG, "cdb_*: txn_begin: %s\n",
db_strerror(ret));
abort();
}
static void dbpanic(DB_ENV * env, int errval)
{
- lprintf(CTDL_EMERG, "cdb_*: Berkeley DB panic: %d\n", errval);
+ CtdlLogPrintf(CTDL_EMERG, "cdb_*: Berkeley DB panic: %d\n", errval);
}
static void cclose(DBC * cursor)
int ret;
if ((ret = cursor->c_close(cursor))) {
- lprintf(CTDL_EMERG, "cdb_*: c_close: %s\n",
+ CtdlLogPrintf(CTDL_EMERG, "cdb_*: c_close: %s\n",
db_strerror(ret));
abort();
}
for (i = 0; i < MAXCDB; i++)
if (cursors[i] != NULL) {
- lprintf(CTDL_EMERG,
- "cdb_*: cursor still in progress on cdb %d: %s\n",
+ CtdlLogPrintf(CTDL_EMERG,
+ "cdb_*: cursor still in progress on cdb %02x: %s\n",
i, msg);
abort();
}
}
-static void check_handles(void *arg)
+void check_handles(void *arg)
{
if (arg != NULL) {
- struct cdbtsd *tsd = (struct cdbtsd *) arg;
+ ThreadTSD *tsd = (ThreadTSD *) arg;
bailIfCursor(tsd->cursors, "in check_handles");
if (tsd->tid != NULL) {
- lprintf(CTDL_EMERG,
+ CtdlLogPrintf(CTDL_EMERG,
"cdb_*: transaction still in progress!");
abort();
}
}
}
-static void dest_tsd(void *arg)
-{
- if (arg != NULL) {
- check_handles(arg);
- free(arg);
- }
-}
-
-/*
- * Ensure that we have a key for thread-specific data. We don't
- * put anything in here that Citadel cares about; this is just database
- * related stuff like cursors and transactions.
- *
- * This should be called immediately after startup by any thread which wants
- * to use database calls, except for whatever thread calls open_databases.
- */
-void cdb_allocate_tsd(void)
-{
- struct cdbtsd *tsd;
-
- if (pthread_getspecific(tsdkey) != NULL)
- return;
-
- tsd = malloc(sizeof(struct cdbtsd));
-
- tsd->tid = NULL;
-
- memset(tsd->cursors, 0, sizeof tsd->cursors);
- pthread_setspecific(tsdkey, tsd);
-}
-
-void cdb_free_tsd(void)
-{
- dest_tsd(pthread_getspecific(tsdkey));
- pthread_setspecific(tsdkey, NULL);
-}
-
void cdb_check_handles(void)
{
- check_handles(pthread_getspecific(tsdkey));
-}
-
-
-/*
- * Reclaim unused space in the databases. We need to do each one of
- * these discretely, rather than in a loop.
- *
- * This is a stub function in the Sleepycat DB backend, because there is no
- * such API call available.
- */
-void defrag_databases(void)
-{
- /* do nothing */
+ check_handles(pthread_getspecific(ThreadKey));
}
/* Get the list of names. */
if ((ret = dbenv->log_archive(dbenv, &list, flags)) != 0) {
- lprintf(CTDL_ERR, "cdb_cull_logs: %s\n", db_strerror(ret));
+ CtdlLogPrintf(CTDL_ERR, "cdb_cull_logs: %s\n", db_strerror(ret));
return;
}
/* Print the list of names. */
if (list != NULL) {
for (file = list; *file != NULL; ++file) {
- lprintf(CTDL_DEBUG, "Deleting log: %s\n", *file);
+ CtdlLogPrintf(CTDL_DEBUG, "Deleting log: %s\n", *file);
ret = unlink(*file);
if (ret != 0) {
snprintf(errmsg, sizeof(errmsg),
/*
* Request a checkpoint of the database.
*/
-static void cdb_checkpoint(void)
+void cdb_checkpoint(void)
{
int ret;
- static time_t last_run = 0L;
+// static time_t last_run = 0L;
/* Only do a checkpoint once per minute. */
+/*
+ * Don't need this any more, since the thread that calls us sleeps for 60 seconds between calls
+
if ((time(NULL) - last_run) < 60L) {
return;
}
last_run = time(NULL);
+*/
- lprintf(CTDL_DEBUG, "-- db checkpoint --\n");
+ CtdlLogPrintf(CTDL_DEBUG, "-- db checkpoint --\n");
ret = dbenv->txn_checkpoint(dbenv,
MAX_CHECKPOINT_KBYTES,
MAX_CHECKPOINT_MINUTES, 0);
if (ret != 0) {
- lprintf(CTDL_EMERG, "cdb_checkpoint: txn_checkpoint: %s\n",
+ CtdlLogPrintf(CTDL_EMERG, "cdb_checkpoint: txn_checkpoint: %s\n",
db_strerror(ret));
abort();
}
}
-/*
- * Main loop for the checkpoint thread.
- */
-void *checkpoint_thread(void *arg) {
- struct CitContext checkpointCC;
-
- lprintf(CTDL_DEBUG, "checkpoint_thread() initializing\n");
-
- memset(&checkpointCC, 0, sizeof(struct CitContext));
- checkpointCC.internal_pgm = 1;
- checkpointCC.cs_pid = 0;
- pthread_setspecific(MyConKey, (void *)&checkpointCC );
-
- cdb_allocate_tsd();
-
- while (!time_to_die) {
- cdb_checkpoint();
- sleep(1);
- }
-
- lprintf(CTDL_DEBUG, "checkpoint_thread() exiting\n");
- pthread_exit(NULL);
-}
/*
* Open the various databases we'll be using. Any database which
int i;
char dbfilename[SIZ];
u_int32_t flags = 0;
- DIR *dp;
- struct dirent *d;
- char filename[PATH_MAX];
+ int dbversion_major, dbversion_minor, dbversion_patch;
+ int current_dbversion = 0;
+
+ CtdlLogPrintf(CTDL_DEBUG, "cdb_*: open_databases() starting\n");
+ CtdlLogPrintf(CTDL_DEBUG, "Compiled db: %s\n", DB_VERSION_STRING);
+ CtdlLogPrintf(CTDL_INFO, " Linked db: %s\n",
+ db_version(&dbversion_major, &dbversion_minor, &dbversion_patch));
+
+ current_dbversion = (dbversion_major * 1000000) + (dbversion_minor * 1000) + dbversion_patch;
+
+ CtdlLogPrintf(CTDL_DEBUG, "Calculated dbversion: %d\n", current_dbversion);
+ CtdlLogPrintf(CTDL_DEBUG, " Previous dbversion: %d\n", CitControl.MMdbversion);
+
+ if (CitControl.MMdbversion > current_dbversion) {
+ CtdlLogPrintf(CTDL_EMERG, "You are attempting to run the Citadel server using a version\n"
+ "of Berkeley DB that is older than that which last created or\n"
+ "updated the database. Because this would probably cause data\n"
+ "corruption or loss, the server is aborting execution now.\n");
+ exit(CTDLEXIT_DB);
+ }
+
+ CitControl.MMdbversion = current_dbversion;
+ put_control();
- lprintf(CTDL_DEBUG, "cdb_*: open_databases() starting\n");
- lprintf(CTDL_DEBUG, "Compiled db: %s\n", DB_VERSION_STRING);
- lprintf(CTDL_INFO, " Linked db: %s\n",
- db_version(NULL, NULL, NULL));
#ifdef HAVE_ZLIB
- lprintf(CTDL_INFO, "Linked zlib: %s\n", zlibVersion());
+ CtdlLogPrintf(CTDL_INFO, "Linked zlib: %s\n", zlibVersion());
#endif
/*
chmod(ctdl_data_dir, 0700);
chown(ctdl_data_dir, CTDLUID, (-1));
- lprintf(CTDL_DEBUG, "cdb_*: Setting up DB environment\n");
+ CtdlLogPrintf(CTDL_DEBUG, "cdb_*: Setting up DB environment\n");
db_env_set_func_yield(sched_yield);
ret = db_env_create(&dbenv, 0);
if (ret) {
- lprintf(CTDL_EMERG, "cdb_*: db_env_create: %s\n",
+ CtdlLogPrintf(CTDL_EMERG, "cdb_*: db_env_create: %s\n",
db_strerror(ret));
- exit(ret);
+ exit(CTDLEXIT_DB);
}
dbenv->set_errpfx(dbenv, "citserver");
dbenv->set_paniccall(dbenv, dbpanic);
+ dbenv->set_errcall(dbenv, cdb_verbose_err);
+ dbenv->set_errpfx(dbenv, "ctdl");
+#if (DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR >= 3)
dbenv->set_msgcall(dbenv, cdb_verbose_log);
+#endif
dbenv->set_verbose(dbenv, DB_VERB_DEADLOCK, 1);
dbenv->set_verbose(dbenv, DB_VERB_RECOVERY, 1);
- dbenv->set_verbose(dbenv, DB_VERB_REGISTER, 1);
/*
* We want to specify the shared memory buffer pool cachesize,
*/
ret = dbenv->set_cachesize(dbenv, 0, 64 * 1024, 0);
if (ret) {
- lprintf(CTDL_EMERG, "cdb_*: set_cachesize: %s\n",
+ CtdlLogPrintf(CTDL_EMERG, "cdb_*: set_cachesize: %s\n",
db_strerror(ret));
dbenv->close(dbenv, 0);
- exit(ret);
+ exit(CTDLEXIT_DB);
}
if ((ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT))) {
- lprintf(CTDL_EMERG, "cdb_*: set_lk_detect: %s\n",
+ CtdlLogPrintf(CTDL_EMERG, "cdb_*: set_lk_detect: %s\n",
db_strerror(ret));
dbenv->close(dbenv, 0);
- exit(ret);
+ exit(CTDLEXIT_DB);
}
- flags =
- DB_CREATE | DB_RECOVER | DB_INIT_MPOOL |
- DB_PRIVATE | DB_INIT_TXN | DB_INIT_LOCK | DB_THREAD;
- lprintf(CTDL_DEBUG, "dbenv->open(dbenv, %s, %d, 0)\n",
- ctdl_data_dir, flags);
+ flags = DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_INIT_TXN | DB_INIT_LOCK | DB_THREAD | DB_RECOVER;
+ CtdlLogPrintf(CTDL_DEBUG, "dbenv->open(dbenv, %s, %d, 0)\n", ctdl_data_dir, flags);
ret = dbenv->open(dbenv, ctdl_data_dir, flags, 0);
+ if (ret == DB_RUNRECOVERY) {
+ CtdlLogPrintf(CTDL_ALERT, "dbenv->open: %s\n", db_strerror(ret));
+ CtdlLogPrintf(CTDL_ALERT, "Attempting recovery...\n");
+ flags |= DB_RECOVER;
+ ret = dbenv->open(dbenv, ctdl_data_dir, flags, 0);
+ }
+ if (ret == DB_RUNRECOVERY) {
+ CtdlLogPrintf(CTDL_ALERT, "dbenv->open: %s\n", db_strerror(ret));
+ CtdlLogPrintf(CTDL_ALERT, "Attempting catastrophic recovery...\n");
+ flags &= ~DB_RECOVER;
+ flags |= DB_RECOVER_FATAL;
+ ret = dbenv->open(dbenv, ctdl_data_dir, flags, 0);
+ }
if (ret) {
- lprintf(CTDL_DEBUG, "cdb_*: dbenv->open: %s\n",
- db_strerror(ret));
+ CtdlLogPrintf(CTDL_DEBUG, "dbenv->open: %s\n", db_strerror(ret));
dbenv->close(dbenv, 0);
- exit(ret);
+ exit(CTDLEXIT_DB);
}
- lprintf(CTDL_INFO, "cdb_*: Starting up DB\n");
+ CtdlLogPrintf(CTDL_INFO, "Starting up DB\n");
for (i = 0; i < MAXCDB; ++i) {
/* Create a database handle */
ret = db_create(&dbp[i], dbenv, 0);
if (ret) {
- lprintf(CTDL_DEBUG, "cdb_*: db_create: %s\n",
+ CtdlLogPrintf(CTDL_DEBUG, "db_create: %s\n",
db_strerror(ret));
- exit(ret);
+ exit(CTDLEXIT_DB);
}
DB_CREATE | DB_AUTO_COMMIT | DB_THREAD,
0600);
if (ret) {
- lprintf(CTDL_EMERG, "cdb_*: db_open[%d]: %s\n", i,
+ CtdlLogPrintf(CTDL_EMERG, "db_open[%02x]: %s\n", i,
db_strerror(ret));
- exit(ret);
+ exit(CTDLEXIT_DB);
}
}
- if ((ret = pthread_key_create(&tsdkey, dest_tsd))) {
- lprintf(CTDL_EMERG, "cdb_*: pthread_key_create: %s\n",
- strerror(ret));
- exit(1);
- }
+}
- cdb_allocate_tsd();
- /* Now make sure we own all the files, because in a few milliseconds
- * we're going to drop root privs.
- */
+/* Make sure we own all the files, because in a few milliseconds
+ * we're going to drop root privs.
+ */
+void cdb_chmod_data(void) {
+ DIR *dp;
+ struct dirent *d;
+ char filename[PATH_MAX];
+
dp = opendir(ctdl_data_dir);
if (dp != NULL) {
while (d = readdir(dp), d != NULL) {
if (d->d_name[0] != '.') {
snprintf(filename, sizeof filename,
"%s/%s", ctdl_data_dir, d->d_name);
- chmod(filename, 0600);
- chown(filename, CTDLUID, (-1));
+ CtdlLogPrintf(9, "chmod(%s, 0600) returned %d\n",
+ filename, chmod(filename, 0600)
+ );
+ CtdlLogPrintf(9, "chown(%s, CTDLUID, -1) returned %d\n",
+ filename, chown(filename, CTDLUID, (-1))
+ );
}
}
closedir(dp);
}
- lprintf(CTDL_DEBUG, "cdb_*: open_databases() finished\n");
+ CtdlLogPrintf(CTDL_DEBUG, "open_databases() finished\n");
CtdlRegisterProtoHook(cmd_cull, "CULL", "Cull database logs");
}
int a;
int ret;
- cdb_free_tsd();
-
+ ctdl_thread_internal_free_tsd();
+
if ((ret = dbenv->txn_checkpoint(dbenv, 0, 0, 0))) {
- lprintf(CTDL_EMERG,
- "cdb_*: txn_checkpoint: %s\n", db_strerror(ret));
+ CtdlLogPrintf(CTDL_EMERG,
+ "txn_checkpoint: %s\n", db_strerror(ret));
}
+ /* print some statistics... */
+#ifdef DB_STAT_ALL
+ dbenv->lock_stat_print(dbenv, DB_STAT_ALL);
+#endif
+
+ /* close the tables */
for (a = 0; a < MAXCDB; ++a) {
- lprintf(CTDL_INFO, "cdb_*: Closing database %d\n", a);
+ CtdlLogPrintf(CTDL_INFO, "Closing database %02x\n", a);
ret = dbp[a]->close(dbp[a], 0);
if (ret) {
- lprintf(CTDL_EMERG,
- "cdb_*: db_close: %s\n", db_strerror(ret));
+ CtdlLogPrintf(CTDL_EMERG,
+ "db_close: %s\n", db_strerror(ret));
}
}
/* Close the handle. */
ret = dbenv->close(dbenv, 0);
if (ret) {
- lprintf(CTDL_EMERG,
- "cdb_*: DBENV->close: %s\n", db_strerror(ret));
+ CtdlLogPrintf(CTDL_EMERG,
+ "DBENV->close: %s\n", db_strerror(ret));
}
}
(uLongf *) & destLen,
(const Bytef *) compressed_data,
(uLong) sourceLen) != Z_OK) {
- lprintf(CTDL_EMERG, "uncompress() error\n");
+ CtdlLogPrintf(CTDL_EMERG, "uncompress() error\n");
abort();
}
DBT dkey, ddata;
DB_TXN *tid;
- int ret;
+ int ret = 0;
#ifdef HAVE_ZLIB
struct CtdlCompressHeader zheader;
char *compressed_data = NULL;
int compressing = 0;
- size_t buffer_len;
- uLongf destLen;
+ size_t buffer_len = 0;
+ uLongf destLen = 0;
#endif
memset(&dkey, 0, sizeof(DBT));
CtdlCompressHeader)),
&destLen, (Bytef *) cdata, (uLongf) cdatalen,
1) != Z_OK) {
- lprintf(CTDL_EMERG, "compress2() error\n");
+ CtdlLogPrintf(CTDL_EMERG, "compress2() error\n");
abort();
}
zheader.compressed_len = (size_t) destLen;
&ddata, /* data */
0); /* flags */
if (ret) {
- lprintf(CTDL_EMERG, "cdb_store(%d): %s\n", cdb,
+ CtdlLogPrintf(CTDL_EMERG, "cdb_store(%d): %s\n", cdb,
db_strerror(ret));
abort();
}
txabort(tid);
goto retry;
} else {
- lprintf(CTDL_EMERG, "cdb_store(%d): %s\n",
+ CtdlLogPrintf(CTDL_EMERG, "cdb_store(%d): %s\n",
cdb, db_strerror(ret));
abort();
}
if (MYTID != NULL) {
ret = dbp[cdb]->del(dbp[cdb], MYTID, &dkey, 0);
if (ret) {
- lprintf(CTDL_EMERG, "cdb_delete(%d): %s\n", cdb,
+ CtdlLogPrintf(CTDL_EMERG, "cdb_delete(%d): %s\n", cdb,
db_strerror(ret));
if (ret != DB_NOTFOUND)
abort();
txabort(tid);
goto retry;
} else {
- lprintf(CTDL_EMERG, "cdb_delete(%d): %s\n",
+ CtdlLogPrintf(CTDL_EMERG, "cdb_delete(%d): %s\n",
cdb, db_strerror(ret));
abort();
}
DB_POSITION);
if (ret) {
- lprintf(CTDL_EMERG, "localcursor: %s\n", db_strerror(ret));
+ CtdlLogPrintf(CTDL_EMERG, "localcursor: %s\n", db_strerror(ret));
abort();
}
}
if ((ret != 0) && (ret != DB_NOTFOUND)) {
- lprintf(CTDL_EMERG, "cdb_fetch(%d): %s\n", cdb,
+ CtdlLogPrintf(CTDL_EMERG, "cdb_fetch(%d): %s\n", cdb,
db_strerror(ret));
abort();
}
tempcdb = (struct cdbdata *) malloc(sizeof(struct cdbdata));
if (tempcdb == NULL) {
- lprintf(CTDL_EMERG,
+ CtdlLogPrintf(CTDL_EMERG,
"cdb_fetch: Cannot allocate memory for tempcdb\n");
abort();
}
int ret = 0;
if (MYCURSORS[cdb] != NULL) {
- lprintf(CTDL_EMERG,
+ CtdlLogPrintf(CTDL_EMERG,
"cdb_rewind: must close cursor on database %d before reopening.\n",
cdb);
abort();
*/
ret = dbp[cdb]->cursor(dbp[cdb], MYTID, &MYCURSORS[cdb], 0);
if (ret) {
- lprintf(CTDL_EMERG, "cdb_rewind: db_cursor: %s\n",
+ CtdlLogPrintf(CTDL_EMERG, "cdb_rewind: db_cursor: %s\n",
db_strerror(ret));
abort();
}
if (ret) {
if (ret != DB_NOTFOUND) {
- lprintf(CTDL_EMERG, "cdb_next_item(%d): %s\n",
+ CtdlLogPrintf(CTDL_EMERG, "cdb_next_item(%d): %s\n",
cdb, db_strerror(ret));
abort();
}
"can't begin transaction during r/o cursor");
if (MYTID != NULL) {
- lprintf(CTDL_EMERG,
+ CtdlLogPrintf(CTDL_EMERG,
"cdb_begin_transaction: ERROR: nested transaction\n");
abort();
}
for (i = 0; i < MAXCDB; i++)
if (MYCURSORS[i] != NULL) {
- lprintf(CTDL_WARNING,
+ CtdlLogPrintf(CTDL_WARNING,
"cdb_end_transaction: WARNING: cursor %d still open at transaction end\n",
i);
cclose(MYCURSORS[i]);
}
if (MYTID == NULL) {
- lprintf(CTDL_EMERG,
+ CtdlLogPrintf(CTDL_EMERG,
"cdb_end_transaction: ERROR: txcommit(NULL) !!\n");
abort();
} else
u_int32_t count;
if (MYTID != NULL) {
- lprintf(CTDL_EMERG,
+ CtdlLogPrintf(CTDL_EMERG,
"cdb_trunc must not be called in a transaction.\n");
abort();
} else {
/* txabort(tid); */
goto retry;
} else {
- lprintf(CTDL_EMERG,
+ CtdlLogPrintf(CTDL_EMERG,
"cdb_truncate(%d): %s\n", cdb,
db_strerror(ret));
abort();