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>
38 Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
39 It's a likey performance hit, but it might we worth it.
41 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN)
44 static int exit_sig = 0;
46 static bstring dbpath;
47 static struct db_param *dbp;
49 static void sig_exit(int signo)
55 static void block_sigs_onoff(int block)
60 sigaddset(&set, SIGINT);
61 sigaddset(&set, SIGTERM);
63 sigprocmask(SIG_BLOCK, &set, NULL);
65 sigprocmask(SIG_UNBLOCK, &set, NULL);
70 The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
72 1: Success, if transactions are used commit.
73 0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
74 -1: Fatal error, either from t
75 he database or from the socket. Abort the transaction if applicable
76 (which might fail as well) and then exit.
78 We always try to notify the client process about the outcome, the result field
79 of the cnid_dbd_rply structure contains further details.
83 #define min(a,b) ((a)<(b)?(a):(b))
86 static int delete_db(void)
91 EC_NEG1( cwd = open(".", O_RDONLY) );
94 system("rm -f cnid2.db lock log.* __db.*");
95 if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
96 LOG(log_error, logtype_cnid, "main: fatal db lock error");
106 static int wipe_db(void)
111 memset(&key, 0, sizeof(key));
112 memset(&data, 0, sizeof(data));
114 key.data = ROOTINFO_KEY;
115 key.size = ROOTINFO_KEYLEN;
117 if (dbif_get(dbd, DBIF_CNID, &key, &data, 0) <= 0) {
118 LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
122 EC_NEG1_LOG( dbif_close(dbd) );
123 EC_NEG1_LOG( dbif_env_remove(bdata(dbpath)) );
124 EC_ZERO_LOG( delete_db() );
127 if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
128 LOG(log_error, logtype_cnid, "main: fatal db lock error");
132 EC_NULL_LOG( dbd = dbif_init(bdata(dbpath), "cnid2.db") );
134 /* Only recover if we got the lock */
135 EC_NEG1_LOG( dbif_env_open(dbd, dbp, DBOPTIONS | DB_RECOVER) );
137 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
139 EC_NEG1_LOG( dbif_open(dbd, dbp, 0) );
141 memset(&key, 0, sizeof(key));
142 key.data = ROOTINFO_KEY;
143 key.size = ROOTINFO_KEYLEN;
145 if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) != 0) {
146 LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
154 static int loop(struct db_param *dbp)
156 struct cnid_dbd_rqst rqst;
157 struct cnid_dbd_rply rply;
161 time_t now, time_next_flush, time_last_rqst;
163 static char namebuf[MAXPATHLEN + 1];
167 sigprocmask(SIG_SETMASK, NULL, &set);
168 sigdelset(&set, SIGINT);
169 sigdelset(&set, SIGTERM);
173 time_next_flush = now + dbp->flush_interval;
174 time_last_rqst = now;
178 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
179 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
180 dbp->flush_interval, timebuf);
183 timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
189 if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
193 /* comm_rcv returned from select without receiving anything. */
195 /* Received signal (TERM|INT) */
198 if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
202 /* still active connections, reset time_last_rqst */
203 time_last_rqst = now;
205 /* We got a request */
206 time_last_rqst = now;
208 memset(&rply, 0, sizeof(rply));
210 /* ret gets set here */
211 case CNID_DBD_OP_OPEN:
212 case CNID_DBD_OP_CLOSE:
213 /* open/close are noops for now. */
217 case CNID_DBD_OP_ADD:
218 ret = dbd_add(dbd, &rqst, &rply);
220 case CNID_DBD_OP_GET:
221 ret = dbd_get(dbd, &rqst, &rply);
223 case CNID_DBD_OP_RESOLVE:
224 ret = dbd_resolve(dbd, &rqst, &rply);
226 case CNID_DBD_OP_LOOKUP:
227 ret = dbd_lookup(dbd, &rqst, &rply);
229 case CNID_DBD_OP_UPDATE:
230 ret = dbd_update(dbd, &rqst, &rply);
232 case CNID_DBD_OP_DELETE:
233 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
235 case CNID_DBD_OP_GETSTAMP:
236 ret = dbd_getstamp(dbd, &rqst, &rply);
238 case CNID_DBD_OP_REBUILD_ADD:
239 ret = dbd_rebuild_add(dbd, &rqst, &rply);
241 case CNID_DBD_OP_SEARCH:
242 ret = dbd_search(dbd, &rqst, &rply);
244 case CNID_DBD_OP_WIPE:
248 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
253 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
258 if (ret == 0 || cret == 0) {
259 if (dbif_txn_abort(dbd) < 0)
262 ret = dbif_txn_commit(dbd);
266 /* We had a designated txn because we wrote to the db */
269 } /* got a request */
272 Shall we checkpoint bdb ?
273 "flush_interval" seconds passed ?
275 if (now >= time_next_flush) {
276 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
277 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
280 time_next_flush = now + dbp->flush_interval;
282 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
283 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
284 dbp->flush_interval, timebuf);
288 Shall we checkpoint bdb ?
289 Have we commited "count" more changes than "flush_frequency" ?
291 if (count > dbp->flush_frequency) {
292 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
293 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
300 /* ------------------------ */
301 static void switch_to_user(char *dir)
305 if (chdir(dir) < 0) {
306 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
310 if (stat(".", &st) < 0) {
311 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
315 LOG(log_debug, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
316 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
317 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
324 /* ----------------------- */
325 static void set_signal(void)
329 sv.sa_handler = sig_exit;
331 sigemptyset(&sv.sa_mask);
332 sigaddset(&sv.sa_mask, SIGINT);
333 sigaddset(&sv.sa_mask, SIGTERM);
334 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
335 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
338 sv.sa_handler = SIG_IGN;
339 sigemptyset(&sv.sa_mask);
340 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
341 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
346 /* ------------------------ */
347 int main(int argc, char *argv[])
351 int ctrlfd = -1, clntfd = -1;
355 char *volpath = NULL;
357 while (( ret = getopt( argc, argv, "dF:l:p:t:vV")) != -1 ) {
363 obj.cmdlineconfigfile = strdup(optarg);
366 volpath = strdup(optarg);
369 clntfd = atoi(optarg);
372 ctrlfd = atoi(optarg);
376 printf("cnid_dbd (Netatalk %s)\n", VERSION);
381 if (ctrlfd == -1 || clntfd == -1 || !volpath) {
382 LOG(log_error, logtype_cnid, "main: bad IPC fds");
386 EC_ZERO( afp_config_parse(&obj, "cnid_dbd") );
388 EC_ZERO( load_volumes(&obj) );
389 EC_NULL( vol = getvolbypath(&obj, volpath) );
390 EC_ZERO( load_charset(vol) );
393 EC_NULL( dbpath = bfromcstr(vol->v_dbpath) );
394 EC_ZERO( bcatcstr(dbpath, "/.AppleDB") );
396 LOG(log_debug, logtype_cnid, "db dir: \"%s\"", bdata(dbpath));
398 switch_to_user(bdata(dbpath));
401 if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
402 LOG(log_error, logtype_cnid, "main: fatal db lock error");
407 LOG(log_warning, logtype_cnid, "main: too many CNID db opening attempts, wiping the slate clean");
408 EC_ZERO( delete_db() );
413 /* SIGINT and SIGTERM are always off, unless we are in pselect */
416 if ((dbp = db_param_read(bdata(dbpath))) == NULL)
418 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
420 if (NULL == (dbd = dbif_init(bdata(dbpath), "cnid2.db")))
423 /* Only recover if we got the lock */
424 if (dbif_env_open(dbd,
426 (db_locked == LOCK_EXCL) ? DBOPTIONS | DB_RECOVER : DBOPTIONS) < 0)
428 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
430 if (dbif_open(dbd, dbp, 0) < 0) {
435 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
437 /* Downgrade db lock */
438 if (db_locked == LOCK_EXCL) {
439 if (get_lock(LOCK_UNLOCK, NULL) != 0) {
444 if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD) {
451 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
462 if (dbif_close(dbd) < 0)
465 if (dbif_env_remove(bdata(dbpath)) < 0)
473 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
475 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");