]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/main.c
52dc3cf4c4a3bb596a2850ac535c40a70ca1a750
[netatalk.git] / etc / cnid_dbd / main.c
1 /*
2  * $Id: main.c,v 1.1.4.6 2003-12-12 19:27:57 didg Exp $
3  *
4  * Copyright (C) Joerg Lenneis 2003
5  * All Rights Reserved.  See COPYRIGHT.
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 int main(int argc, char *argv[])
193 {
194     struct sigaction sv;
195     struct db_param *dbp;
196     int err = 0;
197     struct stat st;
198     int lockfd;
199     struct flock lock;
200     char *dir;
201         
202     if (argc  != 2) {
203         LOG(log_error, logtype_cnid, "main: not enough arguments");
204         exit(1);
205     }
206     dir = argv[1];
207     if (chdir(dir) < 0) {
208         LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
209         exit(1);
210     }
211     if (stat(".", &st) < 0) {
212         LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
213         exit(1);
214     }
215 #if 0
216     LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
217     if (setegid(st.st_gid) < 0 || seteuid(st.st_uid) < 0) {
218         LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
219         exit(1);
220     }
221 #endif    
222     /* Before we do anything else, check if there is an instance of cnid_dbd
223        running already and silently exit if yes. */
224
225     if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
226         LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
227         exit(1);
228     }
229     
230     lock.l_start  = 0;
231     lock.l_whence = SEEK_SET;
232     lock.l_len    = 0;
233     lock.l_type   = F_WRLCK;
234
235     if (fcntl(lockfd, F_SETLK, &lock) < 0) {
236         if (errno == EACCES || errno == EAGAIN) {
237             exit(0);
238         } else {
239             LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
240             exit(1);
241         }
242     }
243     
244     LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
245     sv.sa_handler = sig_exit;
246     sv.sa_flags = 0;
247     sigemptyset(&sv.sa_mask);
248     sigaddset(&sv.sa_mask, SIGINT);
249     sigaddset(&sv.sa_mask, SIGTERM);
250     if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
251         LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
252         exit(1);
253     }
254     sv.sa_handler = SIG_IGN;
255     sigemptyset(&sv.sa_mask);
256     if (sigaction(SIGPIPE, &sv, NULL) < 0) {
257         LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
258         exit(1);
259     }
260     /* SIGINT and SIGTERM are always off, unless we get a return code of 0 from
261        comm_rcv (no requests for one second, see above in loop()). That means we
262        only shut down after one second of inactivity. */
263     block_sigs_onoff(1);
264     if ((dbp = db_param_read(dir)) == NULL)
265         exit(1);
266
267     if (dbif_env_init(dbp) < 0)
268         exit(2); /* FIXME: same exit code as failure for dbif_open() */  
269     
270 #ifdef CNID_BACKEND_DBD_TXN
271     if (dbif_txn_begin() < 0)
272         exit(6);
273 #endif
274     
275     if (dbif_open(dbp) < 0) {
276 #ifdef CNID_BACKEND_DBD_TXN
277         dbif_txn_abort();
278 #endif
279         dbif_close();
280         exit(2);
281     }
282     if (dbd_stamp() < 0) {
283 #ifdef CNID_BACKEND_DBD_TXN
284         dbif_txn_abort();
285 #endif
286         dbif_close();
287         exit(5);
288     }
289 #ifdef CNID_BACKEND_DBD_TXN
290     if (dbif_txn_commit() < 0)
291         exit(6);
292 #endif
293     if (comm_init(dbp) < 0) {
294         dbif_close();
295         exit(3);
296     }
297     if (loop(dbp) < 0)
298         err++;
299
300 #ifndef CNID_BACKEND_DBD_TXN
301     /* FIXME: Do we really need to sync before closing the DB? Just closing it
302        should be enough. */
303     if (dbif_sync() < 0)
304         err++;
305 #endif        
306
307     if (dbif_close() < 0)
308         err++;
309
310     lock.l_type = F_UNLCK;
311     fcntl(lockfd, F_SETLK, &lock);
312     close(lockfd);
313     
314     if (err)
315         exit(4);
316     else {
317         if (exit_sig)
318             LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
319         else
320             LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");
321         exit(0);
322     }
323 }