]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/main.c
Replace flock() locking with fcntl() in cnid_dbd.
[netatalk.git] / etc / cnid_dbd / main.c
1 /*
2  * $Id: main.c,v 1.1.4.5 2003-12-04 21:33:25 lenneis 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     sleep(3600);
246     sv.sa_handler = sig_exit;
247     sv.sa_flags = 0;
248     sigemptyset(&sv.sa_mask);
249     sigaddset(&sv.sa_mask, SIGINT);
250     sigaddset(&sv.sa_mask, SIGTERM);
251     if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
252         LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
253         exit(1);
254     }
255     sv.sa_handler = SIG_IGN;
256     sigemptyset(&sv.sa_mask);
257     if (sigaction(SIGPIPE, &sv, NULL) < 0) {
258         LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
259         exit(1);
260     }
261     /* SIGINT and SIGTERM are always off, unless we get a return code of 0 from
262        comm_rcv (no requests for one second, see above in loop()). That means we
263        only shut down after one second of inactivity. */
264     block_sigs_onoff(1);
265     if ((dbp = db_param_read(dir)) == NULL)
266         exit(1);
267
268     if (dbif_env_init(dbp) < 0)
269         exit(2); /* FIXME: same exit code as failure for dbif_open() */  
270     
271 #ifdef CNID_BACKEND_DBD_TXN
272     if (dbif_txn_begin() < 0)
273         exit(6);
274 #endif
275     
276     if (dbif_open(dbp) < 0) {
277 #ifdef CNID_BACKEND_DBD_TXN
278         dbif_txn_abort();
279 #endif
280         dbif_close();
281         exit(2);
282     }
283     if (dbd_stamp() < 0) {
284 #ifdef CNID_BACKEND_DBD_TXN
285         dbif_txn_abort();
286 #endif
287         dbif_close();
288         exit(5);
289     }
290 #ifdef CNID_BACKEND_DBD_TXN
291     if (dbif_txn_commit() < 0)
292         exit(6);
293 #endif
294     if (comm_init(dbp) < 0) {
295         dbif_close();
296         exit(3);
297     }
298     if (loop(dbp) < 0)
299         err++;
300
301 #ifndef CNID_BACKEND_DBD_TXN
302     /* FIXME: Do we really need to sync before closing the DB? Just closing it
303        should be enough. */
304     if (dbif_sync() < 0)
305         err++;
306 #endif        
307
308     if (dbif_close() < 0)
309         err++;
310
311     lock.l_type = F_UNLCK;
312     fcntl(lockfd, F_SETLK, &lock);
313     close(lockfd);
314     
315     if (err)
316         exit(4);
317     else {
318         if (exit_sig)
319             LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
320         else
321             LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");
322         exit(0);
323     }
324 }