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