Simple concurrency lock to prevent multiple citservers running at the same time
[citadel.git] / citadel / database.c
index e2654b6e65fb010219e41d8807a1df2842579774..dbfac8dd54ebc1522ea588b2d87c483457183794 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This is a data store backend for the Citadel server which uses Berkeley DB.
  *
- * Copyright (c) 1987-2012 by the citadel.org team
+ * Copyright (c) 1987-2016 by the citadel.org team
  *
  * This program is open source software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License version 3.
 /*****************************************************************************/
 
 #include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
 #include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <dirent.h>
-#include <syslog.h>
 #include <zlib.h>
 
 #ifdef HAVE_DB_H
 #include <db.h>
 #elif defined(HAVE_DB4_DB_H)
-#include <db4/db.h>
+#include <db5/db.h>
 #else
-#error Neither <db.h> nor <db4/db.h> was found by configure. Install db4-devel.
+#error Neither <db.h> nor <db5/db.h> was found by configure. Install db5-devel.
 #endif
 
-
-#if DB_VERSION_MAJOR < 4 || DB_VERSION_MINOR < 1
-#error Citadel requires Berkeley DB v4.1 or newer.  Please upgrade.
+#if DB_VERSION_MAJOR < 5
+#error Citadel requires Berkeley DB v5.0 or newer.  Please upgrade.
 #endif
 
-
 #include <libcitadel.h>
-#include "citadel.h"
-#include "server.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"
-
+#include "control.h"
+#include "citserver.h"
+#include "config.h"
 
 static DB *dbp[MAXCDB];                /* One DB handle for each Citadel database */
 static DB_ENV *dbenv;          /* The DB environment (global) */
@@ -97,8 +80,10 @@ void cdb_verbose_log(const DB_ENV *dbenv, const char *msg)
 /* Verbose logging callback */
 void cdb_verbose_err(const DB_ENV *dbenv, const char *errpfx, const char *msg)
 {
+       int *FOO = NULL;
        syslog(LOG_ALERT, "DB: %s", msg);
        cit_backtrace();
+       *FOO = 1;
 }
 
 
@@ -183,7 +168,7 @@ void cdb_check_handles(void)
 /*
  * Cull the database logs
  */
-static void cdb_cull_logs(void)
+void cdb_cull_logs(void)
 {
        u_int32_t flags;
        int ret;
@@ -219,15 +204,6 @@ static void cdb_cull_logs(void)
        }
 }
 
-/*
- * Manually initiate log file cull.
- */
-void cmd_cull(char *argbuf) {
-       if (CtdlAccessCheck(ac_internal)) return;
-       cdb_cull_logs();
-       cprintf("%d Database log file cull completed.\n", CIT_OK);
-}
-
 
 /*
  * Request a checkpoint of the database.  Called once per minute by the thread manager.
@@ -236,7 +212,7 @@ void cdb_checkpoint(void)
 {
        int ret;
 
-       syslog(LOG_DEBUG, "-- db checkpoint --");
+       MARKM_syslog(LOG_DEBUG, "-- db checkpoint --");
        ret = dbenv->txn_checkpoint(dbenv, MAX_CHECKPOINT_KBYTES, MAX_CHECKPOINT_MINUTES, 0);
 
        if (ret != 0) {
@@ -245,7 +221,7 @@ void cdb_checkpoint(void)
        }
 
        /* After a successful checkpoint, we can cull the unused logs */
-       if (config.c_auto_cull) {
+       if (CtdlGetConfigInt("c_auto_cull")) {
                cdb_cull_logs();
        }
 }
@@ -265,30 +241,10 @@ void open_databases(void)
        char dbfilename[32];
        u_int32_t flags = 0;
        int dbversion_major, dbversion_minor, dbversion_patch;
-       int current_dbversion = 0;
 
        syslog(LOG_DEBUG, "bdb(): open_databases() starting");
        syslog(LOG_DEBUG, "Compiled db: %s", DB_VERSION_STRING);
-       syslog(LOG_INFO, "  Linked db: %s",
-               db_version(&dbversion_major, &dbversion_minor, &dbversion_patch));
-
-       current_dbversion = (dbversion_major * 1000000) + (dbversion_minor * 1000) + dbversion_patch;
-
-       syslog(LOG_DEBUG, "Calculated dbversion: %d", current_dbversion);
-       syslog(LOG_DEBUG, "  Previous dbversion: %d", CitControl.MMdbversion);
-
-       if ( (getenv("SUPPRESS_DBVERSION_CHECK") == NULL)
-          && (CitControl.MMdbversion > current_dbversion) ) {
-               syslog(LOG_EMERG, "You are attempting to run the Citadel server using a version");
-               syslog(LOG_EMERG, "of Berkeley DB that is older than that which last created or");
-               syslog(LOG_EMERG, "updated the database.  Because this would probably cause data");
-               syslog(LOG_EMERG, "corruption or loss, the server is aborting execution now.");
-               exit(CTDLEXIT_DB);
-       }
-
-       CitControl.MMdbversion = current_dbversion;
-       put_control();
-
+       syslog(LOG_INFO, "  Linked db: %s", db_version(&dbversion_major, &dbversion_minor, &dbversion_patch));
        syslog(LOG_INFO, "Linked zlib: %s\n", zlibVersion());
 
        /*
@@ -322,9 +278,7 @@ void open_databases(void)
        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);
 
@@ -435,7 +389,6 @@ void cdb_chmod_data(void) {
        }
 
        syslog(LOG_DEBUG, "open_databases() finished\n");
-       CtdlRegisterProtoHook(cmd_cull, "CULL", "Cull database logs");
 }
 
 
@@ -906,3 +859,141 @@ void cdb_trunc(int cdb)
                }
        }
 }
+
+int SeentDebugEnabled = 0;
+
+#define DBGLOG(LEVEL) if ((LEVEL != LOG_DEBUG) || (SeentDebugEnabled != 0))
+#define SEENM_syslog(LEVEL, FORMAT)                                    \
+       DBGLOG(LEVEL) syslog(LEVEL,                                     \
+                            "%s[%ld]CC[%ld] SEEN[%s][%d] " FORMAT,     \
+                            IOSTR, ioid, ccid, Facility, cType)
+
+#define SEEN_syslog(LEVEL, FORMAT, ...)                                        \
+       DBGLOG(LEVEL) syslog(LEVEL,                                     \
+                            "%s[%ld]CC[%ld] SEEN[%s][%d] " FORMAT,     \
+                            IOSTR, ioid, ccid, Facility, cType,        \
+                            __VA_ARGS__)
+
+time_t CheckIfAlreadySeen(const char *Facility,
+                         StrBuf *guid,
+                         time_t now,
+                         time_t antiexpire,
+                         eCheckType cType,
+                         long ccid,
+                         long ioid)
+{
+       time_t InDBTimeStamp = 0;
+       struct UseTable ut;
+       struct cdbdata *cdbut;
+
+       if (cType != eWrite)
+       {
+               SEEN_syslog(LOG_DEBUG, "Loading [%s]", ChrPtr(guid));
+               cdbut = cdb_fetch(CDB_USETABLE, SKEY(guid));
+               if ((cdbut != NULL) && (cdbut->ptr != NULL)) {
+                       memcpy(&ut, cdbut->ptr,
+                              ((cdbut->len > sizeof(struct UseTable)) ?
+                               sizeof(struct UseTable) : cdbut->len));
+                       InDBTimeStamp = now - ut.ut_timestamp;
+
+                       if (InDBTimeStamp < antiexpire)
+                       {
+                               SEEN_syslog(LOG_DEBUG, "Found - Not expired %ld < %ld", InDBTimeStamp, antiexpire);
+                               cdb_free(cdbut);
+                               return InDBTimeStamp;
+                       }
+                       else
+                       {
+                               SEEN_syslog(LOG_DEBUG, "Found - Expired. %ld >= %ld", InDBTimeStamp, antiexpire);
+                               cdb_free(cdbut);
+                       }
+               }
+               else
+               {
+                       if (cdbut) cdb_free(cdbut);
+                       
+                       SEENM_syslog(LOG_DEBUG, "not Found");
+                       if (cType == eCheckUpdate)
+                               return 0;
+               }
+
+               if (cType == eCheckExist)
+                       return InDBTimeStamp;
+       }
+
+       memcpy(ut.ut_msgid, SKEY(guid));
+       ut.ut_timestamp = now;
+
+       SEENM_syslog(LOG_DEBUG, "Saving new Timestamp");
+       /* rewrite the record anyway, to update the timestamp */
+       cdb_store(CDB_USETABLE,
+                 SKEY(guid),
+                 &ut, sizeof(struct UseTable) );
+
+       SEENM_syslog(LOG_DEBUG, "Done Saving");
+       return InDBTimeStamp;
+}
+
+
+void cmd_rsen(char *argbuf) {
+       char Token[SIZ];
+       long TLen;
+       char Time[SIZ];
+
+       struct UseTable ut;
+       struct cdbdata *cdbut;
+       
+       if (CtdlAccessCheck(ac_aide)) return;
+
+       TLen = extract_token(Token, argbuf, 1, '|', sizeof Token);
+       if (strncmp(argbuf, "GET", 3) == 0) {
+               cdbut = cdb_fetch(CDB_USETABLE, Token, TLen);
+               if (cdbut != NULL) {
+                       memcpy(&ut, cdbut->ptr,
+                              ((cdbut->len > sizeof(struct UseTable)) ?
+                               sizeof(struct UseTable) : cdbut->len));
+                       
+                       cprintf("%d %ld\n", CIT_OK, ut.ut_timestamp);
+               }
+               else {
+                       cprintf("%d not found\n", ERROR + NOT_HERE);
+               }
+
+       }
+       else if (strncmp(argbuf, "SET", 3) == 0) {
+               memcpy(ut.ut_msgid, Token, TLen);
+               extract_token(Time, argbuf, 2, '|', sizeof Time);
+               ut.ut_timestamp = atol(Time);
+               cdb_store(CDB_USETABLE,
+                         Token, TLen,
+                         &ut, sizeof(struct UseTable) );
+               cprintf("%d token updated\n", CIT_OK);
+       }
+       else if (strncmp(argbuf, "DEL", 3) == 0) {
+               if (cdb_delete(CDB_USETABLE, Token, TLen))
+                       cprintf("%d not found\n", ERROR + NOT_HERE);
+               else
+                       cprintf("%d deleted.\n", CIT_OK);
+
+       }
+       else {
+               cprintf("%d Usage: [GET|SET|DEL]|Token|timestamp\n", ERROR);
+       }
+
+}
+void LogDebugEnableSeenEnable(const int n)
+{
+       SeentDebugEnabled = n;
+}
+
+CTDL_MODULE_INIT(database)
+{
+       if (!threading)
+       {
+               CtdlRegisterDebugFlagHook(HKEY("SeenDebug"), LogDebugEnableSeenEnable, &SeentDebugEnabled);
+               CtdlRegisterProtoHook(cmd_rsen, "RSEN", "manipulate Aggregators seen database");
+       }
+
+       /* return our module id for the log */
+       return "database";
+}