source: filterproc.c @ 3965ec3d

release-1.8
Last change on this file since 3965ec3d was 34132f7, checked in by David Benjamin <davidben@mit.edu>, 12 years ago
Rewrite call_filter to use g_spawn_async_with_pipes This simplifies the error-handling code. Also fixes a bug where file descriptors get double-closed in call_filter. Also adds a unit test. The separate prog argument is removed to avoid having to deal with G_SPAWN_FILE_AND_ARGV_ZERO, and since we don't really use it anyway.
  • Property mode set to 100644
File size: 2.2 KB
Line 
1#include <signal.h>
2#include <unistd.h>
3#include <sys/select.h>
4#include <sys/types.h>
5#include <sys/wait.h>
6#include <poll.h>
7#include <fcntl.h>
8#include <string.h>
9
10#include <glib.h>
11
12/* Even in case of error, send_receive is responsible for closing wfd
13 * (to EOF the child) and rfd (for consistency). */
14static int send_receive(int rfd, int wfd, const char *out, char **in)
15{
16  GString *str = g_string_new("");
17  char buf[1024];
18  nfds_t nfds;
19  int err = 0;
20  struct pollfd fds[2];
21
22  fcntl(rfd, F_SETFL, O_NONBLOCK | fcntl(rfd, F_GETFL));
23  fcntl(wfd, F_SETFL, O_NONBLOCK | fcntl(wfd, F_GETFL));
24
25  fds[0].fd = rfd;
26  fds[0].events = POLLIN;
27  fds[1].fd = wfd;
28  fds[1].events = POLLOUT;
29
30  if(!out || !*out) {
31    /* Nothing to write. Close our end so the child doesn't hang waiting. */
32    close(wfd); wfd = -1;
33    out = NULL;
34  }
35
36  while(1) {
37    if(out && *out) {
38      nfds = 2;
39    } else {
40      nfds = 1;
41    }
42    err = poll(fds, nfds, -1);
43    if(err < 0) {
44      break;
45    }
46    if(out && *out) {
47      if(fds[1].revents & POLLOUT) {
48        err = write(wfd, out, strlen(out));
49        if(err > 0) {
50          out += err;
51        }
52        if(err < 0) {
53          out = NULL;
54        }
55      }
56      if(!out || !*out || fds[1].revents & (POLLERR | POLLHUP)) {
57        close(wfd); wfd = -1;
58        out = NULL;
59      }
60    }
61    if(fds[0].revents & POLLIN) {
62      err = read(rfd, buf, sizeof(buf));
63      if(err <= 0) {
64        break;
65      }
66      g_string_append_len(str, buf, err);
67    } else if(fds[0].revents & (POLLHUP | POLLERR)) {
68      err = 0;
69      break;
70    }
71  }
72
73  if (wfd >= 0) close(wfd);
74  close(rfd);
75  *in = g_string_free(str, err < 0);
76  return err;
77}
78
79int call_filter(const char *const *argv, const char *in, char **out, int *status)
80{
81  int err;
82  GPid child_pid;
83  int child_stdin, child_stdout;
84
85  if (!g_spawn_async_with_pipes(NULL, (char**)argv, NULL,
86                                G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
87                                NULL, NULL,
88                                &child_pid, &child_stdin, &child_stdout, NULL,
89                                NULL)) {
90    return 1;
91  }
92
93  err = send_receive(child_stdout, child_stdin, in, out);
94  if (err == 0) {
95    waitpid(child_pid, status, 0);
96  }
97  return err;
98}
Note: See TracBrowser for help on using the repository browser.