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;
289 set_processname("cnid_dbd");
291 /* FIXME: implement -d from cnid_metad */
293 LOG(log_error, logtype_cnid, "main: not enough arguments");
297 ctrlfd = atoi(argv[2]);
298 clntfd = atoi(argv[3]);
299 logconfig = strdup(argv[4]);
302 /* Load .volinfo file */
303 if (loadvolinfo(argv[1], &volinfo) == -1) {
304 LOG(log_error, logtype_cnid, "Cant load volinfo for \"%s\"", argv[1]);
307 /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
308 char dbpath[MAXPATHLEN+1];
309 if ((strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
310 LOG(log_error, logtype_cnid, "CNID db pathname too long: \"%s\"", volinfo.v_dbpath);
313 strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
314 strcat(dbpath, "/.AppleDB");
316 if (vol_load_charsets(&volinfo) == -1) {
317 LOG(log_error, logtype_cnid, "Error loading charsets!");
320 LOG(log_debug, logtype_cnid, "db dir: \"%s\"", dbpath);
322 switch_to_user(dbpath);
325 if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1) {
326 LOG(log_error, logtype_cnid, "main: fatal db lock error");
329 if (db_locked != LOCK_EXCL) {
330 /* Couldn't get exclusive lock, try shared lock */
331 if ((db_locked = get_lock(LOCK_SHRD, NULL)) != LOCK_SHRD) {
332 LOG(log_error, logtype_cnid, "main: fatal db lock error");
339 /* SIGINT and SIGTERM are always off, unless we are in pselect */
342 if ((dbp = db_param_read(dbpath)) == NULL)
344 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
346 if (NULL == (dbd = dbif_init(dbpath, "cnid2.db")))
349 /* Only recover if we got the lock */
350 if (dbif_env_open(dbd,
352 (db_locked == LOCK_EXCL) ? DBOPTIONS | DB_RECOVER : DBOPTIONS) < 0)
353 exit(2); /* FIXME: same exit code as failure for dbif_open() */
354 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
356 if (dbif_open(dbd, dbp, 0) < 0) {
360 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
362 /* Downgrade db lock */
363 if (db_locked == LOCK_EXCL) {
364 if (get_lock(LOCK_UNLOCK, NULL) != 0) {
368 if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD) {
375 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
383 if (dbif_close(dbd) < 0)
386 if (dbif_env_remove(dbpath) < 0)
392 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
394 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");