2 * $Id: main.c,v 1.12 2009-10-18 17:50:13 didg Exp $
4 * Copyright (C) Joerg Lenneis 2003
5 * Copyright (c) Frank Lahm 2009
6 * All Rights Reserved. See COPYING.
11 #endif /* HAVE_CONFIG_H */
15 #endif /* HAVE_UNISTD_H */
18 #endif /* HAVE_FCNTL_H */
24 #ifdef HAVE_SYS_TYPES_H
25 #include <sys/types.h>
26 #endif /* HAVE_SYS_TYPES_H */
27 #include <sys/param.h>
28 #ifdef HAVE_SYS_STAT_H
30 #endif /* HAVE_SYS_STAT_H */
34 #include <netatalk/endian.h>
35 #include <atalk/cnid_dbd_private.h>
36 #include <atalk/logger.h>
43 #define LOCKFILENAME "lock"
46 Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
47 It's a likey performance hit, but it might we worth it.
49 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | DB_RECOVER)
53 static int exit_sig = 0;
55 static void sig_exit(int signo)
61 static void block_sigs_onoff(int block)
66 sigaddset(&set, SIGINT);
67 sigaddset(&set, SIGTERM);
69 sigprocmask(SIG_BLOCK, &set, NULL);
71 sigprocmask(SIG_UNBLOCK, &set, NULL);
76 The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
78 1: Success, if transactions are used commit.
79 0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
80 -1: Fatal error, either from the database or from the socket. Abort the transaction if applicable
81 (which might fail as well) and then exit.
83 We always try to notify the client process about the outcome, the result field
84 of the cnid_dbd_rply structure contains further details.
88 #define min(a,b) ((a)<(b)?(a):(b))
91 static int loop(struct db_param *dbp)
93 struct cnid_dbd_rqst rqst;
94 struct cnid_dbd_rply rply;
98 time_t now, time_next_flush, time_last_rqst;
100 static char namebuf[MAXPATHLEN + 1];
104 sigprocmask(SIG_SETMASK, NULL, &set);
105 sigdelset(&set, SIGINT);
106 sigdelset(&set, SIGTERM);
110 time_next_flush = now + dbp->flush_interval;
111 time_last_rqst = now;
115 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
116 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
117 dbp->flush_interval, timebuf);
120 timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
126 if ((cret = comm_rcv(&rqst, timeout, &set)) < 0)
132 /* comm_rcv returned from select without receiving anything. */
134 /* Received signal (TERM|INT) */
136 if (dbp->idle_timeout && comm_nbe() <= 0 && (now - time_last_rqst) > dbp->idle_timeout)
140 /* We got a request */
141 time_last_rqst = now;
143 memset(&rply, 0, sizeof(rply));
145 /* ret gets set here */
146 case CNID_DBD_OP_OPEN:
147 case CNID_DBD_OP_CLOSE:
148 /* open/close are noops for now. */
152 case CNID_DBD_OP_ADD:
153 ret = dbd_add(dbd, &rqst, &rply);
155 case CNID_DBD_OP_GET:
156 ret = dbd_get(dbd, &rqst, &rply);
158 case CNID_DBD_OP_RESOLVE:
159 ret = dbd_resolve(dbd, &rqst, &rply);
161 case CNID_DBD_OP_LOOKUP:
162 ret = dbd_lookup(dbd, &rqst, &rply);
164 case CNID_DBD_OP_UPDATE:
165 ret = dbd_update(dbd, &rqst, &rply);
167 case CNID_DBD_OP_DELETE:
168 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
170 case CNID_DBD_OP_GETSTAMP:
171 ret = dbd_getstamp(dbd, &rqst, &rply);
173 case CNID_DBD_OP_REBUILD_ADD:
174 ret = dbd_rebuild_add(dbd, &rqst, &rply);
177 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
182 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
187 if (ret == 0 || cret == 0) {
188 if (dbif_txn_abort(dbd) < 0)
191 ret = dbif_txn_commit(dbd);
195 /* We had a designated txn because we wrote to the db */
198 } /* got a request */
201 Shall we checkpoint bdb ?
202 "flush_interval" seconds passed ?
204 if (now > time_next_flush) {
205 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
206 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
209 time_next_flush = now + dbp->flush_interval;
211 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
212 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
213 dbp->flush_interval, timebuf);
217 Shall we checkpoint bdb ?
218 Have we commited "count" more changes than "flush_frequency" ?
220 if (count > dbp->flush_frequency) {
221 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
222 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
229 /* ------------------------ */
230 static void switch_to_user(char *dir)
234 if (chdir(dir) < 0) {
235 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
239 if (stat(".", &st) < 0) {
240 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
244 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
245 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
246 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
252 /* ------------------------ */
253 static int get_lock(void)
258 if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
259 LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
264 lock.l_whence = SEEK_SET;
266 lock.l_type = F_WRLCK;
268 if (fcntl(lockfd, F_SETLK, &lock) < 0) {
269 if (errno == EACCES || errno == EAGAIN) {
272 LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
280 /* ----------------------- */
281 static void set_signal(void)
285 sv.sa_handler = sig_exit;
287 sigemptyset(&sv.sa_mask);
288 sigaddset(&sv.sa_mask, SIGINT);
289 sigaddset(&sv.sa_mask, SIGTERM);
290 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
291 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
294 sv.sa_handler = SIG_IGN;
295 sigemptyset(&sv.sa_mask);
296 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
297 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
302 /* ----------------------- */
303 static void free_lock(int lockfd)
308 lock.l_whence = SEEK_SET;
310 lock.l_type = F_UNLCK;
311 fcntl(lockfd, F_SETLK, &lock);
315 /* ------------------------ */
316 int main(int argc, char *argv[])
318 struct db_param *dbp;
320 int lockfd, ctrlfd, clntfd;
321 char *dir, *logconfig;
323 set_processname("cnid_dbd");
325 /* FIXME: implement -d from cnid_metad */
327 LOG(log_error, logtype_cnid, "main: not enough arguments");
332 ctrlfd = atoi(argv[2]);
333 clntfd = atoi(argv[3]);
334 logconfig = strdup(argv[4]);
339 /* Before we do anything else, check if there is an instance of cnid_dbd
340 running already and silently exit if yes. */
343 LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
347 /* SIGINT and SIGTERM are always off, unless we are in pselect */
350 if ((dbp = db_param_read(dir, CNID_DBD)) == NULL)
352 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
354 if (NULL == (dbd = dbif_init(".", "cnid2.db")))
357 if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
358 exit(2); /* FIXME: same exit code as failure for dbif_open() */
359 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
361 if (dbif_open(dbd, dbp, 0) < 0) {
365 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
367 if (dbd_stamp(dbd) < 0) {
371 LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
373 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
381 if (dbif_close(dbd) < 0)
384 if (dbif_prep_upgrade(dir) < 0)
392 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
394 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");