• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/netatalk-2.2.0/etc/cnid_dbd/
1/*
2 * Copyright (C) Joerg Lenneis 2003
3 * Copyright (c) Frank Lahm 2009
4 * All Rights Reserved.  See COPYING.
5 */
6
7#ifdef HAVE_CONFIG_H
8#include "config.h"
9#endif /* HAVE_CONFIG_H */
10
11#ifdef HAVE_UNISTD_H
12#include <unistd.h>
13#endif /* HAVE_UNISTD_H */
14#ifdef HAVE_FCNTL_H
15#include <fcntl.h>
16#endif /* HAVE_FCNTL_H */
17#include <stdio.h>
18#include <stdlib.h>
19#include <errno.h>
20#include <signal.h>
21#include <string.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
27#include <sys/stat.h>
28#endif /* HAVE_SYS_STAT_H */
29#include <time.h>
30#include <sys/file.h>
31
32#include <netatalk/endian.h>
33#include <atalk/cnid_dbd_private.h>
34#include <atalk/logger.h>
35#include <atalk/volinfo.h>
36
37#include "db_param.h"
38#include "dbif.h"
39#include "dbd.h"
40#include "comm.h"
41
42/*
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.
45 */
46#define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN)
47
48/* Global, needed by pack.c:idxname() */
49struct volinfo volinfo;
50
51static DBD *dbd;
52static int exit_sig = 0;
53static int db_locked;
54
55static void sig_exit(int signo)
56{
57    exit_sig = signo;
58    return;
59}
60
61static void block_sigs_onoff(int block)
62{
63    sigset_t set;
64
65    sigemptyset(&set);
66    sigaddset(&set, SIGINT);
67    sigaddset(&set, SIGTERM);
68    if (block)
69        sigprocmask(SIG_BLOCK, &set, NULL);
70    else
71        sigprocmask(SIG_UNBLOCK, &set, NULL);
72    return;
73}
74
75/*
76  The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
77
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.
83
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.
86
87*/
88#ifndef min
89#define min(a,b)        ((a)<(b)?(a):(b))
90#endif
91
92static int loop(struct db_param *dbp)
93{
94    struct cnid_dbd_rqst rqst;
95    struct cnid_dbd_rply rply;
96    time_t timeout;
97    int ret, cret;
98    int count;
99    time_t now, time_next_flush, time_last_rqst;
100    char timebuf[64];
101    static char namebuf[MAXPATHLEN + 1];
102    sigset_t set;
103
104    sigemptyset(&set);
105    sigprocmask(SIG_SETMASK, NULL, &set);
106    sigdelset(&set, SIGINT);
107    sigdelset(&set, SIGTERM);
108
109    count = 0;
110    now = time(NULL);
111    time_next_flush = now + dbp->flush_interval;
112    time_last_rqst = now;
113
114    rqst.name = namebuf;
115
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);
119
120    while (1) {
121        timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
122        if (timeout > now)
123            timeout -= now;
124        else
125            timeout = 1;
126
127        if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
128            return -1;
129
130        if (cret == 0) {
131            /* comm_rcv returned from select without receiving anything. */
132            if (exit_sig) {
133                /* Received signal (TERM|INT) */
134                return 0;
135            }
136            if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
137                /* Idle timeout */
138                return 0;
139            }
140            /* still active connections, reset time_last_rqst */
141            time_last_rqst = now;
142        } else {
143            /* We got a request */
144            time_last_rqst = now;
145
146            memset(&rply, 0, sizeof(rply));
147            switch(rqst.op) {
148                /* ret gets set here */
149            case CNID_DBD_OP_OPEN:
150            case CNID_DBD_OP_CLOSE:
151                /* open/close are noops for now. */
152                rply.namelen = 0;
153                ret = 1;
154                break;
155            case CNID_DBD_OP_ADD:
156                ret = dbd_add(dbd, &rqst, &rply, 0);
157                break;
158            case CNID_DBD_OP_GET:
159                ret = dbd_get(dbd, &rqst, &rply);
160                break;
161            case CNID_DBD_OP_RESOLVE:
162                ret = dbd_resolve(dbd, &rqst, &rply);
163                break;
164            case CNID_DBD_OP_LOOKUP:
165                ret = dbd_lookup(dbd, &rqst, &rply, 0);
166                break;
167            case CNID_DBD_OP_UPDATE:
168                ret = dbd_update(dbd, &rqst, &rply);
169                break;
170            case CNID_DBD_OP_DELETE:
171                ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
172                break;
173            case CNID_DBD_OP_GETSTAMP:
174                ret = dbd_getstamp(dbd, &rqst, &rply);
175                break;
176            case CNID_DBD_OP_REBUILD_ADD:
177                ret = dbd_rebuild_add(dbd, &rqst, &rply);
178                break;
179            case CNID_DBD_OP_SEARCH:
180                ret = dbd_search(dbd, &rqst, &rply);
181                break;
182            default:
183                LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
184                ret = -1;
185                break;
186            }
187
188            if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
189                dbif_txn_abort(dbd);
190                return -1;
191            }
192
193            if (ret == 0 || cret == 0) {
194                if (dbif_txn_abort(dbd) < 0)
195                    return -1;
196            } else {
197                ret = dbif_txn_commit(dbd);
198                if (  ret < 0)
199                    return -1;
200                else if ( ret > 0 )
201                    /* We had a designated txn because we wrote to the db */
202                    count++;
203            }
204        } /* got a request */
205
206        /*
207          Shall we checkpoint bdb ?
208          "flush_interval" seconds passed ?
209        */
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)
213                return -1;
214            count = 0;
215            time_next_flush = now + dbp->flush_interval;
216
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);
220        }
221
222        /*
223           Shall we checkpoint bdb ?
224           Have we commited "count" more changes than "flush_frequency" ?
225        */
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)
229                return -1;
230            count = 0;
231        }
232    } /* while(1) */
233}
234
235/* ------------------------ */
236static void switch_to_user(char *dir)
237{
238    struct stat st;
239
240    if (chdir(dir) < 0) {
241        LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
242        exit(1);
243    }
244
245    if (stat(".", &st) < 0) {
246        LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
247        exit(1);
248    }
249    if (!getuid()) {
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));
253            exit(1);
254        }
255    }
256}
257
258
259/* ----------------------- */
260static void set_signal(void)
261{
262    struct sigaction sv;
263
264    sv.sa_handler = sig_exit;
265    sv.sa_flags = 0;
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));
271        exit(1);
272    }
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));
277        exit(1);
278    }
279}
280
281/* ------------------------ */
282int main(int argc, char *argv[])
283{
284    struct db_param *dbp;
285    int err = 0;
286    int ctrlfd, clntfd;
287    char *logconfig;
288
289    set_processname("cnid_dbd");
290
291    /* FIXME: implement -d from cnid_metad */
292    if (argc  != 5) {
293        LOG(log_error, logtype_cnid, "main: not enough arguments");
294        exit(1);
295    }
296
297    ctrlfd = atoi(argv[2]);
298    clntfd = atoi(argv[3]);
299    logconfig = strdup(argv[4]);
300    setuplog(logconfig);
301
302    /* Load .volinfo file */
303    if (loadvolinfo(argv[1], &volinfo) == -1) {
304        LOG(log_error, logtype_cnid, "Cant load volinfo for \"%s\"", argv[1]);
305        exit(EXIT_FAILURE);
306    }
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);
311        exit(EXIT_FAILURE);
312    }
313    strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
314    strcat(dbpath, "/.AppleDB");
315
316    if (vol_load_charsets(&volinfo) == -1) {
317        LOG(log_error, logtype_cnid, "Error loading charsets!");
318        exit(EXIT_FAILURE);
319    }
320    LOG(log_debug, logtype_cnid, "db dir: \"%s\"", dbpath);
321
322    switch_to_user(dbpath);
323
324    /* Get db lock */
325    if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1) {
326        LOG(log_error, logtype_cnid, "main: fatal db lock error");
327        exit(1);
328    }
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");
333            exit(1);
334        }
335    }
336
337    set_signal();
338
339    /* SIGINT and SIGTERM are always off, unless we are in pselect */
340    block_sigs_onoff(1);
341
342    if ((dbp = db_param_read(dbpath)) == NULL)
343        exit(1);
344    LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
345
346    if (NULL == (dbd = dbif_init(dbpath, "cnid2.db")))
347        exit(2);
348
349    /* Only recover if we got the lock */
350    if (dbif_env_open(dbd,
351                      dbp,
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");
355
356    if (dbif_open(dbd, dbp, 0) < 0) {
357        dbif_close(dbd);
358        exit(2);
359    }
360    LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
361
362    /* Downgrade db lock  */
363    if (db_locked == LOCK_EXCL) {
364        if (get_lock(LOCK_UNLOCK, NULL) != 0) {
365            dbif_close(dbd);
366            exit(2);
367        }
368        if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD) {
369            dbif_close(dbd);
370            exit(2);
371        }
372    }
373
374
375    if (comm_init(dbp, ctrlfd, clntfd) < 0) {
376        dbif_close(dbd);
377        exit(3);
378    }
379
380    if (loop(dbp) < 0)
381        err++;
382
383    if (dbif_close(dbd) < 0)
384        err++;
385
386    if (dbif_env_remove(dbpath) < 0)
387        err++;
388
389    if (err)
390        exit(4);
391    else if (exit_sig)
392        LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
393    else
394        LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");
395
396    return 0;
397}
398