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);
178 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
183 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
188 if (ret == 0 || cret == 0) {
189 if (dbif_txn_abort(dbd) < 0)
192 ret = dbif_txn_commit(dbd);
196 /* We had a designated txn because we wrote to the db */
199 } /* got a request */
202 Shall we checkpoint bdb ?
203 "flush_interval" seconds passed ?
205 if (now >= time_next_flush) {
206 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
207 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
210 time_next_flush = now + dbp->flush_interval;
212 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
213 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
214 dbp->flush_interval, timebuf);
218 Shall we checkpoint bdb ?
219 Have we commited "count" more changes than "flush_frequency" ?
221 if (count > dbp->flush_frequency) {
222 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
223 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
230 /* ------------------------ */
231 static void switch_to_user(char *dir)
235 if (chdir(dir) < 0) {
236 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
240 if (stat(".", &st) < 0) {
241 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
245 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
246 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
247 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
253 /* ------------------------ */
254 static int get_lock(void)
259 if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
260 LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
265 lock.l_whence = SEEK_SET;
267 lock.l_type = F_WRLCK;
269 if (fcntl(lockfd, F_SETLK, &lock) < 0) {
270 if (errno == EACCES || errno == EAGAIN) {
273 LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
281 /* ----------------------- */
282 static void set_signal(void)
286 sv.sa_handler = sig_exit;
288 sigemptyset(&sv.sa_mask);
289 sigaddset(&sv.sa_mask, SIGINT);
290 sigaddset(&sv.sa_mask, SIGTERM);
291 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
292 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
295 sv.sa_handler = SIG_IGN;
296 sigemptyset(&sv.sa_mask);
297 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
298 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
303 /* ----------------------- */
304 static void free_lock(int lockfd)
309 lock.l_whence = SEEK_SET;
311 lock.l_type = F_UNLCK;
312 fcntl(lockfd, F_SETLK, &lock);
316 /* ------------------------ */
317 int main(int argc, char *argv[])
319 struct db_param *dbp;
321 int lockfd, ctrlfd, clntfd;
322 char *dir, *logconfig;
324 set_processname("cnid_dbd");
326 /* FIXME: implement -d from cnid_metad */
328 LOG(log_error, logtype_cnid, "main: not enough arguments");
333 ctrlfd = atoi(argv[2]);
334 clntfd = atoi(argv[3]);
335 logconfig = strdup(argv[4]);
340 /* Before we do anything else, check if there is an instance of cnid_dbd
341 running already and silently exit if yes. */
344 LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
348 /* SIGINT and SIGTERM are always off, unless we are in pselect */
351 if ((dbp = db_param_read(dir, CNID_DBD)) == NULL)
353 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
355 if (NULL == (dbd = dbif_init(".", "cnid2.db")))
358 if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
359 exit(2); /* FIXME: same exit code as failure for dbif_open() */
360 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
362 if (dbif_open(dbd, dbp, 0) < 0) {
366 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
368 if (dbd_stamp(dbd) < 0) {
372 LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
374 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
382 if (dbif_close(dbd) < 0)
385 if (dbif_prep_upgrade(dir) < 0)
393 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
395 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");