]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/main.c
e45adb076d9de1b462b02a6929a1de5202ccb56e
[netatalk.git] / etc / cnid_dbd / main.c
1 /*
2  * $Id: main.c,v 1.1.4.10.2.2 2004-12-21 13:36:12 didg 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         default:
169             LOG(log_error, logtype_cnid, "loop: unknow op %d", rqst.op);
170             break;
171         }       
172
173         if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
174 #ifdef CNID_BACKEND_DBD_TXN
175             dbif_txn_abort();
176 #endif /* CNID_BACKEND_DBD_TXN */
177             return -1;
178         }
179 #ifdef CNID_BACKEND_DBD_TXN
180         if (ret == 0 || cret == 0) {
181             if (dbif_txn_abort() < 0) 
182                 return -1;
183         } else {
184             if (dbif_txn_commit() < 0) 
185                 return -1;
186         }
187 #endif /* CNID_BACKEND_DBD_TXN */
188     }
189 }
190
191 /* ------------------------ */
192 static void switch_to_user(char *dir)
193 {
194     struct stat st;
195
196     if (chdir(dir) < 0) {
197         LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
198         exit(1);
199     }
200     
201     if (stat(".", &st) < 0) {
202         LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
203         exit(1);
204     }
205     if (!getuid()) {
206         LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
207         if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
208             LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
209             exit(1);
210         }
211     }
212 }
213
214 /* ------------------------ */
215 int get_lock(void)
216 {
217     int lockfd;
218     struct flock lock;
219
220     if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
221         LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
222         exit(1);
223     }
224     
225     lock.l_start  = 0;
226     lock.l_whence = SEEK_SET;
227     lock.l_len    = 0;
228     lock.l_type   = F_WRLCK;
229
230     if (fcntl(lockfd, F_SETLK, &lock) < 0) {
231         if (errno == EACCES || errno == EAGAIN) {
232             exit(0);
233         } else {
234             LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
235             exit(1);
236         }
237     }
238     
239     return lockfd;
240 }
241
242 /* ----------------------- */
243 void set_signal(void)
244 {
245     struct sigaction sv;
246
247     sv.sa_handler = sig_exit;
248     sv.sa_flags = 0;
249     sigemptyset(&sv.sa_mask);
250     sigaddset(&sv.sa_mask, SIGINT);
251     sigaddset(&sv.sa_mask, SIGTERM);
252     if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
253         LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
254         exit(1);
255     }
256     sv.sa_handler = SIG_IGN;
257     sigemptyset(&sv.sa_mask);
258     if (sigaction(SIGPIPE, &sv, NULL) < 0) {
259         LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
260         exit(1);
261     }
262 }
263
264 /* ----------------------- */
265 void free_lock(int lockfd)
266 {
267     struct flock lock;
268
269     lock.l_start  = 0;
270     lock.l_whence = SEEK_SET;
271     lock.l_len    = 0;
272     lock.l_type = F_UNLCK;
273     fcntl(lockfd, F_SETLK, &lock);
274     close(lockfd);
275 }
276
277 /* ------------------------ */
278 int main(int argc, char *argv[])
279 {
280     struct db_param *dbp;
281     int err = 0;
282     int ret;
283     int lockfd, ctrlfd, clntfd;
284     char *dir;
285        
286     set_processname("cnid_dbd");
287     syslog_setup(log_debug, logtype_default, logoption_ndelay | logoption_pid, logfacility_daemon);
288     
289     if (argc  != 4) {
290         LOG(log_error, logtype_cnid, "main: not enough arguments");
291         exit(1);
292     }
293
294     dir = argv[1];
295     ctrlfd = atoi(argv[2]);
296     clntfd = atoi(argv[3]);
297
298     switch_to_user(dir);
299     
300     /* Before we do anything else, check if there is an instance of cnid_dbd
301        running already and silently exit if yes. */
302     lockfd = get_lock();
303     
304     LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
305     
306     set_signal();
307     
308     /* SIGINT and SIGTERM are always off, unless we get a return code of 0 from
309        comm_rcv (no requests for one second, see above in loop()). That means we
310        only shut down after one second of inactivity. */
311     block_sigs_onoff(1);
312
313     if ((dbp = db_param_read(dir)) == NULL)
314         exit(1);
315
316     if (dbif_env_init(dbp) < 0)
317         exit(2); /* FIXME: same exit code as failure for dbif_open() */  
318     
319 #ifdef CNID_BACKEND_DBD_TXN
320     if (dbif_txn_begin() < 0)
321         exit(6);
322 #endif
323     
324     if (dbif_open(dbp, 0) < 0) {
325 #ifdef CNID_BACKEND_DBD_TXN
326         dbif_txn_abort();
327 #endif
328         dbif_close();
329         exit(2);
330     }
331
332 #ifndef CNID_BACKEND_DBD_TXN
333     if (dbp->check && (ret = dbd_check(dir))) {
334         if (ret < 0) {
335             dbif_close();
336             exit(2);
337         }
338         dbif_closedb();
339         LOG(log_info, logtype_cnid, "main: re-opening, secondaries will be rebuilt. This may take some time");
340         if (dbif_open(dbp, 1) < 0) {
341             LOG(log_info, logtype_cnid, "main: re-opening databases failed");
342             dbif_close();
343             exit(2);
344         }
345         LOG(log_info, logtype_cnid, "main: rebuilt done");
346     }
347 #endif
348
349     if (dbd_stamp() < 0) {
350 #ifdef CNID_BACKEND_DBD_TXN
351         dbif_txn_abort();
352 #endif
353         dbif_close();
354         exit(5);
355     }
356 #ifdef CNID_BACKEND_DBD_TXN
357     if (dbif_txn_commit() < 0)
358         exit(6);
359 #endif
360
361     if (comm_init(dbp, ctrlfd, clntfd) < 0) {
362         dbif_close();
363         exit(3);
364     }
365
366     if (loop(dbp) < 0)
367         err++;
368
369 #ifndef CNID_BACKEND_DBD_TXN
370     /* FIXME: Do we really need to sync before closing the DB? Just closing it
371        should be enough. */
372     if (dbif_sync() < 0)
373         err++;
374 #endif
375
376     if (dbif_close() < 0)
377         err++;
378         
379     free_lock(lockfd);
380     
381     if (err)
382         exit(4);
383     else if (exit_sig)
384         LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
385     else
386         LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");
387     
388     return 0;
389 }