/*
- * $Id: main.c,v 1.9 2009-07-12 09:21:34 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
#include <netatalk/endian.h>
#include <atalk/cnid_dbd_private.h>
#include <atalk/logger.h>
+#include <atalk/util.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;
-
static int exit_sig = 0;
+static int db_locked;
static void sig_exit(int signo)
{
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);
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;
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);
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);
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;
}
}
-/* ------------------------ */
-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;
}
}
-/* ----------------------- */
-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;
+ int ctrlfd, clntfd;
char *dir, *logconfig;
set_processname("cnid_dbd");
switch_to_user(dir);
- /* Before we do anything else, check if there is an instance of cnid_dbd
- running already and silently exit if yes. */
- lockfd = get_lock();
+ /* Get db lock */
+ if ((db_locked = get_lock(LOCK_EXCL, dir)) == -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);
+ }
+ }
LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
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 (NULL == (dbd = dbif_init(".", "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");
}
LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
+ /* 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);
+ }
+ }
+
+
if (dbd_stamp(dbd) < 0) {
dbif_close(dbd);
exit(5);
if (dbif_close(dbd) < 0)
err++;
- free_lock(lockfd);
+ if (dbif_prep_upgrade(dir) < 0)
+ err++;
if (err)
exit(4);