X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=etc%2Fcnid_dbd%2Fmain.c;h=0067504a0c6b3c4fd081e7eccbf1550272498a2b;hb=2f88f394f83e819415a7a505b80ba7b57615649a;hp=da4cd9a4e7085d764590140786580a3e82a3bb9d;hpb=bee44bcbf6b614d7e737602f27a41d041d8efee7;p=netatalk.git diff --git a/etc/cnid_dbd/main.c b/etc/cnid_dbd/main.c index da4cd9a4..0067504a 100644 --- a/etc/cnid_dbd/main.c +++ b/etc/cnid_dbd/main.c @@ -8,50 +8,46 @@ #include "config.h" #endif /* HAVE_CONFIG_H */ -#ifdef HAVE_UNISTD_H #include -#endif /* HAVE_UNISTD_H */ -#ifdef HAVE_FCNTL_H #include -#endif /* HAVE_FCNTL_H */ #include #include #include #include #include -#ifdef HAVE_SYS_TYPES_H #include -#endif /* HAVE_SYS_TYPES_H */ #include -#ifdef HAVE_SYS_STAT_H #include -#endif /* HAVE_SYS_STAT_H */ #include #include +#include -#include #include #include -#include +#include +#include +#include +#include +#include #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) - -/* Global, needed by pack.c:idxname() */ -struct volinfo volinfo; +#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 bstring dbpath; +static struct db_param *dbp; +static struct vol *vol; static void sig_exit(int signo) { @@ -86,9 +82,205 @@ static void block_sigs_onoff(int block) of the cnid_dbd_rply structure contains further details. */ -#ifndef min -#define min(a,b) ((a)<(b)?(a):(b)) -#endif + +/*! + * Get lock on db lock file + * + * @args cmd (r) lock command: + * LOCK_FREE: close lockfd + * LOCK_UNLOCK: unlock lockm keep lockfd open + * LOCK_EXCL: F_WRLCK on lockfd + * LOCK_SHRD: F_RDLCK on lockfd + * @args dbpath (r) path to lockfile, only used on first call, + * later the stored fd is used + * @returns LOCK_FREE/LOCK_UNLOCK return 0 on success, -1 on error + * LOCK_EXCL/LOCK_SHRD return LOCK_EXCL or LOCK_SHRD respectively on + * success, 0 if the lock couldn't be acquired, -1 on other errors + */ +static int get_lock(int cmd, const char *dbpath) +{ + static int lockfd = -1; + int ret; + char lockpath[PATH_MAX]; + struct stat st; + + LOG(log_debug, logtype_cnid, "get_lock(%s, \"%s\")", + cmd == LOCK_EXCL ? "LOCK_EXCL" : + cmd == LOCK_SHRD ? "LOCK_SHRD" : + cmd == LOCK_FREE ? "LOCK_FREE" : + cmd == LOCK_UNLOCK ? "LOCK_UNLOCK" : "UNKNOWN", + dbpath ? dbpath : ""); + + switch (cmd) { + case LOCK_FREE: + if (lockfd == -1) + return -1; + close(lockfd); + lockfd = -1; + return 0; + + case LOCK_UNLOCK: + if (lockfd == -1) + return -1; + return unlock(lockfd, 0, SEEK_SET, 0); + + case LOCK_EXCL: + case LOCK_SHRD: + if (lockfd == -1) { + if ( (strlen(dbpath) + strlen(LOCKFILENAME+1)) > (PATH_MAX - 1) ) { + LOG(log_error, logtype_cnid, ".AppleDB pathname too long"); + return -1; + } + strncpy(lockpath, dbpath, PATH_MAX - 1); + strcat(lockpath, "/"); + strcat(lockpath, LOCKFILENAME); + + if ((lockfd = open(lockpath, O_RDWR | O_CREAT, 0644)) < 0) { + LOG(log_error, logtype_cnid, "Error opening lockfile: %s", strerror(errno)); + return -1; + } + + if ((stat(dbpath, &st)) != 0) { + LOG(log_error, logtype_cnid, "Error statting lockfile: %s", strerror(errno)); + return -1; + } + + if ((chown(lockpath, st.st_uid, st.st_gid)) != 0) { + LOG(log_error, logtype_cnid, "Error inheriting lockfile permissions: %s", + strerror(errno)); + return -1; + } + } + + if (cmd == LOCK_EXCL) + ret = write_lock(lockfd, 0, SEEK_SET, 0); + else + ret = read_lock(lockfd, 0, SEEK_SET, 0); + + if (ret != 0) { + if (cmd == LOCK_SHRD) + LOG(log_error, logtype_cnid, "Volume CNID db is locked, try again..."); + return 0; + } + + LOG(log_debug, logtype_cnid, "get_lock: got %s lock", + cmd == LOCK_EXCL ? "LOCK_EXCL" : "LOCK_SHRD"); + return cmd; + + default: + return -1; + } /* switch(cmd) */ + + /* deadc0de, never get here */ + return -1; +} + +static int open_db(void) +{ + EC_INIT; + + /* Get db lock */ + if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) { + LOG(log_error, logtype_cnid, "main: fatal db lock error"); + EC_FAIL; + } + + if (NULL == (dbd = dbif_init(bdata(dbpath), "cnid2.db"))) + EC_FAIL; + + /* Only recover if we got the lock */ + if (dbif_env_open(dbd, dbp, DBOPTIONS | DB_RECOVER) < 0) + EC_FAIL; + + LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment"); + + if (dbif_open(dbd, dbp, 0) < 0) + EC_FAIL; + + LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases"); + +EC_CLEANUP: + if (ret != 0) { + if (dbd) { + (void)dbif_close(dbd); + dbd = NULL; + } + } + + EC_EXIT; +} + +static int delete_db(void) +{ + EC_INIT; + int cwd = -1; + + EC_ZERO( get_lock(LOCK_FREE, bdata(dbpath)) ); + EC_NEG1( cwd = open(".", O_RDONLY) ); + chdir(cfrombstr(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; + } + + LOG(log_warning, logtype_cnid, "Recreated CNID BerkeleyDB databases of volume \"%s\"", vol->v_localname); + +EC_CLEANUP: + if (cwd != -1) { + fchdir(cwd); + close(cwd); + } + EC_EXIT; +} + + +/** + * Close dbd if open, delete it, reopen + * + * Also tries to copy the rootinfo key, that would allow for keeping the db stamp + * and last used CNID + **/ +static int reinit_db(void) +{ + EC_INIT; + DBT key, data; + bool copyRootInfo = false; + + if (dbd) { + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + key.data = ROOTINFO_KEY; + key.size = ROOTINFO_KEYLEN; + + if (dbif_get(dbd, DBIF_CNID, &key, &data, 0) <= 0) { + LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record"); + copyRootInfo = false; + } else { + copyRootInfo = true; + } + (void)dbif_close(dbd); + } + + EC_ZERO_LOG( delete_db() ); + EC_ZERO_LOG( open_db() ); + + if (copyRootInfo == true) { + memset(&key, 0, sizeof(key)); + key.data = ROOTINFO_KEY; + key.size = ROOTINFO_KEYLEN; + + if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) != 0) { + LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key"); + EC_FAIL; + } + } + +EC_CLEANUP: + EC_EXIT; +} static int loop(struct db_param *dbp) { @@ -119,7 +311,7 @@ static int loop(struct db_param *dbp) dbp->flush_interval, timebuf); while (1) { - timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout); + timeout = MIN(time_next_flush, time_last_rqst + dbp->idle_timeout); if (timeout > now) timeout -= now; else @@ -154,7 +346,7 @@ static int loop(struct db_param *dbp) ret = 1; break; case CNID_DBD_OP_ADD: - ret = dbd_add(dbd, &rqst, &rply, 0); + ret = dbd_add(dbd, &rqst, &rply); break; case CNID_DBD_OP_GET: ret = dbd_get(dbd, &rqst, &rply); @@ -163,7 +355,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, 0); + ret = dbd_lookup(dbd, &rqst, &rply); break; case CNID_DBD_OP_UPDATE: ret = dbd_update(dbd, &rqst, &rply); @@ -180,6 +372,9 @@ static int loop(struct db_param *dbp) case CNID_DBD_OP_SEARCH: ret = dbd_search(dbd, &rqst, &rply); break; + case CNID_DBD_OP_WIPE: + ret = reinit_db(); + break; default: LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op); ret = -1; @@ -248,7 +443,7 @@ static void switch_to_user(char *dir) exit(1); } if (!getuid()) { - LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid); + LOG(log_debug, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid); if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) { LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno)); exit(1); @@ -256,34 +451,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) { - LOG(log_error, logtype_cnid, "get_lock: locked"); - 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) @@ -307,110 +474,98 @@ 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[]) { - struct db_param *dbp; - int err = 0; - int lockfd, ctrlfd, clntfd; - char *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); + EC_INIT; + int delete_bdb = 0; + int ctrlfd = -1, clntfd = -1; + AFPObj obj = { 0 }; + char *volpath = NULL; + + while (( ret = getopt( argc, argv, "dF:l:p:t:vV")) != -1 ) { + switch (ret) { + case 'd': + /* this is now just ignored, as we do it automatically anyway */ + 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': + case 'V': + printf("cnid_dbd (Netatalk %s)\n", VERSION); + return -1; + } } - ctrlfd = atoi(argv[2]); - clntfd = atoi(argv[3]); - logconfig = strdup(argv[4]); - setuplog(logconfig); - - /* Load .volinfo file */ - if (loadvolinfo(argv[1], &volinfo) == -1) { - LOG(log_error, logtype_cnid, "Cant load volinfo for \"%s\"", argv[1]); + if (ctrlfd == -1 || clntfd == -1 || !volpath) { + LOG(log_error, logtype_cnid, "main: bad IPC fds"); 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"); - 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); + EC_ZERO( afp_config_parse(&obj, "cnid_dbd") ); - switch_to_user(dbpath); + EC_ZERO( load_volumes(&obj) ); + EC_NULL( vol = getvolbypath(&obj, volpath) ); + EC_ZERO( load_charset(vol) ); + pack_setvol(vol); - /* Before we do anything else, check if there is an instance of cnid_dbd - running already and silently exit if yes. */ - lockfd = get_lock(); + EC_NULL( dbpath = bfromcstr(vol->v_dbpath) ); + EC_ZERO( bcatcstr(dbpath, "/.AppleDB") ); + + LOG(log_debug, logtype_cnid, "db dir: \"%s\"", bdata(dbpath)); + + switch_to_user(bdata(dbpath)); set_signal(); /* SIGINT and SIGTERM are always off, unless we are in pselect */ block_sigs_onoff(1); - if ((dbp = db_param_read(dbpath)) == 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(dbpath, "cnid2.db"))) - exit(2); - - 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(dbd, dbp, 0) < 0) { - dbif_close(dbd); - exit(2); + if (open_db() != 0) { + LOG(log_error, logtype_cnid, "Failed to open CNID database for volume \"%s\"", vol->v_localname); + EC_ZERO_LOG( reinit_db() ); } - LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases"); 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_env_remove(dbpath) < 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; }