]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/main.c
Merge from branch-2-1
[netatalk.git] / etc / cnid_dbd / main.c
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
36 #include "db_param.h"
37 #include "dbif.h"
38 #include "dbd.h"
39 #include "comm.h"
40
41 #define LOCKFILENAME  "lock"
42
43 /* 
44    Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
45    It's a likey performance hit, but it might we worth it.
46  */
47 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | DB_RECOVER)
48
49 static DBD *dbd;
50
51 static int exit_sig = 0;
52
53 static void sig_exit(int signo)
54 {
55     exit_sig = signo;
56     return;
57 }
58
59 static void block_sigs_onoff(int block)
60 {
61     sigset_t set;
62
63     sigemptyset(&set);
64     sigaddset(&set, SIGINT);
65     sigaddset(&set, SIGTERM);
66     if (block)
67         sigprocmask(SIG_BLOCK, &set, NULL);
68     else
69         sigprocmask(SIG_UNBLOCK, &set, NULL);
70     return;
71 }
72
73 /*
74   The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
75
76   1: Success, if transactions are used commit.
77   0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
78   -1: Fatal error, either from t
79   he database or from the socket. Abort the transaction if applicable
80   (which might fail as well) and then exit.
81
82   We always try to notify the client process about the outcome, the result field
83   of the cnid_dbd_rply structure contains further details.
84
85 */
86 #ifndef min
87 #define min(a,b)        ((a)<(b)?(a):(b))
88 #endif
89
90 static int loop(struct db_param *dbp)
91 {
92     struct cnid_dbd_rqst rqst;
93     struct cnid_dbd_rply rply;
94     time_t timeout;
95     int ret, cret;
96     int count;
97     time_t now, time_next_flush, time_last_rqst;
98     char timebuf[64];
99     static char namebuf[MAXPATHLEN + 1];
100     sigset_t set;
101
102     sigemptyset(&set);
103     sigprocmask(SIG_SETMASK, NULL, &set);
104     sigdelset(&set, SIGINT);
105     sigdelset(&set, SIGTERM);
106
107     count = 0;
108     now = time(NULL);
109     time_next_flush = now + dbp->flush_interval;
110     time_last_rqst = now;
111
112     rqst.name = namebuf;
113
114     strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
115     LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
116         dbp->flush_interval, timebuf);
117
118     while (1) {
119         timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
120         if (timeout > now)
121             timeout -= now;
122         else
123             timeout = 1;
124
125         if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
126             return -1;
127
128         if (cret == 0) {
129             /* comm_rcv returned from select without receiving anything. */
130             if (exit_sig) {
131                 /* Received signal (TERM|INT) */
132                 return 0;
133             }
134             if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
135                 /* Idle timeout */
136                 return 0;
137             }
138             /* still active connections, reset time_last_rqst */
139             time_last_rqst = now;
140         } else {
141             /* We got a request */
142             time_last_rqst = now;
143
144             memset(&rply, 0, sizeof(rply));
145             switch(rqst.op) {
146                 /* ret gets set here */
147             case CNID_DBD_OP_OPEN:
148             case CNID_DBD_OP_CLOSE:
149                 /* open/close are noops for now. */
150                 rply.namelen = 0;
151                 ret = 1;
152                 break;
153             case CNID_DBD_OP_ADD:
154                 ret = dbd_add(dbd, &rqst, &rply, 0);
155                 break;
156             case CNID_DBD_OP_GET:
157                 ret = dbd_get(dbd, &rqst, &rply);
158                 break;
159             case CNID_DBD_OP_RESOLVE:
160                 ret = dbd_resolve(dbd, &rqst, &rply);
161                 break;
162             case CNID_DBD_OP_LOOKUP:
163                 ret = dbd_lookup(dbd, &rqst, &rply, 0);
164                 break;
165             case CNID_DBD_OP_UPDATE:
166                 ret = dbd_update(dbd, &rqst, &rply);
167                 break;
168             case CNID_DBD_OP_DELETE:
169                 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
170                 break;
171             case CNID_DBD_OP_GETSTAMP:
172                 ret = dbd_getstamp(dbd, &rqst, &rply);
173                 break;
174             case CNID_DBD_OP_REBUILD_ADD:
175                 ret = dbd_rebuild_add(dbd, &rqst, &rply);
176                 break;
177             default:
178                 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
179                 ret = -1;
180                 break;
181             }
182
183             if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
184                 dbif_txn_abort(dbd);
185                 return -1;
186             }
187             
188             if (ret == 0 || cret == 0) {
189                 if (dbif_txn_abort(dbd) < 0)
190                     return -1;
191             } else {
192                 ret = dbif_txn_commit(dbd);
193                 if (  ret < 0)
194                     return -1;
195                 else if ( ret > 0 )
196                     /* We had a designated txn because we wrote to the db */
197                     count++;
198             }
199         } /* got a request */
200
201         /*
202           Shall we checkpoint bdb ?
203           "flush_interval" seconds passed ?
204         */
205         if (now >= time_next_flush) {
206             LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
207             if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
208                 return -1;
209             count = 0;
210             time_next_flush = now + dbp->flush_interval;
211
212             strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
213             LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
214                 dbp->flush_interval, timebuf);
215         }
216
217         /* 
218            Shall we checkpoint bdb ?
219            Have we commited "count" more changes than "flush_frequency" ?
220         */
221         if (count > dbp->flush_frequency) {
222             LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
223             if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
224                 return -1;
225             count = 0;
226         }
227     } /* while(1) */
228 }
229
230 /* ------------------------ */
231 static void switch_to_user(char *dir)
232 {
233     struct stat st;
234
235     if (chdir(dir) < 0) {
236         LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
237         exit(1);
238     }
239
240     if (stat(".", &st) < 0) {
241         LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
242         exit(1);
243     }
244     if (!getuid()) {
245         LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
246         if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
247             LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
248             exit(1);
249         }
250     }
251 }
252
253 /* ------------------------ */
254 static int get_lock(void)
255 {
256     int lockfd;
257     struct flock lock;
258
259     if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
260         LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
261         exit(1);
262     }
263
264     lock.l_start  = 0;
265     lock.l_whence = SEEK_SET;
266     lock.l_len    = 0;
267     lock.l_type   = F_WRLCK;
268
269     if (fcntl(lockfd, F_SETLK, &lock) < 0) {
270         if (errno == EACCES || errno == EAGAIN) {
271             exit(0);
272         } else {
273             LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
274             exit(1);
275         }
276     }
277
278     return lockfd;
279 }
280
281 /* ----------------------- */
282 static void set_signal(void)
283 {
284     struct sigaction sv;
285
286     sv.sa_handler = sig_exit;
287     sv.sa_flags = 0;
288     sigemptyset(&sv.sa_mask);
289     sigaddset(&sv.sa_mask, SIGINT);
290     sigaddset(&sv.sa_mask, SIGTERM);
291     if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
292         LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
293         exit(1);
294     }
295     sv.sa_handler = SIG_IGN;
296     sigemptyset(&sv.sa_mask);
297     if (sigaction(SIGPIPE, &sv, NULL) < 0) {
298         LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
299         exit(1);
300     }
301 }
302
303 /* ----------------------- */
304 static void free_lock(int lockfd)
305 {
306     struct flock lock;
307
308     lock.l_start  = 0;
309     lock.l_whence = SEEK_SET;
310     lock.l_len    = 0;
311     lock.l_type = F_UNLCK;
312     fcntl(lockfd, F_SETLK, &lock);
313     close(lockfd);
314 }
315
316 /* ------------------------ */
317 int main(int argc, char *argv[])
318 {
319     struct db_param *dbp;
320     int err = 0;
321     int lockfd, ctrlfd, clntfd;
322     char *dir, *logconfig;
323
324     set_processname("cnid_dbd");
325
326     /* FIXME: implement -d from cnid_metad */
327     if (argc  != 5) {
328         LOG(log_error, logtype_cnid, "main: not enough arguments");
329         exit(1);
330     }
331
332     dir = argv[1];
333     ctrlfd = atoi(argv[2]);
334     clntfd = atoi(argv[3]);
335     logconfig = strdup(argv[4]);
336     setuplog(logconfig);
337
338     switch_to_user(dir);
339
340     /* Before we do anything else, check if there is an instance of cnid_dbd
341        running already and silently exit if yes. */
342     lockfd = get_lock();
343
344     LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
345
346     set_signal();
347
348     /* SIGINT and SIGTERM are always off, unless we are in pselect */
349     block_sigs_onoff(1);
350
351     if ((dbp = db_param_read(dir, CNID_DBD)) == NULL)
352         exit(1);
353     LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
354
355     if (NULL == (dbd = dbif_init(".", "cnid2.db")))
356         exit(2);
357
358     if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
359         exit(2); /* FIXME: same exit code as failure for dbif_open() */
360     LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
361
362     if (dbif_open(dbd, dbp, 0) < 0) {
363         dbif_close(dbd);
364         exit(2);
365     }
366     LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
367
368     if (dbd_stamp(dbd) < 0) {
369         dbif_close(dbd);
370         exit(5);
371     }
372     LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
373
374     if (comm_init(dbp, ctrlfd, clntfd) < 0) {
375         dbif_close(dbd);
376         exit(3);
377     }
378
379     if (loop(dbp) < 0)
380         err++;
381
382     if (dbif_close(dbd) < 0)
383         err++;
384
385     if (dbif_prep_upgrade(dir) < 0)
386         err++;
387
388     free_lock(lockfd);
389
390     if (err)
391         exit(4);
392     else if (exit_sig)
393         LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
394     else
395         LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");
396
397     return 0;
398 }