]> arthur.barton.de Git - netatalk.git/blobdiff - etc/cnid_dbd/main.c
Merge master
[netatalk.git] / etc / cnid_dbd / main.c
index 37be4749c32702d0b4d6cdbcbdd808d18a6e27a0..4f5fd725b09fc4bbba95b5dca23d202a9af216d9 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: main.c,v 1.8 2009-05-20 10:29:22 franklahm Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
  * Copyright (c) Frank Lahm 2009
  * All Rights Reserved.  See COPYING.
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
-#ifdef HAVE_UNISTD_H
 #include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-#ifdef HAVE_FCNTL_H
 #include <fcntl.h>
-#endif /* HAVE_FCNTL_H */
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <signal.h>
 #include <string.h>
-#ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
-#endif /* HAVE_SYS_TYPES_H */
 #include <sys/param.h>
-#ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
-#endif /* HAVE_SYS_STAT_H */
 #include <time.h>
 #include <sys/file.h>
+#include <arpa/inet.h>
 
-#include <netatalk/endian.h>
 #include <atalk/cnid_dbd_private.h>
 #include <atalk/logger.h>
+#include <atalk/volinfo.h>
 
 #include "db_param.h"
 #include "dbif.h"
 #include "dbd.h"
 #include "comm.h"
 
-#define LOCKFILENAME  "lock"
-
 /* 
    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)
+#define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN)
 
-static DBD *dbd;
+/* Global, needed by pack.c:idxname() */
+struct volinfo volinfo;
 
+static DBD *dbd;
 static int exit_sig = 0;
+static int db_locked;
 
 static void sig_exit(int signo)
 {
@@ -77,23 +69,34 @@ 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;
     int count;
     time_t now, time_next_flush, time_last_rqst;
     char timebuf[64];
     static char namebuf[MAXPATHLEN + 1];
+    sigset_t set;
+
+    sigemptyset(&set);
+    sigprocmask(SIG_SETMASK, NULL, &set);
+    sigdelset(&set, SIGINT);
+    sigdelset(&set, SIGTERM);
 
     count = 0;
     now = time(NULL);
@@ -107,23 +110,27 @@ static int loop(struct db_param *dbp)
         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 (cret == 0) {
             /* comm_rcv returned from select without receiving anything. */
-
-            /* Give signals a chance... */
-            block_sigs_onoff(0);
-            block_sigs_onoff(1);
-            if (exit_sig)
+            if (exit_sig) {
                 /* Received signal (TERM|INT) */
                 return 0;
-            if (dbp->idle_timeout && comm_nbe() <= 0 && (now - time_last_rqst) > dbp->idle_timeout)
+            }
+            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;
@@ -138,7 +145,7 @@ static int loop(struct db_param *dbp)
                 ret = 1;
                 break;
             case CNID_DBD_OP_ADD:
-                ret = dbd_add(dbd, &rqst, &rply);
+                ret = dbd_add(dbd, &rqst, &rply, 0);
                 break;
             case CNID_DBD_OP_GET:
                 ret = dbd_get(dbd, &rqst, &rply);
@@ -147,13 +154,13 @@ static int loop(struct db_param *dbp)
                 ret = dbd_resolve(dbd, &rqst, &rply);
                 break;
             case CNID_DBD_OP_LOOKUP:
-                ret = dbd_lookup(dbd, &rqst, &rply);
+                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);
+                ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
                 break;
             case CNID_DBD_OP_GETSTAMP:
                 ret = dbd_getstamp(dbd, &rqst, &rply);
@@ -161,12 +168,15 @@ static int loop(struct db_param *dbp)
             case CNID_DBD_OP_REBUILD_ADD:
                 ret = dbd_rebuild_add(dbd, &rqst, &rply);
                 break;
+            case CNID_DBD_OP_SEARCH:
+                ret = dbd_search(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;
@@ -189,7 +199,7 @@ static int loop(struct db_param *dbp)
           Shall we checkpoint bdb ?
           "flush_interval" seconds passed ?
         */
-        if (now > time_next_flush) {
+        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;
@@ -237,36 +247,9 @@ static void switch_to_user(char *dir)
     }
 }
 
-/* ------------------------ */
-int get_lock(void)
-{
-    int lockfd;
-    struct flock lock;
-
-    if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
-        LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
-        exit(1);
-    }
-
-    lock.l_start  = 0;
-    lock.l_whence = SEEK_SET;
-    lock.l_len    = 0;
-    lock.l_type   = F_WRLCK;
-
-    if (fcntl(lockfd, F_SETLK, &lock) < 0) {
-        if (errno == EACCES || errno == EAGAIN) {
-            exit(0);
-        } else {
-            LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
-            exit(1);
-        }
-    }
-
-    return lockfd;
-}
 
 /* ----------------------- */
-void set_signal(void)
+static void set_signal(void)
 {
     struct sigaction sv;
 
@@ -287,26 +270,13 @@ void set_signal(void)
     }
 }
 
-/* ----------------------- */
-void free_lock(int lockfd)
-{
-    struct flock lock;
-
-    lock.l_start  = 0;
-    lock.l_whence = SEEK_SET;
-    lock.l_len    = 0;
-    lock.l_type = F_UNLCK;
-    fcntl(lockfd, F_SETLK, &lock);
-    close(lockfd);
-}
-
 /* ------------------------ */
 int main(int argc, char *argv[])
 {
     struct db_param *dbp;
     int err = 0;
-    int lockfd, ctrlfd, clntfd;
-    char *dir, *logconfig;
+    int ctrlfd, clntfd;
+    char *logconfig;
 
     set_processname("cnid_dbd");
 
@@ -316,35 +286,62 @@ int main(int argc, char *argv[])
         exit(1);
     }
 
-    dir = argv[1];
     ctrlfd = atoi(argv[2]);
     clntfd = atoi(argv[3]);
     logconfig = strdup(argv[4]);
     setuplog(logconfig);
 
-    switch_to_user(dir);
+    /* Load .volinfo file */
+    if (loadvolinfo(argv[1], &volinfo) == -1) {
+        LOG(log_error, logtype_cnid, "Cant load volinfo for \"%s\"", argv[1]);
+        exit(EXIT_FAILURE);
+    }
+    /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
+    char dbpath[MAXPATHLEN+1];
+    if ((strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
+        LOG(log_error, logtype_cnid, "CNID db pathname too long: \"%s\"", volinfo.v_dbpath);
+        exit(EXIT_FAILURE);
+    }
+    strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
+    strcat(dbpath, "/.AppleDB");
 
-    /* Before we do anything else, check if there is an instance of cnid_dbd
-       running already and silently exit if yes. */
-    lockfd = get_lock();
+    if (vol_load_charsets(&volinfo) == -1) {
+        LOG(log_error, logtype_cnid, "Error loading charsets!");
+        exit(EXIT_FAILURE);
+    }
+    LOG(log_debug, logtype_cnid, "db dir: \"%s\"", dbpath);
 
-    LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
+    switch_to_user(dbpath);
+
+    /* Get db lock */
+    if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1) {
+        LOG(log_error, logtype_cnid, "main: fatal db lock error");
+        exit(1);
+    }
+    if (db_locked != LOCK_EXCL) {
+        /* Couldn't get exclusive lock, try shared lock  */
+        if ((db_locked = get_lock(LOCK_SHRD, NULL)) != LOCK_SHRD) {
+            LOG(log_error, logtype_cnid, "main: fatal db lock error");
+            exit(1);
+        }
+    }
 
     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, CNID_DBD)) == NULL)
+    if ((dbp = db_param_read(dbpath)) == NULL)
         exit(1);
     LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
 
-    if (NULL == (dbd = dbif_init(".", "cnid2.db")))
+    if (NULL == (dbd = dbif_init(dbpath, "cnid2.db")))
         exit(2);
 
-    if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
+    /* Only recover if we got the lock */
+    if (dbif_env_open(dbd,
+                      dbp,
+                      (db_locked == LOCK_EXCL) ? DBOPTIONS | DB_RECOVER : DBOPTIONS) < 0)
         exit(2); /* FIXME: same exit code as failure for dbif_open() */
     LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
 
@@ -354,11 +351,18 @@ int main(int argc, char *argv[])
     }
     LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
 
-    if (dbd_stamp(dbd) < 0) {
-        dbif_close(dbd);
-        exit(5);
+    /* Downgrade db lock  */
+    if (db_locked == LOCK_EXCL) {
+        if (get_lock(LOCK_UNLOCK, NULL) != 0) {
+            dbif_close(dbd);
+            exit(2);
+        }
+        if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD) {
+            dbif_close(dbd);
+            exit(2);
+        }
     }
-    LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
+
 
     if (comm_init(dbp, ctrlfd, clntfd) < 0) {
         dbif_close(dbd);
@@ -371,7 +375,8 @@ int main(int argc, char *argv[])
     if (dbif_close(dbd) < 0)
         err++;
 
-    free_lock(lockfd);
+    if (dbif_env_remove(dbpath) < 0)
+        err++;
 
     if (err)
         exit(4);