master-service-settings.c revision 4970e8dfcb18c1353e44b82e4ec836d165b7de82
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen#include "lib.h"
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen#include "array.h"
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen#include "istream.h"
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi#include "write-full.h"
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi#include "str.h"
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen#include "settings-parser.h"
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen#include "master-service-private.h"
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen#include "master-service-settings.h"
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen#include <stddef.h>
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen#include <stdlib.h>
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen#include <unistd.h>
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen#include <sys/stat.h>
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen#define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf"
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen#define CONFIG_HANDSHAKE "VERSION\t1\t0\n"
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen#undef DEF
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen#define DEF(type, name) \
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen { type, #name, offsetof(struct master_service_settings, name), NULL }
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainenstatic struct setting_define master_service_setting_defines[] = {
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen DEF(SET_STR, log_path),
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen DEF(SET_STR, info_log_path),
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen DEF(SET_STR, log_timestamp),
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen DEF(SET_STR, syslog_facility),
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen DEF(SET_BOOL, version_ignore),
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen SETTING_DEFINE_LIST_END
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen};
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainenstatic struct master_service_settings master_service_default_settings = {
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen MEMBER(log_path) "",
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen MEMBER(info_log_path) "",
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen MEMBER(log_timestamp) DEFAULT_FAILURE_STAMP_FORMAT,
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen MEMBER(syslog_facility) "mail",
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen MEMBER(version_ignore) FALSE
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen};
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainenstruct setting_parser_info master_service_setting_parser_info = {
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen MEMBER(defines) master_service_setting_defines,
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen MEMBER(defaults) &master_service_default_settings,
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen MEMBER(parent) NULL,
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen MEMBER(dynamic_parsers) NULL,
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen MEMBER(parent_offset) (size_t)-1,
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen MEMBER(type_offset) (size_t)-1,
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen MEMBER(struct_size) sizeof(struct master_service_settings)
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen};
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainenstatic void ATTR_NORETURN
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainenmaster_service_exec_config(struct master_service *service, bool preserve_home)
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen{
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen const char **conf_argv, *path, *const *paths, *binary_path;
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen char full_path[PATH_MAX];
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen binary_path = service->argv[0];
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen if (*service->argv[0] == '/') {
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen /* already have the path */
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen } else if (strchr(service->argv[0], '/') != NULL) {
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen /* relative to current directory */
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen if (realpath(service->argv[0], full_path) == NULL)
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen i_fatal("realpath(%s) failed: %m", service->argv[0]);
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen binary_path = full_path;
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen } else if ((path = getenv("PATH")) != NULL) {
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen /* we have to find our executable from path */
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen paths = t_strsplit(path, ":");
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen for (; *paths != NULL; paths++) {
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi path = t_strconcat(*paths, "/", binary_path, NULL);
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi if (access(path, X_OK) == 0) {
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi binary_path = path;
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi break;
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi }
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi }
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi }
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi if (!service->keep_environment)
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi master_service_env_clean(preserve_home);
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi /* @UNSAFE */
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi conf_argv = t_new(const char *, 6 + (service->argc + 1) + 1);
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi conf_argv[0] = DOVECOT_CONFIG_BIN_PATH;
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi conf_argv[1] = "-f";
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi conf_argv[2] = t_strconcat("service=", service->name, NULL);
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi conf_argv[3] = "-c";
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi conf_argv[4] = service->config_path;
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi conf_argv[5] = "-e";
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi conf_argv[6] = binary_path;
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi memcpy(conf_argv+7, service->argv + 1,
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi (service->argc) * sizeof(conf_argv[0]));
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi execv(conf_argv[0], (char **)conf_argv);
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi i_fatal("execv(%s) failed: %m", conf_argv[0]);
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi}
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvistatic int
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvimaster_service_read_config(struct master_service *service, const char *path,
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi const struct master_service_settings_input *input,
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi const char **error_r)
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi{
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi struct stat st;
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi int fd, ret;
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi if (service->config_fd != -1) {
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi fd = service->config_fd;
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi service->config_fd = -1;
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi } else {
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi fd = net_connect_unix(path);
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi if (fd < 0) {
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi *error_r = t_strdup_printf(
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi "net_connect_unix(%s) failed: %m", path);
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi if (stat(path, &st) == 0 && !S_ISFIFO(st.st_mode)) {
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi /* it's a file, not a socket */
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi master_service_exec_config(service,
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi input->preserve_home);
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi }
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi return -1;
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi }
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi net_set_nonblock(fd, FALSE);
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi }
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi T_BEGIN {
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi string_t *str;
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen str = t_str_new(128);
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen str_append(str, CONFIG_HANDSHAKE"REQ");
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen if (input->module != NULL)
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen str_printfa(str, "\tmodule=%s", input->module);
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi if (input->service != NULL)
9983cbb4cef92d68ffde87dcc91913cc2fed3da5Martti Rannanjärvi str_printfa(str, "\tservice=%s", input->service);
bc6a872cc6cecb4e3f8d0a99236c4ab484f2f754Timo Sirainen 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));
}
str_append_c(str, '\n');
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 fd;
}
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_expandeded(parser, service->set_pool,
t_strcut(overrides[i], '='));
}
return 0;
}
int master_service_settings_read(struct master_service *service,
const struct master_service_settings_input *input,
const char **error_r)
{
ARRAY_DEFINE(all_roots, const struct setting_parser_info *);
const struct setting_parser_info *tmp_root;
struct setting_parser_context *parser;
struct istream *istream;
const char *path, *error, *env, *const *keys;
void **sets;
unsigned int i;
int ret, fd = -1;
if (getenv("DOVECONF_ENV") == NULL && !service->default_settings) {
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) {
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", 8192);
}
if (input->dyn_parsers != NULL) {
settings_parser_info_update(service->set_pool,
input->dyn_parsers);
}
p_array_init(&all_roots, service->set_pool, 8);
tmp_root = &master_service_setting_parser_info;
array_append(&all_roots, &tmp_root, 1);
if (input->roots != NULL) {
for (i = 0; input->roots[i] != NULL; i++)
array_append(&all_roots, &input->roots[i], 1);
}
parser = settings_parser_init_list(service->set_pool,
array_idx(&all_roots, 0), array_count(&all_roots),
SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
if (fd != -1) {
istream = i_stream_create_fd(fd, (size_t)-1, FALSE);
ret = settings_parse_stream_read(parser, istream);
i_stream_unref(&istream);
i_assert(ret <= 0);
if (ret < 0) {
*error_r = settings_parser_get_error(parser);
(void)close(fd);
return -1;
}
}
if ((service->flags & MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN) != 0)
service->config_fd = fd;
else if (fd != -1)
(void)close(fd);
/* let environment override settings. especially useful for the
settings from userdb. */
if (settings_parse_environ(parser) < 0) {
*error_r = settings_parser_get_error(parser);
return -1;
}
env = getenv("VARS_EXPANDED");
if (env != NULL) T_BEGIN {
keys = t_strsplit(env, " ");
settings_parse_set_keys_expandeded(parser, service->set_pool,
keys);
} T_END;
if (array_is_created(&service->config_overrides)) {
if (master_service_apply_config_overrides(service, parser,
error_r) < 0)
return -1;
}
if (!settings_parser_check(parser, service->set_pool, &error)) {
*error_r = t_strdup_printf("Invalid settings: %s", error);
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 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;
memset(&input, 0, sizeof(input));
input.roots = roots;
input.module = service->name;
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)
{
void **sets;
sets = settings_parser_get_list(service->set_parser);
return sets[0];
}
void **master_service_settings_get_others(struct master_service *service)
{
return settings_parser_get_list(service->set_parser) + 1;
}
int master_service_set(struct master_service *service, const char *line)
{
return settings_parse_line(service->set_parser, line);
}