]> arthur.barton.de Git - netatalk.git/blobdiff - etc/cnid_dbd/main.c
Refine CNID salvatio strategies
[netatalk.git] / etc / cnid_dbd / main.c
index 6c21d8597aa227f9ed32b71f73fb9ffba500b8eb..42cf28489f14e61c055a73a73eb9a75a74742347 100644 (file)
@@ -1,7 +1,8 @@
 /*
- * $Id: main.c,v 1.3 2009-03-31 11:40:26 franklahm Exp $
+ * $Id: main.c,v 1.16 2009-11-25 14:59:15 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
+ * Copyright (c) Frank Lahm 2009
  * All Rights Reserved.  See COPYING.
  */
 
 #include "dbd.h"
 #include "comm.h"
 
-
 #define LOCKFILENAME  "lock"
 
-static int exit_sig = 0;
+/* 
+   Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
+   It's a likey performance hit, but it might we worth it.
+ */
+#define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | DB_RECOVER)
 
+static DBD *dbd;
+
+static int exit_sig = 0;
 
 static void sig_exit(int signo)
 {
@@ -70,126 +77,156 @@ static void block_sigs_onoff(int block)
 
   1: Success, if transactions are used commit.
   0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
-  -1: Fatal error, either from the database or from the socket. Abort the transaction if applicable
+  -1: Fatal error, either from t
+  he database or from the socket. Abort the transaction if applicable
   (which might fail as well) and then exit.
 
   We always try to notify the client process about the outcome, the result field
   of the cnid_dbd_rply structure contains further details.
 
 */
+#ifndef min
+#define min(a,b)        ((a)<(b)?(a):(b))
+#endif
 
 static int loop(struct db_param *dbp)
 {
     struct cnid_dbd_rqst rqst;
     struct cnid_dbd_rply rply;
+    time_t timeout;
     int ret, cret;
-    time_t now, time_next_flush, time_last_rqst;
     int count;
+    time_t now, time_next_flush, time_last_rqst;
+    char timebuf[64];
     static char namebuf[MAXPATHLEN + 1];
-    u_int32_t checkp_flags;
+    sigset_t set;
+
+    sigemptyset(&set);
+    sigprocmask(SIG_SETMASK, NULL, &set);
+    sigdelset(&set, SIGINT);
+    sigdelset(&set, SIGTERM);
 
     count = 0;
     now = time(NULL);
     time_next_flush = now + dbp->flush_interval;
     time_last_rqst = now;
-    if (dbp->nosync)
-        checkp_flags = DB_FORCE;
-    else
-        checkp_flags = 0;
 
     rqst.name = namebuf;
 
+    strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
+    LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
+        dbp->flush_interval, timebuf);
+
     while (1) {
-        if ((cret = comm_rcv(&rqst)) < 0)
-            return -1;
+        timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
+        if (timeout > now)
+            timeout -= now;
+        else
+            timeout = 1;
 
-        now = time(NULL);
+        if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
+            return -1;
 
-        if (count > dbp->flush_frequency || now > time_next_flush) {
-#ifdef CNID_BACKEND_DBD_TXN
-            if (dbif_txn_checkpoint(0, 0, checkp_flags) < 0)
+        if (cret == 0) {
+            /* comm_rcv returned from select without receiving anything. */
+            if (exit_sig) {
+                /* Received signal (TERM|INT) */
+                return 0;
+            }
+            if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
+                /* Idle timeout */
+                return 0;
+            }
+            /* still active connections, reset time_last_rqst */
+            time_last_rqst = now;
+        } else {
+            /* We got a request */
+            time_last_rqst = now;
+
+            memset(&rply, 0, sizeof(rply));
+            switch(rqst.op) {
+                /* ret gets set here */
+            case CNID_DBD_OP_OPEN:
+            case CNID_DBD_OP_CLOSE:
+                /* open/close are noops for now. */
+                rply.namelen = 0;
+                ret = 1;
+                break;
+            case CNID_DBD_OP_ADD:
+                ret = dbd_add(dbd, &rqst, &rply, 0);
+                break;
+            case CNID_DBD_OP_GET:
+                ret = dbd_get(dbd, &rqst, &rply);
+                break;
+            case CNID_DBD_OP_RESOLVE:
+                ret = dbd_resolve(dbd, &rqst, &rply);
+                break;
+            case CNID_DBD_OP_LOOKUP:
+                ret = dbd_lookup(dbd, &rqst, &rply, 0);
+                break;
+            case CNID_DBD_OP_UPDATE:
+                ret = dbd_update(dbd, &rqst, &rply);
+                break;
+            case CNID_DBD_OP_DELETE:
+                ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
+                break;
+            case CNID_DBD_OP_GETSTAMP:
+                ret = dbd_getstamp(dbd, &rqst, &rply);
+                break;
+            case CNID_DBD_OP_REBUILD_ADD:
+                ret = dbd_rebuild_add(dbd, &rqst, &rply);
+                break;
+            default:
+                LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
+                ret = -1;
+                break;
+            }
+            
+            if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
+                dbif_txn_abort(dbd);
                 return -1;
-#else
-            if (dbif_sync() < 0)
+            }
+            
+            if (ret == 0 || cret == 0) {
+                if (dbif_txn_abort(dbd) < 0)
+                    return -1;
+            } else {
+                ret = dbif_txn_commit(dbd);
+                if (  ret < 0)
+                    return -1;
+                else if ( ret > 0 )
+                    /* We had a designated txn because we wrote to the db */
+                    count++;
+            }
+        } /* got a request */
+
+        /*
+          Shall we checkpoint bdb ?
+          "flush_interval" seconds passed ?
+        */
+        if (now >= time_next_flush) {
+            LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
+            if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
                 return -1;
-#endif
             count = 0;
             time_next_flush = now + dbp->flush_interval;
-        }
-
-        if (cret == 0) {
-            block_sigs_onoff(0);
-            block_sigs_onoff(1);
-            if (exit_sig)
-                return 0;
-            if (dbp->idle_timeout && comm_nbe() <= 0 && (now - time_last_rqst) > dbp->idle_timeout)
-                return 0;
-            continue;
-        }
-        /* We got a request */
-        time_last_rqst = now;
-        count++;
 
-#ifdef CNID_BACKEND_DBD_TXN
-        if (dbif_txn_begin() < 0)
-            return -1;
-#endif /* CNID_BACKEND_DBD_TXN */
-
-        memset(&rply, 0, sizeof(rply));
-        switch(rqst.op) {
-            /* ret gets set here */
-        case CNID_DBD_OP_OPEN:
-        case CNID_DBD_OP_CLOSE:
-            /* open/close are noops for now. */
-            rply.namelen = 0;
-            ret = 1;
-            break;
-        case CNID_DBD_OP_ADD:
-            ret = dbd_add(&rqst, &rply);
-            break;
-        case CNID_DBD_OP_GET:
-            ret = dbd_get(&rqst, &rply);
-            break;
-        case CNID_DBD_OP_RESOLVE:
-            ret = dbd_resolve(&rqst, &rply);
-            break;
-        case CNID_DBD_OP_LOOKUP:
-            ret = dbd_lookup(&rqst, &rply);
-            break;
-        case CNID_DBD_OP_UPDATE:
-            ret = dbd_update(&rqst, &rply);
-            break;
-        case CNID_DBD_OP_DELETE:
-            ret = dbd_delete(&rqst, &rply);
-            break;
-        case CNID_DBD_OP_GETSTAMP:
-            ret = dbd_getstamp(&rqst, &rply);
-            break;
-        case CNID_DBD_OP_REBUILD_ADD:
-            ret = dbd_rebuild_add(&rqst, &rply);
-            break;
-        default:
-            LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
-            ret = -1;
-            break;
+            strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
+            LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
+                dbp->flush_interval, timebuf);
         }
 
-        if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
-#ifdef CNID_BACKEND_DBD_TXN
-            dbif_txn_abort();
-#endif /* CNID_BACKEND_DBD_TXN */
-            return -1;
-        }
-#ifdef CNID_BACKEND_DBD_TXN
-        if (ret == 0 || cret == 0) {
-            if (dbif_txn_abort() < 0)
-                return -1;
-        } else {
-            if (dbif_txn_commit() < 0)
+        /* 
+           Shall we checkpoint bdb ?
+           Have we commited "count" more changes than "flush_frequency" ?
+        */
+        if (count > dbp->flush_frequency) {
+            LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
+            if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
                 return -1;
+            count = 0;
         }
-#endif /* CNID_BACKEND_DBD_TXN */
-    }
+    } /* while(1) */
 }
 
 /* ------------------------ */
@@ -216,7 +253,7 @@ static void switch_to_user(char *dir)
 }
 
 /* ------------------------ */
-int get_lock(void)
+static int get_lock(void)
 {
     int lockfd;
     struct flock lock;
@@ -244,7 +281,7 @@ int get_lock(void)
 }
 
 /* ----------------------- */
-void set_signal(void)
+static void set_signal(void)
 {
     struct sigaction sv;
 
@@ -266,7 +303,7 @@ void set_signal(void)
 }
 
 /* ----------------------- */
-void free_lock(int lockfd)
+static void free_lock(int lockfd)
 {
     struct flock lock;
 
@@ -283,12 +320,12 @@ int main(int argc, char *argv[])
 {
     struct db_param *dbp;
     int err = 0;
-    int ret;
     int lockfd, ctrlfd, clntfd;
     char *dir, *logconfig;
 
     set_processname("cnid_dbd");
 
+    /* FIXME: implement -d from cnid_metad */
     if (argc  != 5) {
         LOG(log_error, logtype_cnid, "main: not enough arguments");
         exit(1);
@@ -310,75 +347,44 @@ int main(int argc, char *argv[])
 
     set_signal();
 
-    /* SIGINT and SIGTERM are always off, unless we get a return code of 0 from
-       comm_rcv (no requests for one second, see above in loop()). That means we
-       only shut down after one second of inactivity. */
+    /* SIGINT and SIGTERM are always off, unless we are in pselect */
     block_sigs_onoff(1);
 
-    if ((dbp = db_param_read(dir)) == NULL)
+    if ((dbp = db_param_read(dir, CNID_DBD)) == NULL)
         exit(1);
+    LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
 
-    if (dbif_env_init(dbp) < 0)
-        exit(2); /* FIXME: same exit code as failure for dbif_open() */
+    if (NULL == (dbd = dbif_init(".", "cnid2.db")))
+        exit(2);
 
-#ifdef CNID_BACKEND_DBD_TXN
-    if (dbif_txn_begin() < 0)
-        exit(6);
-#endif
+    if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
+        exit(2); /* FIXME: same exit code as failure for dbif_open() */
+    LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
 
-    if (dbif_open(dbp, 0) < 0) {
-#ifdef CNID_BACKEND_DBD_TXN
-        dbif_txn_abort();
-#endif
-        dbif_close();
+    if (dbif_open(dbd, dbp, 0) < 0) {
+        dbif_close(dbd);
         exit(2);
     }
+    LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
 
-#ifndef CNID_BACKEND_DBD_TXN
-    if (dbp->check && (ret = dbd_check(dir))) {
-        if (ret < 0) {
-            dbif_close();
-            exit(2);
-        }
-        dbif_closedb();
-        LOG(log_info, logtype_cnid, "main: re-opening, secondaries will be rebuilt. This may take some time");
-        if (dbif_open(dbp, 1) < 0) {
-            LOG(log_info, logtype_cnid, "main: re-opening databases failed");
-            dbif_close();
-            exit(2);
-        }
-        LOG(log_info, logtype_cnid, "main: rebuilt done");
-    }
-#endif
-
-    if (dbd_stamp() < 0) {
-#ifdef CNID_BACKEND_DBD_TXN
-        dbif_txn_abort();
-#endif
-        dbif_close();
+    if (dbd_stamp(dbd) < 0) {
+        dbif_close(dbd);
         exit(5);
     }
-#ifdef CNID_BACKEND_DBD_TXN
-    if (dbif_txn_commit() < 0)
-        exit(6);
-#endif
+    LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
 
     if (comm_init(dbp, ctrlfd, clntfd) < 0) {
-        dbif_close();
+        dbif_close(dbd);
         exit(3);
     }
 
     if (loop(dbp) < 0)
         err++;
 
-#ifndef CNID_BACKEND_DBD_TXN
-    /* FIXME: Do we really need to sync before closing the DB? Just closing it
-       should be enough. */
-    if (dbif_sync() < 0)
+    if (dbif_close(dbd) < 0)
         err++;
-#endif
 
-    if (dbif_close() < 0)
+    if (dbif_prep_upgrade(dir) < 0)
         err++;
 
     free_lock(lockfd);