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/netatalk_conf.h>
30 #include <atalk/util.h>
39 Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
40 It's a likey performance hit, but it might we worth it.
42 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN)
45 static int exit_sig = 0;
47 static bstring dbpath;
48 static struct db_param *dbp;
49 static struct vol *vol;
51 static void sig_exit(int signo)
57 static void block_sigs_onoff(int block)
62 sigaddset(&set, SIGINT);
63 sigaddset(&set, SIGTERM);
65 sigprocmask(SIG_BLOCK, &set, NULL);
67 sigprocmask(SIG_UNBLOCK, &set, NULL);
72 The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
74 1: Success, if transactions are used commit.
75 0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
76 -1: Fatal error, either from t
77 he database or from the socket. Abort the transaction if applicable
78 (which might fail as well) and then exit.
80 We always try to notify the client process about the outcome, the result field
81 of the cnid_dbd_rply structure contains further details.
86 * Get lock on db lock file
88 * @args cmd (r) lock command:
89 * LOCK_FREE: close lockfd
90 * LOCK_UNLOCK: unlock lockm keep lockfd open
91 * LOCK_EXCL: F_WRLCK on lockfd
92 * LOCK_SHRD: F_RDLCK on lockfd
93 * @args dbpath (r) path to lockfile, only used on first call,
94 * later the stored fd is used
95 * @returns LOCK_FREE/LOCK_UNLOCK return 0 on success, -1 on error
96 * LOCK_EXCL/LOCK_SHRD return LOCK_EXCL or LOCK_SHRD respectively on
97 * success, 0 if the lock couldn't be acquired, -1 on other errors
99 static int get_lock(int cmd, const char *dbpath)
101 static int lockfd = -1;
103 char lockpath[PATH_MAX];
106 LOG(log_debug, logtype_cnid, "get_lock(%s, \"%s\")",
107 cmd == LOCK_EXCL ? "LOCK_EXCL" :
108 cmd == LOCK_SHRD ? "LOCK_SHRD" :
109 cmd == LOCK_FREE ? "LOCK_FREE" :
110 cmd == LOCK_UNLOCK ? "LOCK_UNLOCK" : "UNKNOWN",
111 dbpath ? dbpath : "");
124 return unlock(lockfd, 0, SEEK_SET, 0);
129 if ( (strlen(dbpath) + strlen(LOCKFILENAME+1)) > (PATH_MAX - 1) ) {
130 LOG(log_error, logtype_cnid, ".AppleDB pathname too long");
133 strncpy(lockpath, dbpath, PATH_MAX - 1);
134 strcat(lockpath, "/");
135 strcat(lockpath, LOCKFILENAME);
137 if ((lockfd = open(lockpath, O_RDWR | O_CREAT, 0644)) < 0) {
138 LOG(log_error, logtype_cnid, "Error opening lockfile: %s", strerror(errno));
142 if ((stat(dbpath, &st)) != 0) {
143 LOG(log_error, logtype_cnid, "Error statting lockfile: %s", strerror(errno));
147 if ((chown(lockpath, st.st_uid, st.st_gid)) != 0) {
148 LOG(log_error, logtype_cnid, "Error inheriting lockfile permissions: %s",
154 if (cmd == LOCK_EXCL)
155 ret = write_lock(lockfd, 0, SEEK_SET, 0);
157 ret = read_lock(lockfd, 0, SEEK_SET, 0);
160 if (cmd == LOCK_SHRD)
161 LOG(log_error, logtype_cnid, "Volume CNID db is locked, try again...");
165 LOG(log_debug, logtype_cnid, "get_lock: got %s lock",
166 cmd == LOCK_EXCL ? "LOCK_EXCL" : "LOCK_SHRD");
173 /* deadc0de, never get here */
177 static int open_db(void)
182 if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
183 LOG(log_error, logtype_cnid, "main: fatal db lock error");
187 if (NULL == (dbd = dbif_init(bdata(dbpath), "cnid2.db")))
190 /* Only recover if we got the lock */
191 if (dbif_env_open(dbd, dbp, DBOPTIONS | DB_RECOVER) < 0)
194 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
196 if (dbif_open(dbd, dbp, 0) < 0)
199 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
204 (void)dbif_close(dbd);
212 static int delete_db(void)
217 EC_ZERO( get_lock(LOCK_FREE, bdata(dbpath)) );
218 EC_NEG1( cwd = open(".", O_RDONLY) );
219 chdir(bdata(dbpath));
220 system("rm -f cnid2.db lock log.* __db.*");
222 if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
223 LOG(log_error, logtype_cnid, "main: fatal db lock error");
227 LOG(log_warning, logtype_cnid, "Recreated CNID BerkeleyDB databases of volume \"%s\"", vol->v_localname);
237 * Close dbd if open, delete it, reopen
239 * Also tries to copy the rootinfo key, that would allow for keeping the db stamp
242 static int reinit_db(void)
246 bool copyRootInfo = false;
249 memset(&key, 0, sizeof(key));
250 memset(&data, 0, sizeof(data));
252 key.data = ROOTINFO_KEY;
253 key.size = ROOTINFO_KEYLEN;
255 if (dbif_get(dbd, DBIF_CNID, &key, &data, 0) <= 0) {
256 LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
257 copyRootInfo = false;
261 (void)dbif_close(dbd);
264 EC_ZERO_LOG( delete_db() );
265 EC_ZERO_LOG( open_db() );
267 if (copyRootInfo == true) {
268 memset(&key, 0, sizeof(key));
269 key.data = ROOTINFO_KEY;
270 key.size = ROOTINFO_KEYLEN;
272 if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) != 0) {
273 LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
282 static int loop(struct db_param *dbp)
284 struct cnid_dbd_rqst rqst;
285 struct cnid_dbd_rply rply;
289 time_t now, time_next_flush, time_last_rqst;
291 static char namebuf[MAXPATHLEN + 1];
295 sigprocmask(SIG_SETMASK, NULL, &set);
296 sigdelset(&set, SIGINT);
297 sigdelset(&set, SIGTERM);
301 time_next_flush = now + dbp->flush_interval;
302 time_last_rqst = now;
306 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
307 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
308 dbp->flush_interval, timebuf);
311 timeout = MIN(time_next_flush, time_last_rqst + dbp->idle_timeout);
317 if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
321 /* comm_rcv returned from select without receiving anything. */
323 /* Received signal (TERM|INT) */
326 if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
330 /* still active connections, reset time_last_rqst */
331 time_last_rqst = now;
333 /* We got a request */
334 time_last_rqst = now;
336 memset(&rply, 0, sizeof(rply));
338 /* ret gets set here */
339 case CNID_DBD_OP_OPEN:
340 case CNID_DBD_OP_CLOSE:
341 /* open/close are noops for now. */
345 case CNID_DBD_OP_ADD:
346 ret = dbd_add(dbd, &rqst, &rply);
348 case CNID_DBD_OP_GET:
349 ret = dbd_get(dbd, &rqst, &rply);
351 case CNID_DBD_OP_RESOLVE:
352 ret = dbd_resolve(dbd, &rqst, &rply);
354 case CNID_DBD_OP_LOOKUP:
355 ret = dbd_lookup(dbd, &rqst, &rply);
357 case CNID_DBD_OP_UPDATE:
358 ret = dbd_update(dbd, &rqst, &rply);
360 case CNID_DBD_OP_DELETE:
361 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
363 case CNID_DBD_OP_GETSTAMP:
364 ret = dbd_getstamp(dbd, &rqst, &rply);
366 case CNID_DBD_OP_REBUILD_ADD:
367 ret = dbd_rebuild_add(dbd, &rqst, &rply);
369 case CNID_DBD_OP_SEARCH:
370 ret = dbd_search(dbd, &rqst, &rply);
372 case CNID_DBD_OP_WIPE:
376 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
381 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
386 if (ret == 0 || cret == 0) {
387 if (dbif_txn_abort(dbd) < 0)
390 ret = dbif_txn_commit(dbd);
394 /* We had a designated txn because we wrote to the db */
397 } /* got a request */
400 Shall we checkpoint bdb ?
401 "flush_interval" seconds passed ?
403 if (now >= time_next_flush) {
404 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
405 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
408 time_next_flush = now + dbp->flush_interval;
410 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
411 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
412 dbp->flush_interval, timebuf);
416 Shall we checkpoint bdb ?
417 Have we commited "count" more changes than "flush_frequency" ?
419 if (count > dbp->flush_frequency) {
420 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
421 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
428 /* ------------------------ */
429 static void switch_to_user(char *dir)
433 if (chdir(dir) < 0) {
434 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
438 if (stat(".", &st) < 0) {
439 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
443 LOG(log_debug, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
444 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
445 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
452 /* ----------------------- */
453 static void set_signal(void)
457 sv.sa_handler = sig_exit;
459 sigemptyset(&sv.sa_mask);
460 sigaddset(&sv.sa_mask, SIGINT);
461 sigaddset(&sv.sa_mask, SIGTERM);
462 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
463 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
466 sv.sa_handler = SIG_IGN;
467 sigemptyset(&sv.sa_mask);
468 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
469 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
474 /* ------------------------ */
475 int main(int argc, char *argv[])
479 int ctrlfd = -1, clntfd = -1;
482 char *volpath = NULL;
484 while (( ret = getopt( argc, argv, "dF:l:p:t:vV")) != -1 ) {
487 /* this is now just ignored, as we do it automatically anyway */
491 obj.cmdlineconfigfile = strdup(optarg);
494 volpath = strdup(optarg);
497 clntfd = atoi(optarg);
500 ctrlfd = atoi(optarg);
504 printf("cnid_dbd (Netatalk %s)\n", VERSION);
509 if (ctrlfd == -1 || clntfd == -1 || !volpath) {
510 LOG(log_error, logtype_cnid, "main: bad IPC fds");
514 EC_ZERO( afp_config_parse(&obj, "cnid_dbd") );
516 EC_ZERO( load_volumes(&obj) );
517 EC_NULL( vol = getvolbypath(&obj, volpath) );
518 EC_ZERO( load_charset(vol) );
521 EC_NULL( dbpath = bfromcstr(vol->v_dbpath) );
522 EC_ZERO( bcatcstr(dbpath, "/.AppleDB") );
524 LOG(log_debug, logtype_cnid, "db dir: \"%s\"", bdata(dbpath));
526 switch_to_user(bdata(dbpath));
530 /* SIGINT and SIGTERM are always off, unless we are in pselect */
533 if ((dbp = db_param_read(bdata(dbpath))) == NULL)
535 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
537 if (open_db() != 0) {
538 LOG(log_error, logtype_cnid, "Failed to open CNID database for volume \"%s\"", vol->v_localname);
539 EC_ZERO_LOG( reinit_db() );
542 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
553 if (dbif_close(dbd) < 0)
556 if (dbif_env_remove(bdata(dbpath)) < 0)
564 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
566 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");