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