How to capture stdin, stdout and stderr of child program!
This is an article that I had posted in my livejournal on 21st-Apr-2006 05:28 pm. I’m trying to migrate the most valuable stuff to wordpress.
Hi everyone,
I have been asking this particular question to everyone for some time, the solution of which I found just now. What I wanted was to capture the standard file streams of a child process. Also that, I wanted not to use any named pipes for this purpose. One of the many ways I found was using a popen() system call. But, it seem to give to the parent either of stdin or stdout, but not both, and most importantly never stderr.
I used to wonder how various IDE’s used to execute compilers and debuggers in the background providing them with proper input and interpret their output. Anyway, I could find a solution to my problem. The basic fact is that “the child process always inherits the parent’s file descriptors”. ie, if a child can be made to inherit the file descriptors 0, 1 and 2, which actually points to some pipes with their other end in the parent process, the the parent effectively captures the child’s standard file descriptors.
So, what should be done is that, before fork(), the parent has to create the required number of pipes(one pipe for each stream) and make its own standard descriptors point to the file descriptors of the child end(supposed) of the pipes. After doing the fork(), the child will have its standard descriptors as what the parent had till the fork is called(ie, one of the ends of pipes).
Suppose you have to write something to the stdin of a child program. The steps to follow are as given below:
1. Create a pipe – use pipe() – the pipe will have a read end and a write end – two descriptors
2. Duplicate parent’s stdin – use oldstream=dup(). this is just like to save our original stream
3. Close stdin – So that we can assign the read end(child’s stdin) of the pipe to file descriptor 0
4. Make read end of pipe as stdin – use dup2() to duplicate the read end fd as 0
5. do the fork() – From now on whatever the parent and child reads from stdin will come from the read end of pipe.
6. Close unnecessary fds – Close the read and write end file descriptors from the child – it’ll use only stdin from now on. You may also close read end from parent since the parent is only going to write to the pipe.
7. Restore parent’s stdin – close the fd 0, use dup2() to use the saved(oldstream) copy of the parent’s stdin.
8. Start writing to child – Now start writing to the write end of the pipe to write to the stdin of child.
The above steps may be confusing. But if spent little time to think, you’ll surely get the idea. For more help see the code given below which captures both stdin and stdout of the child which is here the ‘bc’ calculator:
#include <unistd.h>
#include <stdio.h>
main()
{
int outfd[2];
int infd[2];int oldstdin, oldstdout;
pipe(outfd); // Where the parent is going to write to
pipe(infd); // From where parent is going to readoldstdin = dup(0); // Save current stdin
oldstdout = dup(1); // Save stdoutclose(0);
close(1);dup2(outfd[0], 0); // Make the read end of outfd pipe as stdin
dup2(infd[1],1); // Make the write end of infd as stdoutif(!fork())
{
char *argv[]={”/usr/bin/bc”,
“-q”,
0};close(outfd[0]); // Not required for the child
close(outfd[1]);
close(infd[0]);
close(infd[1]);execv(argv[0],argv);
}
else
{
char input[100];close(0); // Restore the original std fds of parent
close(1);
dup2(oldstdin, 0);
dup2(oldstdout, 1);close(outfd[0]); // These are being used by the child
close(infd[1]);write(outfd[1],”2^32\n”,5); // Write to child’s stdin
input[read(infd[0],input,100)] = 0; // Read from child’s stdout
printf(”%s”,input);
}
}
I apologise for such a lengthy post.
bye for now.

I implemented a flexible TCP server which can listen to multiple ports, forks on an incoming connection and dispatches it to a handler from a loadable library, configurable per port. It logs everything which the child processes output to stdout or stderr useing this method and mostly it works just fine.
I however found one serious issue with this when porting an existing service to use this new generic server instead of it’s own implementation. When using the freopen function in a child process to remap the stdout or stderr to a file for logging purposes, select sees data available all the time on the input pipe of that child process, but when reading from it, no data is available. The server program starts consuming 100% CPU power because of this.
I don’t get a read error on reading the pipe, just returns 0, I get nothing on the exception mask from the select function to which I added the pipes, it just keeps telling me that there is data available on this socket while there is none.
when read call returns 0, it means that the file/socket/pipe is closed. A nonblocking fd will return -1 and EAGAIN in errno to tell that it has no more data. select always returns if one of the sockets is closed.
hey man, thanks, I wasn’t sure how this process works. I would never have guessed using the dup command to shuffle the stdin and stdout pipes between the parent and child processes, kudos to you.
btw, I had written the post out of haste and I think you need not dup the stdin and stdout of parent at all. instead, it should be enough that the pipes are duped to the child’s stdin/stdout (inside the child process) just before the exec
This post was very helpful. Jineshkj’s correction is true. You don’t have to do anything to the parent’s stdin or stdout. In practice, this allows a few modifications.
1. Don’t do any dup, dup2, or close before the fork (oldstdin and oldstdout are thus unnecessary).
2. in the child, close STDOUT_FILENO and STDIN_FILENO, then immediately call use dup2 to copy outfd[0] to STDIN_FILENO and infd[1] to STDOUT_FILENO. All of the custom file descriptors (elements of outfd and infd) can then be closed. Finally, exec the actual command (in this example bc).
3. in the parent after the fork, you can close outfd[0])and infd[1], as they are only used by the child. Then, write to outfd[1] and read from infd[0].
If I may summarize, the above comments/corrections led me to:
#include
#include
main()
{
int outfd[2];
int infd[2];
pipe(outfd); /* Where the parent is going to write to */
pipe(infd); /* From where parent is going to read */
if(!fork())
{
close(STDOUT_FILENO);
close(STDIN_FILENO);
dup2(outfd[0], STDIN_FILENO);
dup2(infd[1], STDOUT_FILENO);
close(outfd[0]); /* Not required for the child */
close(outfd[1]);
close(infd[0]);
close(infd[1]);
system(”/usr/bin/bc -q”);
}
else
{
char input[100];
close(outfd[0]); /* These are being used by the child */
close(infd[1]);
write(outfd[1],”2^32\n”,5); /* Write to child’s stdin */
input[read(infd[0],input,100)] = 0; /* Read from child’s stdout */
printf(”%s”,input);
close(outfd[1]);
close(infd[0]);
}
}
Which seems to work o.k.
Thanks!