memset(&input, 0, sizeof(input));
input.roots = set_roots;
input.module = "doveadm";
+ input.preserve_user = TRUE;
input.preserve_home = TRUE;
if (master_service_settings_read(master_service, &input,
&output, &error) < 0)
if (dup2(fd, STDIN_FILENO) < 0)
i_fatal("dup2() failed: %m");
- master_service_env_clean(TRUE);
+ master_service_env_clean();
execv_const(sendmail_path, argv);
}
if dovecot was started with -p parameter. */
#define MASTER_SSL_KEY_PASSWORD_ENV "SSL_KEY_PASSWORD"
+/* getenv(DOVECOT_PRESERVE_ENVS_ENV) returns a space separated list of
+ environments that should be preserved. */
+#define DOVECOT_PRESERVE_ENVS_ENV "DOVECOT_PRESERVE_ENVS"
+
/* Write pipe to anvil. */
#define MASTER_ANVIL_FD 3
/* Anvil reads new log fds from this fd */
const struct master_service_settings_input *input)
{
const char **conf_argv, *binary_path = service->argv[0];
+ const char *home = NULL, *user = NULL;
unsigned int i, argv_max_count;
(void)t_binary_abspath(&binary_path);
- if (!service->keep_environment)
- master_service_env_clean(input->preserve_home);
+ if (!service->keep_environment && !input->preserve_environment) {
+ if (input->preserve_home)
+ home = getenv("HOME");
+ if (input->preserve_user)
+ user = getenv("USER");
+ master_service_env_clean();
+ if (home != NULL)
+ env_put(t_strconcat("HOME=", home, NULL));
+ if (user != NULL)
+ env_put(t_strconcat("USER=", user, NULL));
+ }
if (input->use_sysexits)
env_put("USE_SYSEXITS=1");
struct master_service_settings_input {
const struct setting_parser_info *const *roots;
const char *config_path;
+ bool preserve_environment;
+ bool preserve_user;
bool preserve_home;
bool never_exec;
bool use_sysexits;
master_status_update(service);
}
-void master_service_env_clean(bool preserve_home)
+void master_service_env_clean(void)
{
- static const char *preserve_envs[] = {
- "HOME", /* keep as the first element */
- "USER",
- "TZ",
-#ifdef DEBUG
- "GDB",
-#endif
-#ifdef HAVE_SYSTEMD
- "LISTEN_PID",
- "LISTEN_FDS",
-#endif
- NULL
- };
- env_clean_except(preserve_envs + (preserve_home ? 0 : 1));
+ const char *value = getenv(DOVECOT_PRESERVE_ENVS_ENV);
+
+ if (value == NULL || *value == '\0')
+ env_clean();
+ else T_BEGIN {
+ value = t_strconcat(value, " "DOVECOT_PRESERVE_ENVS_ENV, NULL);
+ env_clean_except(t_strsplit_spaces(value, " "));
+ } T_END;
}
void master_service_set_client_limit(struct master_service *service,
before calling this. */
void master_service_init_finish(struct master_service *service);
-/* Clean environment from everything except TZ, USER and optionally HOME. */
-void master_service_env_clean(bool preserve_home);
+/* Clean environment from everything except the ones listed in
+ DOVECOT_PRESERVE_ENVS environment. */
+void master_service_env_clean(void);
/* Initialize logging. */
void master_service_init_log(struct master_service *service,
memset(&set_input, 0, sizeof(set_input));
set_input.roots = ctx->set_roots;
+ set_input.preserve_user = TRUE;
/* settings reader may exec doveconf, which is going to clear
environment, and if we're not doing a userdb lookup we want to
use $HOME */
static char *pidfile_path;
static failure_callback_t *orig_fatal_callback;
static failure_callback_t *orig_error_callback;
-static const char *child_process_env[3]; /* @UNSAFE */
static const struct setting_parser_info *set_roots[] = {
&master_setting_parser_info,
sets = master_service_settings_get_others(master_service);
set = sets[0];
- if (services_create(set, child_process_env,
- &new_services, &error) < 0) {
+ if (services_create(set, &new_services, &error) < 0) {
/* new configuration is invalid, keep the old */
i_error("Config reload failed: %s", error);
return;
input.roots = set_roots;
input.module = "master";
input.parse_full_config = TRUE;
+ input.preserve_environment = TRUE;
if (master_service_settings_read(master_service, &input, &output,
&error) < 0)
i_fatal("Error reading configuration: %s", error);
return master_service_settings_get_others(master_service)[0];
}
+static void master_set_import_environment(const struct master_settings *set)
+{
+ const char *const *envs, *key, *value;
+ ARRAY_TYPE(const_string) keys;
+
+ if (*set->import_environment == '\0')
+ return;
+
+ t_array_init(&keys, 8);
+ envs = t_strsplit_spaces(set->import_environment, " ");
+ for (; *envs != NULL; envs++) {
+ value = strchr(*envs, '=');
+ if (value == NULL)
+ key = *envs;
+ else {
+ key = t_strdup_until(*envs, value);
+ env_put(*envs);
+ }
+ array_append(&keys, &key, 1);
+ }
+ (void)array_append_space(&keys);
+
+ value = t_strarray_join(array_idx(&keys, 0), " ");
+ env_put(t_strconcat(DOVECOT_PRESERVE_ENVS_ENV"=", value, NULL));
+}
+
static void main_log_startup(void)
{
#define STARTUP_STRING PACKAGE_NAME" v"DOVECOT_VERSION_FULL" starting up"
int main(int argc, char *argv[])
{
- static const char *preserve_envs[] = {
- /* AIX depends on TZ to get the timezone correctly. */
- "TZ",
-#ifdef HAVE_SYSTEMD
- "LISTEN_PID",
- "LISTEN_FDS",
-#endif
- NULL
- };
struct master_settings *set;
- unsigned int child_process_env_idx = 0;
- const char *error, *env_tz, *doveconf_arg = NULL;
+ const char *error, *doveconf_arg = NULL;
failure_callback_t *orig_info_callback, *orig_debug_callback;
bool foreground = FALSE, ask_key_pass = FALSE;
bool doubleopts[argc];
#ifdef DEBUG
if (getenv("GDB") == NULL)
fd_debug_verify_leaks(3, 1024);
- else
- child_process_env[child_process_env_idx++] = "GDB=1";
#endif
/* drop -- prefix from all --args. ugly, but the only way that it
works with standard getopt() in all OSes.. */
master_settings_do_fixes(set);
fatal_log_check(set);
- /* clean up the environment */
- env_clean_except(preserve_envs);
-
- env_tz = getenv("TZ");
- if (env_tz != NULL) {
- child_process_env[child_process_env_idx++] =
- t_strconcat("TZ=", env_tz, NULL);
- }
- i_assert(child_process_env_idx <
- sizeof(child_process_env) / sizeof(child_process_env[0]));
- child_process_env[child_process_env_idx] = NULL;
+ T_BEGIN {
+ master_set_import_environment(set);
+ } T_END;
+ master_service_env_clean();
/* create service structures from settings. if there are any errors in
service configuration we'll catch it here. */
service_pids_init();
service_anvil_global_init();
- if (services_create(set, child_process_env, &services, &error) < 0)
+ if (services_create(set, &services, &error) < 0)
i_fatal("%s", error);
services->config->config_file_path = get_full_config_path(services);
static const struct setting_define master_setting_defines[] = {
DEF(SET_STR, base_dir),
DEF(SET_STR, libexec_dir),
+ DEF(SET_STR, import_environment),
DEF(SET_STR, protocols),
DEF(SET_STR, listen),
DEF(SET_ENUM, ssl),
SETTING_DEFINE_LIST_END
};
+/* <settings checks> */
+#ifdef HAVE_SYSTEMD
+# define ENV_SYSTEMD " LISTEN_PID LISTEN_FDS"
+#else
+# define ENV_SYSTEMD ""
+#endif
+#ifdef DEBUG
+# define ENV_GDB " GDB"
+#else
+# define ENV_GDB ""
+#endif
+/* </settings checks> */
+
static const struct master_settings master_default_settings = {
.base_dir = PKG_RUNDIR,
.libexec_dir = PKG_LIBEXECDIR,
+ .import_environment = "TZ" ENV_SYSTEMD ENV_GDB,
.protocols = "imap pop3 lmtp",
.listen = "*, ::",
.ssl = "yes:no:required",
struct master_settings {
const char *base_dir;
const char *libexec_dir;
+ const char *import_environment;
const char *protocols;
const char *listen;
const char *ssl;
service_process_setup_environment(struct service *service, unsigned int uid)
{
const struct master_service_settings *set = service->list->service_set;
- const char *const *p;
- /* remove all environment, and put back what we need */
- env_clean();
- for (p = service->list->child_process_env; *p != NULL; p++)
- env_put(*p);
+ master_service_env_clean();
switch (service->type) {
case SERVICE_TYPE_CONFIG:
}
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;
service_list->service_set = master_service_settings_get(master_service);
service_list->set_pool = master_service_settings_detach(master_service);
service_list->set = set;
- service_list->child_process_env = child_process_env;
service_list->master_log_fd[0] = -1;
service_list->master_log_fd[1] = -1;
struct service *config;
struct service *log;
struct service *anvil;
- const char *const *child_process_env;
/* nonblocking log fds usd by master */
int master_log_fd[2];
/* Create all services from settings */
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 */