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