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/volinfo.h>
35 Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
36 It's a likey performance hit, but it might we worth it.
38 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN)
40 /* Global, needed by pack.c:idxname() */
41 struct volinfo volinfo;
44 static int exit_sig = 0;
47 static void sig_exit(int signo)
53 static void block_sigs_onoff(int block)
58 sigaddset(&set, SIGINT);
59 sigaddset(&set, SIGTERM);
61 sigprocmask(SIG_BLOCK, &set, NULL);
63 sigprocmask(SIG_UNBLOCK, &set, NULL);
68 The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
70 1: Success, if transactions are used commit.
71 0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
72 -1: Fatal error, either from t
73 he database or from the socket. Abort the transaction if applicable
74 (which might fail as well) and then exit.
76 We always try to notify the client process about the outcome, the result field
77 of the cnid_dbd_rply structure contains further details.
81 #define min(a,b) ((a)<(b)?(a):(b))
84 static int loop(struct db_param *dbp)
86 struct cnid_dbd_rqst rqst;
87 struct cnid_dbd_rply rply;
91 time_t now, time_next_flush, time_last_rqst;
93 static char namebuf[MAXPATHLEN + 1];
97 sigprocmask(SIG_SETMASK, NULL, &set);
98 sigdelset(&set, SIGINT);
99 sigdelset(&set, SIGTERM);
103 time_next_flush = now + dbp->flush_interval;
104 time_last_rqst = now;
108 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
109 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
110 dbp->flush_interval, timebuf);
113 timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
119 if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
123 /* comm_rcv returned from select without receiving anything. */
125 /* Received signal (TERM|INT) */
128 if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
132 /* still active connections, reset time_last_rqst */
133 time_last_rqst = now;
135 /* We got a request */
136 time_last_rqst = now;
138 memset(&rply, 0, sizeof(rply));
140 /* ret gets set here */
141 case CNID_DBD_OP_OPEN:
142 case CNID_DBD_OP_CLOSE:
143 /* open/close are noops for now. */
147 case CNID_DBD_OP_ADD:
148 ret = dbd_add(dbd, &rqst, &rply, 0);
150 case CNID_DBD_OP_GET:
151 ret = dbd_get(dbd, &rqst, &rply);
153 case CNID_DBD_OP_RESOLVE:
154 ret = dbd_resolve(dbd, &rqst, &rply);
156 case CNID_DBD_OP_LOOKUP:
157 ret = dbd_lookup(dbd, &rqst, &rply, 0);
159 case CNID_DBD_OP_UPDATE:
160 ret = dbd_update(dbd, &rqst, &rply);
162 case CNID_DBD_OP_DELETE:
163 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
165 case CNID_DBD_OP_GETSTAMP:
166 ret = dbd_getstamp(dbd, &rqst, &rply);
168 case CNID_DBD_OP_REBUILD_ADD:
169 ret = dbd_rebuild_add(dbd, &rqst, &rply);
171 case CNID_DBD_OP_SEARCH:
172 ret = dbd_search(dbd, &rqst, &rply);
175 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
180 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
185 if (ret == 0 || cret == 0) {
186 if (dbif_txn_abort(dbd) < 0)
189 ret = dbif_txn_commit(dbd);
193 /* We had a designated txn because we wrote to the db */
196 } /* got a request */
199 Shall we checkpoint bdb ?
200 "flush_interval" seconds passed ?
202 if (now >= time_next_flush) {
203 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
204 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
207 time_next_flush = now + dbp->flush_interval;
209 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
210 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
211 dbp->flush_interval, timebuf);
215 Shall we checkpoint bdb ?
216 Have we commited "count" more changes than "flush_frequency" ?
218 if (count > dbp->flush_frequency) {
219 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
220 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
227 /* ------------------------ */
228 static void switch_to_user(char *dir)
232 if (chdir(dir) < 0) {
233 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
237 if (stat(".", &st) < 0) {
238 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
242 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
243 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
244 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
251 /* ----------------------- */
252 static void set_signal(void)
256 sv.sa_handler = sig_exit;
258 sigemptyset(&sv.sa_mask);
259 sigaddset(&sv.sa_mask, SIGINT);
260 sigaddset(&sv.sa_mask, SIGTERM);
261 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
262 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
265 sv.sa_handler = SIG_IGN;
266 sigemptyset(&sv.sa_mask);
267 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
268 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
273 /* ------------------------ */
274 int main(int argc, char *argv[])
276 struct db_param *dbp;
277 int err = 0, ret, delete_bdb = 0;
281 set_processname("cnid_dbd");
283 while (( ret = getopt( argc, argv, "vVd")) != -1 ) {
287 printf("cnid_dbd (Netatalk %s)\n", VERSION);
295 if (argc - optind != 4) {
296 LOG(log_error, logtype_cnid, "main: not enough arguments");
300 /* Load .volinfo file */
301 if (loadvolinfo(argv[optind], &volinfo) == -1) {
302 LOG(log_error, logtype_cnid, "Cant load volinfo for \"%s\"", argv[1]);
305 /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
306 char dbpath[MAXPATHLEN+1];
307 if ((strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
308 LOG(log_error, logtype_cnid, "CNID db pathname too long: \"%s\"", volinfo.v_dbpath);
311 strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
312 strcat(dbpath, "/.AppleDB");
314 ctrlfd = atoi(argv[optind + 1]);
315 clntfd = atoi(argv[optind + 2]);
316 logconfig = strdup(argv[optind + 3]);
319 if (vol_load_charsets(&volinfo) == -1) {
320 LOG(log_error, logtype_cnid, "Error loading charsets!");
323 LOG(log_debug, logtype_cnid, "db dir: \"%s\"", dbpath);
325 switch_to_user(dbpath);
328 if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1) {
329 LOG(log_error, logtype_cnid, "main: fatal db lock error");
332 if (db_locked != LOCK_EXCL) {
333 /* Couldn't get exclusive lock, try shared lock */
334 if ((db_locked = get_lock(LOCK_SHRD, NULL)) != LOCK_SHRD) {
335 LOG(log_error, logtype_cnid, "main: fatal db lock error");
340 if (delete_bdb && (db_locked == LOCK_EXCL)) {
341 LOG(log_warning, logtype_cnid, "main: too many CNID db opening attempts, wiping the slate clean");
343 system("rm -f cnid2.db lock log.* __db.*");
344 if ((db_locked = get_lock(LOCK_EXCL, dbpath)) != LOCK_EXCL) {
345 LOG(log_error, logtype_cnid, "main: fatal db lock error");
352 /* SIGINT and SIGTERM are always off, unless we are in pselect */
355 if ((dbp = db_param_read(dbpath)) == NULL)
357 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
359 if (NULL == (dbd = dbif_init(dbpath, "cnid2.db")))
362 /* Only recover if we got the lock */
363 if (dbif_env_open(dbd,
365 (db_locked == LOCK_EXCL) ? DBOPTIONS | DB_RECOVER : DBOPTIONS) < 0)
366 exit(2); /* FIXME: same exit code as failure for dbif_open() */
367 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
369 if (dbif_open(dbd, dbp, 0) < 0) {
373 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
375 /* Downgrade db lock */
376 if (db_locked == LOCK_EXCL) {
377 if (get_lock(LOCK_UNLOCK, NULL) != 0) {
381 if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD) {
388 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
396 if (dbif_close(dbd) < 0)
399 if (dbif_env_remove(dbpath) < 0)
405 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
407 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");