2 * Copyright (C) Joerg Lenneis 2003
3 * Copyright (c) Frank Lahm 2009
4 * All Rights Reserved. See COPYING.
9 #endif /* HAVE_CONFIG_H */
13 #endif /* HAVE_UNISTD_H */
16 #endif /* HAVE_FCNTL_H */
22 #ifdef HAVE_SYS_TYPES_H
23 #include <sys/types.h>
24 #endif /* HAVE_SYS_TYPES_H */
25 #include <sys/param.h>
26 #ifdef HAVE_SYS_STAT_H
28 #endif /* HAVE_SYS_STAT_H */
32 #include <netatalk/endian.h>
33 #include <atalk/cnid_dbd_private.h>
34 #include <atalk/logger.h>
35 #include <atalk/volinfo.h>
42 #define LOCKFILENAME "lock"
45 Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
46 It's a likey performance hit, but it might we worth it.
48 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | DB_RECOVER)
51 struct volinfo volinfo;
54 static int exit_sig = 0;
56 static void sig_exit(int signo)
62 static void block_sigs_onoff(int block)
67 sigaddset(&set, SIGINT);
68 sigaddset(&set, SIGTERM);
70 sigprocmask(SIG_BLOCK, &set, NULL);
72 sigprocmask(SIG_UNBLOCK, &set, NULL);
77 The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
79 1: Success, if transactions are used commit.
80 0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
81 -1: Fatal error, either from t
82 he database or from the socket. Abort the transaction if applicable
83 (which might fail as well) and then exit.
85 We always try to notify the client process about the outcome, the result field
86 of the cnid_dbd_rply structure contains further details.
90 #define min(a,b) ((a)<(b)?(a):(b))
93 static int loop(struct db_param *dbp)
95 struct cnid_dbd_rqst rqst;
96 struct cnid_dbd_rply rply;
100 time_t now, time_next_flush, time_last_rqst;
102 static char namebuf[MAXPATHLEN + 1];
106 sigprocmask(SIG_SETMASK, NULL, &set);
107 sigdelset(&set, SIGINT);
108 sigdelset(&set, SIGTERM);
112 time_next_flush = now + dbp->flush_interval;
113 time_last_rqst = now;
117 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
118 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
119 dbp->flush_interval, timebuf);
122 timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
128 if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
132 /* comm_rcv returned from select without receiving anything. */
134 /* Received signal (TERM|INT) */
137 if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
141 /* still active connections, reset time_last_rqst */
142 time_last_rqst = now;
144 /* We got a request */
145 time_last_rqst = now;
147 memset(&rply, 0, sizeof(rply));
149 /* ret gets set here */
150 case CNID_DBD_OP_OPEN:
151 case CNID_DBD_OP_CLOSE:
152 /* open/close are noops for now. */
156 case CNID_DBD_OP_ADD:
157 ret = dbd_add(dbd, &rqst, &rply, 0);
159 case CNID_DBD_OP_GET:
160 ret = dbd_get(dbd, &rqst, &rply);
162 case CNID_DBD_OP_RESOLVE:
163 ret = dbd_resolve(dbd, &rqst, &rply);
165 case CNID_DBD_OP_LOOKUP:
166 ret = dbd_lookup(dbd, &rqst, &rply, 0);
168 case CNID_DBD_OP_UPDATE:
169 ret = dbd_update(dbd, &rqst, &rply);
171 case CNID_DBD_OP_DELETE:
172 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
174 case CNID_DBD_OP_GETSTAMP:
175 ret = dbd_getstamp(dbd, &rqst, &rply);
177 case CNID_DBD_OP_REBUILD_ADD:
178 ret = dbd_rebuild_add(dbd, &rqst, &rply);
180 case CNID_DBD_OP_SEARCH:
181 ret = dbd_search(dbd, &rqst, &rply);
184 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
189 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
194 if (ret == 0 || cret == 0) {
195 if (dbif_txn_abort(dbd) < 0)
198 ret = dbif_txn_commit(dbd);
202 /* We had a designated txn because we wrote to the db */
205 } /* got a request */
208 Shall we checkpoint bdb ?
209 "flush_interval" seconds passed ?
211 if (now >= time_next_flush) {
212 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
213 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
216 time_next_flush = now + dbp->flush_interval;
218 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
219 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
220 dbp->flush_interval, timebuf);
224 Shall we checkpoint bdb ?
225 Have we commited "count" more changes than "flush_frequency" ?
227 if (count > dbp->flush_frequency) {
228 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
229 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
236 /* ------------------------ */
237 static void switch_to_user(char *dir)
241 if (chdir(dir) < 0) {
242 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
246 if (stat(".", &st) < 0) {
247 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
251 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
252 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
253 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
259 /* ------------------------ */
260 static int get_lock(void)
265 if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
266 LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
271 lock.l_whence = SEEK_SET;
273 lock.l_type = F_WRLCK;
275 if (fcntl(lockfd, F_SETLK, &lock) < 0) {
276 if (errno == EACCES || errno == EAGAIN) {
277 LOG(log_error, logtype_cnid, "get_lock: locked");
280 LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
288 /* ----------------------- */
289 static void set_signal(void)
293 sv.sa_handler = sig_exit;
295 sigemptyset(&sv.sa_mask);
296 sigaddset(&sv.sa_mask, SIGINT);
297 sigaddset(&sv.sa_mask, SIGTERM);
298 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
299 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
302 sv.sa_handler = SIG_IGN;
303 sigemptyset(&sv.sa_mask);
304 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
305 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
310 /* ----------------------- */
311 static void free_lock(int lockfd)
316 lock.l_whence = SEEK_SET;
318 lock.l_type = F_UNLCK;
319 fcntl(lockfd, F_SETLK, &lock);
323 /* ------------------------ */
324 int main(int argc, char *argv[])
326 struct db_param *dbp;
328 int lockfd, ctrlfd, clntfd;
331 set_processname("cnid_dbd");
333 /* FIXME: implement -d from cnid_metad */
335 LOG(log_error, logtype_cnid, "main: not enough arguments");
339 ctrlfd = atoi(argv[2]);
340 clntfd = atoi(argv[3]);
341 logconfig = strdup(argv[4]);
344 /* Load .volinfo file */
345 if (loadvolinfo(argv[1], &volinfo) == -1) {
346 LOG(log_error, logtype_cnid, "Cant load volinfo for \"%s\"", argv[1]);
349 /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
350 char dbpath[MAXPATHLEN+1];
351 if ((strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
352 LOG(log_error, logtype_cnid, "CNID db pathname too long: \"%s\"", volinfo.v_dbpath);
355 strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
356 strcat(dbpath, "/.AppleDB");
358 if (vol_load_charsets(&volinfo) == -1) {
359 LOG(log_error, logtype_cnid, "Error loading charsets!");
362 LOG(log_note, logtype_cnid, "db dir: \"%s\"", dbpath);
364 switch_to_user(dbpath);
366 /* Before we do anything else, check if there is an instance of cnid_dbd
367 running already and silently exit if yes. */
372 /* SIGINT and SIGTERM are always off, unless we are in pselect */
375 if ((dbp = db_param_read(dbpath, CNID_DBD)) == NULL)
377 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
379 if (NULL == (dbd = dbif_init(".", "cnid2.db")))
382 if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
383 exit(2); /* FIXME: same exit code as failure for dbif_open() */
384 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
386 if (dbif_open(dbd, dbp, 0) < 0) {
390 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
392 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
400 if (dbif_close(dbd) < 0)
403 if (dbif_env_remove(dbpath) < 0)
411 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
413 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");