]> arthur.barton.de Git - netatalk.git/blobdiff - etc/cnid_dbd/main.c
Convert ad file suite and CNID daemons to use new config code
[netatalk.git] / etc / cnid_dbd / main.c
index 5b7c8a7d9866dbd4c487e054e70da77269ea5e23..b8906f902bdcc3579a382b2502bc966c8c027ad7 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: main.c,v 1.12 2009-10-18 17:50:13 didg 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/errchk.h>
+#include <atalk/bstrlib.h>
+#include <atalk/netatalk_conf.h>
 
 #include "db_param.h"
 #include "dbif.h"
 #include "dbd.h"
 #include "comm.h"
-
-#define LOCKFILENAME  "lock"
+#include "pack.h"
 
 /* 
    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;
-
 static int exit_sig = 0;
+static int db_locked;
 
 static void sig_exit(int signo)
 {
@@ -77,7 +69,8 @@ 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
@@ -123,19 +116,21 @@ static int loop(struct db_param *dbp)
         else
             timeout = 1;
 
-        if ((cret = comm_rcv(&rqst, timeout, &set)) < 0)
+        if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
             return -1;
 
-        now = time(NULL);
-
         if (cret == 0) {
             /* comm_rcv returned from select without receiving anything. */
-            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;
@@ -150,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);
@@ -159,7 +154,7 @@ 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);
@@ -173,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;
@@ -201,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;
@@ -249,33 +247,6 @@ static void switch_to_user(char *dir)
     }
 }
 
-/* ------------------------ */
-static 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;
-}
 
 /* ----------------------- */
 static void set_signal(void)
@@ -299,99 +270,152 @@ static void set_signal(void)
     }
 }
 
-/* ----------------------- */
-static 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[])
 {
+    EC_INIT;
     struct db_param *dbp;
-    int err = 0;
-    int lockfd, ctrlfd, clntfd;
-    char *dir, *logconfig;
+    int delete_bdb = 0;
+    int ctrlfd = -1, clntfd = -1;
+    char *logconfig;
+    AFPObj obj = { 0 };
+    struct vol *vol;
+    char *volpath = NULL;
+    bstring dbpath;
+
+    while (( ret = getopt( argc, argv, "dF:l:p:t:v")) != -1 ) {
+        switch (ret) {
+        case 'd':
+            delete_bdb = 1;
+            break;
+        case 'F':
+            obj.cmdlineconfigfile = strdup(optarg);
+            break;
+        case 'p':
+            volpath = strdup(optarg);
+            break;
+        case 'l':
+            clntfd = atoi(optarg);
+            break;
+        case 't':
+            ctrlfd = atoi(optarg);
+            break;
+        case 'v':
+            printf("cnid_dbd (Netatalk %s)\n", VERSION);
+            return -1;
+        }
+    }
+
+    if (ctrlfd == -1 || clntfd == -1 || !volpath) {
+        LOG(log_error, logtype_cnid, "main: bad IPC fds");
+        exit(EXIT_FAILURE);
+    }
+
+    EC_ZERO( afp_config_parse(&obj) );
 
     set_processname("cnid_dbd");
+    setuplog(obj.options.logconfig, obj.options.logfile);
 
-    /* FIXME: implement -d from cnid_metad */
-    if (argc  != 5) {
-        LOG(log_error, logtype_cnid, "main: not enough arguments");
-        exit(1);
-    }
+    EC_ZERO( load_volumes(&obj, NULL) );
+    EC_NULL( vol = getvolbypath(volpath) );
 
-    dir = argv[1];
-    ctrlfd = atoi(argv[2]);
-    clntfd = atoi(argv[3]);
-    logconfig = strdup(argv[4]);
-    setuplog(logconfig);
+    pack_setvol(vol);
 
-    switch_to_user(dir);
+    EC_NULL( dbpath = bfromcstr(vol->v_dbpath) );
+    EC_ZERO( bcatcstr(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();
+    LOG(log_debug, logtype_cnid, "db dir: \"%s\"", bdata(dbpath));
+
+    switch_to_user(bdata(dbpath));
+
+    /* Get db lock */
+    if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) == -1) {
+        LOG(log_error, logtype_cnid, "main: fatal db lock error");
+        EC_FAIL;
+    }
+    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");
+            EC_FAIL;
+        }
+    }
 
-    LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
+    if (delete_bdb && (db_locked == LOCK_EXCL)) {
+        LOG(log_warning, logtype_cnid, "main: too many CNID db opening attempts, wiping the slate clean");
+        chdir(bdata(dbpath));
+        system("rm -f cnid2.db lock log.* __db.*");
+        if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
+            LOG(log_error, logtype_cnid, "main: fatal db lock error");
+            EC_FAIL;
+        }
+    }
 
     set_signal();
 
     /* SIGINT and SIGTERM are always off, unless we are in pselect */
     block_sigs_onoff(1);
 
-    if ((dbp = db_param_read(dir, CNID_DBD)) == NULL)
-        exit(1);
+    if ((dbp = db_param_read(bdata(dbpath))) == NULL)
+        EC_FAIL;
     LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
 
-    if (NULL == (dbd = dbif_init(".", "cnid2.db")))
-        exit(2);
+    if (NULL == (dbd = dbif_init(bdata(dbpath), "cnid2.db")))
+        EC_FAIL;
 
-    if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
-        exit(2); /* FIXME: same exit code as failure for dbif_open() */
+    /* Only recover if we got the lock */
+    if (dbif_env_open(dbd,
+                      dbp,
+                      (db_locked == LOCK_EXCL) ? DBOPTIONS | DB_RECOVER : DBOPTIONS) < 0)
+        EC_FAIL;
     LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
 
     if (dbif_open(dbd, dbp, 0) < 0) {
-        dbif_close(dbd);
-        exit(2);
+        ret = -1;
+        goto close_db;
     }
+
     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) {
+            ret = -1;
+            goto close_db;
+        }
+
+        if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD) {
+            ret = -1;
+            goto close_db;
+        }
     }
-    LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
+
 
     if (comm_init(dbp, ctrlfd, clntfd) < 0) {
-        dbif_close(dbd);
-        exit(3);
+        ret = -1;
+        goto close_db;
     }
 
-    if (loop(dbp) < 0)
-        err++;
+    if (loop(dbp) < 0) {
+        ret = -1;
+        goto close_db;
+    }
 
+close_db:
     if (dbif_close(dbd) < 0)
-        err++;
+        ret = -1;
 
-    if (dbif_prep_upgrade(dir) < 0)
-        err++;
+    if (dbif_env_remove(bdata(dbpath)) < 0)
+        ret = -1;
 
-    free_lock(lockfd);
+EC_CLEANUP:
+    if (ret != 0)
+        exit(1);
 
-    if (err)
-        exit(4);
-    else if (exit_sig)
+    if (exit_sig)
         LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
     else
         LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");
 
-    return 0;
+    EC_EXIT;
 }