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/errchk.h>
28 #include <atalk/bstrlib.h>
29 #include <atalk/netatalk_conf.h>
38 Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
39 It's a likey performance hit, but it might we worth it.
41 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN)
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[])
277 struct db_param *dbp;
279 int ctrlfd = -1, clntfd = -1;
283 char *volpath = NULL;
286 while (( ret = getopt( argc, argv, "dF:l:p:t:vV")) != -1 ) {
292 obj.cmdlineconfigfile = strdup(optarg);
295 volpath = strdup(optarg);
298 clntfd = atoi(optarg);
301 ctrlfd = atoi(optarg);
305 printf("cnid_dbd (Netatalk %s)\n", VERSION);
310 if (ctrlfd == -1 || clntfd == -1 || !volpath) {
311 LOG(log_error, logtype_cnid, "main: bad IPC fds");
315 EC_ZERO( afp_config_parse(&obj) );
317 set_processname("cnid_dbd");
318 setuplog(obj.options.logconfig, obj.options.logfile);
320 EC_ZERO( load_volumes(&obj, NULL) );
321 EC_NULL( vol = getvolbypath(volpath) );
322 EC_ZERO( load_charset(vol) );
325 EC_NULL( dbpath = bfromcstr(vol->v_dbpath) );
326 EC_ZERO( bcatcstr(dbpath, "/.AppleDB") );
328 LOG(log_debug, logtype_cnid, "db dir: \"%s\"", bdata(dbpath));
330 switch_to_user(bdata(dbpath));
333 if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) == -1) {
334 LOG(log_error, logtype_cnid, "main: fatal db lock error");
337 if (db_locked != LOCK_EXCL) {
338 /* Couldn't get exclusive lock, try shared lock */
339 if ((db_locked = get_lock(LOCK_SHRD, NULL)) != LOCK_SHRD) {
340 LOG(log_error, logtype_cnid, "main: fatal db lock error");
345 if (delete_bdb && (db_locked == LOCK_EXCL)) {
346 LOG(log_warning, logtype_cnid, "main: too many CNID db opening attempts, wiping the slate clean");
347 chdir(bdata(dbpath));
348 system("rm -f cnid2.db lock log.* __db.*");
349 if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
350 LOG(log_error, logtype_cnid, "main: fatal db lock error");
357 /* SIGINT and SIGTERM are always off, unless we are in pselect */
360 if ((dbp = db_param_read(bdata(dbpath))) == NULL)
362 LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
364 if (NULL == (dbd = dbif_init(bdata(dbpath), "cnid2.db")))
367 /* Only recover if we got the lock */
368 if (dbif_env_open(dbd,
370 (db_locked == LOCK_EXCL) ? DBOPTIONS | DB_RECOVER : DBOPTIONS) < 0)
372 LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
374 if (dbif_open(dbd, dbp, 0) < 0) {
379 LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
381 /* Downgrade db lock */
382 if (db_locked == LOCK_EXCL) {
383 if (get_lock(LOCK_UNLOCK, NULL) != 0) {
388 if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD) {
395 if (comm_init(dbp, ctrlfd, clntfd) < 0) {
406 if (dbif_close(dbd) < 0)
409 if (dbif_env_remove(bdata(dbpath)) < 0)
417 LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
419 LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");