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