2 * $Id: main.c,v 1.4 2009-04-21 08:55:44 franklahm Exp $
4 * Copyright (C) Joerg Lenneis 2003
5 * Copyright (c) Frank Lahm 2009
6 * All Rights Reserved. See COPYING.
13 We use AUTO_COMMIT for our BerkeleyDB environment. This avoids explicit transactions
14 for every bdb access which speeds up reads. But in order to be able to rollback
15 in case of errors we start a transaction once we encounter the first write.
16 The logic to do this is stuffed two levels lower into the dbif.c file and functions
17 dbif_put and dbif_del.
22 #endif /* HAVE_CONFIG_H */
26 #endif /* HAVE_UNISTD_H */
29 #endif /* HAVE_FCNTL_H */
35 #ifdef HAVE_SYS_TYPES_H
36 #include <sys/types.h>
37 #endif /* HAVE_SYS_TYPES_H */
38 #include <sys/param.h>
39 #ifdef HAVE_SYS_STAT_H
41 #endif /* HAVE_SYS_STAT_H */
45 #include <netatalk/endian.h>
46 #include <atalk/cnid_dbd_private.h>
47 #include <atalk/logger.h>
55 #define LOCKFILENAME "lock"
57 static int exit_sig = 0;
60 static void sig_exit(int signo)
66 static void block_sigs_onoff(int block)
71 sigaddset(&set, SIGINT);
72 sigaddset(&set, SIGTERM);
74 sigprocmask(SIG_BLOCK, &set, NULL);
76 sigprocmask(SIG_UNBLOCK, &set, NULL);
81 The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
83 1: Success, if transactions are used commit.
84 0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
85 -1: Fatal error, either from the database or from the socket. Abort the transaction if applicable
86 (which might fail as well) and then exit.
88 We always try to notify the client process about the outcome, the result field
89 of the cnid_dbd_rply structure contains further details.
93 static int loop(struct db_param *dbp)
95 struct cnid_dbd_rqst rqst;
96 struct cnid_dbd_rply rply;
99 time_t now, time_next_flush, time_last_rqst;
101 static char namebuf[MAXPATHLEN + 1];
105 time_next_flush = now + dbp->flush_interval;
106 time_last_rqst = now;
110 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
111 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
112 dbp->flush_interval, timebuf);
115 if ((cret = comm_rcv(&rqst)) < 0)
121 /* comm_rcv returned from select without receiving anything. */
123 /* Give signals a chance... */
127 /* Received signal (TERM|INT) */
129 if (dbp->idle_timeout && comm_nbe() <= 0 && (now - time_last_rqst) > dbp->idle_timeout)
133 /* We got a request */
134 time_last_rqst = now;
136 memset(&rply, 0, sizeof(rply));
138 /* ret gets set here */
139 case CNID_DBD_OP_OPEN:
140 case CNID_DBD_OP_CLOSE:
141 /* open/close are noops for now. */
145 case CNID_DBD_OP_ADD:
146 ret = dbd_add(&rqst, &rply);
148 case CNID_DBD_OP_GET:
149 ret = dbd_get(&rqst, &rply);
151 case CNID_DBD_OP_RESOLVE:
152 ret = dbd_resolve(&rqst, &rply);
154 case CNID_DBD_OP_LOOKUP:
155 ret = dbd_lookup(&rqst, &rply);
157 case CNID_DBD_OP_UPDATE:
158 ret = dbd_update(&rqst, &rply);
160 case CNID_DBD_OP_DELETE:
161 ret = dbd_delete(&rqst, &rply);
163 case CNID_DBD_OP_GETSTAMP:
164 ret = dbd_getstamp(&rqst, &rply);
166 case CNID_DBD_OP_REBUILD_ADD:
167 ret = dbd_rebuild_add(&rqst, &rply);
170 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
175 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
180 if (ret == 0 || cret == 0) {
181 if (dbif_txn_abort() < 0)
184 ret = dbif_txn_commit();
188 /* We had a designated txn because we wrote to the db */
191 } /* got a request */
194 Shall we checkpoint bdb ?
195 "flush_interval" seconds passed ?
197 if (now > time_next_flush) {
198 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
199 if (dbif_txn_checkpoint(0, 0, 0) < 0)
202 time_next_flush = now + dbp->flush_interval;
204 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
205 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
206 dbp->flush_interval, timebuf);
210 Shall we checkpoint bdb ?
211 Have we commited "count" more changes than "flush_frequency" ?
213 if (count > dbp->flush_frequency) {
214 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
215 if (dbif_txn_checkpoint(0, 0, 0) < 0)
222 /* ------------------------ */
223 static void switch_to_user(char *dir)
227 if (chdir(dir) < 0) {
228 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
232 if (stat(".", &st) < 0) {
233 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
237 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
238 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
239 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
245 /* ------------------------ */
251 if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
252 LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
257 lock.l_whence = SEEK_SET;
259 lock.l_type = F_WRLCK;
261 if (fcntl(lockfd, F_SETLK, &lock) < 0) {
262 if (errno == EACCES || errno == EAGAIN) {
265 LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
273 /* ----------------------- */
274 void set_signal(void)
278 sv.sa_handler = sig_exit;
280 sigemptyset(&sv.sa_mask);
281 sigaddset(&sv.sa_mask, SIGINT);
282 sigaddset(&sv.sa_mask, SIGTERM);
283 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
284 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
287 sv.sa_handler = SIG_IGN;
288 sigemptyset(&sv.sa_mask);
289 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
290 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
295 /* ----------------------- */
296 void free_lock(int lockfd)
301 lock.l_whence = SEEK_SET;
303 lock.l_type = F_UNLCK;
304 fcntl(lockfd, F_SETLK, &lock);
308 /* ------------------------ */
309 int main(int argc, char *argv[])
311 struct db_param *dbp;
313 int lockfd, ctrlfd, clntfd;
314 char *dir, *logconfig;
316 set_processname("cnid_dbd");
319 LOG(log_error, logtype_cnid, "main: not enough arguments");
324 ctrlfd = atoi(argv[2]);
325 clntfd = atoi(argv[3]);
326 logconfig = strdup(argv[4]);
331 /* Before we do anything else, check if there is an instance of cnid_dbd
332 running already and silently exit if yes. */
335 LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
339 /* SIGINT and SIGTERM are always off, unless we get a return code of 0 from
340 comm_rcv (no requests for one second, see above in loop()). That means we
341 only shut down after one second of inactivity. */
344 if ((dbp = db_param_read(dir, DBD)) == NULL)
346 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
348 if (dbif_env_init(dbp) < 0)
349 exit(2); /* FIXME: same exit code as failure for dbif_open() */
350 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
352 if (dbif_open(dbp, 0) < 0) {
356 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
358 if (dbd_stamp() < 0) {
362 LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
364 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
372 if (dbif_close() < 0)
380 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
382 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");