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>
35 Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
36 It's a likey performance hit, but it might we worth it.
38 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN)
40 /* Global, needed by pack.c:idxname() */
41 struct volinfo volinfo;
44 static int exit_sig = 0;
47 static void sig_exit(int signo)
53 static void block_sigs_onoff(int block)
58 sigaddset(&set, SIGINT);
59 sigaddset(&set, SIGTERM);
61 sigprocmask(SIG_BLOCK, &set, NULL);
63 sigprocmask(SIG_UNBLOCK, &set, NULL);
68 The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
70 1: Success, if transactions are used commit.
71 0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
72 -1: Fatal error, either from t
73 he database or from the socket. Abort the transaction if applicable
74 (which might fail as well) and then exit.
76 We always try to notify the client process about the outcome, the result field
77 of the cnid_dbd_rply structure contains further details.
81 #define min(a,b) ((a)<(b)?(a):(b))
84 static int loop(struct db_param *dbp)
86 struct cnid_dbd_rqst rqst;
87 struct cnid_dbd_rply rply;
91 time_t now, time_next_flush, time_last_rqst;
93 static char namebuf[MAXPATHLEN + 1];
97 sigprocmask(SIG_SETMASK, NULL, &set);
98 sigdelset(&set, SIGINT);
99 sigdelset(&set, SIGTERM);
103 time_next_flush = now + dbp->flush_interval;
104 time_last_rqst = now;
108 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
109 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
110 dbp->flush_interval, timebuf);
113 timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
119 if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
123 /* comm_rcv returned from select without receiving anything. */
125 /* Received signal (TERM|INT) */
128 if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
132 /* still active connections, reset time_last_rqst */
133 time_last_rqst = now;
135 /* We got a request */
136 time_last_rqst = now;
138 memset(&rply, 0, sizeof(rply));
140 /* ret gets set here */
141 case CNID_DBD_OP_OPEN:
142 case CNID_DBD_OP_CLOSE:
143 /* open/close are noops for now. */
147 case CNID_DBD_OP_ADD:
148 ret = dbd_add(dbd, &rqst, &rply, 0);
150 case CNID_DBD_OP_GET:
151 ret = dbd_get(dbd, &rqst, &rply);
153 case CNID_DBD_OP_RESOLVE:
154 ret = dbd_resolve(dbd, &rqst, &rply);
156 case CNID_DBD_OP_LOOKUP:
157 ret = dbd_lookup(dbd, &rqst, &rply, 0);
159 case CNID_DBD_OP_UPDATE:
160 ret = dbd_update(dbd, &rqst, &rply);
162 case CNID_DBD_OP_DELETE:
163 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
165 case CNID_DBD_OP_GETSTAMP:
166 ret = dbd_getstamp(dbd, &rqst, &rply);
168 case CNID_DBD_OP_REBUILD_ADD:
169 ret = dbd_rebuild_add(dbd, &rqst, &rply);
171 case CNID_DBD_OP_SEARCH:
172 ret = dbd_search(dbd, &rqst, &rply);
175 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
180 if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
185 if (ret == 0 || cret == 0) {
186 if (dbif_txn_abort(dbd) < 0)
189 ret = dbif_txn_commit(dbd);
193 /* We had a designated txn because we wrote to the db */
196 } /* got a request */
199 Shall we checkpoint bdb ?
200 "flush_interval" seconds passed ?
202 if (now >= time_next_flush) {
203 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
204 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
207 time_next_flush = now + dbp->flush_interval;
209 strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
210 LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
211 dbp->flush_interval, timebuf);
215 Shall we checkpoint bdb ?
216 Have we commited "count" more changes than "flush_frequency" ?
218 if (count > dbp->flush_frequency) {
219 LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
220 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
227 /* ------------------------ */
228 static void switch_to_user(char *dir)
232 if (chdir(dir) < 0) {
233 LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
237 if (stat(".", &st) < 0) {
238 LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
242 LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
243 if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
244 LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
251 /* ----------------------- */
252 static void set_signal(void)
256 sv.sa_handler = sig_exit;
258 sigemptyset(&sv.sa_mask);
259 sigaddset(&sv.sa_mask, SIGINT);
260 sigaddset(&sv.sa_mask, SIGTERM);
261 if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
262 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
265 sv.sa_handler = SIG_IGN;
266 sigemptyset(&sv.sa_mask);
267 if (sigaction(SIGPIPE, &sv, NULL) < 0) {
268 LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
273 /* ------------------------ */
274 int main(int argc, char *argv[])
276 struct db_param *dbp;
277 int err = 0, ret, delete_bdb = 0;
281 set_processname("cnid_dbd");
283 while (( ret = getopt( argc, argv, "d")) != -1 ) {
291 if (argc - optind != 4) {
292 LOG(log_error, logtype_cnid, "main: not enough arguments");
296 /* Load .volinfo file */
297 if (loadvolinfo(argv[optind], &volinfo) == -1) {
298 LOG(log_error, logtype_cnid, "Cant load volinfo for \"%s\"", argv[1]);
301 /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
302 char dbpath[MAXPATHLEN+1];
303 if ((strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
304 LOG(log_error, logtype_cnid, "CNID db pathname too long: \"%s\"", volinfo.v_dbpath);
307 strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
308 strcat(dbpath, "/.AppleDB");
310 ctrlfd = atoi(argv[optind + 1]);
311 clntfd = atoi(argv[optind + 2]);
312 logconfig = strdup(argv[optind + 3]);
315 if (vol_load_charsets(&volinfo) == -1) {
316 LOG(log_error, logtype_cnid, "Error loading charsets!");
319 LOG(log_debug, logtype_cnid, "db dir: \"%s\"", dbpath);
321 switch_to_user(dbpath);
324 if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1) {
325 LOG(log_error, logtype_cnid, "main: fatal db lock error");
328 if (db_locked != LOCK_EXCL) {
329 /* Couldn't get exclusive lock, try shared lock */
330 if ((db_locked = get_lock(LOCK_SHRD, NULL)) != LOCK_SHRD) {
331 LOG(log_error, logtype_cnid, "main: fatal db lock error");
336 if (delete_bdb && (db_locked == LOCK_EXCL)) {
337 LOG(log_warning, logtype_cnid, "main: too many CNID db opening attempts, wiping the slate clean");
339 system("rm -f cnid2.db lock log.* __db.*");
340 if ((db_locked = get_lock(LOCK_EXCL, dbpath)) != LOCK_EXCL) {
341 LOG(log_error, logtype_cnid, "main: fatal db lock error");
348 /* SIGINT and SIGTERM are always off, unless we are in pselect */
351 if ((dbp = db_param_read(dbpath)) == NULL)
353 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
355 if (NULL == (dbd = dbif_init(dbpath, "cnid2.db")))
358 /* Only recover if we got the lock */
359 if (dbif_env_open(dbd,
361 (db_locked == LOCK_EXCL) ? DBOPTIONS | DB_RECOVER : DBOPTIONS) < 0)
362 exit(2); /* FIXME: same exit code as failure for dbif_open() */
363 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
365 if (dbif_open(dbd, dbp, 0) < 0) {
369 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
371 /* Downgrade db lock */
372 if (db_locked == LOCK_EXCL) {
373 if (get_lock(LOCK_UNLOCK, NULL) != 0) {
377 if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD) {
384 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
392 if (dbif_close(dbd) < 0)
395 if (dbif_env_remove(dbpath) < 0)
401 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
403 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");