2 * $Id: main.c,v 1.6 2009-05-06 11:54:24 franklahm 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 static int loop(struct db_param *dbp)
90 struct cnid_dbd_rqst rqst;
91 struct cnid_dbd_rply rply;
94 time_t now, time_next_flush, time_last_rqst;
96 static char namebuf[MAXPATHLEN + 1];
100 time_next_flush = now + dbp->flush_interval;
101 time_last_rqst = now;
105 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
106 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
107 dbp->flush_interval, timebuf);
110 if ((cret = comm_rcv(&rqst)) < 0)
116 /* comm_rcv returned from select without receiving anything. */
118 /* Give signals a chance... */
122 /* Received signal (TERM|INT) */
124 if (dbp->idle_timeout && comm_nbe() <= 0 && (now - time_last_rqst) > dbp->idle_timeout)
128 /* We got a request */
129 time_last_rqst = now;
131 memset(&rply, 0, sizeof(rply));
133 /* ret gets set here */
134 case CNID_DBD_OP_OPEN:
135 case CNID_DBD_OP_CLOSE:
136 /* open/close are noops for now. */
140 case CNID_DBD_OP_ADD:
141 ret = dbd_add(dbd, &rqst, &rply);
143 case CNID_DBD_OP_GET:
144 ret = dbd_get(dbd, &rqst, &rply);
146 case CNID_DBD_OP_RESOLVE:
147 ret = dbd_resolve(dbd, &rqst, &rply);
149 case CNID_DBD_OP_LOOKUP:
150 ret = dbd_lookup(dbd, &rqst, &rply);
152 case CNID_DBD_OP_UPDATE:
153 ret = dbd_update(dbd, &rqst, &rply);
155 case CNID_DBD_OP_DELETE:
156 ret = dbd_delete(dbd, &rqst, &rply);
158 case CNID_DBD_OP_GETSTAMP:
159 ret = dbd_getstamp(dbd, &rqst, &rply);
161 case CNID_DBD_OP_REBUILD_ADD:
162 ret = dbd_rebuild_add(dbd, &rqst, &rply);
165 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
170 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
175 if (ret == 0 || cret == 0) {
176 if (dbif_txn_abort(dbd) < 0)
179 ret = dbif_txn_commit(dbd);
183 /* We had a designated txn because we wrote to the db */
186 } /* got a request */
189 Shall we checkpoint bdb ?
190 "flush_interval" seconds passed ?
192 if (now > time_next_flush) {
193 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
194 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
197 time_next_flush = now + dbp->flush_interval;
199 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
200 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
201 dbp->flush_interval, timebuf);
205 Shall we checkpoint bdb ?
206 Have we commited "count" more changes than "flush_frequency" ?
208 if (count > dbp->flush_frequency) {
209 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
210 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
217 /* ------------------------ */
218 static void switch_to_user(char *dir)
222 if (chdir(dir) < 0) {
223 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
227 if (stat(".", &st) < 0) {
228 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
232 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
233 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
234 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
240 /* ------------------------ */
246 if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
247 LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
252 lock.l_whence = SEEK_SET;
254 lock.l_type = F_WRLCK;
256 if (fcntl(lockfd, F_SETLK, &lock) < 0) {
257 if (errno == EACCES || errno == EAGAIN) {
260 LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
268 /* ----------------------- */
269 void set_signal(void)
273 sv.sa_handler = sig_exit;
275 sigemptyset(&sv.sa_mask);
276 sigaddset(&sv.sa_mask, SIGINT);
277 sigaddset(&sv.sa_mask, SIGTERM);
278 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
279 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
282 sv.sa_handler = SIG_IGN;
283 sigemptyset(&sv.sa_mask);
284 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
285 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
290 /* ----------------------- */
291 void free_lock(int lockfd)
296 lock.l_whence = SEEK_SET;
298 lock.l_type = F_UNLCK;
299 fcntl(lockfd, F_SETLK, &lock);
303 /* ------------------------ */
304 int main(int argc, char *argv[])
306 struct db_param *dbp;
308 int lockfd, ctrlfd, clntfd;
309 char *dir, *logconfig;
311 set_processname("cnid_dbd");
314 LOG(log_error, logtype_cnid, "main: not enough arguments");
319 ctrlfd = atoi(argv[2]);
320 clntfd = atoi(argv[3]);
321 logconfig = strdup(argv[4]);
326 /* Before we do anything else, check if there is an instance of cnid_dbd
327 running already and silently exit if yes. */
330 LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
334 /* SIGINT and SIGTERM are always off, unless we get a return code of 0 from
335 comm_rcv (no requests for one second, see above in loop()). That means we
336 only shut down after one second of inactivity. */
339 if ((dbp = db_param_read(dir, CNID_DBD)) == NULL)
341 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
343 if (NULL == (dbd = dbif_init("cnid2.db")))
346 if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
347 exit(2); /* FIXME: same exit code as failure for dbif_open() */
348 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
350 if (dbif_open(dbd, dbp, 0) < 0) {
354 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
356 if (dbd_stamp(dbd) < 0) {
360 LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
362 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
370 if (dbif_close(dbd) < 0)
378 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
380 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");