/* * ovdb_monitor * Performs database maintenance tasks * + Transaction checkpoints * + Deadlock detection * + Transaction log removal */ #include "config.h" #include "clibrary.h" #include "portable/setproctitle.h" #include "portable/wait.h" #include #include #include #include "inn/innconf.h" #include "inn/messages.h" #include "libinn.h" #include "ov.h" #include "../storage/ovdb/ovdb.h" #include "../storage/ovdb/ovdb-private.h" #ifndef USE_BERKELEY_DB int main(int argc UNUSED, char **argv UNUSED) { exit(0); } #else /* USE_BERKELEY_DB */ static int signalled = 0; static void sigfunc(int sig UNUSED) { signalled = 1; } static pid_t deadlockpid = 0; static pid_t checkpointpid = 0; static pid_t logremoverpid = 0; static int putpid(const char *path) { char buf[30]; int fd = open(path, O_WRONLY|O_TRUNC|O_CREAT, 0664); if(!fd) { syswarn("cannot open %s", path); return -1; } snprintf(buf, sizeof(buf), "%d\n", getpid()); if(write(fd, buf, strlen(buf)) < 0) { syswarn("cannot write to %s", path); close(fd); return -1; } close(fd); return 0; } static void deadlock(void) { int ret, status = 0; u_int32_t atype = DB_LOCK_YOUNGEST; if(ovdb_open_berkeleydb(OV_WRITE, 0)) _exit(1); setproctitle("deadlock"); while(!signalled) { #if DB_VERSION_MAJOR == 2 ret = lock_detect(OVDBenv->lk_info, 0, atype); #elif DB_VERSION_MAJOR == 3 ret = lock_detect(OVDBenv, 0, atype, NULL); #else ret = OVDBenv->lock_detect(OVDBenv, 0, atype, NULL); #endif if(ret != 0) { warn("OVDB: lock_detect: %s", db_strerror(ret)); status = 1; break; } sleep(30); } ovdb_close_berkeleydb(); _exit(status); } static void checkpoint(void) { int ret, status = 0; DB *db; #if DB_VERSION_MAJOR == 2 DB_INFO dbinfo; #endif if(ovdb_open_berkeleydb(OV_WRITE, 0)) _exit(1); setproctitle("checkpoint"); /* Open a database and close it. This is so a necessary initialization gets performed (by the db->open function). */ #if DB_VERSION_MAJOR == 2 memset(&dbinfo, 0, sizeof dbinfo); ret = db_open("version", DB_BTREE, DB_CREATE, 0666, OVDBenv, &dbinfo, &db); if (ret != 0) { warn("OVDB: checkpoint: db_open failed: %s", db_strerror(ret)); _exit(1); } #else ret = db_create(&db, OVDBenv, 0); if (ret != 0) { warn("OVDB: checkpoint: db_create: %s", db_strerror(ret)); _exit(1); } #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1) ret = db->open(db, NULL, "version", NULL, DB_BTREE, DB_CREATE, 0666); #else ret = db->open(db, "version", NULL, DB_BTREE, DB_CREATE, 0666); #endif if (ret != 0) { db->close(db, 0); warn("OVDB: checkpoint: version open: %s", db_strerror(ret)); _exit(1); } #endif db->close(db, 0); while(!signalled) { #if DB_VERSION_MAJOR == 2 ret = txn_checkpoint(OVDBenv->tx_info, 2048, 1); #elif DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR == 0 ret = txn_checkpoint(OVDBenv, 2048, 1); #elif DB_VERSION_MAJOR == 3 ret = txn_checkpoint(OVDBenv, 2048, 1, 0); #elif DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR < 1 ret = OVDBenv->txn_checkpoint(OVDBenv, 2048, 1, 0); #else OVDBenv->txn_checkpoint(OVDBenv, 2048, 1, 0); #endif #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1) sleep(30); #else if(ret != 0 && ret != DB_INCOMPLETE) { warn("OVDB: txn_checkpoint: %s", db_strerror(ret)); status = 1; break; } if(ret == DB_INCOMPLETE) sleep(2); else sleep(30); #endif } ovdb_close_berkeleydb(); _exit(status); } static void logremover(void) { int ret, status = 0; char **listp, **p; if(ovdb_open_berkeleydb(OV_WRITE, 0)) _exit(1); setproctitle("logremover"); while(!signalled) { #if DB_VERSION_MAJOR == 2 ret = log_archive(OVDBenv->lg_info, &listp, DB_ARCH_ABS, malloc); #elif DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR <= 2 ret = log_archive(OVDBenv, &listp, DB_ARCH_ABS, malloc); #elif DB_VERSION_MAJOR == 3 ret = log_archive(OVDBenv, &listp, DB_ARCH_ABS); #else ret = OVDBenv->log_archive(OVDBenv, &listp, DB_ARCH_ABS); #endif if(ret != 0) { warn("OVDB: log_archive: %s", db_strerror(ret)); status = 1; break; } if(listp != NULL) { for(p = listp; *p; p++) unlink(*p); free(listp); } sleep(45); } ovdb_close_berkeleydb(); _exit(status); } static int start_process(pid_t *pid, void (*func)(void)) { pid_t child; switch(child = fork()) { case 0: (*func)(); _exit(0); case -1: syswarn("cannot fork"); return -1; default: *pid = child; return 0; } /*NOTREACHED*/ } static void cleanup(int status) { int cs; if(deadlockpid) kill(deadlockpid, SIGTERM); if(checkpointpid) kill(checkpointpid, SIGTERM); if(logremoverpid) kill(logremoverpid, SIGTERM); xsignal(SIGINT, SIG_DFL); xsignal(SIGTERM, SIG_DFL); xsignal(SIGHUP, SIG_DFL); if(deadlockpid) waitpid(deadlockpid, &cs, 0); if(checkpointpid) waitpid(checkpointpid, &cs, 0); if(logremoverpid) waitpid(logremoverpid, &cs, 0); unlink(concatpath(innconf->pathrun, OVDB_MONITOR_PIDFILE)); exit(status); } static void monitorloop(void) { int cs, restartit; pid_t child; while(!signalled) { child = waitpid(-1, &cs, WNOHANG); if(child > 0) { if(WIFSIGNALED(cs)) { restartit = 0; } else { if(WEXITSTATUS(cs) == 0) restartit = 1; else restartit = 0; } if(child == deadlockpid) { deadlockpid = 0; if(restartit && start_process(&deadlockpid, deadlock)) cleanup(1); } else if(child == checkpointpid) { checkpointpid = 0; if(restartit && start_process(&checkpointpid, checkpoint)) cleanup(1); } else if(child == logremoverpid) { logremoverpid = 0; if(restartit && start_process(&logremoverpid, logremover)) cleanup(1); } if(!restartit) cleanup(1); } sleep(20); } cleanup(0); } int main(int argc, char **argv) { char *pidfile; setproctitle_init(argc, argv); openlog("ovdb_monitor", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG); message_program_name = "ovdb_monitor"; if(argc != 2 || strcmp(argv[1], SPACES)) die("should be started by ovdb_init"); message_handlers_warn(1, message_log_syslog_err); message_handlers_die(1, message_log_syslog_err); if (!innconf_read(NULL)) exit(1); if(strcmp(innconf->ovmethod, "ovdb")) die("ovmethod not set to ovdb in inn.conf"); if(!ovdb_check_user()) die("command must be run as user " NEWSUSER); if(!ovdb_getlock(OVDB_LOCK_ADMIN)) die("cannot lock database"); xsignal(SIGINT, sigfunc); xsignal(SIGTERM, sigfunc); xsignal(SIGHUP, sigfunc); pidfile = concatpath(innconf->pathrun, OVDB_MONITOR_PIDFILE); if(putpid(pidfile)) exit(1); if(start_process(&deadlockpid, deadlock)) cleanup(1); if(start_process(&checkpointpid, checkpoint)) cleanup(1); if(start_process(&logremoverpid, logremover)) cleanup(1); monitorloop(); /* Never reached. */ return 1; } #endif /* USE_BERKELEY_DB */