aboutsummaryrefslogtreecommitdiff
path: root/qna/subproc.c
diff options
context:
space:
mode:
Diffstat (limited to 'qna/subproc.c')
-rw-r--r--qna/subproc.c187
1 files changed, 186 insertions, 1 deletions
diff --git a/qna/subproc.c b/qna/subproc.c
index 160492d..c040810 100644
--- a/qna/subproc.c
+++ b/qna/subproc.c
@@ -1,4 +1,189 @@
+#define _GNU_SOURCE
#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+#include <wait.h>
+#include <poll.h>
-int main \ No newline at end of file
+#define ARGV0 "subproc"
+#define STDIO_BUFSIZE 4096
+#define DELIM '\n' // -print0 option
+
+struct child {
+ pid_t pid;
+ struct {
+ int in;
+ int out;
+ int err;
+ } std;
+};
+
+struct pbuf {
+ char buf[STDIO_BUFSIZE];
+ size_t len;
+ int fd;
+};
+
+static size_t do_output (const int fd, char *buf, size_t len) {
+ char *end;
+ size_t outlen;
+
+ end = memrchr(buf, DELIM, len);
+ if (end == NULL) {
+ // one problem with this: buf getting full
+ write(fd, buf, len);
+ return len;
+ }
+
+ outlen = end - buf + 1;
+ write(fd, buf, outlen);
+
+ return outlen;
+}
+
+int main (const int argc, const char **argv) {
+ const size_t nb_procs = argc - 1;
+ const size_t nb_pfds = nb_procs * 2;
+ int ec = 0;
+ struct child subprocs[nb_procs];
+ struct pollfd pfds[nb_pfds];
+ struct pbuf pbufs[nb_pfds];
+ size_t nb_hup;
+ int s;
+
+ memset(subprocs, 0, sizeof(subprocs));
+ memset(pfds, 0, sizeof(pfds));
+ memset(pbufs, 0, sizeof(pbufs));
+ for (size_t i = 0; i < nb_pfds; i += 1) {
+ pbufs[i].fd = -1;
+ }
+
+ if (argc == 1) {
+ fprintf(stderr, "Usage: "ARGV0" <DIR> [DIR ...]\n");
+ return 2;
+ }
+
+ for (int i = 0, j = 0; i < argc - 1; i += 1, j += 2) {
+ const char *path = argv[i + 1];
+ struct child *sp = subprocs + i;
+ struct {
+ int in[2];
+ int out[2];
+ int err[2];
+ } p;
+
+ if (pipe(p.in) != 0 || pipe(p.out) != 0 || pipe(p.err) != 0) {
+ perror(ARGV0": pipe()");
+ return 1;
+ }
+
+ sp->pid = fork();
+ if (sp->pid > 0) {
+ close(p.in[0]);
+ close(p.out[1]);
+ close(p.err[1]);
+ sp->std.in = p.in[1];
+ pfds[j].fd = sp->std.out = p.out[0];
+ pfds[j + 1].fd = sp->std.err = p.err[0];
+ pfds[j].events = pfds[j + 1].events = POLLIN;
+ pbufs[j].fd = STDOUT_FILENO;
+ pbufs[j + 1].fd = STDERR_FILENO;
+ }
+ else if (sp->pid == 0) {
+ const char *new_argv[] = { "find", path, NULL };
+
+ close(p.in[1]);
+ close(p.out[0]);
+ close(p.err[0]);
+ sp->std.in = p.in[0];
+ sp->std.out = p.out[1];
+ sp->std.err = p.err[1];
+
+ if (dup2(sp->std.in, STDIN_FILENO) < 0 ||
+ dup2(sp->std.out, STDOUT_FILENO) < 0 ||
+ dup2(sp->std.err, STDERR_FILENO) < 0)
+ {
+ perror(ARGV0": dup2()");
+ abort();
+ }
+ close(sp->std.in);
+ close(sp->std.out);
+ close(sp->std.err);
+
+ execvp("find", (char*const*)new_argv);
+ perror(ARGV0": execvp()");
+ abort();
+ }
+ else {
+ perror(ARGV0": fork()");
+ return 1;
+ }
+ }
+
+ nb_hup = 0;
+ while (nb_hup < nb_pfds) {
+ int fr;
+
+ fr = poll(pfds, (nfds_t)nb_pfds, -1);
+ if (fr == 0) {
+ // should never reach unless poll() was called w/ timeout
+ break;
+ }
+ else if (fr < 0) {
+ perror(ARGV0": poll()");
+ abort();
+ }
+
+ for (size_t i = 0; i < nb_pfds; i += 1) {
+ struct pollfd *pfd = pfds + i;
+
+ if (pfd->revents == 0) {
+ continue;
+ }
+
+ if (pfd->revents & POLLIN) {
+ struct pbuf *pb = pbufs + i;
+ const size_t iofr = read(
+ pfd->fd,
+ pb->buf + pb->len,
+ sizeof(pb->buf) - pb->len);
+
+ if (iofr > 0) {
+ size_t outlen;
+ size_t rem;
+
+ pb->len += (size_t)iofr;
+ outlen = do_output(pb->fd, pb->buf, pb->len);
+ rem = pb->len - outlen;
+ memmove(pb->buf, pb->buf + outlen, rem);
+ pb->len = rem;
+ }
+ else if (iofr == 0) {
+ do_output(pb->fd, pb->buf, pb->len);
+ pb->len = 0;
+ }
+ else {
+ perror(ARGV0": read()");
+ abort();
+ }
+ }
+ else if (pfd->revents & POLLHUP) {
+ pfd->fd = -1;
+ nb_hup += 1;
+ }
+ }
+ }
+
+ while (wait(&s) >= 0) {
+ if (WIFEXITED(s) && WEXITSTATUS(s) != 0) {
+ ec = 1;
+ }
+ }
+
+ return ec;
+}