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