I propose a new strategy, R2:
function do(commands) if commands is of size 1 exec commands[0] || die split commands into c1 (first command) c2 (the rest of them) open if fork close input end of pipe dup output of pipe to stdin do (c2) || die close output end of pipe dup input of pipe to stdout exec c1 || die
Using a recursive function, especially if you maintain a list, will help you simplify your logic. You do not need to worry about the depth of the stack here, as all of your address space will be rewritten anyway.
In other news from the man page :
After successfully returning from one of these system calls, the old and new file descriptors can be used interchangeably. They refer to the same description of an open file (see open (2)) and thus separate the file offset and file status flags; for example, if the file offset using lseek (2) on one of the descriptors, the offset also changes for the other.
What does it mean when you say you close both ends of the pipe? You really close it - this is AND the standard input / output that your program intends to use.
-> MUTCH LATER EDIT <-
As Jonathan Leffler noted, the above information is correct. I confirmed this with the following program:
#include <unistd.h> int main(){ dup2(0, 7); write(7, "Hey, 1\n", 7); close(0); write(7, "Hey, 2\n", 7); close(7); write(7, "Hey, 3\n", 7); }
The result is the following:
$ gcc dup2Test.c && ./a.out Hey, 1 Hey, 2
Thanks Jonathan!