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>
43 Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
44 It's a likey performance hit, but it might we worth it.
46 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN)
48 /* Global, needed by pack.c:idxname() */
49 struct volinfo volinfo;
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 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);
179 case CNID_DBD_OP_SEARCH:
180 ret = dbd_search(dbd, &rqst, &rply);
183 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
188 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
193 if (ret == 0 || cret == 0) {
194 if (dbif_txn_abort(dbd) < 0)
197 ret = dbif_txn_commit(dbd);
201 /* We had a designated txn because we wrote to the db */
204 } /* got a request */
207 Shall we checkpoint bdb ?
208 "flush_interval" seconds passed ?
210 if (now >= time_next_flush) {
211 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
212 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
215 time_next_flush = now + dbp->flush_interval;
217 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
218 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
219 dbp->flush_interval, timebuf);
223 Shall we checkpoint bdb ?
224 Have we commited "count" more changes than "flush_frequency" ?
226 if (count > dbp->flush_frequency) {
227 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
228 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
235 /* ------------------------ */
236 static void switch_to_user(char *dir)
240 if (chdir(dir) < 0) {
241 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
245 if (stat(".", &st) < 0) {
246 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
250 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
251 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
252 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
259 /* ----------------------- */
260 static void set_signal(void)
264 sv.sa_handler = sig_exit;
266 sigemptyset(&sv.sa_mask);
267 sigaddset(&sv.sa_mask, SIGINT);
268 sigaddset(&sv.sa_mask, SIGTERM);
269 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
270 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
273 sv.sa_handler = SIG_IGN;
274 sigemptyset(&sv.sa_mask);
275 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
276 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
281 /* ------------------------ */
282 int main(int argc, char *argv[])
284 struct db_param *dbp;
285 int err = 0, ret, delete_bdb = 0;
289 set_processname("cnid_dbd");
291 while (( ret = getopt( argc, argv, "d")) != -1 ) {
299 if (argc - optind != 4) {
300 LOG(log_error, logtype_cnid, "main: not enough arguments");
304 /* Load .volinfo file */
305 if (loadvolinfo(argv[optind], &volinfo) == -1) {
306 LOG(log_error, logtype_cnid, "Cant load volinfo for \"%s\"", argv[1]);
309 /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
310 char dbpath[MAXPATHLEN+1];
311 if ((strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
312 LOG(log_error, logtype_cnid, "CNID db pathname too long: \"%s\"", volinfo.v_dbpath);
315 strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
316 strcat(dbpath, "/.AppleDB");
318 ctrlfd = atoi(argv[optind + 1]);
319 clntfd = atoi(argv[optind + 2]);
320 logconfig = strdup(argv[optind + 3]);
323 if (vol_load_charsets(&volinfo) == -1) {
324 LOG(log_error, logtype_cnid, "Error loading charsets!");
327 LOG(log_debug, logtype_cnid, "db dir: \"%s\"", dbpath);
329 switch_to_user(dbpath);
332 if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1) {
333 LOG(log_error, logtype_cnid, "main: fatal db lock error");
336 if (db_locked != LOCK_EXCL) {
337 /* Couldn't get exclusive lock, try shared lock */
338 if ((db_locked = get_lock(LOCK_SHRD, NULL)) != LOCK_SHRD) {
339 LOG(log_error, logtype_cnid, "main: fatal db lock error");
344 if (delete_bdb && (db_locked == LOCK_EXCL)) {
345 LOG(log_warning, logtype_cnid, "main: too many CNID db opening attempts, wiping the slate clean");
347 system("rm -f cnid2.db lock log.* __db.*");
348 if ((db_locked = get_lock(LOCK_EXCL, dbpath)) != LOCK_EXCL) {
349 LOG(log_error, logtype_cnid, "main: fatal db lock error");
356 /* SIGINT and SIGTERM are always off, unless we are in pselect */
359 if ((dbp = db_param_read(dbpath)) == NULL)
361 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
363 if (NULL == (dbd = dbif_init(dbpath, "cnid2.db")))
366 /* Only recover if we got the lock */
367 if (dbif_env_open(dbd,
369 (db_locked == LOCK_EXCL) ? DBOPTIONS | DB_RECOVER : DBOPTIONS) < 0)
370 exit(2); /* FIXME: same exit code as failure for dbif_open() */
371 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
373 if (dbif_open(dbd, dbp, 0) < 0) {
377 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
379 /* Downgrade db lock */
380 if (db_locked == LOCK_EXCL) {
381 if (get_lock(LOCK_UNLOCK, NULL) != 0) {
385 if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD) {
392 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
400 if (dbif_close(dbd) < 0)
403 if (dbif_env_remove(dbpath) < 0)
409 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
411 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");