X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=etc%2Fcnid_dbd%2Fmain.c;h=e050a0e5137c4c1010f98ffc30865edffd4d384a;hb=2f3b0704e93342aaa09d2ec79f936d7c0ebbb828;hp=41e23bf60fea6f4b42e15c10b6fd91939f55e990;hpb=ecfc96169ab669b578e53fa8e13592934fe37788;p=netatalk.git diff --git a/etc/cnid_dbd/main.c b/etc/cnid_dbd/main.c index 41e23bf6..e050a0e5 100644 --- a/etc/cnid_dbd/main.c +++ b/etc/cnid_dbd/main.c @@ -1,7 +1,6 @@ /* - * $Id: main.c,v 1.2 2005-04-28 20:49:48 bfernhomberg Exp $ - * * Copyright (C) Joerg Lenneis 2003 + * Copyright (c) Frank Lahm 2009 * All Rights Reserved. See COPYING. */ @@ -33,17 +32,25 @@ #include #include #include +#include #include "db_param.h" #include "dbif.h" #include "dbd.h" #include "comm.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) -#define LOCKFILENAME "lock" +/* 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) { @@ -54,7 +61,7 @@ static void sig_exit(int signo) static void block_sigs_onoff(int block) { sigset_t set; - + sigemptyset(&set); sigaddset(&set, SIGINT); sigaddset(&set, SIGTERM); @@ -65,131 +72,164 @@ static void block_sigs_onoff(int block) return; } -/* - The dbd_XXX and comm_XXX functions all obey the same protocol for return values: - - 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 - (which might fail as well) and then exit. +/* + The dbd_XXX and comm_XXX functions all obey the same protocol for return values: + + 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 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) + timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout); + if (timeout > now) + timeout -= now; + else + timeout = 1; + + if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0) return -1; - now = time(NULL); - - 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; + 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; -#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; + 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); } - /* 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; - } - 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) */ } /* ------------------------ */ @@ -201,7 +241,7 @@ static void switch_to_user(char *dir) LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno)); exit(1); } - + if (stat(".", &st) < 0) { LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno)); exit(1); @@ -215,36 +255,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; @@ -265,129 +278,141 @@ 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 ret; - int lockfd, ctrlfd, clntfd; - char *dir; - + int err = 0, ret, delete_bdb = 0; + int ctrlfd, clntfd; + char *logconfig; + set_processname("cnid_dbd"); - syslog_setup(log_debug, logtype_default, logoption_ndelay | logoption_pid, logfacility_daemon); - - if (argc != 4) { + + while (( ret = getopt( argc, argv, "vVd")) != -1 ) { + switch (ret) { + case 'v': + case 'V': + printf("cnid_dbd (Netatalk %s)\n", VERSION); + return -1; + case 'd': + delete_bdb = 1; + break; + } + } + + if (argc - optind != 4) { LOG(log_error, logtype_cnid, "main: not enough arguments"); + exit(EXIT_FAILURE); + } + + /* Load .volinfo file */ + if (loadvolinfo(argv[optind], &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"); + + ctrlfd = atoi(argv[optind + 1]); + clntfd = atoi(argv[optind + 2]); + logconfig = strdup(argv[optind + 3]); + setuplog(logconfig); + + 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); + + 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); + } + } + + if (delete_bdb && (db_locked == LOCK_EXCL)) { + LOG(log_warning, logtype_cnid, "main: too many CNID db opening attempts, wiping the slate clean"); + chdir(dbpath); + system("rm -f cnid2.db lock log.* __db.*"); + if ((db_locked = get_lock(LOCK_EXCL, dbpath)) != LOCK_EXCL) { + LOG(log_error, logtype_cnid, "main: fatal db lock error"); + exit(EXIT_FAILURE); + } + } - dir = argv[1]; - ctrlfd = atoi(argv[2]); - clntfd = atoi(argv[3]); - - 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(); - - 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)) == NULL) + if ((dbp = db_param_read(dbpath)) == 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() */ - -#ifdef CNID_BACKEND_DBD_TXN - if (dbif_txn_begin() < 0) - exit(6); -#endif - - if (dbif_open(dbp, 0) < 0) { -#ifdef CNID_BACKEND_DBD_TXN - dbif_txn_abort(); -#endif - dbif_close(); + if (NULL == (dbd = dbif_init(dbpath, "cnid2.db"))) + exit(2); + + /* 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"); + + 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(); + /* Downgrade db lock */ + if (db_locked == LOCK_EXCL) { + if (get_lock(LOCK_UNLOCK, NULL) != 0) { + dbif_close(dbd); 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(); + if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD) { + dbif_close(dbd); 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(); - exit(5); - } -#ifdef CNID_BACKEND_DBD_TXN - if (dbif_txn_commit() < 0) - exit(6); -#endif 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_env_remove(dbpath) < 0) err++; - - free_lock(lockfd); - + if (err) exit(4); else if (exit_sig) - LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig); + LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig); else - LOG(log_info, logtype_cnid, "main: Idle timeout, exiting"); - + LOG(log_info, logtype_cnid, "main: Idle timeout, exiting"); + return 0; }