2 * Copyright (C) Joerg Lenneis 2003
3 * Copyright (c) Frank Lahm 2009
4 * All Rights Reserved. See COPYING.
9 #endif /* HAVE_CONFIG_H */
13 #endif /* HAVE_UNISTD_H */
16 #endif /* HAVE_FCNTL_H */
22 #ifdef HAVE_SYS_TYPES_H
23 #include <sys/types.h>
24 #endif /* HAVE_SYS_TYPES_H */
25 #include <sys/param.h>
26 #ifdef HAVE_SYS_STAT_H
28 #endif /* HAVE_SYS_STAT_H */
32 #include <netatalk/endian.h>
33 #include <atalk/cnid_dbd_private.h>
34 #include <atalk/logger.h>
35 #include <atalk/volinfo.h>
42 #define LOCKFILENAME "lock"
45 Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
46 It's a likey performance hit, but it might we worth it.
48 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN)
50 /* Global, needed by pack.c:idxname() */
51 struct volinfo volinfo;
54 static int exit_sig = 0;
57 static void sig_exit(int signo)
63 static void block_sigs_onoff(int block)
68 sigaddset(&set, SIGINT);
69 sigaddset(&set, SIGTERM);
71 sigprocmask(SIG_BLOCK, &set, NULL);
73 sigprocmask(SIG_UNBLOCK, &set, NULL);
78 * Get lock on db lock file
80 * @args cmd (r) !=0: lock, 0: unlock
81 * @returns 1 if lock was acquired, 0 if file is already locked, -1 on error
83 static int get_lock(int cmd)
85 static int lockfd = -1;
93 lock.l_whence = SEEK_SET;
95 lock.l_type = F_UNLCK;
96 fcntl(lockfd, F_SETLK, &lock);
103 if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
104 LOG(log_error, logtype_cnid, "get_lock: error opening lockfile: %s", strerror(errno));
110 lock.l_whence = SEEK_SET;
112 lock.l_type = F_WRLCK;
114 if (fcntl(lockfd, F_SETLK, &lock) < 0) {
115 if (errno == EACCES || errno == EAGAIN) {
116 LOG(log_debug, logtype_cnid, "get_lock: couldn't lock");
119 LOG(log_error, logtype_cnid, "get_lock: fcntl F_WRLCK lockfile: %s", strerror(errno));
124 LOG(log_debug, logtype_cnid, "get_lock: got lock");
129 The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
131 1: Success, if transactions are used commit.
132 0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
133 -1: Fatal error, either from t
134 he database or from the socket. Abort the transaction if applicable
135 (which might fail as well) and then exit.
137 We always try to notify the client process about the outcome, the result field
138 of the cnid_dbd_rply structure contains further details.
142 #define min(a,b) ((a)<(b)?(a):(b))
145 static int loop(struct db_param *dbp)
147 struct cnid_dbd_rqst rqst;
148 struct cnid_dbd_rply rply;
152 time_t now, time_next_flush, time_last_rqst;
154 static char namebuf[MAXPATHLEN + 1];
158 sigprocmask(SIG_SETMASK, NULL, &set);
159 sigdelset(&set, SIGINT);
160 sigdelset(&set, SIGTERM);
164 time_next_flush = now + dbp->flush_interval;
165 time_last_rqst = now;
169 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
170 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
171 dbp->flush_interval, timebuf);
175 * If we haven't got the lock yet, get it now.
176 * Prevents a race with dbd:
177 * 1. no cnid_dbd running
178 * 2. dbd -r starts, gets the lock
179 * 3. cnid_dbd starts, doesn't get lock, doesn't run recovery, all is fine
180 * 4. dbd from (2) finishes, drops lock
181 * 5. anothet dbd but this time with -re is started which
182 * - succeeds getting the lock
183 * - runs recovery => this kills (3)
186 if ((db_locked = get_lock(1)) == -1)
188 timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
194 if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
198 /* comm_rcv returned from select without receiving anything. */
200 /* Received signal (TERM|INT) */
203 if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
207 /* still active connections, reset time_last_rqst */
208 time_last_rqst = now;
210 /* We got a request */
211 time_last_rqst = now;
213 memset(&rply, 0, sizeof(rply));
215 /* ret gets set here */
216 case CNID_DBD_OP_OPEN:
217 case CNID_DBD_OP_CLOSE:
218 /* open/close are noops for now. */
222 case CNID_DBD_OP_ADD:
223 ret = dbd_add(dbd, &rqst, &rply, 0);
225 case CNID_DBD_OP_GET:
226 ret = dbd_get(dbd, &rqst, &rply);
228 case CNID_DBD_OP_RESOLVE:
229 ret = dbd_resolve(dbd, &rqst, &rply);
231 case CNID_DBD_OP_LOOKUP:
232 ret = dbd_lookup(dbd, &rqst, &rply, 0);
234 case CNID_DBD_OP_UPDATE:
235 ret = dbd_update(dbd, &rqst, &rply);
237 case CNID_DBD_OP_DELETE:
238 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
240 case CNID_DBD_OP_GETSTAMP:
241 ret = dbd_getstamp(dbd, &rqst, &rply);
243 case CNID_DBD_OP_REBUILD_ADD:
244 ret = dbd_rebuild_add(dbd, &rqst, &rply);
246 case CNID_DBD_OP_SEARCH:
247 ret = dbd_search(dbd, &rqst, &rply);
250 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
255 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
260 if (ret == 0 || cret == 0) {
261 if (dbif_txn_abort(dbd) < 0)
264 ret = dbif_txn_commit(dbd);
268 /* We had a designated txn because we wrote to the db */
271 } /* got a request */
274 Shall we checkpoint bdb ?
275 "flush_interval" seconds passed ?
277 if (now >= time_next_flush) {
278 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
279 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
282 time_next_flush = now + dbp->flush_interval;
284 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
285 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
286 dbp->flush_interval, timebuf);
290 Shall we checkpoint bdb ?
291 Have we commited "count" more changes than "flush_frequency" ?
293 if (count > dbp->flush_frequency) {
294 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
295 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
302 /* ------------------------ */
303 static void switch_to_user(char *dir)
307 if (chdir(dir) < 0) {
308 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
312 if (stat(".", &st) < 0) {
313 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
317 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
318 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
319 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
326 /* ----------------------- */
327 static void set_signal(void)
331 sv.sa_handler = sig_exit;
333 sigemptyset(&sv.sa_mask);
334 sigaddset(&sv.sa_mask, SIGINT);
335 sigaddset(&sv.sa_mask, SIGTERM);
336 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
337 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
340 sv.sa_handler = SIG_IGN;
341 sigemptyset(&sv.sa_mask);
342 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
343 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
348 /* ------------------------ */
349 int main(int argc, char *argv[])
351 struct db_param *dbp;
356 set_processname("cnid_dbd");
358 /* FIXME: implement -d from cnid_metad */
360 LOG(log_error, logtype_cnid, "main: not enough arguments");
364 ctrlfd = atoi(argv[2]);
365 clntfd = atoi(argv[3]);
366 logconfig = strdup(argv[4]);
369 /* Load .volinfo file */
370 if (loadvolinfo(argv[1], &volinfo) == -1) {
371 LOG(log_error, logtype_cnid, "Cant load volinfo for \"%s\"", argv[1]);
374 /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
375 char dbpath[MAXPATHLEN+1];
376 if ((strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
377 LOG(log_error, logtype_cnid, "CNID db pathname too long: \"%s\"", volinfo.v_dbpath);
380 strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
381 strcat(dbpath, "/.AppleDB");
383 if (vol_load_charsets(&volinfo) == -1) {
384 LOG(log_error, logtype_cnid, "Error loading charsets!");
387 LOG(log_debug, logtype_cnid, "db dir: \"%s\"", dbpath);
389 switch_to_user(dbpath);
391 /* Before we do anything else, check if there is an instance of cnid_dbd
392 running already and silently exit if yes. */
393 if ((db_locked = get_lock(1)) == -1) {
399 /* SIGINT and SIGTERM are always off, unless we are in pselect */
402 if ((dbp = db_param_read(dbpath)) == NULL)
404 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
406 if (NULL == (dbd = dbif_init(dbpath, "cnid2.db")))
409 /* Only recover if we got the lock */
410 if (dbif_env_open(dbd,
412 db_locked ? DBOPTIONS | DB_RECOVER : DBOPTIONS) < 0)
413 exit(2); /* FIXME: same exit code as failure for dbif_open() */
414 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
416 if (dbif_open(dbd, dbp, 0) < 0) {
420 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
422 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
430 if (dbif_close(dbd) < 0)
433 if (dbif_env_remove(dbpath) < 0)
441 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
443 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");