diff options
author | David Timber <dxdt@dev.snart.me> | 2024-11-12 08:27:01 +0100 |
---|---|---|
committer | David Timber <dxdt@dev.snart.me> | 2024-11-12 08:35:17 +0100 |
commit | a7df9d245fe0bbc456d564a523911cae3e3e85c0 (patch) | |
tree | c2bd7981d0d8ce9dbe0b66e79af95ba24c598ce9 /qna/subproc.c | |
parent | eab4c56c62577070bce33f92d968fba110c6bc1b (diff) |
Add qna/subproc.c
Diffstat (limited to 'qna/subproc.c')
-rw-r--r-- | qna/subproc.c | 187 |
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; +} |