dovecot-2.2-pigeonhole: lib-sieve/util: program-client: Added su...

pigeonhole at rename-it.nl pigeonhole at rename-it.nl
Sat Jan 4 03:13:52 EET 2014


details:   http://hg.rename-it.nl/dovecot-2.2-pigeonhole/rev/5c3b0283524b
changeset: 1837:5c3b0283524b
user:      Stephan Bosch <stephan at rename-it.nl>
date:      Sat Jan 04 02:13:44 2014 +0100
description:
lib-sieve/util: program-client: Added support for retrieving side-channel data through extra fds.
Some programs can be instructed to output special status information to an extra fd other than stdout or stderr.
Also added an option to drop stderr output from program to /dev/null.

diffstat:

 src/lib-sieve/util/program-client-local.c   |   99 ++++++++++++++++++--
 src/lib-sieve/util/program-client-private.h |   15 +++-
 src/lib-sieve/util/program-client-remote.c  |    4 +-
 src/lib-sieve/util/program-client.c         |  128 +++++++++++++++++++++++++--
 src/lib-sieve/util/program-client.h         |   17 +++-
 5 files changed, 236 insertions(+), 27 deletions(-)

diffs (truncated from 528 to 300 lines):

diff -r 32beba3bfd8d -r 5c3b0283524b src/lib-sieve/util/program-client-local.c
--- a/src/lib-sieve/util/program-client-local.c	Wed Jan 01 23:58:16 2014 +0100
+++ b/src/lib-sieve/util/program-client-local.c	Sat Jan 04 02:13:44 2014 +0100
@@ -28,17 +28,18 @@
 
 static void exec_child
 (const char *bin_path, const char *const *args, const char *const *envs,
-	int in_fd, int out_fd)
+	int in_fd, int out_fd, int *extra_fds, bool drop_stderr)
 {
 	ARRAY_TYPE(const_string) exec_args;
 
+	/* Setup stdin/stdout */
+
 	if ( in_fd < 0 ) {
 		in_fd = open("/dev/null", O_RDONLY);
 
 		if ( in_fd == -1 )
 			i_fatal("open(/dev/null) failed: %m");
 	}
-
 	if ( out_fd < 0 ) {
 		out_fd = open("/dev/null", O_WRONLY);
 
@@ -51,12 +52,34 @@
 	if ( dup2(out_fd, STDOUT_FILENO) < 0 )
 		i_fatal("dup2(stdout) failed: %m");
 
-	/* Close all fds */
 	if ( close(in_fd) < 0 )
 		i_error("close(in_fd) failed: %m");
 	if ( (out_fd != in_fd) && close(out_fd) < 0 )
 		i_error("close(out_fd) failed: %m");
 
+	/* Drop stderr if requested */
+	if ( drop_stderr ) {
+		int err_fd = open("/dev/null", O_WRONLY);
+		if ( err_fd == -1 )
+			i_fatal("open(/dev/null) failed: %m");
+		if ( dup2(err_fd, STDERR_FILENO) < 0 )
+			i_fatal("dup2(stderr) failed: %m");
+		if ( close(err_fd) < 0 )
+			i_error("close(err_fd) failed: %m");
+	}
+
+	/* Setup extra fds */
+	if ( extra_fds != NULL ) {
+		for (; *extra_fds != -1; extra_fds += 2 ) {
+			if ( dup2(extra_fds[0], extra_fds[1]) < 0 )
+				i_fatal("dup2(extra_fd=%d) failed: %m", extra_fds[1]);
+			if ( close(extra_fds[0]) < 0 )
+				i_error("close(extra_fd=%d) failed: %m", extra_fds[1]);
+		}
+	}
+
+	/* Compose argv */
+
 	t_array_init(&exec_args, 16);
 	array_append(&exec_args, &bin_path, 1);
 	if ( args != NULL ) {
@@ -65,12 +88,16 @@
 	}
 	(void)array_append_space(&exec_args);
 
+	/* Setup environment */
+
 	env_clean();
 	if ( envs != NULL ) {
 		for (; *envs != NULL; envs++)
 			env_put(*envs);
 	}
 
+	/* Execute */
+
 	args = array_idx(&exec_args, 0);
 	execvp_const(args[0], args);
 }
@@ -81,7 +108,11 @@
 	struct program_client_local *slclient = 
 		(struct program_client_local *) pclient;
 	int fd[2] = { -1, -1 };
+	struct program_client_extra_fd *efds = NULL;
+	int *parent_extra_fds = NULL, *child_extra_fds = NULL;
+	unsigned int xfd_count = 0, i;
 
+	/* create normal I/O fd */
 	if ( pclient->input != NULL || pclient->output != NULL ||
 		pclient->output_seekable ) {
 		if ( socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0 ) {
@@ -90,14 +121,46 @@
 		}
 	}
 
+	/* create pipes for additional output through side-channel fds */
+	if ( array_is_created(&pclient->extra_fds) ) {
+		int extra_fd[2];
+		
+		efds = array_get_modifiable(&pclient->extra_fds, &xfd_count);
+		if (	xfd_count > 0 ) {
+			parent_extra_fds = t_malloc(sizeof(int) * xfd_count);
+			child_extra_fds = t_malloc(sizeof(int) * xfd_count * 2 + 1);
+			for ( i = 0; i < xfd_count; i++ ) {
+				if ( pipe(extra_fd) < 0 ) {
+					i_error("pipe() failed: %m");
+					return -1;
+				}
+				parent_extra_fds[i] = extra_fd[0];
+				child_extra_fds[i*2+0] = extra_fd[1];
+				child_extra_fds[i*2+1] = efds[i].child_fd;
+			}
+			child_extra_fds[xfd_count*2] = -1;
+		}
+	}
+
+	/* fork child */
 	if ( (slclient->pid = fork()) == (pid_t)-1 ) {
 		i_error("fork() failed: %m");
+
+		/* clean up */
 		if ( fd[0] >= 0 && close(fd[0]) < 0 ) {
 			i_error("close(pipe_fd[0]) failed: %m");
 		}
 		if ( fd[1] >= 0 && close(fd[1]) < 0 ) {
 			i_error("close(pipe_fd[1]) failed: %m");
 		}
+		for ( i = 0; i < xfd_count; i++ ) {
+			if ( close(child_extra_fds[i*2]) < 0 ) {
+				i_error("close(extra_fd[1]) failed: %m");
+			}
+			if ( close(parent_extra_fds[i]) < 0 ) {
+				i_error("close(extra_fd[0]) failed: %m");
+			}
+		}
 		return -1;
 	}
 
@@ -109,27 +172,37 @@
 		if ( fd[1] >= 0 && close(fd[1]) < 0 ) {
 			i_error("close(pipe_fd[1]) failed: %m");
 		}
+		for ( i = 0; i < xfd_count; i++ ) {
+			if ( close(parent_extra_fds[i]) < 0 )
+				i_error("close(extra_fd[0]) failed: %m");
+		}
 
 		if ( array_is_created(&pclient->envs) )
 			envs = array_get(&pclient->envs, &count);
 
 		exec_child(pclient->path, pclient->args, envs,
 			( pclient->input != NULL ? fd[0] : -1 ),
-			( pclient->output != NULL || pclient->output_seekable ? fd[0] : -1 ));
+			( pclient->output != NULL || pclient->output_seekable ? fd[0] : -1 ),
+			child_extra_fds, pclient->set.drop_stderr);
 		i_unreached();
 	}
 
 	/* parent */
-	if ( fd[0] >= 0 && close(fd[0]) < 0 ) {
+	if ( fd[0] >= 0 && close(fd[0]) < 0 )
 		i_error("close(pipe_fd[0]) failed: %m");
-	}
-
 	if ( fd[1] >= 0 ) {
 		net_set_nonblock(fd[1], TRUE);
 		pclient->fd_in =
 			( pclient->output != NULL || pclient->output_seekable ? fd[1] : -1 );
 		pclient->fd_out = ( pclient->input != NULL ? fd[1] : -1 );
 	}
+	for ( i = 0; i < xfd_count; i++ ) {
+		if ( close(child_extra_fds[i*2]) < 0 )
+			i_error("close(extra_fd[1]) failed: %m");
+		net_set_nonblock(parent_extra_fds[i], TRUE);
+		efds[i].parent_fd = parent_extra_fds[i];
+	}
+
 	program_client_init_streams(pclient);
 	return program_client_connected(pclient);
 }
@@ -158,9 +231,9 @@
 
 	/* Calculate timeout */
 	runtime = ioloop_time - pclient->start_time;
-	if ( !force && pclient->set->input_idle_timeout_secs > 0 &&
-		runtime < (time_t)pclient->set->input_idle_timeout_secs )
-		timeout = pclient->set->input_idle_timeout_secs - runtime;
+	if ( !force && pclient->set.input_idle_timeout_secs > 0 &&
+		runtime < (time_t)pclient->set.input_idle_timeout_secs )
+		timeout = pclient->set.input_idle_timeout_secs - runtime;
 
 	if ( pclient->debug ) {
 		i_debug("waiting for program `%s' to finish after %llu seconds",
@@ -169,7 +242,7 @@
 
 	/* Wait for child to exit */
 	force = force ||
-		(timeout == 0 && pclient->set->input_idle_timeout_secs > 0);
+		(timeout == 0 && pclient->set.input_idle_timeout_secs > 0);
 	if ( !force ) {
 		alarm(timeout);
 		ret = waitpid(pid, &status, 0);
@@ -189,7 +262,7 @@
 		if ( pclient->debug ) {
 			i_debug("program `%s' execution timed out after %llu seconds: "
 				"sending TERM signal", pclient->path,
-				(unsigned long long int)pclient->set->input_idle_timeout_secs);
+				(unsigned long long int)pclient->set.input_idle_timeout_secs);
 		}
 
 		/* Kill child gently first */
@@ -278,7 +351,7 @@
 	switch ( error ) {
 	case PROGRAM_CLIENT_ERROR_RUN_TIMEOUT:
 		i_error("program `%s' execution timed out (> %d secs)",
-			pclient->path, pclient->set->input_idle_timeout_secs);
+			pclient->path, pclient->set.input_idle_timeout_secs);
 		break;
 	default:
 		break;
diff -r 32beba3bfd8d -r 5c3b0283524b src/lib-sieve/util/program-client-private.h
--- a/src/lib-sieve/util/program-client-private.h	Wed Jan 01 23:58:16 2014 +0100
+++ b/src/lib-sieve/util/program-client-private.h	Sat Jan 04 02:13:44 2014 +0100
@@ -14,9 +14,20 @@
 	PROGRAM_CLIENT_ERROR_UNKNOWN
 };
 
+struct program_client_extra_fd {
+	struct program_client *pclient;
+
+	int child_fd, parent_fd;
+	struct istream *input;
+	struct io *io;
+
+	program_client_fd_callback_t *callback;
+	void *context;
+};
+
 struct program_client {
 	pool_t pool;
-	const struct program_client_settings *set;
+	struct program_client_settings set;
 
 	char *path;
 	const char **args;
@@ -32,6 +43,8 @@
 	struct ostream *output, *program_output;
 	char *temp_prefix;
 
+	ARRAY(struct program_client_extra_fd) extra_fds;
+
 	enum program_client_error error;
 	int exit_code;
 
diff -r 32beba3bfd8d -r 5c3b0283524b src/lib-sieve/util/program-client-remote.c
--- a/src/lib-sieve/util/program-client-remote.c	Wed Jan 01 23:58:16 2014 +0100
+++ b/src/lib-sieve/util/program-client-remote.c	Sat Jan 04 02:13:44 2014 +0100
@@ -288,11 +288,11 @@
 	switch ( error ) {
 	case PROGRAM_CLIENT_ERROR_CONNECT_TIMEOUT:
 		i_error("program `%s' socket connection timed out (> %d msecs)",
-			pclient->path, pclient->set->client_connect_timeout_msecs);
+			pclient->path, pclient->set.client_connect_timeout_msecs);
 		break;
 	case PROGRAM_CLIENT_ERROR_RUN_TIMEOUT:
 		i_error("program `%s' execution timed out (> %d secs)",
-			pclient->path, pclient->set->input_idle_timeout_secs);
+			pclient->path, pclient->set.input_idle_timeout_secs);
 		break;
 	default:
 		break;
diff -r 32beba3bfd8d -r 5c3b0283524b src/lib-sieve/util/program-client.c
--- a/src/lib-sieve/util/program-client.c	Wed Jan 01 23:58:16 2014 +0100
+++ b/src/lib-sieve/util/program-client.c	Sat Jan 04 02:13:44 2014 +0100
@@ -29,9 +29,9 @@
 
 static int program_client_connect(struct program_client *pclient)
 {
-	if (pclient->set->client_connect_timeout_msecs != 0) {
+	if (pclient->set.client_connect_timeout_msecs != 0) {
 		pclient->to = timeout_add
-			(pclient->set->client_connect_timeout_msecs,
+			(pclient->set.client_connect_timeout_msecs,
 				program_client_connect_timeout, pclient);
 	}
 
@@ -55,6 +55,26 @@
 	return ret;
 }
 
+static void program_client_disconnect_extra_fds
+(struct program_client *pclient)
+{
+	struct program_client_extra_fd *efds;
+	unsigned int i, count;
+	
+	if (!array_is_created(&pclient->extra_fds))
+		return;
+
+	efds = array_get_modifiable(&pclient->extra_fds, &count);
+	for ( i = 0; i < count; i++ ) {
+		if ( efds[i].input != NULL )


More information about the dovecot-cvs mailing list