summaryrefslogtreecommitdiffstats
path: root/src/lib-master/master-service-settings.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:36:47 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:36:47 +0000
commit0441d265f2bb9da249c7abf333f0f771fadb4ab5 (patch)
tree3f3789daa2f6db22da6e55e92bee0062a7d613fe /src/lib-master/master-service-settings.c
parentInitial commit. (diff)
downloaddovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.tar.xz
dovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.zip
Adding upstream version 1:2.3.21+dfsg1.upstream/1%2.3.21+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib-master/master-service-settings.c')
-rw-r--r--src/lib-master/master-service-settings.c816
1 files changed, 816 insertions, 0 deletions
diff --git a/src/lib-master/master-service-settings.c b/src/lib-master/master-service-settings.c
new file mode 100644
index 0000000..f0f0797
--- /dev/null
+++ b/src/lib-master/master-service-settings.c
@@ -0,0 +1,816 @@
+/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "event-filter.h"
+#include "path-util.h"
+#include "istream.h"
+#include "write-full.h"
+#include "str.h"
+#include "strescape.h"
+#include "syslog-util.h"
+#include "eacces-error.h"
+#include "env-util.h"
+#include "execv-const.h"
+#include "settings-parser.h"
+#include "stats-client.h"
+#include "master-service-private.h"
+#include "master-service-ssl-settings.h"
+#include "master-service-settings.h"
+
+#include <stddef.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/stat.h>
+
+#define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf"
+#define DOVECOT_CONFIG_SOCKET_PATH PKG_RUNDIR"/config"
+
+#define CONFIG_READ_TIMEOUT_SECS 10
+#define CONFIG_HANDSHAKE "VERSION\tconfig\t2\t0\n"
+
+#undef DEF
+#define DEF(type, name) \
+ SETTING_DEFINE_STRUCT_##type(#name, name, struct master_service_settings)
+
+static bool
+master_service_settings_check(void *_set, pool_t pool, const char **error_r);
+
+static const struct setting_define master_service_setting_defines[] = {
+ DEF(STR, base_dir),
+ DEF(STR, state_dir),
+ DEF(STR, instance_name),
+ DEF(STR, log_path),
+ DEF(STR, info_log_path),
+ DEF(STR, debug_log_path),
+ DEF(STR, log_timestamp),
+ DEF(STR, log_debug),
+ DEF(STR, log_core_filter),
+ DEF(STR, process_shutdown_filter),
+ DEF(STR, syslog_facility),
+ DEF(STR, import_environment),
+ DEF(STR, stats_writer_socket_path),
+ DEF(SIZE, config_cache_size),
+ DEF(BOOL, version_ignore),
+ DEF(BOOL, shutdown_clients),
+ DEF(BOOL, verbose_proctitle),
+
+ DEF(STR, haproxy_trusted_networks),
+ DEF(TIME, haproxy_timeout),
+
+ SETTING_DEFINE_LIST_END
+};
+
+/* <settings checks> */
+#ifdef HAVE_LIBSYSTEMD
+# define ENV_SYSTEMD " LISTEN_PID LISTEN_FDS NOTIFY_SOCKET"
+#else
+# define ENV_SYSTEMD ""
+#endif
+#ifdef DEBUG
+# define ENV_GDB " GDB DEBUG_SILENT"
+#else
+# define ENV_GDB ""
+#endif
+/* </settings checks> */
+
+static const struct master_service_settings master_service_default_settings = {
+ .base_dir = PKG_RUNDIR,
+ .state_dir = PKG_STATEDIR,
+ .instance_name = PACKAGE,
+ .log_path = "syslog",
+ .info_log_path = "",
+ .debug_log_path = "",
+ .log_timestamp = DEFAULT_FAILURE_STAMP_FORMAT,
+ .log_debug = "",
+ .log_core_filter = "",
+ .process_shutdown_filter = "",
+ .syslog_facility = "mail",
+ .import_environment = "TZ CORE_OUTOFMEM CORE_ERROR" ENV_SYSTEMD ENV_GDB,
+ .stats_writer_socket_path = "stats-writer",
+ .config_cache_size = 1024*1024,
+ .version_ignore = FALSE,
+ .shutdown_clients = TRUE,
+ .verbose_proctitle = FALSE,
+
+ .haproxy_trusted_networks = "",
+ .haproxy_timeout = 3
+};
+
+const struct setting_parser_info master_service_setting_parser_info = {
+ .module_name = "master",
+ .defines = master_service_setting_defines,
+ .defaults = &master_service_default_settings,
+
+ .type_offset = SIZE_MAX,
+ .struct_size = sizeof(struct master_service_settings),
+
+ .parent_offset = SIZE_MAX,
+ .check_func = master_service_settings_check
+};
+
+/* <settings checks> */
+static bool
+setting_filter_parse(const char *set_name, const char *set_value,
+ void (*handle_filter)(struct event_filter *) ATTR_UNUSED,
+ const char **error_r)
+{
+ struct event_filter *filter;
+ const char *error;
+
+ if (set_value[0] == '\0')
+ return TRUE;
+
+ filter = event_filter_create();
+ if (event_filter_parse(set_value, filter, &error) < 0) {
+ *error_r = t_strdup_printf("Invalid %s: %s", set_name, error);
+ event_filter_unref(&filter);
+ return FALSE;
+ }
+#ifndef CONFIG_BINARY
+ handle_filter(filter);
+#endif
+ event_filter_unref(&filter);
+ return TRUE;
+}
+
+static void
+master_service_set_process_shutdown_filter_wrapper(struct event_filter *filter)
+{
+ master_service_set_process_shutdown_filter(master_service, filter);
+}
+
+static bool
+master_service_settings_check(void *_set, pool_t pool ATTR_UNUSED,
+ const char **error_r)
+{
+ struct master_service_settings *set = _set;
+ int facility;
+
+ if (*set->log_path == '\0') {
+ /* default to syslog logging */
+ set->log_path = "syslog";
+ }
+ if (!syslog_facility_find(set->syslog_facility, &facility)) {
+ *error_r = t_strdup_printf("Unknown syslog_facility: %s",
+ set->syslog_facility);
+ return FALSE;
+ }
+
+ if (!setting_filter_parse("log_debug", set->log_debug,
+ event_set_global_debug_log_filter, error_r))
+ return FALSE;
+ if (!setting_filter_parse("log_core_filter", set->log_core_filter,
+ event_set_global_core_log_filter, error_r))
+ return FALSE;
+ if (!setting_filter_parse("process_shutdown_filter",
+ set->process_shutdown_filter,
+ master_service_set_process_shutdown_filter_wrapper,
+ error_r))
+ return FALSE;
+ return TRUE;
+}
+/* </settings checks> */
+
+static void strarr_push(ARRAY_TYPE(const_string) *argv, const char *str)
+{
+ array_push_back(argv, &str);
+}
+
+static void ATTR_NORETURN
+master_service_exec_config(struct master_service *service,
+ const struct master_service_settings_input *input)
+{
+ ARRAY_TYPE(const_string) conf_argv;
+ const char *binary_path = service->argv[0];
+ const char *error = NULL;
+
+ if (!t_binary_abspath(&binary_path, &error)) {
+ i_fatal("t_binary_abspath(%s) failed: %s", binary_path, error);
+ }
+
+ if (!service->keep_environment && !input->preserve_environment) {
+ if (input->preserve_home)
+ master_service_import_environment("HOME");
+ if (input->preserve_user)
+ master_service_import_environment("USER");
+ if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0)
+ master_service_import_environment("LOG_STDERR_TIMESTAMP");
+
+ /* doveconf empties the environment before exec()ing us back
+ if DOVECOT_PRESERVE_ENVS is set, so make sure it is. */
+ if (getenv(DOVECOT_PRESERVE_ENVS_ENV) == NULL)
+ env_put(DOVECOT_PRESERVE_ENVS_ENV, "");
+ } else {
+ /* make sure doveconf doesn't remove any environment */
+ env_remove(DOVECOT_PRESERVE_ENVS_ENV);
+ }
+ if (input->use_sysexits)
+ env_put("USE_SYSEXITS", "1");
+
+ t_array_init(&conf_argv, 11 + (service->argc + 1) + 1);
+ strarr_push(&conf_argv, DOVECOT_CONFIG_BIN_PATH);
+ if (input->service != NULL) {
+ strarr_push(&conf_argv, "-f");
+ strarr_push(&conf_argv,
+ t_strconcat("service=", input->service, NULL));
+ }
+ strarr_push(&conf_argv, "-c");
+ strarr_push(&conf_argv, service->config_path);
+ if (input->module != NULL) {
+ strarr_push(&conf_argv, "-m");
+ strarr_push(&conf_argv, input->module);
+ }
+ if (input->extra_modules != NULL) {
+ for (unsigned int i = 0; input->extra_modules[i] != NULL; i++) {
+ strarr_push(&conf_argv, "-m");
+ strarr_push(&conf_argv, input->extra_modules[i]);
+ }
+ }
+ if ((service->flags & MASTER_SERVICE_FLAG_DISABLE_SSL_SET) == 0 &&
+ (input->module != NULL || input->extra_modules != NULL)) {
+ strarr_push(&conf_argv, "-m");
+ if (service->want_ssl_server)
+ strarr_push(&conf_argv, "ssl-server");
+ else
+ strarr_push(&conf_argv, "ssl");
+ }
+ if (input->parse_full_config)
+ strarr_push(&conf_argv, "-p");
+
+ strarr_push(&conf_argv, "-e");
+ strarr_push(&conf_argv, binary_path);
+ array_append(&conf_argv, (const char *const *)service->argv + 1,
+ service->argc);
+ array_append_zero(&conf_argv);
+
+ const char *const *argv = array_front(&conf_argv);
+ execv_const(argv[0], argv);
+}
+
+static void
+config_error_update_path_source(struct master_service *service,
+ const struct master_service_settings_input *input,
+ const char **error)
+{
+ if (input->config_path == NULL && service->config_path_from_master) {
+ *error = t_strdup_printf("%s (path is from %s environment)",
+ *error, MASTER_CONFIG_FILE_ENV);
+ }
+}
+
+static void
+config_exec_fallback(struct master_service *service,
+ const struct master_service_settings_input *input,
+ const char **error)
+{
+ const char *path, *stat_error;
+ struct stat st;
+ int saved_errno = errno;
+
+ if (input->never_exec) {
+ *error = t_strdup_printf(
+ "%s - doveconf execution fallback is disabled", *error);
+ return;
+ }
+
+ path = input->config_path != NULL ? input->config_path :
+ master_service_get_config_path(service);
+ if (stat(path, &st) < 0)
+ stat_error = t_strdup_printf("stat(%s) failed: %m", path);
+ else if (S_ISSOCK(st.st_mode))
+ stat_error = t_strdup_printf("%s is a UNIX socket", path);
+ else if (S_ISFIFO(st.st_mode))
+ stat_error = t_strdup_printf("%s is a FIFO", path);
+ else {
+ /* it's a file, not a socket/pipe */
+ master_service_exec_config(service, input);
+ }
+ *error = t_strdup_printf(
+ "%s - Also failed to read config by executing doveconf: %s",
+ *error, stat_error);
+ config_error_update_path_source(service, input, error);
+ errno = saved_errno;
+}
+
+static int
+master_service_open_config(struct master_service *service,
+ const struct master_service_settings_input *input,
+ const char **path_r, const char **error_r)
+{
+ struct stat st;
+ const char *path;
+ int fd;
+
+ *path_r = path = input->config_path != NULL ? input->config_path :
+ master_service_get_config_path(service);
+
+ if (service->config_fd != -1 && input->config_path == NULL &&
+ !service->config_path_changed_with_param) {
+ /* use the already opened config socket */
+ fd = service->config_fd;
+ service->config_fd = -1;
+ return fd;
+ }
+
+ if (!service->config_path_from_master &&
+ !service->config_path_changed_with_param &&
+ input->config_path == NULL) {
+ /* first try to connect to the default config socket.
+ configuration may contain secrets, so in default config
+ this fails because the socket is 0600. it's useful for
+ developers though. :) */
+ fd = net_connect_unix(DOVECOT_CONFIG_SOCKET_PATH);
+ if (fd >= 0) {
+ *path_r = DOVECOT_CONFIG_SOCKET_PATH;
+ net_set_nonblock(fd, FALSE);
+ return fd;
+ }
+ /* fallback to executing doveconf */
+ }
+
+ if (stat(path, &st) < 0) {
+ *error_r = errno == EACCES ? eacces_error_get("stat", path) :
+ t_strdup_printf("stat(%s) failed: %m", path);
+ config_error_update_path_source(service, input, error_r);
+ return -1;
+ }
+
+ if (!S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode)) {
+ /* it's not an UNIX socket, don't even try to connect */
+ fd = -1;
+ errno = ENOTSOCK;
+ } else {
+ fd = net_connect_unix_with_retries(path, 1000);
+ }
+ if (fd < 0) {
+ *error_r = t_strdup_printf("net_connect_unix(%s) failed: %m",
+ path);
+ config_exec_fallback(service, input, error_r);
+ return -1;
+ }
+ net_set_nonblock(fd, FALSE);
+ return fd;
+}
+
+static void
+config_build_request(struct master_service *service, string_t *str,
+ const struct master_service_settings_input *input)
+{
+ str_append(str, "REQ");
+ if (input->module != NULL)
+ str_printfa(str, "\tmodule=%s", input->module);
+ if (input->extra_modules != NULL) {
+ for (unsigned int i = 0; input->extra_modules[i] != NULL; i++)
+ str_printfa(str, "\tmodule=%s", input->extra_modules[i]);
+ }
+ if ((service->flags & MASTER_SERVICE_FLAG_DISABLE_SSL_SET) == 0 &&
+ (input->module != NULL || input->extra_modules != NULL)) {
+ str_printfa(str, "\tmodule=%s",
+ service->want_ssl_server ? "ssl-server" : "ssl");
+ }
+ if (input->no_ssl_ca)
+ str_append(str, "\texclude=ssl_ca\texclude=ssl_verify_client_cert");
+ if (input->service != NULL)
+ str_printfa(str, "\tservice=%s", input->service);
+ if (input->username != NULL)
+ str_printfa(str, "\tuser=%s", input->username);
+ if (input->local_ip.family != 0)
+ str_printfa(str, "\tlip=%s", net_ip2addr(&input->local_ip));
+ if (input->remote_ip.family != 0)
+ str_printfa(str, "\trip=%s", net_ip2addr(&input->remote_ip));
+ if (input->local_name != NULL)
+ str_printfa(str, "\tlname=%s", input->local_name);
+ str_append_c(str, '\n');
+}
+
+static int
+config_send_request(struct master_service *service,
+ const struct master_service_settings_input *input,
+ int fd, const char *path, const char **error_r)
+{
+ int ret;
+
+ T_BEGIN {
+ string_t *str;
+
+ str = t_str_new(128);
+ str_append(str, CONFIG_HANDSHAKE);
+ config_build_request(service, str, input);
+ ret = write_full(fd, str_data(str), str_len(str));
+ } T_END;
+ if (ret < 0) {
+ *error_r = t_strdup_printf("write_full(%s) failed: %m", path);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+config_send_filters_request(int fd, const char *path, const char **error_r)
+{
+ int ret;
+ ret = write_full(fd, CONFIG_HANDSHAKE"FILTERS\n", strlen(CONFIG_HANDSHAKE"FILTERS\n"));
+ if (ret < 0) {
+ *error_r = t_strdup_printf("write_full(%s) failed: %m", path);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+master_service_apply_config_overrides(struct master_service *service,
+ struct setting_parser_context *parser,
+ const char **error_r)
+{
+ const char *const *overrides;
+ unsigned int i, count;
+
+ overrides = array_get(&service->config_overrides, &count);
+ for (i = 0; i < count; i++) {
+ if (settings_parse_line(parser, overrides[i]) < 0) {
+ *error_r = t_strdup_printf(
+ "Invalid -o parameter %s: %s", overrides[i],
+ settings_parser_get_error(parser));
+ return -1;
+ }
+ settings_parse_set_key_expanded(parser, service->set_pool,
+ t_strcut(overrides[i], '='));
+ }
+ return 0;
+}
+
+static int
+config_read_reply_header(struct istream *istream, const char *path, pool_t pool,
+ const struct master_service_settings_input *input,
+ struct master_service_settings_output *output_r,
+ const char **error_r)
+{
+ const char *line;
+ ssize_t ret;
+
+ while ((ret = i_stream_read(istream)) > 0) {
+ line = i_stream_next_line(istream);
+ if (line != NULL)
+ break;
+ }
+ if (ret <= 0) {
+ if (ret == 0)
+ return 1;
+ *error_r = istream->stream_errno != 0 ?
+ t_strdup_printf("read(%s) failed: %s", path,
+ i_stream_get_error(istream)) :
+ t_strdup_printf("read(%s) failed: EOF", path);
+ return -1;
+ }
+
+ T_BEGIN {
+ const char *const *arg = t_strsplit_tabescaped(line);
+ ARRAY_TYPE(const_string) services;
+
+ p_array_init(&services, pool, 8);
+ for (; *arg != NULL; arg++) {
+ if (strcmp(*arg, "service-uses-local") == 0)
+ output_r->service_uses_local = TRUE;
+ else if (strcmp(*arg, "service-uses-remote") == 0)
+ output_r->service_uses_remote = TRUE;
+ if (strcmp(*arg, "used-local") == 0)
+ output_r->used_local = TRUE;
+ else if (strcmp(*arg, "used-remote") == 0)
+ output_r->used_remote = TRUE;
+ else if (str_begins(*arg, "service=")) {
+ const char *name = p_strdup(pool, *arg + 8);
+ array_push_back(&services, &name);
+ }
+ }
+ if (input->service == NULL) {
+ array_append_zero(&services);
+ output_r->specific_services = array_front(&services);
+ }
+ } T_END;
+ return 0;
+}
+
+void master_service_config_socket_try_open(struct master_service *service)
+{
+ struct master_service_settings_input input;
+ const char *path, *error;
+ int fd;
+
+ /* we'll get here before command line parameters have been parsed,
+ so -O, -c and -i parameters haven't been handled yet at this point.
+ this means we could end up opening config socket connection
+ unnecessarily, but this isn't a problem. we'll just have to
+ ignore it later on. (unfortunately there isn't a master_service_*()
+ call where this function would be better called.) */
+ if (getenv("DOVECONF_ENV") != NULL ||
+ (service->flags & MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS) != 0)
+ return;
+
+ i_zero(&input);
+ input.never_exec = TRUE;
+ fd = master_service_open_config(service, &input, &path, &error);
+ if (fd != -1)
+ service->config_fd = fd;
+}
+
+int master_service_settings_get_filters(struct master_service *service,
+ const char *const **filters,
+ const char **error_r)
+{
+ struct master_service_settings_input input;
+ int fd;
+ bool retry = TRUE;
+ const char *path = NULL;
+ ARRAY_TYPE(const_string) filters_tmp;
+ t_array_init(&filters_tmp, 8);
+ i_zero(&input);
+
+ if (getenv("DOVECONF_ENV") == NULL &&
+ (service->flags & MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS) == 0) {
+ retry = service->config_fd != -1;
+ for (;;) {
+ fd = master_service_open_config(service, &input, &path, error_r);
+ if (fd == -1) {
+ return -1;
+ }
+ if (config_send_filters_request(fd, path, error_r) == 0)
+ break;
+
+ i_close_fd(&fd);
+ if (!retry)
+ return -1;
+ retry = FALSE;
+ }
+ service->config_fd = fd;
+ struct istream *is = i_stream_create_fd(fd, SIZE_MAX);
+ const char *line;
+ /* try read response */
+ while((line = i_stream_read_next_line(is)) != NULL) {
+ if (*line == '\0')
+ break;
+ if (str_begins(line, "FILTER\t")) {
+ line = t_strdup(line+7);
+ array_push_back(&filters_tmp, &line);
+ }
+ }
+ i_stream_unref(&is);
+ }
+
+ array_append_zero(&filters_tmp);
+ *filters = array_front(&filters_tmp);
+ return 0;
+}
+
+int master_service_settings_read(struct master_service *service,
+ const struct master_service_settings_input *input,
+ struct master_service_settings_output *output_r,
+ const char **error_r)
+{
+ ARRAY(const struct setting_parser_info *) all_roots;
+ const struct setting_parser_info *tmp_root;
+ struct setting_parser_context *parser;
+ struct istream *istream;
+ const char *path = NULL, *error;
+ void **sets;
+ unsigned int i;
+ int ret, fd = -1;
+ time_t now, timeout;
+ bool use_environment, retry;
+
+ i_zero(output_r);
+
+ if (getenv("DOVECONF_ENV") == NULL &&
+ (service->flags & MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS) == 0) {
+ retry = service->config_fd != -1;
+ for (;;) {
+ fd = master_service_open_config(service, input,
+ &path, error_r);
+ if (fd == -1) {
+ if (errno == EACCES)
+ output_r->permission_denied = TRUE;
+ return -1;
+ }
+
+ if (config_send_request(service, input, fd,
+ path, error_r) == 0)
+ break;
+ i_close_fd(&fd);
+ if (!retry) {
+ config_exec_fallback(service, input, error_r);
+ return -1;
+ }
+ /* config process died, retry connecting */
+ retry = FALSE;
+ }
+ }
+
+ if (service->set_pool != NULL) {
+ if (service->set_parser != NULL)
+ settings_parser_deinit(&service->set_parser);
+ p_clear(service->set_pool);
+ } else {
+ service->set_pool =
+ pool_alloconly_create("master service settings", 16384);
+ }
+
+ p_array_init(&all_roots, service->set_pool, 8);
+ tmp_root = &master_service_setting_parser_info;
+ array_push_back(&all_roots, &tmp_root);
+ tmp_root = &master_service_ssl_setting_parser_info;
+ array_push_back(&all_roots, &tmp_root);
+ if (service->want_ssl_server) {
+ tmp_root = &master_service_ssl_server_setting_parser_info;
+ array_push_back(&all_roots, &tmp_root);
+ }
+ if (input->roots != NULL) {
+ for (i = 0; input->roots[i] != NULL; i++)
+ array_push_back(&all_roots, &input->roots[i]);
+ }
+
+ parser = settings_parser_init_list(service->set_pool,
+ array_front(&all_roots), array_count(&all_roots),
+ SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
+
+ if (fd != -1) {
+ istream = i_stream_create_fd(fd, SIZE_MAX);
+ now = time(NULL);
+ timeout = now + CONFIG_READ_TIMEOUT_SECS;
+ do {
+ alarm(timeout - now);
+ ret = config_read_reply_header(istream, path,
+ service->set_pool, input,
+ output_r, error_r);
+ if (ret == 0) {
+ ret = settings_parse_stream_read(parser,
+ istream);
+ if (ret < 0)
+ *error_r = t_strdup(
+ settings_parser_get_error(parser));
+ }
+ alarm(0);
+ if (ret <= 0)
+ break;
+
+ /* most likely timed out, but just in case some other
+ signal was delivered early check if we need to
+ continue */
+ now = time(NULL);
+ } while (now < timeout);
+ i_stream_unref(&istream);
+
+ if (ret != 0) {
+ if (ret > 0) {
+ *error_r = t_strdup_printf(
+ "Timeout reading config from %s", path);
+ }
+ i_close_fd(&fd);
+ config_exec_fallback(service, input, error_r);
+ settings_parser_deinit(&parser);
+ return -1;
+ }
+
+ if ((service->flags & MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN) != 0 &&
+ service->config_fd == -1 && input->config_path == NULL)
+ service->config_fd = fd;
+ else
+ i_close_fd(&fd);
+ use_environment = FALSE;
+ } else {
+ use_environment = TRUE;
+ }
+
+ if (use_environment || service->keep_environment) {
+ if (settings_parse_environ(parser) < 0) {
+ *error_r = t_strdup(settings_parser_get_error(parser));
+ settings_parser_deinit(&parser);
+ return -1;
+ }
+ }
+
+ if (array_is_created(&service->config_overrides)) {
+ if (master_service_apply_config_overrides(service, parser,
+ error_r) < 0) {
+ settings_parser_deinit(&parser);
+ return -1;
+ }
+ }
+
+ if (!settings_parser_check(parser, service->set_pool, &error)) {
+ *error_r = t_strdup_printf("Invalid settings: %s", error);
+ settings_parser_deinit(&parser);
+ return -1;
+ }
+
+ sets = settings_parser_get_list(parser);
+ service->set = sets[0];
+ service->set_parser = parser;
+
+ if (service->set->version_ignore &&
+ (service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0) {
+ /* running standalone. we want to ignore plugin versions. */
+ service->version_string = NULL;
+ }
+ if ((service->flags & MASTER_SERVICE_FLAG_DONT_SEND_STATS) == 0 &&
+ (service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0) {
+ /* When running standalone (e.g. doveadm) try to connect to the
+ stats socket, but don't log an error if it's not running.
+ It may be intentional. Non-standalone stats-client
+ initialization was already done earlier. */
+ master_service_init_stats_client(service, TRUE);
+ }
+
+ if (service->set->shutdown_clients)
+ master_service_set_die_with_master(master_service, TRUE);
+
+ /* if we change any settings afterwards, they're in expanded form.
+ especially all settings from userdb are already expanded. */
+ settings_parse_set_expanded(service->set_parser, TRUE);
+ return 0;
+}
+
+int master_service_settings_read_simple(struct master_service *service,
+ const struct setting_parser_info **roots,
+ const char **error_r)
+{
+ struct master_service_settings_input input;
+ struct master_service_settings_output output;
+
+ i_zero(&input);
+ input.roots = roots;
+ input.module = service->name;
+ return master_service_settings_read(service, &input, &output, 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)
+{
+ void **sets;
+
+ sets = settings_parser_get_list(service->set_parser);
+ return sets[0];
+}
+
+void **master_service_settings_get_others(struct master_service *service)
+{
+ return master_service_settings_parser_get_others(service,
+ service->set_parser);
+}
+
+void **master_service_settings_parser_get_others(struct master_service *service,
+ const struct setting_parser_context *set_parser)
+{
+ return settings_parser_get_list(set_parser) + 2 +
+ (service->want_ssl_server ? 1 : 0);
+}
+
+struct setting_parser_context *
+master_service_get_settings_parser(struct master_service *service)
+{
+ return service->set_parser;
+}
+
+int master_service_set(struct master_service *service, const char *line)
+{
+ return settings_parse_line(service->set_parser, line);
+}
+
+bool master_service_set_has_config_override(struct master_service *service,
+ const char *key)
+{
+ const char *override, *key_root;
+ bool ret;
+
+ if (!array_is_created(&service->config_overrides))
+ return FALSE;
+
+ key_root = settings_parse_unalias(service->set_parser, key);
+ if (key_root == NULL)
+ key_root = key;
+
+ array_foreach_elem(&service->config_overrides, override) {
+ T_BEGIN {
+ const char *okey, *okey_root;
+
+ okey = t_strcut(override, '=');
+ okey_root = settings_parse_unalias(service->set_parser,
+ okey);
+ if (okey_root == NULL)
+ okey_root = okey;
+ ret = strcmp(okey_root, key_root) == 0;
+ } T_END;
+
+ if (ret)
+ return TRUE;
+ }
+ return FALSE;
+}