#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #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 ...]\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; }