chiark / gitweb /
debugging for thing that crashed
[innduct.git] / frontends / ovdb_monitor.c
1 /*
2  * ovdb_monitor
3  *   Performs database maintenance tasks
4  *   + Transaction checkpoints
5  *   + Deadlock detection
6  *   + Transaction log removal
7  */
8
9 #include "config.h"
10 #include "clibrary.h"
11 #include "portable/setproctitle.h"
12 #include "portable/wait.h"
13 #include <fcntl.h>
14 #include <signal.h>
15 #include <syslog.h>
16
17 #include "inn/innconf.h"
18 #include "inn/messages.h"
19 #include "libinn.h"
20 #include "ov.h"
21
22 #include "../storage/ovdb/ovdb.h"
23 #include "../storage/ovdb/ovdb-private.h"
24
25 #ifndef USE_BERKELEY_DB
26
27 int main(int argc UNUSED, char **argv UNUSED)
28 {
29     exit(0);
30 }
31
32 #else /* USE_BERKELEY_DB */
33
34 static int signalled = 0;
35 static void sigfunc(int sig UNUSED)
36 {
37     signalled = 1;
38 }
39
40
41 static pid_t deadlockpid = 0;
42 static pid_t checkpointpid = 0;
43 static pid_t logremoverpid = 0;
44
45 static int putpid(const char *path)
46 {
47     char buf[30];
48     int fd = open(path, O_WRONLY|O_TRUNC|O_CREAT, 0664);
49     if(!fd) {
50         syswarn("cannot open %s", path);
51         return -1;
52     }
53     snprintf(buf, sizeof(buf), "%d\n", getpid());
54     if(write(fd, buf, strlen(buf)) < 0) {
55         syswarn("cannot write to %s", path);
56         close(fd);
57         return -1;
58     }
59     close(fd);
60     return 0;
61 }
62
63 static void deadlock(void)
64 {
65     int ret, status = 0;
66     u_int32_t atype = DB_LOCK_YOUNGEST;
67
68     if(ovdb_open_berkeleydb(OV_WRITE, 0))
69         _exit(1);
70
71     setproctitle("deadlock");
72
73     while(!signalled) {
74 #if DB_VERSION_MAJOR == 2
75         ret = lock_detect(OVDBenv->lk_info, 0, atype);
76 #elif DB_VERSION_MAJOR == 3
77         ret = lock_detect(OVDBenv, 0, atype, NULL);
78 #else
79         ret = OVDBenv->lock_detect(OVDBenv, 0, atype, NULL);
80 #endif
81         if(ret != 0) {
82             warn("OVDB: lock_detect: %s", db_strerror(ret));
83             status = 1;
84             break;
85         }
86         sleep(30);
87     }
88
89     ovdb_close_berkeleydb();
90     _exit(status);
91 }
92
93 static void checkpoint(void)
94 {
95     int ret, status = 0;
96     DB *db;
97 #if DB_VERSION_MAJOR == 2
98     DB_INFO dbinfo;
99 #endif
100
101     if(ovdb_open_berkeleydb(OV_WRITE, 0))
102         _exit(1);
103
104     setproctitle("checkpoint");
105
106     /* Open a database and close it.  This is so a necessary initialization
107        gets performed (by the db->open function).  */
108
109 #if DB_VERSION_MAJOR == 2
110     memset(&dbinfo, 0, sizeof dbinfo);
111     ret = db_open("version", DB_BTREE, DB_CREATE, 0666, OVDBenv, &dbinfo, &db);
112     if (ret != 0) {
113         warn("OVDB: checkpoint: db_open failed: %s", db_strerror(ret));
114         _exit(1);
115     }
116 #else
117     ret = db_create(&db, OVDBenv, 0);
118     if (ret != 0) {
119         warn("OVDB: checkpoint: db_create: %s", db_strerror(ret));
120         _exit(1);
121     }
122 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1)
123     ret = db->open(db, NULL, "version", NULL, DB_BTREE, DB_CREATE, 0666);
124 #else
125     ret = db->open(db, "version", NULL, DB_BTREE, DB_CREATE, 0666);
126 #endif
127     if (ret != 0) {
128         db->close(db, 0);
129         warn("OVDB: checkpoint: version open: %s", db_strerror(ret));
130         _exit(1);
131     }
132 #endif
133     db->close(db, 0);
134
135
136     while(!signalled) {
137 #if DB_VERSION_MAJOR == 2
138         ret = txn_checkpoint(OVDBenv->tx_info, 2048, 1);
139 #elif DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR == 0
140         ret = txn_checkpoint(OVDBenv, 2048, 1);
141 #elif DB_VERSION_MAJOR == 3
142         ret = txn_checkpoint(OVDBenv, 2048, 1, 0);
143 #elif DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR < 1
144         ret = OVDBenv->txn_checkpoint(OVDBenv, 2048, 1, 0);
145 #else
146         OVDBenv->txn_checkpoint(OVDBenv, 2048, 1, 0);
147 #endif
148 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1)
149         sleep(30);
150 #else
151         if(ret != 0 && ret != DB_INCOMPLETE) {
152             warn("OVDB: txn_checkpoint: %s", db_strerror(ret));
153             status = 1;
154             break;
155         }
156         if(ret == DB_INCOMPLETE)
157             sleep(2);
158         else
159             sleep(30);
160 #endif
161     }
162
163     ovdb_close_berkeleydb();
164     _exit(status);
165 }
166
167 static void logremover(void)
168 {
169     int ret, status = 0;
170     char **listp, **p;
171
172     if(ovdb_open_berkeleydb(OV_WRITE, 0))
173         _exit(1);
174
175     setproctitle("logremover");
176
177     while(!signalled) {
178 #if DB_VERSION_MAJOR == 2
179         ret = log_archive(OVDBenv->lg_info, &listp, DB_ARCH_ABS, malloc);
180 #elif DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR <= 2
181         ret = log_archive(OVDBenv, &listp, DB_ARCH_ABS, malloc);
182 #elif DB_VERSION_MAJOR == 3
183         ret = log_archive(OVDBenv, &listp, DB_ARCH_ABS);
184 #else
185         ret = OVDBenv->log_archive(OVDBenv, &listp, DB_ARCH_ABS);
186 #endif
187         if(ret != 0) {
188             warn("OVDB: log_archive: %s", db_strerror(ret));
189             status = 1;
190             break;
191         }
192         if(listp != NULL) {
193             for(p = listp; *p; p++)
194                 unlink(*p);
195             free(listp);
196         }
197         sleep(45);
198     }
199
200     ovdb_close_berkeleydb();
201     _exit(status);
202 }
203
204 static int start_process(pid_t *pid, void (*func)(void))
205 {
206     pid_t child;
207
208     switch(child = fork()) {
209     case 0:
210         (*func)();
211         _exit(0);
212     case -1:
213         syswarn("cannot fork");
214         return -1;
215     default:
216         *pid = child;
217         return 0;
218     }
219     /*NOTREACHED*/
220 }
221
222 static void cleanup(int status)
223 {
224     int cs;
225
226     if(deadlockpid)
227         kill(deadlockpid, SIGTERM);
228     if(checkpointpid)
229         kill(checkpointpid, SIGTERM);
230     if(logremoverpid)
231         kill(logremoverpid, SIGTERM);
232
233     xsignal(SIGINT, SIG_DFL);
234     xsignal(SIGTERM, SIG_DFL);
235     xsignal(SIGHUP, SIG_DFL);
236
237     if(deadlockpid)
238         waitpid(deadlockpid, &cs, 0);
239     if(checkpointpid)
240         waitpid(checkpointpid, &cs, 0);
241     if(logremoverpid)
242         waitpid(logremoverpid, &cs, 0);
243
244     unlink(concatpath(innconf->pathrun, OVDB_MONITOR_PIDFILE));
245     exit(status);
246 }
247
248 static void monitorloop(void)
249 {
250     int cs, restartit;
251     pid_t child;
252
253     while(!signalled) {
254         child = waitpid(-1, &cs, WNOHANG);
255         if(child > 0) {
256             if(WIFSIGNALED(cs)) {
257                 restartit = 0;
258             } else {
259                 if(WEXITSTATUS(cs) == 0)
260                     restartit = 1;
261                 else
262                     restartit = 0;
263             }
264             if(child == deadlockpid) {
265                 deadlockpid = 0;
266                 if(restartit && start_process(&deadlockpid, deadlock))
267                     cleanup(1);
268             } else if(child == checkpointpid) {
269                 checkpointpid = 0;
270                 if(restartit && start_process(&checkpointpid, checkpoint))
271                     cleanup(1);
272             } else if(child == logremoverpid) {
273                 logremoverpid = 0;
274                 if(restartit && start_process(&logremoverpid, logremover))
275                     cleanup(1);
276             }
277             if(!restartit)
278                 cleanup(1);
279         }
280         sleep(20);
281     }
282     cleanup(0);
283 }
284
285
286 int main(int argc, char **argv)
287 {
288     char *pidfile;
289
290     setproctitle_init(argc, argv);
291
292     openlog("ovdb_monitor", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG);
293     message_program_name = "ovdb_monitor";
294
295     if(argc != 2 || strcmp(argv[1], SPACES))
296         die("should be started by ovdb_init");
297     message_handlers_warn(1, message_log_syslog_err);
298     message_handlers_die(1, message_log_syslog_err);
299
300     if (!innconf_read(NULL))
301         exit(1);
302
303     if(strcmp(innconf->ovmethod, "ovdb"))
304         die("ovmethod not set to ovdb in inn.conf");
305     if(!ovdb_check_user())
306         die("command must be run as user " NEWSUSER);
307     if(!ovdb_getlock(OVDB_LOCK_ADMIN))
308         die("cannot lock database");
309
310     xsignal(SIGINT, sigfunc);
311     xsignal(SIGTERM, sigfunc);
312     xsignal(SIGHUP, sigfunc);
313
314     pidfile = concatpath(innconf->pathrun, OVDB_MONITOR_PIDFILE);
315     if(putpid(pidfile))
316         exit(1);
317     if(start_process(&deadlockpid, deadlock))
318         cleanup(1);
319     if(start_process(&checkpointpid, checkpoint))
320         cleanup(1);
321     if(start_process(&logremoverpid, logremover))
322         cleanup(1);
323
324     monitorloop();
325
326     /* Never reached. */
327     return 1;
328 }
329
330 #endif /* USE_BERKELEY_DB */
331