source: filterproc.c @ e8db357

Last change on this file since e8db357 was e8db357, checked in by Anders Kaseorg <andersk@mit.edu>, 7 years ago
filterproc: Rewrite using GIOChannel The old poll() loop was confusing to both humans and the Coverity Scan service. We might as well do things the GLib way anyway. Signed-off-by: Anders Kaseorg <andersk@mit.edu>
  • Property mode set to 100644
File size: 3.2 KB
Line 
1#include "filterproc.h"
2#include <sys/wait.h>
3#include <string.h>
4#include <glib.h>
5
6struct filter_data {
7  const char **in;
8  const char *in_end;
9  GString *out_str;
10  GMainLoop *loop;
11  int err;
12};
13
14static gboolean filter_stdin(GIOChannel *channel, GIOCondition condition, gpointer data_)
15{
16  struct filter_data *data = data_;
17  gboolean done = condition & (G_IO_ERR | G_IO_HUP);
18
19  if (condition & G_IO_OUT) {
20    gsize n;
21    GIOStatus ret = g_io_channel_write_chars(channel, *data->in, data->in_end - *data->in, &n, NULL);
22    *data->in += n;
23    if (ret == G_IO_STATUS_ERROR)
24      data->err = 1;
25    if (ret == G_IO_STATUS_ERROR || *data->in == data->in_end)
26      done = TRUE;
27  }
28
29  if (condition & G_IO_ERR)
30    data->err = 1;
31
32  if (done)
33    g_io_channel_shutdown(channel, TRUE, NULL);
34  return !done;
35}
36
37static gboolean filter_stdout(GIOChannel *channel, GIOCondition condition, gpointer data_)
38{
39  struct filter_data *data = data_;
40  gboolean done = condition & (G_IO_ERR | G_IO_HUP);
41
42  if (condition & (G_IO_IN | G_IO_HUP)) {
43    gchar *buf;
44    gsize n;
45    GIOStatus ret = g_io_channel_read_to_end(channel, &buf, &n, NULL);
46    g_string_append_len(data->out_str, buf, n);
47    g_free(buf);
48    if (ret == G_IO_STATUS_ERROR) {
49      data->err = 1;
50      done = TRUE;
51    }
52  }
53
54  if (condition & G_IO_ERR)
55    data->err = 1;
56
57  if (done) {
58    g_io_channel_shutdown(channel, TRUE, NULL);
59    g_main_loop_quit(data->loop);
60  }
61  return !done;
62}
63
64int call_filter(const char *const *argv, const char *in, char **out, int *status)
65{
66  GPid child_pid;
67  int child_stdin, child_stdout;
68
69  if (!g_spawn_async_with_pipes(NULL, (char**)argv, NULL,
70                                G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
71                                NULL, NULL,
72                                &child_pid, &child_stdin, &child_stdout, NULL,
73                                NULL)) {
74    *out = NULL;
75    return 1;
76  }
77
78  if (in == NULL) in = "";
79  GMainContext *context = g_main_context_new();
80  struct filter_data data = {&in, in + strlen(in), g_string_new(""), g_main_loop_new(context, FALSE), 0};
81
82  GIOChannel *channel = g_io_channel_unix_new(child_stdin);
83  g_io_channel_set_encoding(channel, NULL, NULL);
84  g_io_channel_set_flags(channel, g_io_channel_get_flags(channel) | G_IO_FLAG_NONBLOCK, NULL);
85  GSource *source = g_io_create_watch(channel, G_IO_OUT | G_IO_ERR | G_IO_HUP);
86  g_io_channel_unref(channel);
87  g_source_set_callback(source, (GSourceFunc)filter_stdin, &data, NULL);
88  g_source_attach(source, context);
89  g_source_unref(source);
90
91  channel = g_io_channel_unix_new(child_stdout);
92  g_io_channel_set_encoding(channel, NULL, NULL);
93  g_io_channel_set_flags(channel, g_io_channel_get_flags(channel) | G_IO_FLAG_NONBLOCK, NULL);
94  source = g_io_create_watch(channel, G_IO_IN | G_IO_ERR | G_IO_HUP);
95  g_io_channel_unref(channel);
96  g_source_set_callback(source, (GSourceFunc)filter_stdout, &data, NULL);
97  g_source_attach(source, context);
98  g_source_unref(source);
99
100  g_main_loop_run(data.loop);
101
102  g_main_loop_unref(data.loop);
103  g_main_context_unref(context);
104
105  waitpid(child_pid, status, 0);
106  g_spawn_close_pid(child_pid);
107  *out = g_string_free(data.out_str, data.err);
108  return data.err;
109}
Note: See TracBrowser for help on using the repository browser.