2 * Copyright (C) Joerg Lenneis 2003
3 * Copyright (c) Frank Lahm 2009
4 * All Rights Reserved. See COPYING.
9 #endif /* HAVE_CONFIG_H */
18 #include <sys/types.h>
19 #include <sys/param.h>
23 #include <arpa/inet.h>
25 #include <atalk/cnid_dbd_private.h>
26 #include <atalk/logger.h>
27 #include <atalk/errchk.h>
28 #include <atalk/bstrlib.h>
29 #include <atalk/bstradd.h>
30 #include <atalk/netatalk_conf.h>
31 #include <atalk/util.h>
40 Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
41 It's a likey performance hit, but it might we worth it.
43 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN)
46 static int exit_sig = 0;
48 static bstring dbpath;
49 static struct db_param *dbp;
50 static struct vol *vol;
52 static void sig_exit(int signo)
58 static void block_sigs_onoff(int block)
63 sigaddset(&set, SIGINT);
64 sigaddset(&set, SIGTERM);
66 sigprocmask(SIG_BLOCK, &set, NULL);
68 sigprocmask(SIG_UNBLOCK, &set, NULL);
73 The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
75 1: Success, if transactions are used commit.
76 0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
77 -1: Fatal error, either from t
78 he database or from the socket. Abort the transaction if applicable
79 (which might fail as well) and then exit.
81 We always try to notify the client process about the outcome, the result field
82 of the cnid_dbd_rply structure contains further details.
87 * Get lock on db lock file
89 * @args cmd (r) lock command:
90 * LOCK_FREE: close lockfd
91 * LOCK_UNLOCK: unlock lockm keep lockfd open
92 * LOCK_EXCL: F_WRLCK on lockfd
93 * LOCK_SHRD: F_RDLCK on lockfd
94 * @args dbpath (r) path to lockfile, only used on first call,
95 * later the stored fd is used
96 * @returns LOCK_FREE/LOCK_UNLOCK return 0 on success, -1 on error
97 * LOCK_EXCL/LOCK_SHRD return LOCK_EXCL or LOCK_SHRD respectively on
98 * success, 0 if the lock couldn't be acquired, -1 on other errors
100 static int get_lock(int cmd, const char *dbpath)
102 static int lockfd = -1;
104 char lockpath[PATH_MAX];
107 LOG(log_debug, logtype_cnid, "get_lock(%s, \"%s\")",
108 cmd == LOCK_EXCL ? "LOCK_EXCL" :
109 cmd == LOCK_SHRD ? "LOCK_SHRD" :
110 cmd == LOCK_FREE ? "LOCK_FREE" :
111 cmd == LOCK_UNLOCK ? "LOCK_UNLOCK" : "UNKNOWN",
112 dbpath ? dbpath : "");
125 return unlock(lockfd, 0, SEEK_SET, 0);
130 if ( (strlen(dbpath) + strlen(LOCKFILENAME+1)) > (PATH_MAX - 1) ) {
131 LOG(log_error, logtype_cnid, ".AppleDB pathname too long");
134 strncpy(lockpath, dbpath, PATH_MAX - 1);
135 strcat(lockpath, "/");
136 strcat(lockpath, LOCKFILENAME);
138 if ((lockfd = open(lockpath, O_RDWR | O_CREAT, 0644)) < 0) {
139 LOG(log_error, logtype_cnid, "Error opening lockfile: %s", strerror(errno));
143 if ((stat(dbpath, &st)) != 0) {
144 LOG(log_error, logtype_cnid, "Error statting lockfile: %s", strerror(errno));
148 if ((chown(lockpath, st.st_uid, st.st_gid)) != 0) {
149 LOG(log_error, logtype_cnid, "Error inheriting lockfile permissions: %s",
155 if (cmd == LOCK_EXCL)
156 ret = write_lock(lockfd, 0, SEEK_SET, 0);
158 ret = read_lock(lockfd, 0, SEEK_SET, 0);
161 if (cmd == LOCK_SHRD)
162 LOG(log_error, logtype_cnid, "Volume CNID db is locked, try again...");
166 LOG(log_debug, logtype_cnid, "get_lock: got %s lock",
167 cmd == LOCK_EXCL ? "LOCK_EXCL" : "LOCK_SHRD");
174 /* deadc0de, never get here */
178 static int open_db(void)
183 if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
184 LOG(log_error, logtype_cnid, "main: fatal db lock error");
188 if (NULL == (dbd = dbif_init(bdata(dbpath), "cnid2.db")))
191 /* Only recover if we got the lock */
192 if (dbif_env_open(dbd, dbp, DBOPTIONS | DB_RECOVER) < 0)
195 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
197 if (dbif_open(dbd, dbp, 0) < 0)
200 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
205 (void)dbif_close(dbd);
213 static int delete_db(void)
218 EC_ZERO( get_lock(LOCK_FREE, bdata(dbpath)) );
219 EC_NEG1( cwd = open(".", O_RDONLY) );
220 chdir(cfrombstr(dbpath));
221 system("rm -f cnid2.db lock log.* __db.*");
223 if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
224 LOG(log_error, logtype_cnid, "main: fatal db lock error");
228 LOG(log_warning, logtype_cnid, "Recreated CNID BerkeleyDB databases of volume \"%s\"", vol->v_localname);
240 * Close dbd if open, delete it, reopen
242 * Also tries to copy the rootinfo key, that would allow for keeping the db stamp
245 static int reinit_db(void)
249 bool copyRootInfo = false;
252 memset(&key, 0, sizeof(key));
253 memset(&data, 0, sizeof(data));
255 key.data = ROOTINFO_KEY;
256 key.size = ROOTINFO_KEYLEN;
258 if (dbif_get(dbd, DBIF_CNID, &key, &data, 0) <= 0) {
259 LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
260 copyRootInfo = false;
264 (void)dbif_close(dbd);
267 EC_ZERO_LOG( delete_db() );
268 EC_ZERO_LOG( open_db() );
270 if (copyRootInfo == true) {
271 memset(&key, 0, sizeof(key));
272 key.data = ROOTINFO_KEY;
273 key.size = ROOTINFO_KEYLEN;
275 if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) != 0) {
276 LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
285 static int loop(struct db_param *dbp)
287 struct cnid_dbd_rqst rqst;
288 struct cnid_dbd_rply rply;
292 time_t now, time_next_flush, time_last_rqst;
294 static char namebuf[MAXPATHLEN + 1];
298 sigprocmask(SIG_SETMASK, NULL, &set);
299 sigdelset(&set, SIGINT);
300 sigdelset(&set, SIGTERM);
304 time_next_flush = now + dbp->flush_interval;
305 time_last_rqst = now;
309 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
310 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
311 dbp->flush_interval, timebuf);
314 timeout = MIN(time_next_flush, time_last_rqst + dbp->idle_timeout);
320 if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
324 /* comm_rcv returned from select without receiving anything. */
326 /* Received signal (TERM|INT) */
329 if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
333 /* still active connections, reset time_last_rqst */
334 time_last_rqst = now;
336 /* We got a request */
337 time_last_rqst = now;
339 memset(&rply, 0, sizeof(rply));
341 /* ret gets set here */
342 case CNID_DBD_OP_OPEN:
343 case CNID_DBD_OP_CLOSE:
344 /* open/close are noops for now. */
348 case CNID_DBD_OP_ADD:
349 ret = dbd_add(dbd, &rqst, &rply);
351 case CNID_DBD_OP_GET:
352 ret = dbd_get(dbd, &rqst, &rply);
354 case CNID_DBD_OP_RESOLVE:
355 ret = dbd_resolve(dbd, &rqst, &rply);
357 case CNID_DBD_OP_LOOKUP:
358 ret = dbd_lookup(dbd, &rqst, &rply);
360 case CNID_DBD_OP_UPDATE:
361 ret = dbd_update(dbd, &rqst, &rply);
363 case CNID_DBD_OP_DELETE:
364 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
366 case CNID_DBD_OP_GETSTAMP:
367 ret = dbd_getstamp(dbd, &rqst, &rply);
369 case CNID_DBD_OP_REBUILD_ADD:
370 ret = dbd_rebuild_add(dbd, &rqst, &rply);
372 case CNID_DBD_OP_SEARCH:
373 ret = dbd_search(dbd, &rqst, &rply);
375 case CNID_DBD_OP_WIPE:
379 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
384 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
389 if (ret == 0 || cret == 0) {
390 if (dbif_txn_abort(dbd) < 0)
393 ret = dbif_txn_commit(dbd);
397 /* We had a designated txn because we wrote to the db */
400 } /* got a request */
403 Shall we checkpoint bdb ?
404 "flush_interval" seconds passed ?
406 if (now >= time_next_flush) {
407 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
408 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
411 time_next_flush = now + dbp->flush_interval;
413 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
414 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
415 dbp->flush_interval, timebuf);
419 Shall we checkpoint bdb ?
420 Have we commited "count" more changes than "flush_frequency" ?
422 if (count > dbp->flush_frequency) {
423 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
424 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
431 /* ------------------------ */
432 static void switch_to_user(char *dir)
436 if (chdir(dir) < 0) {
437 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
441 if (stat(".", &st) < 0) {
442 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
446 LOG(log_debug, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
447 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
448 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
455 /* ----------------------- */
456 static void set_signal(void)
460 sv.sa_handler = sig_exit;
462 sigemptyset(&sv.sa_mask);
463 sigaddset(&sv.sa_mask, SIGINT);
464 sigaddset(&sv.sa_mask, SIGTERM);
465 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
466 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
469 sv.sa_handler = SIG_IGN;
470 sigemptyset(&sv.sa_mask);
471 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
472 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
477 /* ------------------------ */
478 int main(int argc, char *argv[])
482 int ctrlfd = -1, clntfd = -1;
484 char *volpath = NULL;
486 while (( ret = getopt( argc, argv, "dF:l:p:t:vV")) != -1 ) {
489 /* this is now just ignored, as we do it automatically anyway */
493 obj.cmdlineconfigfile = strdup(optarg);
496 volpath = strdup(optarg);
499 clntfd = atoi(optarg);
502 ctrlfd = atoi(optarg);
506 printf("cnid_dbd (Netatalk %s)\n", VERSION);
511 if (ctrlfd == -1 || clntfd == -1 || !volpath) {
512 LOG(log_error, logtype_cnid, "main: bad IPC fds");
516 EC_ZERO( afp_config_parse(&obj, "cnid_dbd") );
518 EC_ZERO( load_volumes(&obj) );
519 EC_NULL( vol = getvolbypath(&obj, volpath) );
520 EC_ZERO( load_charset(vol) );
523 EC_NULL( dbpath = bfromcstr(vol->v_dbpath) );
524 EC_ZERO( bcatcstr(dbpath, "/.AppleDB") );
526 LOG(log_debug, logtype_cnid, "db dir: \"%s\"", bdata(dbpath));
528 switch_to_user(bdata(dbpath));
532 /* SIGINT and SIGTERM are always off, unless we are in pselect */
535 if ((dbp = db_param_read(bdata(dbpath))) == NULL)
537 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
539 if (open_db() != 0) {
540 LOG(log_error, logtype_cnid, "Failed to open CNID database for volume \"%s\"", vol->v_localname);
541 EC_ZERO_LOG( reinit_db() );
544 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
555 if (dbif_close(dbd) < 0)
558 if (dbif_env_remove(bdata(dbpath)) < 0)
566 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
568 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");