Either way, ccl_popen3() is the money function, and main() demonstrates usage. Compiles on my machine with gcc popen3.c -o popen3 -std=gnu99 -Wall -Wextra -Werror.
#include <stdlib.h> #include <unistd.h> #define READ_END 0 #define WRITE_END 1 #define NENDS 2 #define OPEN_STDIN (1 << STDIN_FILENO) #define OPEN_STDOUT (1 << STDOUT_FILENO) #define OPEN_STDERR (1 << STDERR_FILENO) #define OPEN_ALL (OPEN_STDIN | OPEN_STDOUT | OPEN_STDERR) #define MAX_STREAMS 3 __attribute__((noreturn)) static void popen3_child(const char *exe, char *const argv[], int shell, int streams, int pipes[MAX_STREAMS][NENDS]) { for (int i = 0 ; i < MAX_STREAMS ; i++) if (streams & (1 << i)) { close(pipes[i][i == STDIN_FILENO ? WRITE_END : READ_END]); if (dup2(pipes[i][i == STDIN_FILENO ? READ_END : WRITE_END], i) == -1) exit(127); } if (shell) execvp(exe, argv); else execv(exe, argv); /* Any return from execv()/execvp() is an error. */ exit(127); } static void popen3_parent(int streams, int fds[], int pipes[][NENDS]) { for (int i = 0 ; i < MAX_STREAMS ; i++) if (streams & (1 << i)) { close(pipes[i][i == STDIN_FILENO ? READ_END : WRITE_END]); fds[i] = pipes[i][i == STDIN_FILENO ? WRITE_END : READ_END]; } } /* * TODO: (i) different functions for child and parent, (ii) * close original pipe fd's. */ pid_t ccl_popen3(const char *exe, char *const argv[], int shell, int streams, int fds[]) { int pipes[MAX_STREAMS][NENDS] = {{-1, -1}, {-1, -1}, {-1, -1}}; for (int i = 0 ; i < MAX_STREAMS ; i++) if ((streams & (1 << i)) && (pipe(pipes[i]) == -1)) goto err; pid_t pid = fork(); switch (pid) { case -1: /* error */ goto err; case 0: /* child */ popen3_child(exe, argv, shell, streams, pipes); break; default: /* parent */ popen3_parent(streams, fds, pipes); return pid; } err: for (int p = 0 ; p < MAX_STREAMS ; p++) for (int e = 0 ; e < NENDS ; e++) if (pipes[p][e] != -1) close(pipes[p][e]); return -1; } #include <poll.h> #include <stdio.h> void ccl_poll_input(int fds[3]) { struct pollfd pollfds[2] = { { .fd = fds[STDOUT_FILENO], .events = POLLIN }, { .fd = fds[STDERR_FILENO], .events = POLLIN } }; int running = 1; while (running) { int res = poll(pollfds, 2, -1); if (res == -1) { perror("poll()"); exit(EXIT_FAILURE); } struct pollfd *pfd = pollfds; for (int i = 0 ; i < 2 ; i++, pfd++) { if (pfd->revents & (POLLERR | POLLNVAL)) { fprintf(stderr, "poll() error\n"); exit(EXIT_FAILURE); } if (pfd->revents & (POLLIN | POLLHUP)) { char buf[1024]; ssize_t nread = read(pfd->fd, buf, sizeof(buf) - 1); if (nread == -1) { perror("read()"); exit(EXIT_FAILURE); } if (nread == 0) { /* EOF */ running = 0; break; } buf[nread] = '\0'; printf(" Read from %d: \"%s\"\n", pfd->fd, buf); } } } } #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <poll.h> int main(void) { int grep_fds[3]; const char *exe = "/bin/grep"; char *const argv[] = {"grep", "-h", "XX", "jkfdsl", "-", NULL}; pid_t grep_pid = ccl_popen3(exe, argv, 0, OPEN_ALL, grep_fds); if (grep_pid == -1) { perror("ccl_popen3()"); exit(EXIT_FAILURE); } /* Write some input to grep's standard input, and close * it. */ const char *input = "abcd\ngg9XXp\nDqdXXpird\n"; if (write(grep_fds[STDIN_FILENO], input, strlen(input)) != (ssize_t)strlen(input)) { perror("write()"); exit(EXIT_FAILURE); } close(grep_fds[STDIN_FILENO]); printf("out: %d, err: %d\n", grep_fds[STDOUT_FILENO], grep_fds[STDERR_FILENO]); struct pollfd pollfds[2] = { { .fd = grep_fds[STDOUT_FILENO], .events = POLLIN }, { .fd = grep_fds[STDERR_FILENO], .events = POLLIN } }; int running = 1; while (running) { int res = poll(pollfds, 2, -1); if (res == -1) { perror("poll()"); exit(EXIT_FAILURE); } struct pollfd *pfd = pollfds; for (int i = 0 ; i < 2 ; i++, pfd++) { if (pfd->revents & (POLLERR | POLLNVAL)) { fprintf(stderr, "poll() error\n"); exit(EXIT_FAILURE); } if (pfd->revents & (POLLIN | POLLHUP)) { char buf[1024]; ssize_t nread = read(pfd->fd, buf, sizeof(buf) - 1); if (nread == -1) { perror("read()"); exit(EXIT_FAILURE); } if (nread == 0) { /* EOF */ running = 0; break; } buf[nread] = '\0'; printf(" Read from %d: \"%s\"\n", pfd->fd, buf); } } } int grep_status; if (waitpid(grep_pid, &grep_status, 0) == -1) { perror("waitpid()"); exit(EXIT_FAILURE); } if (WIFEXITED(grep_status)) printf("grep exited with status %d\n", WEXITSTATUS(grep_status)); else // TODO: This better. printf("grep terminated abnormally\n"); return EXIT_SUCCESS; }
No comments:
Post a Comment