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>
41 #define LOCKFILENAME "lock"
44 Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
45 It's a likey performance hit, but it might we worth it.
47 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | DB_RECOVER)
51 static int exit_sig = 0;
53 static void sig_exit(int signo)
59 static void block_sigs_onoff(int block)
64 sigaddset(&set, SIGINT);
65 sigaddset(&set, SIGTERM);
67 sigprocmask(SIG_BLOCK, &set, NULL);
69 sigprocmask(SIG_UNBLOCK, &set, NULL);
74 The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
76 1: Success, if transactions are used commit.
77 0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
78 -1: Fatal error, either from t
79 he database or from the socket. Abort the transaction if applicable
80 (which might fail as well) and then exit.
82 We always try to notify the client process about the outcome, the result field
83 of the cnid_dbd_rply structure contains further details.
87 #define min(a,b) ((a)<(b)?(a):(b))
90 static int loop(struct db_param *dbp)
92 struct cnid_dbd_rqst rqst;
93 struct cnid_dbd_rply rply;
97 time_t now, time_next_flush, time_last_rqst;
99 static char namebuf[MAXPATHLEN + 1];
103 sigprocmask(SIG_SETMASK, NULL, &set);
104 sigdelset(&set, SIGINT);
105 sigdelset(&set, SIGTERM);
109 time_next_flush = now + dbp->flush_interval;
110 time_last_rqst = now;
114 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
115 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
116 dbp->flush_interval, timebuf);
119 timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
125 if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
129 /* comm_rcv returned from select without receiving anything. */
131 /* Received signal (TERM|INT) */
134 if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
138 /* still active connections, reset time_last_rqst */
139 time_last_rqst = now;
141 /* We got a request */
142 time_last_rqst = now;
144 memset(&rply, 0, sizeof(rply));
146 /* ret gets set here */
147 case CNID_DBD_OP_OPEN:
148 case CNID_DBD_OP_CLOSE:
149 /* open/close are noops for now. */
153 case CNID_DBD_OP_ADD:
154 ret = dbd_add(dbd, &rqst, &rply, 0);
156 case CNID_DBD_OP_GET:
157 ret = dbd_get(dbd, &rqst, &rply);
159 case CNID_DBD_OP_RESOLVE:
160 ret = dbd_resolve(dbd, &rqst, &rply);
162 case CNID_DBD_OP_LOOKUP:
163 ret = dbd_lookup(dbd, &rqst, &rply, 0);
165 case CNID_DBD_OP_UPDATE:
166 ret = dbd_update(dbd, &rqst, &rply);
168 case CNID_DBD_OP_DELETE:
169 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
171 case CNID_DBD_OP_GETSTAMP:
172 ret = dbd_getstamp(dbd, &rqst, &rply);
174 case CNID_DBD_OP_REBUILD_ADD:
175 ret = dbd_rebuild_add(dbd, &rqst, &rply);
177 case CNID_DBD_OP_SEARCH:
178 ret = dbd_search(dbd, &rqst, &rply);
181 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
186 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
191 if (ret == 0 || cret == 0) {
192 if (dbif_txn_abort(dbd) < 0)
195 ret = dbif_txn_commit(dbd);
199 /* We had a designated txn because we wrote to the db */
202 } /* got a request */
205 Shall we checkpoint bdb ?
206 "flush_interval" seconds passed ?
208 if (now >= time_next_flush) {
209 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
210 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
213 time_next_flush = now + dbp->flush_interval;
215 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
216 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
217 dbp->flush_interval, timebuf);
221 Shall we checkpoint bdb ?
222 Have we commited "count" more changes than "flush_frequency" ?
224 if (count > dbp->flush_frequency) {
225 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
226 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
233 /* ------------------------ */
234 static void switch_to_user(char *dir)
238 if (chdir(dir) < 0) {
239 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
243 if (stat(".", &st) < 0) {
244 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
248 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
249 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
250 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
256 /* ------------------------ */
257 static int get_lock(void)
262 if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
263 LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
268 lock.l_whence = SEEK_SET;
270 lock.l_type = F_WRLCK;
272 if (fcntl(lockfd, F_SETLK, &lock) < 0) {
273 if (errno == EACCES || errno == EAGAIN) {
276 LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
284 /* ----------------------- */
285 static void set_signal(void)
289 sv.sa_handler = sig_exit;
291 sigemptyset(&sv.sa_mask);
292 sigaddset(&sv.sa_mask, SIGINT);
293 sigaddset(&sv.sa_mask, SIGTERM);
294 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
295 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
298 sv.sa_handler = SIG_IGN;
299 sigemptyset(&sv.sa_mask);
300 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
301 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
306 /* ----------------------- */
307 static void free_lock(int lockfd)
312 lock.l_whence = SEEK_SET;
314 lock.l_type = F_UNLCK;
315 fcntl(lockfd, F_SETLK, &lock);
319 /* ------------------------ */
320 int main(int argc, char *argv[])
322 struct db_param *dbp;
324 int lockfd, ctrlfd, clntfd;
325 char *dir, *logconfig;
327 set_processname("cnid_dbd");
329 /* FIXME: implement -d from cnid_metad */
331 LOG(log_error, logtype_cnid, "main: not enough arguments");
336 ctrlfd = atoi(argv[2]);
337 clntfd = atoi(argv[3]);
338 logconfig = strdup(argv[4]);
343 /* Before we do anything else, check if there is an instance of cnid_dbd
344 running already and silently exit if yes. */
347 LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
351 /* SIGINT and SIGTERM are always off, unless we are in pselect */
354 if ((dbp = db_param_read(dir, CNID_DBD)) == NULL)
356 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
358 if (NULL == (dbd = dbif_init(".", "cnid2.db")))
361 if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
362 exit(2); /* FIXME: same exit code as failure for dbif_open() */
363 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
365 if (dbif_open(dbd, dbp, 0) < 0) {
369 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
371 if (dbd_stamp(dbd) < 0) {
375 LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
377 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
385 if (dbif_close(dbd) < 0)
388 if (dbif_env_remove(dir) < 0)
396 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
398 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");