]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-program-client: Expose asynchronous API
authorAki Tuomi <aki.tuomi@dovecot.fi>
Fri, 7 Oct 2016 16:48:59 +0000 (19:48 +0300)
committerAki Tuomi <aki.tuomi@dovecot.fi>
Sun, 9 Oct 2016 20:46:36 +0000 (23:46 +0300)
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

index 47d442129c5177fa941f9b5191d7883e44c143a2..fcf3a941b0e8dc01e75f8ca9e415ba3a2fe9c236 100644 (file)
@@ -35,7 +35,6 @@ struct program_client {
 
        int fd_in, fd_out;
        struct io *io;
-       struct ioloop *ioloop;
        struct timeout *to;
        time_t start_time;
 
@@ -45,6 +44,9 @@ struct program_client {
 
        ARRAY(struct program_client_extra_fd) extra_fds;
 
+       program_client_callback_t *callback;
+       void *context;
+
        enum program_client_error error;
        int exit_code;
 
index dba5cfd16b2b41269106815c3f24f7f7739a305b..a64f36f6b62812f59fa88b0ab858a6503fe00c91 100644 (file)
@@ -199,8 +199,10 @@ void program_client_remote_connected(struct program_client *pclient)
        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);
index 263a4af66bd4d8b3a0276ee41048f911f515934b..00c48265e191ff980201eeb64df36fdbee00f220 100644 (file)
 #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 @@ void program_client_disconnect(struct program_client *pclient, bool force)
 {
        int ret, error = FALSE;
 
-       if (pclient->ioloop != NULL)
-               io_loop_stop(pclient->ioloop);
-
        if (pclient->disconnected)
                return;
 
@@ -153,6 +159,12 @@ void program_client_disconnect(struct program_client *pclient, bool force)
        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 @@ void program_client_program_input(struct program_client *pclient)
                                        program_client_disconnect(pclient, FALSE);
                        }
                }
-       }
+       } else
+               program_client_disconnect(pclient, FALSE);
 }
 
 static
@@ -499,31 +512,85 @@ void program_client_destroy(struct program_client **_pclient)
 
        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 @@ int program_client_run(struct program_client *pclient)
                                (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;
 }
-
index 40606952a68418113b726f3362b5cdbfabb8cc56..651c82d6a946630f6c86a141b28f02e4f8c2b8ba 100644 (file)
@@ -18,6 +18,7 @@ struct program_client_settings {
 };
 
 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 @@ void program_client_set_output_seekable(struct program_client *pclient,
        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_extra_fd(struct program_client *pclient, int fd,
 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