changeset 20787:11ea2902f99d

lib-program-client: Expose asynchronous API
author Aki Tuomi <aki.tuomi@dovecot.fi>
date Fri, 07 Oct 2016 19:48:59 +0300
parents 10510f5116ff
children c6717848c98a
files src/lib-program-client/program-client-private.h src/lib-program-client/program-client-remote.c src/lib-program-client/program-client.c src/lib-program-client/program-client.h
diffstat 4 files changed, 98 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-program-client/program-client-private.h	Thu Oct 06 11:57:27 2016 +0300
+++ b/src/lib-program-client/program-client-private.h	Fri Oct 07 19:48:59 2016 +0300
@@ -35,7 +35,6 @@
 
 	int fd_in, fd_out;
 	struct io *io;
-	struct ioloop *ioloop;
 	struct timeout *to;
 	time_t start_time;
 
@@ -45,6 +44,9 @@
 
 	ARRAY(struct program_client_extra_fd) extra_fds;
 
+	program_client_callback_t *callback;
+	void *context;
+
 	enum program_client_error error;
 	int exit_code;
 
--- a/src/lib-program-client/program-client-remote.c	Thu Oct 06 11:57:27 2016 +0300
+++ b/src/lib-program-client/program-client-remote.c	Fri Oct 07 19:48:59 2016 +0300
@@ -199,8 +199,10 @@
 	program_client_init_streams(pclient);
 
 	if (!slclient->noreply) {
+		struct istream *is = pclient->program_input;
 		pclient->program_input =
 			program_client_istream_create(pclient, pclient->program_input);
+		i_stream_unref(&is);
 	}
 
 	str = t_str_new(1024);
--- a/src/lib-program-client/program-client.c	Thu Oct 06 11:57:27 2016 +0300
+++ b/src/lib-program-client/program-client.c	Fri Oct 07 19:48:59 2016 +0300
@@ -17,8 +17,17 @@
 #define MAX_OUTPUT_BUFFER_SIZE 16384
 #define MAX_OUTPUT_MEMORY_BUFFER (1024*128)
 
-static int program_client_seekable_fd_callback
-(const char **path_r, void *context)
+static
+void program_client_callback(struct program_client *pclient, int result, void *context)
+{
+	program_client_callback_t *callback = pclient->callback;
+	i_assert(pclient->callback != NULL);
+	pclient->callback = NULL;
+	callback(result, context);
+}
+
+static
+int program_client_seekable_fd_callback(const char **path_r, void *context)
 {
 	struct program_client *pclient = (struct program_client *)context;
 	string_t *path;
@@ -115,9 +124,6 @@
 {
 	int ret, error = FALSE;
 
-	if (pclient->ioloop != NULL)
-		io_loop_stop(pclient->ioloop);
-
 	if (pclient->disconnected)
 		return;
 
@@ -153,6 +159,12 @@
 	if (error && pclient->error == PROGRAM_CLIENT_ERROR_NONE) {
 		pclient->error = PROGRAM_CLIENT_ERROR_OTHER;
 	}
+
+	program_client_callback(pclient,
+		pclient->error != PROGRAM_CLIENT_ERROR_NONE ?
+			-1 :
+			pclient->exit_code,
+		pclient->context);
 }
 
 void program_client_fail(struct program_client *pclient, enum program_client_error error)
@@ -317,7 +329,8 @@
 					program_client_disconnect(pclient, FALSE);
 			}
 		}
-	}
+	} else
+		program_client_disconnect(pclient, FALSE);
 }
 
 static
@@ -499,31 +512,85 @@
 
 	if (pclient->input != NULL)
 		i_stream_unref(&pclient->input);
+	if (pclient->program_input != NULL)
+		i_stream_unref(&pclient->program_input);
+	if (pclient->program_output != NULL)
+		o_stream_unref(&pclient->program_output);
 	if (pclient->output != NULL)
 		o_stream_unref(&pclient->output);
 	if (pclient->seekable_output != NULL)
 		i_stream_unref(&pclient->seekable_output);
 	if (pclient->io != NULL)
 		io_remove(&pclient->io);
-	if (pclient->ioloop != NULL)
-		io_loop_destroy(&pclient->ioloop);
 	i_free(pclient->temp_prefix);
 	pool_unref(&pclient->pool);
 	*_pclient = NULL;
 }
 
+void program_client_switch_ioloop(struct program_client *pclient)
+{
+	if (pclient->input != NULL)
+		i_stream_switch_ioloop(pclient->input);
+	if (pclient->program_input != NULL)
+		i_stream_switch_ioloop(pclient->program_input);
+	if (pclient->seekable_output != NULL)
+		i_stream_switch_ioloop(pclient->seekable_output);
+	if (pclient->output != NULL)
+		o_stream_switch_ioloop(pclient->output);
+	if (pclient->program_output != NULL)
+		o_stream_switch_ioloop(pclient->program_output);
+	if (pclient->to != NULL)
+		pclient->to = io_loop_move_timeout(&pclient->to);
+	if (pclient->io != NULL)
+		pclient->io = io_loop_move_io(&pclient->io);
+}
+
+static
+void program_client_run_callback(int result, int *context)
+{
+	*context = result;
+	io_loop_stop(current_ioloop);
+}
+
 int program_client_run(struct program_client *pclient)
 {
+	int ret = 0;
+	struct ioloop *prev_ioloop = current_ioloop;
+	struct ioloop *ioloop = io_loop_create();
+
+	program_client_switch_ioloop(pclient);
+
+	program_client_run_async(pclient, program_client_run_callback, &ret);
+
+	if (ret == 0) {
+		io_loop_run(ioloop);
+	}
+
+	io_loop_set_current(prev_ioloop);
+	program_client_switch_ioloop(pclient);
+	io_loop_set_current(ioloop);
+	io_loop_destroy(&ioloop);
+
+	if (pclient->error != PROGRAM_CLIENT_ERROR_NONE)
+		return -1;
+
+	return pclient->exit_code;
+}
+
+#undef program_client_run_async
+void program_client_run_async(struct program_client *pclient, program_client_callback_t *callback, void *context)
+{
 	int ret;
 
-	/* reset */
+	i_assert(callback != NULL);
+
 	pclient->disconnected = FALSE;
 	pclient->exit_code = 1;
 	pclient->error = PROGRAM_CLIENT_ERROR_NONE;
 
-	pclient->ioloop = io_loop_create();
-
-	if ((ret=program_client_connect(pclient)) >= 0) {
+	pclient->callback = callback;
+	pclient->context = context;
+	if ((ret = program_client_connect(pclient)) >= 0) {
 		/* run output */
 		if (ret > 0 && pclient->program_output != NULL &&
 		    (ret = o_stream_flush(pclient->program_output)) == 0) {
@@ -531,27 +598,15 @@
 				(pclient->program_output,
 				 program_client_program_output, pclient);
 		}
-
-		/* run i/o event loop */
 		if (ret < 0) {
 			i_error("write(%s) failed: %s",
 				o_stream_get_name(pclient->program_output),
 				o_stream_get_error(pclient->program_output));
 			pclient->error = PROGRAM_CLIENT_ERROR_IO;
-		} else if (!pclient->disconnected &&
-			(ret == 0 || program_client_input_pending(pclient))) {
-			io_loop_run(pclient->ioloop);
+			program_client_callback(pclient, ret, context);
+			return;
 		}
-
-		/* finished */
-		program_client_disconnect(pclient, FALSE);
+	} else {
+		program_client_callback(pclient, ret, context);
 	}
-
-	io_loop_destroy(&pclient->ioloop);
-
-	if (pclient->error != PROGRAM_CLIENT_ERROR_NONE)
-		return -1;
-
-	return pclient->exit_code;
 }
-
--- a/src/lib-program-client/program-client.h	Thu Oct 06 11:57:27 2016 +0300
+++ b/src/lib-program-client/program-client.h	Fri Oct 07 19:48:59 2016 +0300
@@ -18,6 +18,7 @@
 };
 
 typedef void program_client_fd_callback_t(void *context, struct istream *input);
+typedef void program_client_callback_t(int, void *);
 
 struct program_client *program_client_local_create(const char *bin_path,
 	const char *const *args,
@@ -37,6 +38,8 @@
 	const char *temp_prefix);
 struct istream *program_client_get_output_seekable(struct program_client *pclient);
 
+void program_client_switch_ioloop(struct program_client *pclient);
+
 /* Program provides side-channel output through an extra fd */
 void program_client_set_extra_fd(struct program_client *pclient, int fd,
 	 program_client_fd_callback_t * callback, void *context);
@@ -49,6 +52,13 @@
 void program_client_set_env(struct program_client *pclient,
 	const char *name, const char *value);
 
+/* Since script service cannot return system exit code, the exit value shall be
+   -1, 0, or 1. -1 is internal error, 0 is failure and 1 is success */
 int program_client_run(struct program_client *pclient);
+void program_client_run_async(struct program_client *pclient, program_client_callback_t *, void*);
+#define program_client_run_async(pclient, callback, context) \
+	program_client_run_async(pclient, (program_client_callback_t*)callback, (char*)context + \
+		CALLBACK_TYPECHECK(callback, \
+			void (*)(int, typeof(context))))
 
 #endif