changeset 12726:cec7fa92ff48

Added import_environment setting. This also cleans up different places in code where TZ and other environments are preserved. If it's not in the import_environment setting, it's not preserved.
author Timo Sirainen <tss@iki.fi>
date Thu, 10 Feb 2011 01:54:26 +0200
parents 98f13cc1e649
children 4374ae187075
files src/doveadm/doveadm.c src/lib-lda/smtp-client.c src/lib-master/master-interface.h src/lib-master/master-service-settings.c src/lib-master/master-service-settings.h src/lib-master/master-service.c src/lib-master/master-service.h src/lib-storage/mail-storage-service.c src/master/main.c src/master/master-settings.c src/master/master-settings.h src/master/service-process.c src/master/service.c src/master/service.h
diffstat 14 files changed, 84 insertions(+), 57 deletions(-) [+]
line wrap: on
line diff
--- a/src/doveadm/doveadm.c	Thu Feb 10 00:45:51 2011 +0200
+++ b/src/doveadm/doveadm.c	Thu Feb 10 01:54:26 2011 +0200
@@ -234,6 +234,7 @@
 	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)
--- a/src/lib-lda/smtp-client.c	Thu Feb 10 00:45:51 2011 +0200
+++ b/src/lib-lda/smtp-client.c	Thu Feb 10 01:54:26 2011 +0200
@@ -51,7 +51,7 @@
 	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);
 }
--- a/src/lib-master/master-interface.h	Thu Feb 10 00:45:51 2011 +0200
+++ b/src/lib-master/master-interface.h	Thu Feb 10 01:54:26 2011 +0200
@@ -59,6 +59,10 @@
    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 */
--- a/src/lib-master/master-service-settings.c	Thu Feb 10 00:45:51 2011 +0200
+++ b/src/lib-master/master-service-settings.c	Thu Feb 10 01:54:26 2011 +0200
@@ -90,12 +90,22 @@
 			   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");
 
--- a/src/lib-master/master-service-settings.h	Thu Feb 10 00:45:51 2011 +0200
+++ b/src/lib-master/master-service-settings.h	Thu Feb 10 01:54:26 2011 +0200
@@ -21,6 +21,8 @@
 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;
--- a/src/lib-master/master-service.c	Thu Feb 10 00:45:51 2011 +0200
+++ b/src/lib-master/master-service.c	Thu Feb 10 01:54:26 2011 +0200
@@ -391,22 +391,16 @@
 	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,
--- a/src/lib-master/master-service.h	Thu Feb 10 00:45:51 2011 +0200
+++ b/src/lib-master/master-service.h	Thu Feb 10 01:54:26 2011 +0200
@@ -59,8 +59,9 @@
    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,
--- a/src/lib-storage/mail-storage-service.c	Thu Feb 10 00:45:51 2011 +0200
+++ b/src/lib-storage/mail-storage-service.c	Thu Feb 10 01:54:26 2011 +0200
@@ -652,6 +652,7 @@
 
 	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 */
--- a/src/master/main.c	Thu Feb 10 00:45:51 2011 +0200
+++ b/src/master/main.c	Thu Feb 10 01:54:26 2011 +0200
@@ -48,7 +48,6 @@
 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,
@@ -314,8 +313,7 @@
 	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;
@@ -377,12 +375,39 @@
 	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"
@@ -598,18 +623,8 @@
 
 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];
@@ -618,8 +633,6 @@
 #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.. */
@@ -740,23 +753,16 @@
 	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);
--- a/src/master/master-settings.c	Thu Feb 10 00:45:51 2011 +0200
+++ b/src/master/master-settings.c	Thu Feb 10 01:54:26 2011 +0200
@@ -170,6 +170,7 @@
 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),
@@ -192,9 +193,23 @@
 	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",
--- a/src/master/master-settings.h	Thu Feb 10 00:45:51 2011 +0200
+++ b/src/master/master-settings.h	Thu Feb 10 01:54:26 2011 +0200
@@ -6,6 +6,7 @@
 struct master_settings {
 	const char *base_dir;
 	const char *libexec_dir;
+	const char *import_environment;
 	const char *protocols;
 	const char *listen;
 	const char *ssl;
--- a/src/master/service-process.c	Thu Feb 10 00:45:51 2011 +0200
+++ b/src/master/service-process.c	Thu Feb 10 01:54:26 2011 +0200
@@ -180,12 +180,8 @@
 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:
--- a/src/master/service.c	Thu Feb 10 00:45:51 2011 +0200
+++ b/src/master/service.c	Thu Feb 10 01:54:26 2011 +0200
@@ -427,7 +427,6 @@
 }
 
 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;
@@ -445,7 +444,6 @@
 	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;
 
--- a/src/master/service.h	Thu Feb 10 00:45:51 2011 +0200
+++ b/src/master/service.h	Thu Feb 10 01:54:26 2011 +0200
@@ -114,7 +114,6 @@
 	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];
@@ -131,7 +130,6 @@
 
 /* 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 */