#include #include #include #include #include #include #include #define CKE(val, name) do { if (val == -1) { perror(name); exit(1); } } while (0) #ifdef DEBUG #define DP(...) fprintf(stderr, __VA_ARGS__); #else #define DP(...) #endif int filefork(char *argv[], int input, int output) { int pid = fork(); switch (pid) { case -1: perror("fork"); exit(1); break; case 0: if (input != -1) { CKE(close(STDIN_FILENO),"close stdin"); CKE(dup2(input, STDIN_FILENO),"dup2 stdin"); } if (output != -1) { CKE(close(STDOUT_FILENO),"close stdout"); CKE(dup2(output, STDOUT_FILENO),"dup2 stdout"); } CKE(execvp(argv[0], argv), "exec"); exit(1); break; default: return pid; } } int echofork(char *str, size_t len, int fd) { int pid = fork(); switch (pid) { case -1: perror("fork"), exit(1); break; case 0: size_t remaining = len; char *cur = str; do { ssize_t res = write(fd, cur, remaining); if (res == -1) { perror("write"); exit(1); } cur += res; remaining -= res; } while (remaining > 0); exit(0); default: return pid; } } char *p1[] = {"sed","-e","s/a/b/g",NULL}; char *p2[] = {"tac",NULL}; char *p3[] = {"sed","-e","s/fish/PIG/",NULL}; char *p4[] = {"rev",NULL}; void empipe(int pipefd[2]) { CKE(pipe(pipefd), "pipe"); fcntl(pipefd[0], F_SETFD, FD_CLOEXEC); fcntl(pipefd[1], F_SETFD, FD_CLOEXEC); } void run(int count, char **progs[], char *inputstr, size_t inputlen) { if (inputstr) count++; int (*pipefds)[2] = malloc((count-1) * sizeof(int[2])); pid_t *pids = malloc(count * sizeof(pid_t)); for (int i = 0; i < count-1; i++) empipe(pipefds[i]); for (int i = 0; i < count; i++) { int input = (i == 0) ? -1 : pipefds[i-1][0]; int output = (i == count-1) ? -1 : pipefds[i ][1]; if (!inputstr) pids[i] = filefork(progs[i ], input, output); else if (i==0) pids[i] = echofork(inputstr, inputlen, output); else pids[i] = filefork(progs[i-1], input, output); DP("[%d] %d\n",i,pids[i]); } // pipes need to be closed everywhere they aren't being used. // close them in the parent process here; CLOEXEC will take care of them everwhere else // (duped fds won't have CLOEXEC set so those ones, ie the ones being used, won't be closed) for (int i = 0; i < count-1; i++) { CKE(close(pipefds[i][0]), "close"); CKE(close(pipefds[i][1]), "close"); } for (int i = 0; i < count; i++) { int status; pid_t rpid = waitpid(pids[i], &status, 0); DP("-> %d %d\n",rpid,WEXITSTATUS(status)); } free(pipefds); free(pids); } int luaopen_run(lua_State *L) { lua_pushliteral(L, "fuck"); return 1; }