]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/main.c
Remove all Appletalk stuff
[netatalk.git] / etc / cnid_dbd / main.c
1 /*
2  * Copyright (C) Joerg Lenneis 2003
3  * Copyright (c) Frank Lahm 2009
4  * All Rights Reserved.  See COPYING.
5  */
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif /* HAVE_CONFIG_H */
10
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <errno.h>
16 #include <signal.h>
17 #include <string.h>
18 #include <sys/types.h>
19 #include <sys/param.h>
20 #include <sys/stat.h>
21 #include <time.h>
22 #include <sys/file.h>
23 #include <arpa/inet.h>
24
25 #include <atalk/cnid_dbd_private.h>
26 #include <atalk/logger.h>
27 #include <atalk/volinfo.h>
28
29 #include "db_param.h"
30 #include "dbif.h"
31 #include "dbd.h"
32 #include "comm.h"
33
34 #define LOCKFILENAME  "lock"
35
36 /* 
37    Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
38    It's a likey performance hit, but it might we worth it.
39  */
40 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | DB_RECOVER)
41
42 /* Global, needed by pack.c:idxname() */
43 struct volinfo volinfo;
44
45 static DBD *dbd;
46 static int exit_sig = 0;
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 t
74   he database or from the socket. Abort the transaction if applicable
75   (which might fail as well) and then exit.
76
77   We always try to notify the client process about the outcome, the result field
78   of the cnid_dbd_rply structure contains further details.
79
80 */
81 #ifndef min
82 #define min(a,b)        ((a)<(b)?(a):(b))
83 #endif
84
85 static int loop(struct db_param *dbp)
86 {
87     struct cnid_dbd_rqst rqst;
88     struct cnid_dbd_rply rply;
89     time_t timeout;
90     int ret, cret;
91     int count;
92     time_t now, time_next_flush, time_last_rqst;
93     char timebuf[64];
94     static char namebuf[MAXPATHLEN + 1];
95     sigset_t set;
96
97     sigemptyset(&set);
98     sigprocmask(SIG_SETMASK, NULL, &set);
99     sigdelset(&set, SIGINT);
100     sigdelset(&set, SIGTERM);
101
102     count = 0;
103     now = time(NULL);
104     time_next_flush = now + dbp->flush_interval;
105     time_last_rqst = now;
106
107     rqst.name = namebuf;
108
109     strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
110     LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
111         dbp->flush_interval, timebuf);
112
113     while (1) {
114         timeout = min(time_next_flush, time_last_rqst +dbp->idle_timeout);
115         if (timeout > now)
116             timeout -= now;
117         else
118             timeout = 1;
119
120         if ((cret = comm_rcv(&rqst, timeout, &set, &now)) < 0)
121             return -1;
122
123         if (cret == 0) {
124             /* comm_rcv returned from select without receiving anything. */
125             if (exit_sig) {
126                 /* Received signal (TERM|INT) */
127                 return 0;
128             }
129             if (now - time_last_rqst >= dbp->idle_timeout && comm_nbe() <= 0) {
130                 /* Idle timeout */
131                 return 0;
132             }
133             /* still active connections, reset time_last_rqst */
134             time_last_rqst = now;
135         } else {
136             /* We got a request */
137             time_last_rqst = now;
138
139             memset(&rply, 0, sizeof(rply));
140             switch(rqst.op) {
141                 /* ret gets set here */
142             case CNID_DBD_OP_OPEN:
143             case CNID_DBD_OP_CLOSE:
144                 /* open/close are noops for now. */
145                 rply.namelen = 0;
146                 ret = 1;
147                 break;
148             case CNID_DBD_OP_ADD:
149                 ret = dbd_add(dbd, &rqst, &rply, 0);
150                 break;
151             case CNID_DBD_OP_GET:
152                 ret = dbd_get(dbd, &rqst, &rply);
153                 break;
154             case CNID_DBD_OP_RESOLVE:
155                 ret = dbd_resolve(dbd, &rqst, &rply);
156                 break;
157             case CNID_DBD_OP_LOOKUP:
158                 ret = dbd_lookup(dbd, &rqst, &rply, 0);
159                 break;
160             case CNID_DBD_OP_UPDATE:
161                 ret = dbd_update(dbd, &rqst, &rply);
162                 break;
163             case CNID_DBD_OP_DELETE:
164                 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
165                 break;
166             case CNID_DBD_OP_GETSTAMP:
167                 ret = dbd_getstamp(dbd, &rqst, &rply);
168                 break;
169             case CNID_DBD_OP_REBUILD_ADD:
170                 ret = dbd_rebuild_add(dbd, &rqst, &rply);
171                 break;
172             case CNID_DBD_OP_SEARCH:
173                 ret = dbd_search(dbd, &rqst, &rply);
174                 break;
175             default:
176                 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
177                 ret = -1;
178                 break;
179             }
180
181             if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
182                 dbif_txn_abort(dbd);
183                 return -1;
184             }
185             
186             if (ret == 0 || cret == 0) {
187                 if (dbif_txn_abort(dbd) < 0)
188                     return -1;
189             } else {
190                 ret = dbif_txn_commit(dbd);
191                 if (  ret < 0)
192                     return -1;
193                 else if ( ret > 0 )
194                     /* We had a designated txn because we wrote to the db */
195                     count++;
196             }
197         } /* got a request */
198
199         /*
200           Shall we checkpoint bdb ?
201           "flush_interval" seconds passed ?
202         */
203         if (now >= time_next_flush) {
204             LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB for volume '%s'", dbp->dir);
205             if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
206                 return -1;
207             count = 0;
208             time_next_flush = now + dbp->flush_interval;
209
210             strftime(timebuf, 63, "%b %d %H:%M:%S.",localtime(&time_next_flush));
211             LOG(log_debug, logtype_cnid, "Checkpoint interval: %d seconds. Next checkpoint: %s",
212                 dbp->flush_interval, timebuf);
213         }
214
215         /* 
216            Shall we checkpoint bdb ?
217            Have we commited "count" more changes than "flush_frequency" ?
218         */
219         if (count > dbp->flush_frequency) {
220             LOG(log_info, logtype_cnid, "Checkpointing BerkeleyDB after %d writes for volume '%s'", count, dbp->dir);
221             if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0)
222                 return -1;
223             count = 0;
224         }
225     } /* while(1) */
226 }
227
228 /* ------------------------ */
229 static void switch_to_user(char *dir)
230 {
231     struct stat st;
232
233     if (chdir(dir) < 0) {
234         LOG(log_error, logtype_cnid, "chdir to %s failed: %s", dir, strerror(errno));
235         exit(1);
236     }
237
238     if (stat(".", &st) < 0) {
239         LOG(log_error, logtype_cnid, "error in stat for %s: %s", dir, strerror(errno));
240         exit(1);
241     }
242     if (!getuid()) {
243         LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", st.st_uid, st.st_gid);
244         if (setgid(st.st_gid) < 0 || setuid(st.st_uid) < 0) {
245             LOG(log_error, logtype_cnid, "uid/gid: %s", strerror(errno));
246             exit(1);
247         }
248     }
249 }
250
251 /* ------------------------ */
252 static int get_lock(void)
253 {
254     int lockfd;
255     struct flock lock;
256
257     if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
258         LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
259         exit(1);
260     }
261
262     lock.l_start  = 0;
263     lock.l_whence = SEEK_SET;
264     lock.l_len    = 0;
265     lock.l_type   = F_WRLCK;
266
267     if (fcntl(lockfd, F_SETLK, &lock) < 0) {
268         if (errno == EACCES || errno == EAGAIN) {
269             LOG(log_error, logtype_cnid, "get_lock: locked");
270             exit(0);
271         } else {
272             LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
273             exit(1);
274         }
275     }
276
277     return lockfd;
278 }
279
280 /* ----------------------- */
281 static void set_signal(void)
282 {
283     struct sigaction sv;
284
285     sv.sa_handler = sig_exit;
286     sv.sa_flags = 0;
287     sigemptyset(&sv.sa_mask);
288     sigaddset(&sv.sa_mask, SIGINT);
289     sigaddset(&sv.sa_mask, SIGTERM);
290     if (sigaction(SIGINT, &sv, NULL) < 0 || sigaction(SIGTERM, &sv, NULL) < 0) {
291         LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
292         exit(1);
293     }
294     sv.sa_handler = SIG_IGN;
295     sigemptyset(&sv.sa_mask);
296     if (sigaction(SIGPIPE, &sv, NULL) < 0) {
297         LOG(log_error, logtype_cnid, "main: sigaction: %s", strerror(errno));
298         exit(1);
299     }
300 }
301
302 /* ----------------------- */
303 static void free_lock(int lockfd)
304 {
305     struct flock lock;
306
307     lock.l_start  = 0;
308     lock.l_whence = SEEK_SET;
309     lock.l_len    = 0;
310     lock.l_type = F_UNLCK;
311     fcntl(lockfd, F_SETLK, &lock);
312     close(lockfd);
313 }
314
315 /* ------------------------ */
316 int main(int argc, char *argv[])
317 {
318     struct db_param *dbp;
319     int err = 0;
320     int lockfd, ctrlfd, clntfd;
321     char *logconfig;
322
323     set_processname("cnid_dbd");
324
325     /* FIXME: implement -d from cnid_metad */
326     if (argc  != 5) {
327         LOG(log_error, logtype_cnid, "main: not enough arguments");
328         exit(1);
329     }
330
331     ctrlfd = atoi(argv[2]);
332     clntfd = atoi(argv[3]);
333     logconfig = strdup(argv[4]);
334     setuplog(logconfig);
335
336     /* Load .volinfo file */
337     if (loadvolinfo(argv[1], &volinfo) == -1) {
338         LOG(log_error, logtype_cnid, "Cant load volinfo for \"%s\"", argv[1]);
339         exit(EXIT_FAILURE);
340     }
341     /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
342     char dbpath[MAXPATHLEN+1];
343     if ((strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
344         LOG(log_error, logtype_cnid, "CNID db pathname too long: \"%s\"", volinfo.v_dbpath);
345         exit(EXIT_FAILURE);
346     }
347     strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
348     strcat(dbpath, "/.AppleDB");
349
350     if (vol_load_charsets(&volinfo) == -1) {
351         LOG(log_error, logtype_cnid, "Error loading charsets!");
352         exit(EXIT_FAILURE);
353     }
354     LOG(log_debug, logtype_cnid, "db dir: \"%s\"", dbpath);
355
356     switch_to_user(dbpath);
357
358     /* Before we do anything else, check if there is an instance of cnid_dbd
359        running already and silently exit if yes. */
360     lockfd = get_lock();
361
362     set_signal();
363
364     /* SIGINT and SIGTERM are always off, unless we are in pselect */
365     block_sigs_onoff(1);
366
367     if ((dbp = db_param_read(dbpath)) == NULL)
368         exit(1);
369     LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
370
371     if (NULL == (dbd = dbif_init(dbpath, "cnid2.db")))
372         exit(2);
373
374     if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
375         exit(2); /* FIXME: same exit code as failure for dbif_open() */
376     LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
377
378     if (dbif_open(dbd, dbp, 0) < 0) {
379         dbif_close(dbd);
380         exit(2);
381     }
382     LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
383
384     if (comm_init(dbp, ctrlfd, clntfd) < 0) {
385         dbif_close(dbd);
386         exit(3);
387     }
388
389     if (loop(dbp) < 0)
390         err++;
391
392     if (dbif_close(dbd) < 0)
393         err++;
394
395     if (dbif_env_remove(dbpath) < 0)
396         err++;
397
398     free_lock(lockfd);
399
400     if (err)
401         exit(4);
402     else if (exit_sig)
403         LOG(log_info, logtype_cnid, "main: Exiting on signal %i", exit_sig);
404     else
405         LOG(log_info, logtype_cnid, "main: Idle timeout, exiting");
406
407     return 0;
408 }