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);
238 * Close dbd if open, delete it, reopen
240 * Also tries to copy the rootinfo key, that would allow for keeping the db stamp
243 static int reinit_db(void)
247 bool copyRootInfo = false;
250 memset(&key, 0, sizeof(key));
251 memset(&data, 0, sizeof(data));
253 key.data = ROOTINFO_KEY;
254 key.size = ROOTINFO_KEYLEN;
256 if (dbif_get(dbd, DBIF_CNID, &key, &data, 0) <= 0) {
257 LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
258 copyRootInfo = false;
262 (void)dbif_close(dbd);
265 EC_ZERO_LOG( delete_db() );
266 EC_ZERO_LOG( open_db() );
268 if (copyRootInfo == true) {
269 memset(&key, 0, sizeof(key));
270 key.data = ROOTINFO_KEY;
271 key.size = ROOTINFO_KEYLEN;
273 if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) != 0) {
274 LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
283 static int loop(struct db_param *dbp)
285 struct cnid_dbd_rqst rqst;
286 struct cnid_dbd_rply rply;
290 time_t now, time_next_flush, time_last_rqst;
292 static char namebuf[MAXPATHLEN + 1];
296 sigprocmask(SIG_SETMASK, NULL, &set);
297 sigdelset(&set, SIGINT);
298 sigdelset(&set, SIGTERM);
302 time_next_flush = now + dbp->flush_interval;
303 time_last_rqst = now;
307 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
308 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
309 dbp->flush_interval, timebuf);
312 timeout = MIN(time_next_flush, time_last_rqst + dbp->idle_timeout);
318 if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
322 /* comm_rcv returned from select without receiving anything. */
324 /* Received signal (TERM|INT) */
327 if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
331 /* still active connections, reset time_last_rqst */
332 time_last_rqst = now;
334 /* We got a request */
335 time_last_rqst = now;
337 memset(&rply, 0, sizeof(rply));
339 /* ret gets set here */
340 case CNID_DBD_OP_OPEN:
341 case CNID_DBD_OP_CLOSE:
342 /* open/close are noops for now. */
346 case CNID_DBD_OP_ADD:
347 ret = dbd_add(dbd, &rqst, &rply);
349 case CNID_DBD_OP_GET:
350 ret = dbd_get(dbd, &rqst, &rply);
352 case CNID_DBD_OP_RESOLVE:
353 ret = dbd_resolve(dbd, &rqst, &rply);
355 case CNID_DBD_OP_LOOKUP:
356 ret = dbd_lookup(dbd, &rqst, &rply);
358 case CNID_DBD_OP_UPDATE:
359 ret = dbd_update(dbd, &rqst, &rply);
361 case CNID_DBD_OP_DELETE:
362 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
364 case CNID_DBD_OP_GETSTAMP:
365 ret = dbd_getstamp(dbd, &rqst, &rply);
367 case CNID_DBD_OP_REBUILD_ADD:
368 ret = dbd_rebuild_add(dbd, &rqst, &rply);
370 case CNID_DBD_OP_SEARCH:
371 ret = dbd_search(dbd, &rqst, &rply);
373 case CNID_DBD_OP_WIPE:
377 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
382 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
387 if (ret == 0 || cret == 0) {
388 if (dbif_txn_abort(dbd) < 0)
391 ret = dbif_txn_commit(dbd);
395 /* We had a designated txn because we wrote to the db */
398 } /* got a request */
401 Shall we checkpoint bdb ?
402 "flush_interval" seconds passed ?
404 if (now >= time_next_flush) {
405 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
406 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
409 time_next_flush = now + dbp->flush_interval;
411 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
412 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
413 dbp->flush_interval, timebuf);
417 Shall we checkpoint bdb ?
418 Have we commited "count" more changes than "flush_frequency" ?
420 if (count > dbp->flush_frequency) {
421 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
422 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
429 /* ------------------------ */
430 static void switch_to_user(char *dir)
434 if (chdir(dir) < 0) {
435 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
439 if (stat(".", &st) < 0) {
440 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
444 LOG(log_debug, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
445 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
446 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
453 /* ----------------------- */
454 static void set_signal(void)
458 sv.sa_handler = sig_exit;
460 sigemptyset(&sv.sa_mask);
461 sigaddset(&sv.sa_mask, SIGINT);
462 sigaddset(&sv.sa_mask, SIGTERM);
463 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
464 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
467 sv.sa_handler = SIG_IGN;
468 sigemptyset(&sv.sa_mask);
469 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
470 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
475 /* ------------------------ */
476 int main(int argc, char *argv[])
480 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");