I'm trying to write a C program to simulate the piping of two or more processes together, like ls | sort | wc so running my code as ./driver ls sort wc should show the same result. I think I'm really close, but I can't seem to find the bug in my code below. Any help would be appreciated, I'm really stumped here.
I think I understand what is supposed to happen, but Im crossing my wires somehow in making it happen. The parent should fork child processes who in turn reroute their STDOUT to the write end of a pipe(a). Any child who is created beyond the first child should consider the read end of this pipe(a) as its STDIN, as well as redirect it's own output to a pipe(b) of it's own.
Say a third process is piped. It should consider the read end of the pipe(b) as STDIN, and again pipe its output to the write end of a new pipe(c) before executing the requested command.
The last case is the when the final process is passed to the pipe. In this example, a fourth process would consider the read end of the pipe(c) but should not need to redirect the STDOUT, just send it to STDOUT as normal.
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define FORK_CHILD 0
static void error_and_exit(void) {
fprintf(stderr, "Error: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
static int fork_or_die(void) {
int pid = fork();
if (pid == -1) {
error_and_exit();
}
return pid;
}
int main(const int argc, const char * argv[])
{
int processes = argc - 1;
int apipe[argc - 1][2];
int pid;
int result = -1;
for (int i = 0; i < processes; i++) {
result = pipe(apipe[i]);
if (result == -1) {
error_and_exit();
}
}
for (int i = 0; i < processes; i++) {
pid = fork_or_die();
// Child process executes process
if (pid == FORK_CHILD) {
// If we are not the first program in the pipe
if (i > 1) {
// Use the output from the previous program in the pipe as our input
// Check the read end of the pipe and STDIN are different descriptors
if (apipe[i - 1][0] != STDIN_FILENO) {
// Send the read end of the pipe to STDIN
if (dup2(apipe[i - 1][0], STDIN_FILENO) == -1) {
error_and_exit();
}
}
}
// Before we execute a process, bind the write end of the pipe to STDOUT
// Don't do this to the last process in the pipe, just send output to STDOUT as normal
if (i < processes - 1) {
// Check the write end of the pipe and STDOUT are different descriptors
if (apipe[i][1] != STDOUT_FILENO) {
// Send the write end of the pipe to STDOUT
if (dup2(apipe[i][1], STDOUT_FILENO) == -1) {
error_and_exit();
}
}
}
// Child executes requested process
if (execlp(argv[i + 1], argv[i + 1], (char *)NULL) == -1) {
error_and_exit();
}
wait(NULL);
}
// Parent does nothing until loop exits (waits for children)
}
return 0;
}
wait(NULL)is out of place. It's in the child code, but will never be reached because it's after theexeclp. - user3386109