2 * $Id: main.c,v 1.13 2009-10-19 05:38:22 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 t
81 he database or from the socket. Abort the transaction if applicable
82 (which might fail as well) and then exit.
84 We always try to notify the client process about the outcome, the result field
85 of the cnid_dbd_rply structure contains further details.
89 #define min(a,b) ((a)<(b)?(a):(b))
92 static int loop(struct db_param *dbp)
94 struct cnid_dbd_rqst rqst;
95 struct cnid_dbd_rply rply;
99 time_t now, time_next_flush, time_last_rqst;
101 static char namebuf[MAXPATHLEN + 1];
105 sigprocmask(SIG_SETMASK, NULL, &set);
106 sigdelset(&set, SIGINT);
107 sigdelset(&set, SIGTERM);
111 time_next_flush = now + dbp->flush_interval;
112 time_last_rqst = now;
116 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
117 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
118 dbp->flush_interval, timebuf);
121 timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
127 if ((cret = comm_rcv(&rqst, timeout, &set)) < 0)
133 /* comm_rcv returned from select without receiving anything. */
135 /* Received signal (TERM|INT) */
137 if (now - time_last_rqst > dbp->idle_timeout) {
138 if (comm_nbe() <= 0) {
143 /* still active connections, reset time_last_rqst */
144 time_last_rqst = now;
148 /* We got a request */
149 time_last_rqst = now;
151 memset(&rply, 0, sizeof(rply));
153 /* ret gets set here */
154 case CNID_DBD_OP_OPEN:
155 case CNID_DBD_OP_CLOSE:
156 /* open/close are noops for now. */
160 case CNID_DBD_OP_ADD:
161 ret = dbd_add(dbd, &rqst, &rply);
163 case CNID_DBD_OP_GET:
164 ret = dbd_get(dbd, &rqst, &rply);
166 case CNID_DBD_OP_RESOLVE:
167 ret = dbd_resolve(dbd, &rqst, &rply);
169 case CNID_DBD_OP_LOOKUP:
170 ret = dbd_lookup(dbd, &rqst, &rply);
172 case CNID_DBD_OP_UPDATE:
173 ret = dbd_update(dbd, &rqst, &rply);
175 case CNID_DBD_OP_DELETE:
176 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
178 case CNID_DBD_OP_GETSTAMP:
179 ret = dbd_getstamp(dbd, &rqst, &rply);
181 case CNID_DBD_OP_REBUILD_ADD:
182 ret = dbd_rebuild_add(dbd, &rqst, &rply);
185 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
190 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
195 if (ret == 0 || cret == 0) {
196 if (dbif_txn_abort(dbd) < 0)
199 ret = dbif_txn_commit(dbd);
203 /* We had a designated txn because we wrote to the db */
206 } /* got a request */
209 Shall we checkpoint bdb ?
210 "flush_interval" seconds passed ?
212 if (now > time_next_flush) {
213 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
214 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
217 time_next_flush = now + dbp->flush_interval;
219 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
220 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
221 dbp->flush_interval, timebuf);
225 Shall we checkpoint bdb ?
226 Have we commited "count" more changes than "flush_frequency" ?
228 if (count > dbp->flush_frequency) {
229 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
230 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
237 /* ------------------------ */
238 static void switch_to_user(char *dir)
242 if (chdir(dir) < 0) {
243 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
247 if (stat(".", &st) < 0) {
248 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
252 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
253 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
254 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
260 /* ------------------------ */
261 static int get_lock(void)
266 if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
267 LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
272 lock.l_whence = SEEK_SET;
274 lock.l_type = F_WRLCK;
276 if (fcntl(lockfd, F_SETLK, &lock) < 0) {
277 if (errno == EACCES || errno == EAGAIN) {
280 LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
288 /* ----------------------- */
289 static void set_signal(void)
293 sv.sa_handler = sig_exit;
295 sigemptyset(&sv.sa_mask);
296 sigaddset(&sv.sa_mask, SIGINT);
297 sigaddset(&sv.sa_mask, SIGTERM);
298 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
299 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
302 sv.sa_handler = SIG_IGN;
303 sigemptyset(&sv.sa_mask);
304 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
305 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
310 /* ----------------------- */
311 static void free_lock(int lockfd)
316 lock.l_whence = SEEK_SET;
318 lock.l_type = F_UNLCK;
319 fcntl(lockfd, F_SETLK, &lock);
323 /* ------------------------ */
324 int main(int argc, char *argv[])
326 struct db_param *dbp;
328 int lockfd, ctrlfd, clntfd;
329 char *dir, *logconfig;
331 set_processname("cnid_dbd");
333 /* FIXME: implement -d from cnid_metad */
335 LOG(log_error, logtype_cnid, "main: not enough arguments");
340 ctrlfd = atoi(argv[2]);
341 clntfd = atoi(argv[3]);
342 logconfig = strdup(argv[4]);
347 /* Before we do anything else, check if there is an instance of cnid_dbd
348 running already and silently exit if yes. */
351 LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
355 /* SIGINT and SIGTERM are always off, unless we are in pselect */
358 if ((dbp = db_param_read(dir, CNID_DBD)) == NULL)
360 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
362 if (NULL == (dbd = dbif_init(".", "cnid2.db")))
365 if (dbif_env_open(dbd, dbp, 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 if (dbd_stamp(dbd) < 0) {
379 LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
381 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
389 if (dbif_close(dbd) < 0)
392 if (dbif_prep_upgrade(dir) < 0)
400 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
402 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");