mblaze/filter.c

155 lines
2.7 KiB
C
Raw Normal View History

#include <sys/wait.h>
2017-08-06 18:05:37 +00:00
#include <errno.h>
2017-08-31 15:30:17 +00:00
#include <fcntl.h>
#include <limits.h>
2017-08-31 15:30:17 +00:00
#include <poll.h>
2017-04-14 00:11:03 +00:00
#include <signal.h>
2016-07-29 13:15:57 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
2016-07-29 13:15:57 +00:00
#include <unistd.h>
int
filter(char *input, size_t inlen, char *cmd, char **outputo, size_t *outleno)
{
char *output;
2016-07-29 15:15:12 +00:00
ssize_t outlen;
2016-07-29 13:15:57 +00:00
ssize_t outalloc = 4096;
pid_t pid;
2017-04-14 00:11:03 +00:00
sigset_t mask, orig_mask;
2017-04-14 18:00:25 +00:00
int r;
2017-04-14 00:11:03 +00:00
sigemptyset(&mask);
sigaddset(&mask, SIGPIPE);
sigprocmask(SIG_BLOCK, &mask, &orig_mask);
2017-08-31 15:30:17 +00:00
2016-07-29 13:15:57 +00:00
outlen = 0;
2017-04-14 00:11:03 +00:00
output = malloc(outalloc);
if (!output)
goto fail;
2016-07-29 13:15:57 +00:00
int pipe0[2];
int pipe1[2];
if (pipe(pipe0) != 0 || pipe(pipe1) != 0)
goto fail;
2017-08-06 18:05:37 +00:00
int got = fcntl(pipe0[1], F_GETFL);
if (got > 0)
fcntl(pipe0[1], F_SETFL, got | O_NONBLOCK);
2016-07-29 13:15:57 +00:00
char *argv[] = { "/bin/sh", "-c", cmd, (char *)0 };
if (!(pid = fork())) {
dup2(pipe0[0], 0);
close(pipe0[1]);
close(pipe0[0]);
dup2(pipe1[1], 1);
close(pipe1[0]);
close(pipe1[1]);
execvp(argv[0], argv);
exit(-1);
}
close(pipe0[0]);
close(pipe1[1]);
2017-08-31 15:30:17 +00:00
2016-07-29 13:15:57 +00:00
if (pid < 0) {
close(pipe0[1]);
close(pipe1[0]);
goto fail;
}
struct pollfd fds[2];
fds[0].fd = pipe1[0];
fds[0].events = POLLIN | POLLHUP;
2016-07-29 13:15:57 +00:00
fds[1].fd = pipe0[1];
fds[1].events = POLLOUT;
while ((fds[0].fd >= 0 || fds[1].fd >= 0) &&
poll(fds, sizeof fds / sizeof fds[0], -1) >= 0) {
if (fds[0].revents & POLLIN) {
if (outlen + 512 > outalloc) {
outalloc *= 2;
if (outalloc < 0)
exit(-1);
output = realloc(output, outalloc);
if (!output)
exit(-1);
}
ssize_t ret = read(fds[0].fd, output + outlen, 512);
if (ret > 0)
outlen += ret;
else
2016-07-29 13:15:57 +00:00
close(fds[0].fd);
} else if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) {
fds[0].fd = -1;
}
2017-08-31 15:30:17 +00:00
2016-07-29 13:15:57 +00:00
if (fds[1].revents & POLLOUT) {
2017-08-06 18:05:37 +00:00
ssize_t ret = write(fds[1].fd, input, inlen);
2016-07-29 13:15:57 +00:00
if (ret > 0) {
input += ret;
inlen -= ret;
}
2017-08-06 18:05:37 +00:00
if (ret <= 0 && errno == EAGAIN) {
/* ignore */
} else if (ret <= 0 || inlen == 0)
2016-07-29 13:15:57 +00:00
close(fds[1].fd);
} else if (fds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) {
fds[1].fd = -1;
}
}
// ok to fail when closed already
2017-01-26 19:27:26 +00:00
close(pipe0[1]);
close(pipe1[0]);
2016-07-29 13:15:57 +00:00
int status;
2017-01-26 19:27:26 +00:00
waitpid(pid, &status, 0);
2017-04-14 18:00:25 +00:00
r = WEXITSTATUS(status);
2016-07-29 13:15:57 +00:00
*outputo = output;
*outleno = outlen;
2017-04-14 18:00:25 +00:00
if (0) {
2016-07-29 13:15:57 +00:00
fail:
2017-04-14 18:00:25 +00:00
*outputo = 0;
*outleno = 0;
free(output);
r = -1;
}
2016-07-29 13:15:57 +00:00
sigpending(&mask);
if (sigismember(&mask, SIGPIPE)) {
int sig;
sigwait(&mask, &sig);
}
2017-04-14 00:11:03 +00:00
sigprocmask(SIG_SETMASK, &orig_mask, 0);
2017-04-14 18:00:25 +00:00
return r;
2016-07-29 13:15:57 +00:00
}
#ifdef TEST
int
main()
{
char *input = "foo\nbar\nbaz";
int e;
char *output;
size_t outlen;
e = filter(input, strlen(input), "rev;exit 2", &output, &outlen);
fwrite(output, 1, outlen, stdout);
printf("%ld -> %d\n", outlen, e);
return 0;
}
#endif