2 * Copyright (C) Joerg Lenneis 2003
3 * Copyright (c) Frank Lahm 2009
4 * All Rights Reserved. See COPYING.
9 #endif /* HAVE_CONFIG_H */
18 #include <sys/types.h>
19 #include <sys/param.h>
23 #include <arpa/inet.h>
25 #include <atalk/cnid_dbd_private.h>
26 #include <atalk/logger.h>
27 #include <atalk/volinfo.h>
34 #define LOCKFILENAME "lock"
37 Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
38 It's a likey performance hit, but it might we worth it.
40 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | DB_RECOVER)
42 /* Global, needed by pack.c:idxname() */
43 struct volinfo volinfo;
46 static int exit_sig = 0;
48 static void sig_exit(int signo)
54 static void block_sigs_onoff(int block)
59 sigaddset(&set, SIGINT);
60 sigaddset(&set, SIGTERM);
62 sigprocmask(SIG_BLOCK, &set, NULL);
64 sigprocmask(SIG_UNBLOCK, &set, NULL);
69 The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
71 1: Success, if transactions are used commit.
72 0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
73 -1: Fatal error, either from t
74 he database or from the socket. Abort the transaction if applicable
75 (which might fail as well) and then exit.
77 We always try to notify the client process about the outcome, the result field
78 of the cnid_dbd_rply structure contains further details.
82 #define min(a,b) ((a)<(b)?(a):(b))
85 static int loop(struct db_param *dbp)
87 struct cnid_dbd_rqst rqst;
88 struct cnid_dbd_rply rply;
92 time_t now, time_next_flush, time_last_rqst;
94 static char namebuf[MAXPATHLEN + 1];
98 sigprocmask(SIG_SETMASK, NULL, &set);
99 sigdelset(&set, SIGINT);
100 sigdelset(&set, SIGTERM);
104 time_next_flush = now + dbp->flush_interval;
105 time_last_rqst = now;
109 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
110 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
111 dbp->flush_interval, timebuf);
114 timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
120 if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
124 /* comm_rcv returned from select without receiving anything. */
126 /* Received signal (TERM|INT) */
129 if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
133 /* still active connections, reset time_last_rqst */
134 time_last_rqst = now;
136 /* We got a request */
137 time_last_rqst = now;
139 memset(&rply, 0, sizeof(rply));
141 /* ret gets set here */
142 case CNID_DBD_OP_OPEN:
143 case CNID_DBD_OP_CLOSE:
144 /* open/close are noops for now. */
148 case CNID_DBD_OP_ADD:
149 ret = dbd_add(dbd, &rqst, &rply, 0);
151 case CNID_DBD_OP_GET:
152 ret = dbd_get(dbd, &rqst, &rply);
154 case CNID_DBD_OP_RESOLVE:
155 ret = dbd_resolve(dbd, &rqst, &rply);
157 case CNID_DBD_OP_LOOKUP:
158 ret = dbd_lookup(dbd, &rqst, &rply, 0);
160 case CNID_DBD_OP_UPDATE:
161 ret = dbd_update(dbd, &rqst, &rply);
163 case CNID_DBD_OP_DELETE:
164 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
166 case CNID_DBD_OP_GETSTAMP:
167 ret = dbd_getstamp(dbd, &rqst, &rply);
169 case CNID_DBD_OP_REBUILD_ADD:
170 ret = dbd_rebuild_add(dbd, &rqst, &rply);
172 case CNID_DBD_OP_SEARCH:
173 ret = dbd_search(dbd, &rqst, &rply);
176 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
181 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
186 if (ret == 0 || cret == 0) {
187 if (dbif_txn_abort(dbd) < 0)
190 ret = dbif_txn_commit(dbd);
194 /* We had a designated txn because we wrote to the db */
197 } /* got a request */
200 Shall we checkpoint bdb ?
201 "flush_interval" seconds passed ?
203 if (now >= time_next_flush) {
204 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
205 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
208 time_next_flush = now + dbp->flush_interval;
210 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
211 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
212 dbp->flush_interval, timebuf);
216 Shall we checkpoint bdb ?
217 Have we commited "count" more changes than "flush_frequency" ?
219 if (count > dbp->flush_frequency) {
220 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
221 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
228 /* ------------------------ */
229 static void switch_to_user(char *dir)
233 if (chdir(dir) < 0) {
234 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
238 if (stat(".", &st) < 0) {
239 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
243 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
244 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
245 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
251 /* ------------------------ */
252 static int get_lock(void)
257 if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
258 LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
263 lock.l_whence = SEEK_SET;
265 lock.l_type = F_WRLCK;
267 if (fcntl(lockfd, F_SETLK, &lock) < 0) {
268 if (errno == EACCES || errno == EAGAIN) {
269 LOG(log_error, logtype_cnid, "get_lock: locked");
272 LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
280 /* ----------------------- */
281 static void set_signal(void)
285 sv.sa_handler = sig_exit;
287 sigemptyset(&sv.sa_mask);
288 sigaddset(&sv.sa_mask, SIGINT);
289 sigaddset(&sv.sa_mask, SIGTERM);
290 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
291 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
294 sv.sa_handler = SIG_IGN;
295 sigemptyset(&sv.sa_mask);
296 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
297 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
302 /* ----------------------- */
303 static void free_lock(int lockfd)
308 lock.l_whence = SEEK_SET;
310 lock.l_type = F_UNLCK;
311 fcntl(lockfd, F_SETLK, &lock);
315 /* ------------------------ */
316 int main(int argc, char *argv[])
318 struct db_param *dbp;
320 int lockfd, ctrlfd, clntfd;
323 set_processname("cnid_dbd");
325 /* FIXME: implement -d from cnid_metad */
327 LOG(log_error, logtype_cnid, "main: not enough arguments");
331 ctrlfd = atoi(argv[2]);
332 clntfd = atoi(argv[3]);
333 logconfig = strdup(argv[4]);
336 /* Load .volinfo file */
337 if (loadvolinfo(argv[1], &volinfo) == -1) {
338 LOG(log_error, logtype_cnid, "Cant load volinfo for \"%s\"", argv[1]);
341 /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
342 char dbpath[MAXPATHLEN+1];
343 if ((strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
344 LOG(log_error, logtype_cnid, "CNID db pathname too long: \"%s\"", volinfo.v_dbpath);
347 strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
348 strcat(dbpath, "/.AppleDB");
350 if (vol_load_charsets(&volinfo) == -1) {
351 LOG(log_error, logtype_cnid, "Error loading charsets!");
354 LOG(log_debug, logtype_cnid, "db dir: \"%s\"", dbpath);
356 switch_to_user(dbpath);
358 /* Before we do anything else, check if there is an instance of cnid_dbd
359 running already and silently exit if yes. */
364 /* SIGINT and SIGTERM are always off, unless we are in pselect */
367 if ((dbp = db_param_read(dbpath)) == NULL)
369 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
371 if (NULL == (dbd = dbif_init(dbpath, "cnid2.db")))
374 if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
375 exit(2); /* FIXME: same exit code as failure for dbif_open() */
376 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
378 if (dbif_open(dbd, dbp, 0) < 0) {
382 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
384 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
392 if (dbif_close(dbd) < 0)
395 if (dbif_env_remove(dbpath) < 0)
403 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
405 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");