2 * $Id: main.c,v 1.5 2009-04-28 13:01:24 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>
54 #define LOCKFILENAME "lock"
57 Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
58 It's a likey performance hit, but it might we worth it.
60 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN)
62 static int exit_sig = 0;
64 static void sig_exit(int signo)
70 static void block_sigs_onoff(int block)
75 sigaddset(&set, SIGINT);
76 sigaddset(&set, SIGTERM);
78 sigprocmask(SIG_BLOCK, &set, NULL);
80 sigprocmask(SIG_UNBLOCK, &set, NULL);
85 The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
87 1: Success, if transactions are used commit.
88 0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
89 -1: Fatal error, either from the database or from the socket. Abort the transaction if applicable
90 (which might fail as well) and then exit.
92 We always try to notify the client process about the outcome, the result field
93 of the cnid_dbd_rply structure contains further details.
97 static int loop(struct db_param *dbp)
99 struct cnid_dbd_rqst rqst;
100 struct cnid_dbd_rply rply;
103 time_t now, time_next_flush, time_last_rqst;
105 static char namebuf[MAXPATHLEN + 1];
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 if ((cret = comm_rcv(&rqst)) < 0)
125 /* comm_rcv returned from select without receiving anything. */
127 /* Give signals a chance... */
131 /* Received signal (TERM|INT) */
133 if (dbp->idle_timeout && comm_nbe() <= 0 && (now - time_last_rqst) > dbp->idle_timeout)
137 /* We got a request */
138 time_last_rqst = now;
140 memset(&rply, 0, sizeof(rply));
142 /* ret gets set here */
143 case CNID_DBD_OP_OPEN:
144 case CNID_DBD_OP_CLOSE:
145 /* open/close are noops for now. */
149 case CNID_DBD_OP_ADD:
150 ret = dbd_add(&rqst, &rply);
152 case CNID_DBD_OP_GET:
153 ret = dbd_get(&rqst, &rply);
155 case CNID_DBD_OP_RESOLVE:
156 ret = dbd_resolve(&rqst, &rply);
158 case CNID_DBD_OP_LOOKUP:
159 ret = dbd_lookup(&rqst, &rply);
161 case CNID_DBD_OP_UPDATE:
162 ret = dbd_update(&rqst, &rply);
164 case CNID_DBD_OP_DELETE:
165 ret = dbd_delete(&rqst, &rply);
167 case CNID_DBD_OP_GETSTAMP:
168 ret = dbd_getstamp(&rqst, &rply);
170 case CNID_DBD_OP_REBUILD_ADD:
171 ret = dbd_rebuild_add(&rqst, &rply);
174 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
179 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
184 if (ret == 0 || cret == 0) {
185 if (dbif_txn_abort() < 0)
188 ret = dbif_txn_commit();
192 /* We had a designated txn because we wrote to the db */
195 } /* got a request */
198 Shall we checkpoint bdb ?
199 "flush_interval" seconds passed ?
201 if (now > time_next_flush) {
202 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
203 if (dbif_txn_checkpoint(0, 0, 0) < 0)
206 time_next_flush = now + dbp->flush_interval;
208 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
209 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
210 dbp->flush_interval, timebuf);
214 Shall we checkpoint bdb ?
215 Have we commited "count" more changes than "flush_frequency" ?
217 if (count > dbp->flush_frequency) {
218 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
219 if (dbif_txn_checkpoint(0, 0, 0) < 0)
226 /* ------------------------ */
227 static void switch_to_user(char *dir)
231 if (chdir(dir) < 0) {
232 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
236 if (stat(".", &st) < 0) {
237 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
241 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
242 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
243 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
249 /* ------------------------ */
255 if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
256 LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
261 lock.l_whence = SEEK_SET;
263 lock.l_type = F_WRLCK;
265 if (fcntl(lockfd, F_SETLK, &lock) < 0) {
266 if (errno == EACCES || errno == EAGAIN) {
269 LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
277 /* ----------------------- */
278 void set_signal(void)
282 sv.sa_handler = sig_exit;
284 sigemptyset(&sv.sa_mask);
285 sigaddset(&sv.sa_mask, SIGINT);
286 sigaddset(&sv.sa_mask, SIGTERM);
287 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
288 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
291 sv.sa_handler = SIG_IGN;
292 sigemptyset(&sv.sa_mask);
293 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
294 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
299 /* ----------------------- */
300 void free_lock(int lockfd)
305 lock.l_whence = SEEK_SET;
307 lock.l_type = F_UNLCK;
308 fcntl(lockfd, F_SETLK, &lock);
312 /* ------------------------ */
313 int main(int argc, char *argv[])
315 struct db_param *dbp;
317 int lockfd, ctrlfd, clntfd;
318 char *dir, *logconfig;
320 set_processname("cnid_dbd");
323 LOG(log_error, logtype_cnid, "main: not enough arguments");
328 ctrlfd = atoi(argv[2]);
329 clntfd = atoi(argv[3]);
330 logconfig = strdup(argv[4]);
335 /* Before we do anything else, check if there is an instance of cnid_dbd
336 running already and silently exit if yes. */
339 LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
343 /* SIGINT and SIGTERM are always off, unless we get a return code of 0 from
344 comm_rcv (no requests for one second, see above in loop()). That means we
345 only shut down after one second of inactivity. */
348 if ((dbp = db_param_read(dir, DBD)) == NULL)
350 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
352 if (dbif_env_init(dbp, DBOPTIONS) < 0)
353 exit(2); /* FIXME: same exit code as failure for dbif_open() */
354 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
356 if (dbif_open(dbp, 0) < 0) {
360 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
362 if (dbd_stamp() < 0) {
366 LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
368 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
376 if (dbif_close() < 0)
384 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
386 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");