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)
52 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 * Get lock on db lock file
78 * @args cmd (r) !=0: lock, 0: unlock
79 * @returns 1 if lock was acquired, 0 if file is already locked, -1 on error
81 static int get_lock(int cmd)
83 static int lockfd = -1;
91 lock.l_whence = SEEK_SET;
93 lock.l_type = F_UNLCK;
94 fcntl(lockfd, F_SETLK, &lock);
101 if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
102 LOG(log_error, logtype_cnid, "get_lock: error opening lockfile: %s", strerror(errno));
108 lock.l_whence = SEEK_SET;
110 lock.l_type = F_WRLCK;
112 if (fcntl(lockfd, F_SETLK, &lock) < 0) {
113 if (errno == EACCES || errno == EAGAIN) {
114 LOG(log_debug, logtype_cnid, "get_lock: couldn't lock");
117 LOG(log_error, logtype_cnid, "get_lock: fcntl F_WRLCK lockfile: %s", strerror(errno));
122 LOG(log_debug, logtype_cnid, "get_lock: got lock");
127 The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
129 1: Success, if transactions are used commit.
130 0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
131 -1: Fatal error, either from t
132 he database or from the socket. Abort the transaction if applicable
133 (which might fail as well) and then exit.
135 We always try to notify the client process about the outcome, the result field
136 of the cnid_dbd_rply structure contains further details.
140 #define min(a,b) ((a)<(b)?(a):(b))
143 static int loop(struct db_param *dbp)
145 struct cnid_dbd_rqst rqst;
146 struct cnid_dbd_rply rply;
150 time_t now, time_next_flush, time_last_rqst;
152 static char namebuf[MAXPATHLEN + 1];
156 sigprocmask(SIG_SETMASK, NULL, &set);
157 sigdelset(&set, SIGINT);
158 sigdelset(&set, SIGTERM);
162 time_next_flush = now + dbp->flush_interval;
163 time_last_rqst = now;
167 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
168 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
169 dbp->flush_interval, timebuf);
173 * If we haven't got the lock yet, get it now.
174 * Prevents a race with dbd:
175 * 1. no cnid_dbd running
176 * 2. dbd -r starts, gets the lock
177 * 3. cnid_dbd starts, doesn't get lock, doesn't run recovery, all is fine
178 * 4. dbd from (2) finishes, drops lock
179 * 5. anothet dbd but this time with -re is started which
180 * - succeeds getting the lock
181 * - runs recovery => this kills (3)
184 if ((db_locked = get_lock(1)) == -1)
186 timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
192 if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
196 /* comm_rcv returned from select without receiving anything. */
198 /* Received signal (TERM|INT) */
201 if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
205 /* still active connections, reset time_last_rqst */
206 time_last_rqst = now;
208 /* We got a request */
209 time_last_rqst = now;
211 memset(&rply, 0, sizeof(rply));
213 /* ret gets set here */
214 case CNID_DBD_OP_OPEN:
215 case CNID_DBD_OP_CLOSE:
216 /* open/close are noops for now. */
220 case CNID_DBD_OP_ADD:
221 ret = dbd_add(dbd, &rqst, &rply, 0);
223 case CNID_DBD_OP_GET:
224 ret = dbd_get(dbd, &rqst, &rply);
226 case CNID_DBD_OP_RESOLVE:
227 ret = dbd_resolve(dbd, &rqst, &rply);
229 case CNID_DBD_OP_LOOKUP:
230 ret = dbd_lookup(dbd, &rqst, &rply, 0);
232 case CNID_DBD_OP_UPDATE:
233 ret = dbd_update(dbd, &rqst, &rply);
235 case CNID_DBD_OP_DELETE:
236 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
238 case CNID_DBD_OP_GETSTAMP:
239 ret = dbd_getstamp(dbd, &rqst, &rply);
241 case CNID_DBD_OP_REBUILD_ADD:
242 ret = dbd_rebuild_add(dbd, &rqst, &rply);
245 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
250 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
255 if (ret == 0 || cret == 0) {
256 if (dbif_txn_abort(dbd) < 0)
259 ret = dbif_txn_commit(dbd);
263 /* We had a designated txn because we wrote to the db */
266 } /* got a request */
269 Shall we checkpoint bdb ?
270 "flush_interval" seconds passed ?
272 if (now >= time_next_flush) {
273 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
274 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
277 time_next_flush = now + dbp->flush_interval;
279 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
280 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
281 dbp->flush_interval, timebuf);
285 Shall we checkpoint bdb ?
286 Have we commited "count" more changes than "flush_frequency" ?
288 if (count > dbp->flush_frequency) {
289 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
290 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
297 /* ------------------------ */
298 static void switch_to_user(char *dir)
302 if (chdir(dir) < 0) {
303 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
307 if (stat(".", &st) < 0) {
308 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
312 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
313 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
314 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
321 /* ----------------------- */
322 static void set_signal(void)
326 sv.sa_handler = sig_exit;
328 sigemptyset(&sv.sa_mask);
329 sigaddset(&sv.sa_mask, SIGINT);
330 sigaddset(&sv.sa_mask, SIGTERM);
331 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
332 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
335 sv.sa_handler = SIG_IGN;
336 sigemptyset(&sv.sa_mask);
337 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
338 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
343 /* ------------------------ */
344 int main(int argc, char *argv[])
346 struct db_param *dbp;
349 char *dir, *logconfig;
351 set_processname("cnid_dbd");
353 /* FIXME: implement -d from cnid_metad */
355 LOG(log_error, logtype_cnid, "main: not enough arguments");
360 ctrlfd = atoi(argv[2]);
361 clntfd = atoi(argv[3]);
362 logconfig = strdup(argv[4]);
367 /* Before we do anything else, check if there is an instance of cnid_dbd
368 running already and silently exit if yes. */
369 if ((db_locked = get_lock(1)) == -1) {
373 LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
377 /* SIGINT and SIGTERM are always off, unless we are in pselect */
380 if ((dbp = db_param_read(dir, CNID_DBD)) == NULL)
382 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
384 if (NULL == (dbd = dbif_init(".", "cnid2.db")))
387 /* Only recover if we got the lock */
388 if (dbif_env_open(dbd,
390 db_locked ? DBOPTIONS | DB_RECOVER : DBOPTIONS) < 0)
391 exit(2); /* FIXME: same exit code as failure for dbif_open() */
392 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
394 if (dbif_open(dbd, dbp, 0) < 0) {
398 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
400 if (dbd_stamp(dbd) < 0) {
404 LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
406 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
414 if (dbif_close(dbd) < 0)
417 if (dbif_prep_upgrade(dir) < 0)
425 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
427 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");