2 * $Id: main.c,v 1.16 2009-11-25 14:59:15 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 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, &now)) < 0)
131 /* comm_rcv returned from select without receiving anything. */
133 /* Received signal (TERM|INT) */
136 if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
140 /* still active connections, reset time_last_rqst */
141 time_last_rqst = now;
143 /* We got a request */
144 time_last_rqst = now;
146 memset(&rply, 0, sizeof(rply));
148 /* ret gets set here */
149 case CNID_DBD_OP_OPEN:
150 case CNID_DBD_OP_CLOSE:
151 /* open/close are noops for now. */
155 case CNID_DBD_OP_ADD:
156 ret = dbd_add(dbd, &rqst, &rply, 0);
158 case CNID_DBD_OP_GET:
159 ret = dbd_get(dbd, &rqst, &rply);
161 case CNID_DBD_OP_RESOLVE:
162 ret = dbd_resolve(dbd, &rqst, &rply);
164 case CNID_DBD_OP_LOOKUP:
165 ret = dbd_lookup(dbd, &rqst, &rply, 0);
167 case CNID_DBD_OP_UPDATE:
168 ret = dbd_update(dbd, &rqst, &rply);
170 case CNID_DBD_OP_DELETE:
171 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
173 case CNID_DBD_OP_GETSTAMP:
174 ret = dbd_getstamp(dbd, &rqst, &rply);
176 case CNID_DBD_OP_REBUILD_ADD:
177 ret = dbd_rebuild_add(dbd, &rqst, &rply);
180 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
185 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
190 if (ret == 0 || cret == 0) {
191 if (dbif_txn_abort(dbd) < 0)
194 ret = dbif_txn_commit(dbd);
198 /* We had a designated txn because we wrote to the db */
201 } /* got a request */
204 Shall we checkpoint bdb ?
205 "flush_interval" seconds passed ?
207 if (now >= time_next_flush) {
208 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
209 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
212 time_next_flush = now + dbp->flush_interval;
214 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
215 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
216 dbp->flush_interval, timebuf);
220 Shall we checkpoint bdb ?
221 Have we commited "count" more changes than "flush_frequency" ?
223 if (count > dbp->flush_frequency) {
224 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
225 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
232 /* ------------------------ */
233 static void switch_to_user(char *dir)
237 if (chdir(dir) < 0) {
238 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
242 if (stat(".", &st) < 0) {
243 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
247 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
248 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
249 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
255 /* ------------------------ */
256 static int get_lock(void)
261 if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
262 LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
267 lock.l_whence = SEEK_SET;
269 lock.l_type = F_WRLCK;
271 if (fcntl(lockfd, F_SETLK, &lock) < 0) {
272 if (errno == EACCES || errno == EAGAIN) {
275 LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
283 /* ----------------------- */
284 static void set_signal(void)
288 sv.sa_handler = sig_exit;
290 sigemptyset(&sv.sa_mask);
291 sigaddset(&sv.sa_mask, SIGINT);
292 sigaddset(&sv.sa_mask, SIGTERM);
293 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
294 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
297 sv.sa_handler = SIG_IGN;
298 sigemptyset(&sv.sa_mask);
299 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
300 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
305 /* ----------------------- */
306 static void free_lock(int lockfd)
311 lock.l_whence = SEEK_SET;
313 lock.l_type = F_UNLCK;
314 fcntl(lockfd, F_SETLK, &lock);
318 /* ------------------------ */
319 int main(int argc, char *argv[])
321 struct db_param *dbp;
323 int lockfd, ctrlfd, clntfd;
324 char *dir, *logconfig;
326 set_processname("cnid_dbd");
328 /* FIXME: implement -d from cnid_metad */
330 LOG(log_error, logtype_cnid, "main: not enough arguments");
335 ctrlfd = atoi(argv[2]);
336 clntfd = atoi(argv[3]);
337 logconfig = strdup(argv[4]);
342 /* Before we do anything else, check if there is an instance of cnid_dbd
343 running already and silently exit if yes. */
346 LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
350 /* SIGINT and SIGTERM are always off, unless we are in pselect */
353 if ((dbp = db_param_read(dir, CNID_DBD)) == NULL)
355 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
357 if (NULL == (dbd = dbif_init(".", "cnid2.db")))
360 if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
361 exit(2); /* FIXME: same exit code as failure for dbif_open() */
362 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
364 if (dbif_open(dbd, dbp, 0) < 0) {
368 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
370 if (dbd_stamp(dbd) < 0) {
374 LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
376 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
384 if (dbif_close(dbd) < 0)
387 if (dbif_prep_upgrade(dir) < 0)
395 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
397 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");