]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/main.c
Implement runtime logging config option to cnid dbd backend. See #2721956.
[netatalk.git] / etc / cnid_dbd / main.c
1 /*
2  * $Id: main.c,v 1.3 2009-03-31 11:40:26 franklahm Exp $
3  *
4  * Copyright (C) Joerg Lenneis 2003
5  * All Rights Reserved.  See COPYING.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #ifdef HAVE_UNISTD_H
13 #include <unistd.h>
14 #endif /* HAVE_UNISTD_H */
15 #ifdef HAVE_FCNTL_H
16 #include <fcntl.h>
17 #endif /* HAVE_FCNTL_H */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <errno.h>
21 #include <signal.h>
22 #include <string.h>
23 #ifdef HAVE_SYS_TYPES_H
24 #include <sys/types.h>
25 #endif /* HAVE_SYS_TYPES_H */
26 #include <sys/param.h>
27 #ifdef HAVE_SYS_STAT_H
28 #include <sys/stat.h>
29 #endif /* HAVE_SYS_STAT_H */
30 #include <time.h>
31 #include <sys/file.h>
32
33 #include <netatalk/endian.h>
34 #include <atalk/cnid_dbd_private.h>
35 #include <atalk/logger.h>
36
37 #include "db_param.h"
38 #include "dbif.h"
39 #include "dbd.h"
40 #include "comm.h"
41
42
43 #define LOCKFILENAME  "lock"
44
45 static int exit_sig = 0;
46
47
48 static void sig_exit(int signo)
49 {
50     exit_sig = signo;
51     return;
52 }
53
54 static void block_sigs_onoff(int block)
55 {
56     sigset_t set;
57
58     sigemptyset(&set);
59     sigaddset(&set, SIGINT);
60     sigaddset(&set, SIGTERM);
61     if (block)
62         sigprocmask(SIG_BLOCK, &set, NULL);
63     else
64         sigprocmask(SIG_UNBLOCK, &set, NULL);
65     return;
66 }
67
68 /*
69   The dbd_XXX and comm_XXX functions all obey the same protocol for return values:
70
71   1: Success, if transactions are used commit.
72   0: Failure, but we continue to serve requests. If transactions are used abort/rollback.
73   -1: Fatal error, either from the database or from the socket. Abort the transaction if applicable
74   (which might fail as well) and then exit.
75
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.
78
79 */
80
81 static int loop(struct db_param *dbp)
82 {
83     struct cnid_dbd_rqst rqst;
84     struct cnid_dbd_rply rply;
85     int ret, cret;
86     time_t now, time_next_flush, time_last_rqst;
87     int count;
88     static char namebuf[MAXPATHLEN + 1];
89     u_int32_t checkp_flags;
90
91     count = 0;
92     now = time(NULL);
93     time_next_flush = now + dbp->flush_interval;
94     time_last_rqst = now;
95     if (dbp->nosync)
96         checkp_flags = DB_FORCE;
97     else
98         checkp_flags = 0;
99
100     rqst.name = namebuf;
101
102     while (1) {
103         if ((cret = comm_rcv(&rqst)) < 0)
104             return -1;
105
106         now = time(NULL);
107
108         if (count > dbp->flush_frequency || now > time_next_flush) {
109 #ifdef CNID_BACKEND_DBD_TXN
110             if (dbif_txn_checkpoint(0, 0, checkp_flags) < 0)
111                 return -1;
112 #else
113             if (dbif_sync() < 0)
114                 return -1;
115 #endif
116             count = 0;
117             time_next_flush = now + dbp->flush_interval;
118         }
119
120         if (cret == 0) {
121             block_sigs_onoff(0);
122             block_sigs_onoff(1);
123             if (exit_sig)
124                 return 0;
125             if (dbp->idle_timeout && comm_nbe() <= 0 && (now - time_last_rqst) > dbp->idle_timeout)
126                 return 0;
127             continue;
128         }
129         /* We got a request */
130         time_last_rqst = now;
131         count++;
132
133 #ifdef CNID_BACKEND_DBD_TXN
134         if (dbif_txn_begin() < 0)
135             return -1;
136 #endif /* CNID_BACKEND_DBD_TXN */
137
138         memset(&rply, 0, sizeof(rply));
139         switch(rqst.op) {
140             /* ret gets set here */
141         case CNID_DBD_OP_OPEN:
142         case CNID_DBD_OP_CLOSE:
143             /* open/close are noops for now. */
144             rply.namelen = 0;
145             ret = 1;
146             break;
147         case CNID_DBD_OP_ADD:
148             ret = dbd_add(&rqst, &rply);
149             break;
150         case CNID_DBD_OP_GET:
151             ret = dbd_get(&rqst, &rply);
152             break;
153         case CNID_DBD_OP_RESOLVE:
154             ret = dbd_resolve(&rqst, &rply);
155             break;
156         case CNID_DBD_OP_LOOKUP:
157             ret = dbd_lookup(&rqst, &rply);
158             break;
159         case CNID_DBD_OP_UPDATE:
160             ret = dbd_update(&rqst, &rply);
161             break;
162         case CNID_DBD_OP_DELETE:
163             ret = dbd_delete(&rqst, &rply);
164             break;
165         case CNID_DBD_OP_GETSTAMP:
166             ret = dbd_getstamp(&rqst, &rply);
167             break;
168         case CNID_DBD_OP_REBUILD_ADD:
169             ret = dbd_rebuild_add(&rqst, &rply);
170             break;
171         default:
172             LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
173             ret = -1;
174             break;
175         }
176
177         if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
178 #ifdef CNID_BACKEND_DBD_TXN
179             dbif_txn_abort();
180 #endif /* CNID_BACKEND_DBD_TXN */
181             return -1;
182         }
183 #ifdef CNID_BACKEND_DBD_TXN
184         if (ret == 0 || cret == 0) {
185             if (dbif_txn_abort() < 0)
186                 return -1;
187         } else {
188             if (dbif_txn_commit() < 0)
189                 return -1;
190         }
191 #endif /* CNID_BACKEND_DBD_TXN */
192     }
193 }
194
195 /* ------------------------ */
196 static void switch_to_user(char *dir)
197 {
198     struct stat st;
199
200     if (chdir(dir) < 0) {
201         LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
202         exit(1);
203     }
204
205     if (stat(".", &st) < 0) {
206         LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
207         exit(1);
208     }
209     if (!getuid()) {
210         LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
211         if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
212             LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
213             exit(1);
214         }
215     }
216 }
217
218 /* ------------------------ */
219 int get_lock(void)
220 {
221     int lockfd;
222     struct flock lock;
223
224     if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
225         LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
226         exit(1);
227     }
228
229     lock.l_start  = 0;
230     lock.l_whence = SEEK_SET;
231     lock.l_len    = 0;
232     lock.l_type   = F_WRLCK;
233
234     if (fcntl(lockfd, F_SETLK, &lock) < 0) {
235         if (errno == EACCES || errno == EAGAIN) {
236             exit(0);
237         } else {
238             LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
239             exit(1);
240         }
241     }
242
243     return lockfd;
244 }
245
246 /* ----------------------- */
247 void set_signal(void)
248 {
249     struct sigaction sv;
250
251     sv.sa_handler = sig_exit;
252     sv.sa_flags = 0;
253     sigemptyset(&sv.sa_mask);
254     sigaddset(&sv.sa_mask, SIGINT);
255     sigaddset(&sv.sa_mask, SIGTERM);
256     if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
257         LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
258         exit(1);
259     }
260     sv.sa_handler = SIG_IGN;
261     sigemptyset(&sv.sa_mask);
262     if (sigaction(SIGPIPE, &sv, NULL) < 0) {
263         LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
264         exit(1);
265     }
266 }
267
268 /* ----------------------- */
269 void free_lock(int lockfd)
270 {
271     struct flock lock;
272
273     lock.l_start  = 0;
274     lock.l_whence = SEEK_SET;
275     lock.l_len    = 0;
276     lock.l_type = F_UNLCK;
277     fcntl(lockfd, F_SETLK, &lock);
278     close(lockfd);
279 }
280
281 /* ------------------------ */
282 int main(int argc, char *argv[])
283 {
284     struct db_param *dbp;
285     int err = 0;
286     int ret;
287     int lockfd, ctrlfd, clntfd;
288     char *dir, *logconfig;
289
290     set_processname("cnid_dbd");
291
292     if (argc  != 5) {
293         LOG(log_error, logtype_cnid, "main: not enough arguments");
294         exit(1);
295     }
296
297     dir = argv[1];
298     ctrlfd = atoi(argv[2]);
299     clntfd = atoi(argv[3]);
300     logconfig = strdup(argv[4]);
301     setuplog(logconfig);
302
303     switch_to_user(dir);
304
305     /* Before we do anything else, check if there is an instance of cnid_dbd
306        running already and silently exit if yes. */
307     lockfd = get_lock();
308
309     LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
310
311     set_signal();
312
313     /* SIGINT and SIGTERM are always off, unless we get a return code of 0 from
314        comm_rcv (no requests for one second, see above in loop()). That means we
315        only shut down after one second of inactivity. */
316     block_sigs_onoff(1);
317
318     if ((dbp = db_param_read(dir)) == NULL)
319         exit(1);
320
321     if (dbif_env_init(dbp) < 0)
322         exit(2); /* FIXME: same exit code as failure for dbif_open() */
323
324 #ifdef CNID_BACKEND_DBD_TXN
325     if (dbif_txn_begin() < 0)
326         exit(6);
327 #endif
328
329     if (dbif_open(dbp, 0) < 0) {
330 #ifdef CNID_BACKEND_DBD_TXN
331         dbif_txn_abort();
332 #endif
333         dbif_close();
334         exit(2);
335     }
336
337 #ifndef CNID_BACKEND_DBD_TXN
338     if (dbp->check && (ret = dbd_check(dir))) {
339         if (ret < 0) {
340             dbif_close();
341             exit(2);
342         }
343         dbif_closedb();
344         LOG(log_info, logtype_cnid, "main: re-opening, secondaries will be rebuilt. This may take some time");
345         if (dbif_open(dbp, 1) < 0) {
346             LOG(log_info, logtype_cnid, "main: re-opening databases failed");
347             dbif_close();
348             exit(2);
349         }
350         LOG(log_info, logtype_cnid, "main: rebuilt done");
351     }
352 #endif
353
354     if (dbd_stamp() < 0) {
355 #ifdef CNID_BACKEND_DBD_TXN
356         dbif_txn_abort();
357 #endif
358         dbif_close();
359         exit(5);
360     }
361 #ifdef CNID_BACKEND_DBD_TXN
362     if (dbif_txn_commit() < 0)
363         exit(6);
364 #endif
365
366     if (comm_init(dbp, ctrlfd, clntfd) < 0) {
367         dbif_close();
368         exit(3);
369     }
370
371     if (loop(dbp) < 0)
372         err++;
373
374 #ifndef CNID_BACKEND_DBD_TXN
375     /* FIXME: Do we really need to sync before closing the DB? Just closing it
376        should be enough. */
377     if (dbif_sync() < 0)
378         err++;
379 #endif
380
381     if (dbif_close() < 0)
382         err++;
383
384     free_lock(lockfd);
385
386     if (err)
387         exit(4);
388     else if (exit_sig)
389         LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
390     else
391         LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");
392
393     return 0;
394 }