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