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, "vVd")) != -1 ) {
295 printf("cnid_dbd (Netatalk %s)\n", VERSION);
303 if (argc - optind != 4) {
304 LOG(log_error, logtype_cnid, "main: not enough arguments");
308 /* Load .volinfo file */
309 if (loadvolinfo(argv[optind], &volinfo) == -1) {
310 LOG(log_error, logtype_cnid, "Cant load volinfo for \"%s\"", argv[1]);
313 /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
314 char dbpath[MAXPATHLEN+1];
315 if ((strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
316 LOG(log_error, logtype_cnid, "CNID db pathname too long: \"%s\"", volinfo.v_dbpath);
319 strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
320 strcat(dbpath, "/.AppleDB");
322 ctrlfd = atoi(argv[optind + 1]);
323 clntfd = atoi(argv[optind + 2]);
324 logconfig = strdup(argv[optind + 3]);
327 if (vol_load_charsets(&volinfo) == -1) {
328 LOG(log_error, logtype_cnid, "Error loading charsets!");
331 LOG(log_debug, logtype_cnid, "db dir: \"%s\"", dbpath);
333 switch_to_user(dbpath);
336 if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1) {
337 LOG(log_error, logtype_cnid, "main: fatal db lock error");
340 if (db_locked != LOCK_EXCL) {
341 /* Couldn't get exclusive lock, try shared lock */
342 if ((db_locked = get_lock(LOCK_SHRD, NULL)) != LOCK_SHRD) {
343 LOG(log_error, logtype_cnid, "main: fatal db lock error");
348 if (delete_bdb && (db_locked == LOCK_EXCL)) {
349 LOG(log_warning, logtype_cnid, "main: too many CNID db opening attempts, wiping the slate clean");
351 system("rm -f cnid2.db lock log.* __db.*");
352 if ((db_locked = get_lock(LOCK_EXCL, dbpath)) != LOCK_EXCL) {
353 LOG(log_error, logtype_cnid, "main: fatal db lock error");
360 /* SIGINT and SIGTERM are always off, unless we are in pselect */
363 if ((dbp = db_param_read(dbpath)) == NULL)
365 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
367 if (NULL == (dbd = dbif_init(dbpath, "cnid2.db")))
370 /* Only recover if we got the lock */
371 if (dbif_env_open(dbd,
373 (db_locked == LOCK_EXCL) ? DBOPTIONS | DB_RECOVER : DBOPTIONS) < 0)
374 exit(2); /* FIXME: same exit code as failure for dbif_open() */
375 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
377 if (dbif_open(dbd, dbp, 0) < 0) {
381 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
383 /* Downgrade db lock */
384 if (db_locked == LOCK_EXCL) {
385 if (get_lock(LOCK_UNLOCK, NULL) != 0) {
389 if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD) {
396 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
404 if (dbif_close(dbd) < 0)
407 if (dbif_env_remove(dbpath) < 0)
413 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
415 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");