Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

file 118 lines (106 sloc) 2.318 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
#include <signal.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <poll.h>
#include <fcntl.h>
#include <string.h>

#include <glib.h>

int send_receive(int rfd, int wfd, const char *out, char **in)
{
  GString *str = g_string_new("");
  char buf[1024];
  nfds_t nfds;
  int err = 0;
  struct pollfd fds[2];
  struct sigaction sig = {.sa_handler = SIG_IGN}, old;

  fcntl(rfd, F_SETFL, O_NONBLOCK | fcntl(rfd, F_GETFL));
  fcntl(wfd, F_SETFL, O_NONBLOCK | fcntl(wfd, F_GETFL));

  fds[0].fd = rfd;
  fds[0].events = POLLIN;
  fds[1].fd = wfd;
  fds[1].events = POLLOUT;

  sigaction(SIGPIPE, &sig, &old);
  
  while(1) {
    if(out && *out) {
      nfds = 2;
    } else {
      nfds = 1;
    }
    err = poll(fds, nfds, -1);
    if(err < 0) {
      break;
    }
    if(out && *out) {
      if(fds[1].revents & POLLOUT) {
        err = write(wfd, out, strlen(out));
        if(err > 0) {
          out += err;
        }
        if(err < 0) {
          out = NULL;
        }
      }
      if(!out || !*out || fds[1].revents & (POLLERR | POLLHUP)) {
        close(wfd);
        out = NULL;
      }
    }
    if(fds[0].revents & POLLIN) {
      err = read(rfd, buf, sizeof(buf));
      if(err <= 0) {
        break;
      }
      g_string_append_len(str, buf, err);
    } else if(fds[0].revents & (POLLHUP | POLLERR)) {
      err = 0;
      break;
    }
  }

  *in = g_string_free(str, err < 0);
  sigaction(SIGPIPE, &old, NULL);
  return err;
}

int call_filter(const char *prog, const char *const *argv, const char *in, char **out, int *status)
{
  int err = 0;
  pid_t pid;
  int rfd[2];
  int wfd[2];

  if((err = pipe(rfd))) goto out;
  if((err = pipe(wfd))) goto out_close_rfd;

  pid = fork();
  if(pid < 0) {
    err = pid;
    goto out_close_all;
  }
  if(pid) {
    /* parent */
    close(rfd[1]);
    close(wfd[0]);
    err = send_receive(rfd[0], wfd[1], in, out);
    if(err == 0) {
      waitpid(pid, status, 0);
    }
  } else {
    /* child */
    close(rfd[0]);
    close(wfd[1]);
    dup2(rfd[1], 1);
    dup2(wfd[0], 0);
    close(rfd[1]);
    close(wfd[0]);

    if(execvp(prog, (char *const *)argv)) {
      _exit(-1);
    }
  }

 out_close_all:
  close(wfd[0]);
  close(wfd[1]);
 out_close_rfd:
  close(rfd[0]);
  close(rfd[1]);
 out:
  return err;
}
Something went wrong with that request. Please try again.