changeset 22070:ded950b2b5d2

util: script: Amended protocol with the ability to convey a set of environment variables that are passed to the script. The acceptable environment variables are selected using the -e parameter; others are ignored silently.
author Stephan Bosch <stephan.bosch@dovecot.fi>
date Tue, 09 May 2017 13:33:23 +0200
parents e6d9a30177c5
children ba38c7002aa4
files src/lib-program-client/program-client-remote.c src/util/script.c
diffstat 2 files changed, 68 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-program-client/program-client-remote.c	Mon May 08 12:48:08 2017 +0200
+++ b/src/lib-program-client/program-client-remote.c	Tue May 09 13:33:23 2017 +0200
@@ -5,6 +5,7 @@
 #include "ioloop.h"
 #include "str.h"
 #include "strescape.h"
+#include "array.h"
 #include "net.h"
 #include "write-full.h"
 #include "eacces-error.h"
@@ -215,6 +216,14 @@
 
 	str = t_str_new(1024);
 	str_append(str, PROGRAM_CLIENT_VERSION_STRING);
+	if (array_is_created(&pclient->envs)) {
+		const char *const *env;
+		array_foreach(&pclient->envs, env) {
+			str_append(str, "env_");
+			str_append_tabescaped(str, *env);
+			str_append_c(str, '\n');
+		}
+	}
 	if (prclient->noreply)
 		str_append(str, "noreply\n");
 	else
--- a/src/util/script.c	Mon May 08 12:48:08 2017 +0200
+++ b/src/util/script.c	Tue May 09 13:33:23 2017 +0200
@@ -20,6 +20,7 @@
 #define SCRIPT_READ_TIMEOUT_SECS 10
 
 static ARRAY_TYPE(const_string) exec_args;
+static const char **accepted_envs;
 
 static void script_verify_version(const char *line)
 {
@@ -32,7 +33,8 @@
 
 
 static void
-exec_child(struct master_service_connection *conn, const char *const *args)
+exec_child(struct master_service_connection *conn,
+	const char *const *args, const char *const *envs)
 {
 	unsigned int i, socket_count;
 
@@ -59,17 +61,23 @@
 	array_append_zero(&exec_args);
 
 	env_clean();
+	if (envs != NULL) {
+		for(; *envs != NULL; envs++)
+			env_put(*envs);
+        }
+
 	args = array_idx(&exec_args, 0);
 	execvp_const(args[0], args);
 }
 
 static bool client_exec_script(struct master_service_connection *conn)
 {
+	ARRAY_TYPE(const_string) envs;
 	const char *const *args;
 	string_t *input;
 	void *buf;
 	size_t prev_size, scanpos;
-	bool header_complete = FALSE;
+	bool header_complete = FALSE, noreply = FALSE;
 	ssize_t ret;
 	int status;
 	pid_t pid;
@@ -144,7 +152,10 @@
 
 	args = t_strsplit(str_c(input), "\n");
 	script_verify_version(*args); args++;
+	t_array_init(&envs, 16);
 	if (*args != NULL) {
+		const char *p;
+
 		if (strncmp(*args, "alarm=", 6) == 0) {
 			unsigned int seconds;
 			if (str_to_uint(*args + 6, &seconds) < 0)
@@ -152,15 +163,33 @@
 			alarm(seconds);
 			args++;
 		}
+		while (strncmp(*args, "env_", 4) == 0) {
+			const char *envname, *env;
+
+			env = t_str_tabunescape(*args+4);
+			p = strchr(env, '=');
+			if (p == NULL)
+				i_fatal("invalid environment variable");
+			envname = t_strdup_until(*args+4, p);
+
+			if (str_array_find(accepted_envs, envname))
+				array_append(&envs, &env, 1);
+			args++;
+		}
 		if (strcmp(*args, "noreply") == 0) {
-			/* no need to fork and check exit status */
-			exec_child(conn, args + 1);
-			i_unreached();
+			noreply = TRUE;
 		}
 		if (**args == '\0')
 			i_fatal("empty options");
 		args++;
 	}
+	array_append_zero(&envs);
+
+	if (noreply) {
+		/* no need to fork and check exit status */
+		exec_child(conn, args, array_idx(&envs, 0));
+		i_unreached();
+	}
 
 	if ((pid = fork()) == (pid_t)-1) {
 		i_error("fork() failed: %m");
@@ -169,7 +198,7 @@
 
 	if (pid == 0) {
 		/* child */
-		exec_child(conn, args);
+		exec_child(conn, args, array_idx(&envs, 0));
 		i_unreached();
 	}
 
@@ -210,15 +239,33 @@
 
 int main(int argc, char *argv[])
 {
+	ARRAY_TYPE(const_string) aenvs;
 	const char *binary;
-	int i;
+	const char *const *envs;
+	int c, i;
+
+	master_service = master_service_init("script", 0, &argc, &argv, "+e:");
 
-	master_service = master_service_init("script", 0, &argc, &argv, "+");
-	if (master_getopt(master_service) > 0)
-		return FATAL_DEFAULT;
+	t_array_init(&aenvs, 16);
+	while ((c = master_getopt(master_service)) > 0) {
+		switch (c) {
+		case 'e':
+			envs = t_strsplit_spaces(optarg,", \t");
+			while (*envs != NULL) {
+				array_append(&aenvs, envs, 1);
+				envs++;
+			}
+			break;
+		default:
+			return FATAL_DEFAULT;
+		}
+	}
 	argc -= optind;
 	argv += optind;
 
+	array_append_zero(&aenvs);
+	accepted_envs = p_strarray_dup(default_pool, array_idx(&aenvs, 0));
+
 	master_service_init_log(master_service, "script: ");
 	if (argv[0] == NULL)
 		i_fatal("Missing script path");
@@ -242,6 +289,8 @@
 	}
 
 	master_service_run(master_service, client_connected);
+	array_free(&exec_args);
+	i_free(accepted_envs);
 	master_service_deinit(&master_service);
 	return 0;
 }