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