Mercurial > dovecot > core-2.2
changeset 9794:da330a07e45c HEAD
master: Support reloading configuration.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 14 Aug 2009 18:14:21 -0400 |
parents | d7ccdbb58a03 |
children | 4011a41cf404 |
files | src/lib-master/master-service-settings.c src/lib-master/master-service-settings.h src/master/main.c src/master/master-settings.h src/master/service-auth-source.c src/master/service-monitor.c src/master/service-monitor.h src/master/service-process.c src/master/service.c src/master/service.h |
diffstat | 10 files changed, 183 insertions(+), 73 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-master/master-service-settings.c Fri Aug 14 18:13:35 2009 -0400 +++ b/src/lib-master/master-service-settings.c Fri Aug 14 18:14:21 2009 -0400 @@ -97,15 +97,13 @@ } static int -master_service_read_config(struct master_service *service, +master_service_read_config(struct master_service *service, const char *path, const struct master_service_settings_input *input, const char **error_r) { - const char *path; struct stat st; int fd, ret; - path = master_service_get_config_path(service); if (service->config_fd != -1) { fd = service->config_fd; service->config_fd = -1; @@ -184,19 +182,22 @@ const struct setting_parser_info *tmp_root; struct setting_parser_context *parser; struct istream *istream; - const char *error, *env, *const *keys; + const char *path, *error, *env, *const *keys; void **sets; unsigned int i; int ret, fd = -1; if (getenv("DOVECONF_ENV") == NULL && !service->default_settings) { - fd = master_service_read_config(service, input, error_r); + path = input->config_path != NULL ? input->config_path : + master_service_get_config_path(service); + fd = master_service_read_config(service, path, input, error_r); if (fd == -1) return -1; } if (service->set_pool != NULL) { - settings_parser_deinit(&service->set_parser); + if (service->set_parser != NULL) + settings_parser_deinit(&service->set_parser); p_clear(service->set_pool); } else { service->set_pool = @@ -289,6 +290,15 @@ return master_service_settings_read(service, &input, error_r); } +pool_t master_service_settings_detach(struct master_service *service) +{ + pool_t pool = service->set_pool; + + settings_parser_deinit(&service->set_parser); + service->set_pool = NULL; + return pool; +} + const struct master_service_settings * master_service_settings_get(struct master_service *service) {
--- a/src/lib-master/master-service-settings.h Fri Aug 14 18:13:35 2009 -0400 +++ b/src/lib-master/master-service-settings.h Fri Aug 14 18:14:21 2009 -0400 @@ -18,6 +18,7 @@ struct master_service_settings_input { const struct setting_parser_info **roots; const struct dynamic_settings_parser *dyn_parsers; + const char *config_path; bool preserve_home; const char *module; @@ -34,6 +35,11 @@ int master_service_settings_read_simple(struct master_service *service, const struct setting_parser_info **roots, const char **error_r); +/* destroy settings parser and clear service's set_pool, so that + master_service_settings_read*() can be called without freeing memory used + by existing settings structures. */ +pool_t master_service_settings_detach(struct master_service *service); + const struct master_service_settings * master_service_settings_get(struct master_service *service); void **master_service_settings_get_others(struct master_service *service);
--- a/src/master/main.c Fri Aug 14 18:13:35 2009 -0400 +++ b/src/master/main.c Fri Aug 14 18:14:21 2009 -0400 @@ -27,6 +27,7 @@ #define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf" +#define MASTER_SERVICE_NAME "master" #define FATAL_FILENAME "master-fatal.lastlog" #define MASTER_PID_FILE_NAME "master.pid" #define SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS (60*3) @@ -41,8 +42,12 @@ static char *pidfile_path; static struct service_list *services; static fatal_failure_callback_t *orig_fatal_callback; +static const char *child_process_env[3]; /* @UNSAFE */ -static const char *child_process_env[3]; /* @UNSAFE */ +static const struct setting_parser_info *set_roots[] = { + &master_setting_parser_info, + NULL +}; void process_exec(const char *cmd, const char *extra_args[]) { @@ -304,32 +309,41 @@ sig_settings_reload(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) { - struct master_settings *new_set; + struct master_service_settings_input input; + const struct master_settings *set; + void **sets; struct service_list *new_services; const char *error; - pool_t pool; /* see if hostname changed */ hostpid_init(); -#if 0 // FIXME - /* FIXME: this loses process structures for existing processes. - figure out something. */ - new_set = master_settings_read(pool, config_binary, config_path); - new_services = new_set == NULL ? NULL : - services_create(new_set, child_process_env, &error); -#endif - if (new_services == NULL) { + memset(&input, 0, sizeof(input)); + input.roots = set_roots; + input.module = MASTER_SERVICE_NAME; + input.config_path = services_get_config_socket_path(services); + if (master_service_settings_read(master_service, &input, &error) < 0) { + i_error("Error reading configuration: %s", error); + return; + } + sets = master_service_settings_get_others(master_service); + set = sets[0]; + + if (services_create(set, child_process_env, + &new_services, &error) < 0) { /* new configuration is invalid, keep the old */ i_error("Config reload failed: %s", error); return; } + new_services->config->config_file_path = + p_strdup(new_services->pool, + services->config->config_file_path); /* switch to new configuration. */ (void)services_listen_using(new_services, services); services_destroy(services); + services = new_services; - services_monitor_start(services); } @@ -342,7 +356,7 @@ static void sig_reap_children(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) { - services_monitor_reap_children(services); + services_monitor_reap_children(); } static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED) @@ -404,6 +418,7 @@ i_free(pidfile_path); services_destroy(services); + service_pids_deinit(); } static const char *get_full_config_path(struct service_list *list) @@ -571,10 +586,6 @@ int main(int argc, char *argv[]) { - static const struct setting_parser_info *set_roots[] = { - &master_setting_parser_info, - NULL - }; struct master_settings *set; unsigned int child_process_env_idx = 0; const char *getopt_str, *error, *env_tz, *doveconf_arg = NULL; @@ -589,7 +600,7 @@ else child_process_env[child_process_env_idx++] = "GDB=1"; #endif - master_service = master_service_init("master", + master_service = master_service_init(MASTER_SERVICE_NAME, MASTER_SERVICE_FLAG_STANDALONE | MASTER_SERVICE_FLAG_DONT_LOG_TO_STDERR, argc, argv); @@ -721,8 +732,8 @@ /* create service structures from settings. if there are any errors in service configuration we'll catch it here. */ - services = services_create(set, child_process_env, &error); - if (services == NULL) + service_pids_init(); + if (services_create(set, child_process_env, &services, &error) < 0) i_fatal("%s", error); services->config->config_file_path = get_full_config_path(services);
--- a/src/master/master-settings.h Fri Aug 14 18:13:35 2009 -0400 +++ b/src/master/master-settings.h Fri Aug 14 18:14:21 2009 -0400 @@ -61,9 +61,6 @@ extern struct setting_parser_info master_setting_parser_info; -struct master_settings * -master_settings_read(pool_t pool, const char *config_binary, - const char *config_path); bool master_settings_do_fixes(const struct master_settings *set); #endif
--- a/src/master/service-auth-source.c Fri Aug 14 18:13:35 2009 -0400 +++ b/src/master/service-auth-source.c Fri Aug 14 18:14:21 2009 -0400 @@ -204,7 +204,7 @@ fd_close_on_exec(client_fd, TRUE); /* we have a request. check its validity. */ - auth_process = hash_table_lookup(service->list->pids, &req.auth_pid); + auth_process = hash_table_lookup(service_pids, &req.auth_pid); if (auth_process == NULL) { service_error(service, "authentication request for unknown " "auth server PID %s", dec2str(req.auth_pid));
--- a/src/master/service-monitor.c Fri Aug 14 18:13:35 2009 -0400 +++ b/src/master/service-monitor.c Fri Aug 14 18:14:21 2009 -0400 @@ -44,7 +44,7 @@ break; } - process = hash_table_lookup(service->list->pids, &status.pid); + process = hash_table_lookup(service_pids, &status.pid); if (process == NULL) { /* we've probably wait()ed it away already. ignore */ return; @@ -239,7 +239,7 @@ service_process_notify_add(service->list->anvil_kills, process); } -void services_monitor_reap_children(struct service_list *service_list) +void services_monitor_reap_children(void) { struct service_process *process; struct service *service; @@ -247,7 +247,7 @@ int status; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { - process = hash_table_lookup(service_list->pids, &pid); + process = hash_table_lookup(service_pids, &pid); if (process == NULL) { i_error("waitpid() returned unknown PID %s", dec2str(pid));
--- a/src/master/service-monitor.h Fri Aug 14 18:13:35 2009 -0400 +++ b/src/master/service-monitor.h Fri Aug 14 18:14:21 2009 -0400 @@ -8,7 +8,7 @@ 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 services_monitor_reap_children(void); void service_monitor_stop(struct service *service); void service_monitor_listen_start(struct service *service);
--- a/src/master/service-process.c Fri Aug 14 18:13:35 2009 -0400 +++ b/src/master/service-process.c Fri Aug 14 18:14:21 2009 -0400 @@ -367,10 +367,9 @@ static void service_process_setup_environment(struct service *service, unsigned int uid) { - const struct master_service_settings *set; - struct service_listener *const *listeners; + const struct master_service_settings *set = service->list->service_set; const char *const *p; - unsigned int limit, count; + unsigned int limit; /* remove all environment, and put back what we need */ env_clean(); @@ -385,7 +384,6 @@ case SERVICE_TYPE_LOG: /* give the log's configuration directly, so it won't depend on config process */ - set = master_service_settings_get(master_service); env_put("DOVECONF_ENV=1"); env_put(t_strconcat("LOG_PATH=", set->log_path, NULL)); env_put(t_strconcat("INFO_LOG_PATH=", set->info_log_path, NULL)); @@ -393,11 +391,8 @@ env_put(t_strconcat("SYSLOG_FACILITY=", set->syslog_facility, NULL)); break; default: - listeners = array_get(&service->list->config->listeners, - &count); - i_assert(count > 0); env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=", - listeners[0]->set.fileset.set->path, NULL)); + services_get_config_socket_path(service->list), NULL)); break; } @@ -534,15 +529,17 @@ service->process_count++; service->process_avail++; - hash_table_insert(service->list->pids, &process->pid, process); + service_list_ref(service->list); + hash_table_insert(service_pids, &process->pid, process); return process; } void service_process_destroy(struct service_process *process) { struct service *service = process->service; + struct service_list *service_list = service->list; - hash_table_remove(service->list->pids, &process->pid); + hash_table_remove(service_pids, &process->pid); if (process->available_count > 0) service->process_avail--; @@ -568,6 +565,7 @@ process->destroyed = TRUE; service_process_unref(process); + service_list_unref(service_list); } void service_process_ref(struct service_process *process)
--- a/src/master/service.c Fri Aug 14 18:13:35 2009 -0400 +++ b/src/master/service.c Fri Aug 14 18:14:21 2009 -0400 @@ -6,6 +6,8 @@ #include "aqueue.h" #include "hash.h" #include "str.h" +#include "master-service.h" +#include "master-service-settings.h" #include "service.h" #include "service-anvil.h" #include "service-process.h" @@ -14,6 +16,10 @@ #include <unistd.h> #include <signal.h> +#define SERVICE_DIE_TIMEOUT_MSECS (1000*10) + +struct hash_table *service_pids; + void service_error(struct service *service, const char *format, ...) { va_list args; @@ -311,9 +317,9 @@ return NULL; } -struct service_list * -services_create(const struct master_settings *set, - const char *const *child_process_env, const char **error_r) +int services_create(const struct master_settings *set, + const char *const *child_process_env, + struct service_list **services_r, const char **error_r) { struct service_list *service_list; struct service *service, *const *services; @@ -325,7 +331,10 @@ pool = pool_alloconly_create("services pool", 4096); service_list = p_new(pool, struct service_list, 1); + service_list->refcount = 1; service_list->pool = pool; + service_list->service_set = master_service_settings_get(master_service); + service_list->set_pool = master_service_settings_detach(master_service); service_list->child_process_env = child_process_env; service_list->master_log_fd[0] = -1; service_list->master_log_fd[1] = -1; @@ -339,21 +348,21 @@ if (service == NULL) { *error_r = t_strdup_printf("service(%s) %s", service_settings[i]->name, error); - return NULL; + return -1; } switch (service->type) { case SERVICE_TYPE_LOG: if (service_list->log != NULL) { *error_r = "Multiple log services specified"; - return NULL; + return -1; } service_list->log = service; break; case SERVICE_TYPE_CONFIG: if (service_list->config != NULL) { *error_r = "Multiple config services specified"; - return NULL; + return -1; } service_list->config = service; break; @@ -376,27 +385,26 @@ *error_r = t_strdup_printf( "auth_dest_service doesn't exist: %s", dest_service); - return NULL; + return -1; } } } if (service_list->log == NULL) { *error_r = "log service not specified"; - return NULL; + return -1; } if (service_list->config == NULL) { *error_r = "config process not specified"; - return NULL; + return -1; } if (service_list_init_anvil(service_list, error_r) < 0) - return NULL; + return -1; - service_list->pids = hash_table_create(default_pool, pool, 0, - pid_hash, pid_hash_cmp); - return service_list; + *services_r = service_list; + return 0; } void service_signal(struct service *service, int signo) @@ -404,7 +412,7 @@ struct hash_iterate_context *iter; void *key, *value; - iter = hash_table_iterate_init(service->list->pids); + iter = hash_table_iterate_init(service_pids); while (hash_table_iterate(iter, &key, &value)) { struct service_process *process = value; @@ -419,27 +427,71 @@ hash_table_iterate_deinit(&iter); } +static void services_kill_timeout(struct service_list *service_list) +{ + struct service *const *services; + unsigned int i, count; + int sig; + + if (!service_list->sigterm_sent) + sig = SIGTERM; + else + sig = SIGKILL; + service_list->sigterm_sent = TRUE; + + i_warning("Processes aren't dying after reload, sending %s.", + sig == SIGTERM ? "SIGTERM" : "SIGKILL"); + + services = array_get(&service_list->services, &count); + for (i = 0; i < count; i++) + service_signal(services[i], sig); +} + void services_destroy(struct service_list *service_list) { - struct hash_iterate_context *iter; - void *key, *value; - /* make sure we log if child processes died unexpectedly */ - services_monitor_reap_children(service_list); + services_monitor_reap_children(); services_monitor_stop(service_list); + service_list_deinit_anvil(service_list); - /* free all child process information */ - iter = hash_table_iterate_init(service_list->pids); - while (hash_table_iterate(iter, &key, &value)) - service_process_destroy(value); - hash_table_iterate_deinit(&iter); + if (service_list->refcount > 1) { + service_list->to_kill = + timeout_add(SERVICE_DIE_TIMEOUT_MSECS, + services_kill_timeout, service_list); + } + + service_list_unref(service_list); +} + +void service_list_ref(struct service_list *service_list) +{ + i_assert(service_list->refcount > 0); + service_list->refcount++; +} - service_list_deinit_anvil(service_list); - hash_table_destroy(&service_list->pids); +void service_list_unref(struct service_list *service_list) +{ + i_assert(service_list->refcount > 0); + if (--service_list->refcount > 0) + return; + + if (service_list->to_kill != NULL) + timeout_remove(&service_list->to_kill); + pool_unref(&service_list->set_pool); pool_unref(&service_list->pool); } +const char *services_get_config_socket_path(struct service_list *service_list) +{ + struct service_listener *const *listeners; + unsigned int count; + + listeners = array_get(&service_list->config->listeners, &count); + i_assert(count > 0); + return listeners[0]->set.fileset.set->path; +} + static void service_throttle_timeout(struct service *service) { timeout_remove(&service->to_throttle); @@ -468,3 +520,22 @@ service_throttle(services[i], secs); } } + +void service_pids_init(void) +{ + service_pids = hash_table_create(default_pool, default_pool, 0, + pid_hash, pid_hash_cmp); +} + +void service_pids_deinit(void) +{ + struct hash_iterate_context *iter; + void *key, *value; + + /* free all child process information */ + iter = hash_table_iterate_init(service_pids); + while (hash_table_iterate(iter, &key, &value)) + service_process_destroy(value); + hash_table_iterate_deinit(&iter); + hash_table_destroy(&service_pids); +}
--- a/src/master/service.h Fri Aug 14 18:13:35 2009 -0400 +++ b/src/master/service.h Fri Aug 14 18:14:21 2009 -0400 @@ -94,10 +94,14 @@ struct service_list { pool_t pool; + pool_t set_pool; + int refcount; + struct timeout *to_kill; + + const struct master_service_settings *service_set; struct service *config; struct service *log; - struct hash_table *pids; const char *const *child_process_env; /* nonblocking log fds usd by master */ @@ -111,16 +115,26 @@ struct service_process_notify *anvil_kills; ARRAY_DEFINE(services, struct service *); + + unsigned int sigterm_sent:1; }; +extern struct hash_table *service_pids; + /* Create all services from settings */ -struct service_list * -services_create(const struct master_settings *set, - const char *const *child_process_env, const char **error_r); +int services_create(const struct master_settings *set, + const char *const *child_process_env, + struct service_list **services_r, const char **error_r); /* Destroy services */ void services_destroy(struct service_list *service_list); +void service_list_ref(struct service_list *service_list); +void service_list_unref(struct service_list *service_list); + +/* Return path to configuration process socket. */ +const char *services_get_config_socket_path(struct service_list *service_list); + /* Send a signal to all processes in a given service */ void service_signal(struct service *service, int signo); @@ -133,4 +147,7 @@ void service_error(struct service *service, const char *format, ...) ATTR_FORMAT(2, 3); +void service_pids_init(void); +void service_pids_deinit(void); + #endif