From b2ed2b25c4c457ec1c99ebe5e9bd66a2e2f89cfd Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Sun, 21 Jun 2009 21:46:46 -0400 Subject: [PATCH] master: If time moves backwards, delay launching new processes. --HG-- branch : HEAD --- src/master/main.c | 22 ++++++++++++++++++++++ src/master/service-monitor.c | 25 ++++++++----------------- src/master/service-monitor.h | 4 ++++ src/master/service-process.c | 5 +++++ src/master/service.c | 30 ++++++++++++++++++++++++++++++ src/master/service.h | 8 ++++++++ 6 files changed, 77 insertions(+), 17 deletions(-) diff --git a/src/master/main.c b/src/master/main.c index 2001b0ca3c..699c76ffbc 100644 --- a/src/master/main.c +++ b/src/master/main.c @@ -1,6 +1,7 @@ /* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */ #include "common.h" +#include "ioloop.h" #include "lib-signals.h" #include "fd-close-on-exec.h" #include "array.h" @@ -28,6 +29,7 @@ #define FATAL_FILENAME "master-fatal.lastlog" #define MASTER_PID_FILE_NAME "master.pid" +#define SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS (60*3) struct master_service *master_service; uid_t master_uid; @@ -418,6 +420,24 @@ static const char *get_full_config_path(struct service_list *list) return p_strconcat(list->pool, cwd, "/", path, NULL); } +static void master_time_moved(time_t old_time, time_t new_time) +{ + unsigned long secs; + + if (new_time >= old_time) + return; + + /* time moved backwards. disable launching new service processes + until */ + secs = old_time - new_time + 1; + if (secs > SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS) + secs = SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS; + services_throttle_time_sensitives(services, secs); + i_warning("Time moved backwards by %lu seconds, " + "waiting for %lu secs until new services are launched again.", + (unsigned long)(old_time - new_time), secs); +} + static void daemonize(void) { pid_t pid; @@ -575,6 +595,8 @@ int main(int argc, char *argv[]) argc, argv); i_set_failure_prefix(""); + io_loop_set_time_moved_callback(current_ioloop, master_time_moved); + master_uid = geteuid(); master_gid = getegid(); diff --git a/src/master/service-monitor.c b/src/master/service-monitor.c index 7b6eaf003c..6b7dd5d479 100644 --- a/src/master/service-monitor.c +++ b/src/master/service-monitor.c @@ -15,11 +15,9 @@ #include #include -#define THROTTLE_TIMEOUT (1000*60) +#define SERVICE_STARTUP_FAILURE_THROTTLE_SECS 60 -static void service_monitor_stop(struct service *service); -static void service_monitor_listen_start(struct service *service); -static void service_monitor_listen_stop(struct service *service); +void service_monitor_stop(struct service *service); static void service_status_input(struct service *service) { @@ -99,22 +97,13 @@ static void service_status_input(struct service *service) process->available_count = status.available_count; } -static void service_throttle_timeout(struct service *service) -{ - timeout_remove(&service->to_throttle); - service_monitor_listen_start(service); -} - static void service_monitor_throttle(struct service *service) { if (service->to_throttle != NULL) return; service_error(service, "command startup failed, throttling"); - service_monitor_listen_stop(service); - - service->to_throttle = timeout_add(THROTTLE_TIMEOUT, - service_throttle_timeout, service); + service_throttle(service, SERVICE_STARTUP_FAILURE_THROTTLE_SECS); } static void service_accept(struct service *service) @@ -136,11 +125,12 @@ static void service_accept(struct service *service) service_monitor_listen_stop(service); } -static void service_monitor_listen_start(struct service *service) +void service_monitor_listen_start(struct service *service) { struct service_listener *const *listeners; unsigned int i, count; + service->listening = TRUE; service->listen_pending = FALSE; listeners = array_get(&service->listeners, &count); @@ -152,7 +142,7 @@ static void service_monitor_listen_start(struct service *service) } } -static void service_monitor_listen_stop(struct service *service) +void service_monitor_listen_stop(struct service *service) { struct service_listener *const *listeners; unsigned int i, count; @@ -164,6 +154,7 @@ static void service_monitor_listen_stop(struct service *service) if (l->io != NULL) io_remove(&l->io); } + service->listening = FALSE; } void services_monitor_start(struct service_list *service_list) @@ -202,7 +193,7 @@ void services_monitor_start(struct service_list *service_list) service_monitor_listen_stop(service_list->config); } -static void service_monitor_stop(struct service *service) +void service_monitor_stop(struct service *service) { int i; diff --git a/src/master/service-monitor.h b/src/master/service-monitor.h index 70838a6ada..85ffb7dc98 100644 --- a/src/master/service-monitor.h +++ b/src/master/service-monitor.h @@ -10,4 +10,8 @@ void services_monitor_stop(struct service_list *service_list); /* Call after SIGCHLD has been detected */ void services_monitor_reap_children(struct service_list *service_list); +void service_monitor_stop(struct service *service); +void service_monitor_listen_start(struct service *service); +void service_monitor_listen_stop(struct service *service); + #endif diff --git a/src/master/service-process.c b/src/master/service-process.c index 731b616637..9b31412a20 100644 --- a/src/master/service-process.c +++ b/src/master/service-process.c @@ -383,6 +383,11 @@ service_process_create(struct service *service, const char *const *auth_args, int fd[2]; pid_t pid; + if (!service->listening) { + /* probably throttling service, don't create new processes */ + return NULL; + } + switch (service->type) { case SERVICE_TYPE_AUTH_SOURCE: case SERVICE_TYPE_AUTH_SERVER: diff --git a/src/master/service.c b/src/master/service.c index 53a85a4356..07ccd6c3ec 100644 --- a/src/master/service.c +++ b/src/master/service.c @@ -1,6 +1,7 @@ /* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */ #include "common.h" +#include "ioloop.h" #include "array.h" #include "aqueue.h" #include "hash.h" @@ -421,3 +422,32 @@ void services_destroy(struct service_list *service_list) hash_table_destroy(&service_list->pids); pool_unref(&service_list->pool); } + +static void service_throttle_timeout(struct service *service) +{ + timeout_remove(&service->to_throttle); + service_monitor_listen_start(service); +} + +void service_throttle(struct service *service, unsigned int secs) +{ + if (service->to_throttle != NULL) + return; + + service_monitor_listen_stop(service); + service->to_throttle = timeout_add(secs * 1000, + service_throttle_timeout, service); +} + +void services_throttle_time_sensitives(struct service_list *list, + unsigned int secs) +{ + struct service *const *services; + unsigned int i, count; + + services = array_get(&list->services, &count); + for (i = 0; i < count; i++) { + if (services[i]->type == SERVICE_TYPE_UNKNOWN) + service_throttle(services[i], secs); + } +} diff --git a/src/master/service.h b/src/master/service.h index 4267d6f3b2..a1a5a36ec3 100644 --- a/src/master/service.h +++ b/src/master/service.h @@ -88,6 +88,8 @@ struct service { /* all processes are in use and new connections are coming */ unsigned int listen_pending:1; + /* service is currently listening for new connections */ + unsigned int listening:1; }; struct service_list { @@ -122,6 +124,12 @@ void services_destroy(struct service_list *service_list); /* Send a signal to all processes in a given service */ void service_signal(struct service *service, int signo); +/* Prevent service from launching new processes for a while. */ +void service_throttle(struct service *service, unsigned int secs); +/* Time moved backwards. Throttle services that care about time. */ +void services_throttle_time_sensitives(struct service_list *list, + unsigned int secs); + void service_error(struct service *service, const char *format, ...) ATTR_FORMAT(2, 3); -- 2.47.3