2 * $Id: main.c,v 1.14 2009-10-19 07:46:35 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) */
138 if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
142 /* still active connections, reset time_last_rqst */
143 time_last_rqst = now;
145 /* We got a request */
146 time_last_rqst = now;
148 memset(&rply, 0, sizeof(rply));
150 /* ret gets set here */
151 case CNID_DBD_OP_OPEN:
152 case CNID_DBD_OP_CLOSE:
153 /* open/close are noops for now. */
157 case CNID_DBD_OP_ADD:
158 ret = dbd_add(dbd, &rqst, &rply);
160 case CNID_DBD_OP_GET:
161 ret = dbd_get(dbd, &rqst, &rply);
163 case CNID_DBD_OP_RESOLVE:
164 ret = dbd_resolve(dbd, &rqst, &rply);
166 case CNID_DBD_OP_LOOKUP:
167 ret = dbd_lookup(dbd, &rqst, &rply);
169 case CNID_DBD_OP_UPDATE:
170 ret = dbd_update(dbd, &rqst, &rply);
172 case CNID_DBD_OP_DELETE:
173 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
175 case CNID_DBD_OP_GETSTAMP:
176 ret = dbd_getstamp(dbd, &rqst, &rply);
178 case CNID_DBD_OP_REBUILD_ADD:
179 ret = dbd_rebuild_add(dbd, &rqst, &rply);
182 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
187 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
192 if (ret == 0 || cret == 0) {
193 if (dbif_txn_abort(dbd) < 0)
196 ret = dbif_txn_commit(dbd);
200 /* We had a designated txn because we wrote to the db */
203 } /* got a request */
206 Shall we checkpoint bdb ?
207 "flush_interval" seconds passed ?
209 if (now >= time_next_flush) {
210 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
211 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
214 time_next_flush = now + dbp->flush_interval;
216 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
217 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
218 dbp->flush_interval, timebuf);
222 Shall we checkpoint bdb ?
223 Have we commited "count" more changes than "flush_frequency" ?
225 if (count > dbp->flush_frequency) {
226 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
227 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
234 /* ------------------------ */
235 static void switch_to_user(char *dir)
239 if (chdir(dir) < 0) {
240 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
244 if (stat(".", &st) < 0) {
245 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
249 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
250 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
251 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
257 /* ------------------------ */
258 static int get_lock(void)
263 if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
264 LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
269 lock.l_whence = SEEK_SET;
271 lock.l_type = F_WRLCK;
273 if (fcntl(lockfd, F_SETLK, &lock) < 0) {
274 if (errno == EACCES || errno == EAGAIN) {
277 LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
285 /* ----------------------- */
286 static void set_signal(void)
290 sv.sa_handler = sig_exit;
292 sigemptyset(&sv.sa_mask);
293 sigaddset(&sv.sa_mask, SIGINT);
294 sigaddset(&sv.sa_mask, SIGTERM);
295 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
296 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
299 sv.sa_handler = SIG_IGN;
300 sigemptyset(&sv.sa_mask);
301 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
302 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
307 /* ----------------------- */
308 static void free_lock(int lockfd)
313 lock.l_whence = SEEK_SET;
315 lock.l_type = F_UNLCK;
316 fcntl(lockfd, F_SETLK, &lock);
320 /* ------------------------ */
321 int main(int argc, char *argv[])
323 struct db_param *dbp;
325 int lockfd, ctrlfd, clntfd;
326 char *dir, *logconfig;
328 set_processname("cnid_dbd");
330 /* FIXME: implement -d from cnid_metad */
332 LOG(log_error, logtype_cnid, "main: not enough arguments");
337 ctrlfd = atoi(argv[2]);
338 clntfd = atoi(argv[3]);
339 logconfig = strdup(argv[4]);
344 /* Before we do anything else, check if there is an instance of cnid_dbd
345 running already and silently exit if yes. */
348 LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
352 /* SIGINT and SIGTERM are always off, unless we are in pselect */
355 if ((dbp = db_param_read(dir, CNID_DBD)) == NULL)
357 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
359 if (NULL == (dbd = dbif_init(".", "cnid2.db")))
362 if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
363 exit(2); /* FIXME: same exit code as failure for dbif_open() */
364 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
366 if (dbif_open(dbd, dbp, 0) < 0) {
370 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
372 if (dbd_stamp(dbd) < 0) {
376 LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
378 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
386 if (dbif_close(dbd) < 0)
389 if (dbif_prep_upgrade(dir) < 0)
397 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
399 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");