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.

Update: the original code has been updated to avoid altering parent’s standard I/O streams

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 2 pipes (parent uses one for writing and the other for reading) – use pipe() – a pipe is represented as an int[2] array – [0] is read fd and [1] is write fd
2. do the fork() – child process will exec() the program of interest
3. Dup2() the relevant pipe file descriptors to stdin and stdout of child – read end of parent’s write pipe is duped to stdin of child – write end of parent’s read pipe is duped to stdout of child
4. Close unnecessary fds in both parent and child
5. Child now exec()s the program
6. Parent writes to the write end of write pipe and reads from read end of read pipe

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>

/* since pipes are unidirectional, we need two pipes.
   one for data to flow from parent's stdout to child's
   stdin and the other for child's stdout to flow to
   parent's stdin */

#define NUM_PIPES          2

#define PARENT_WRITE_PIPE  0
#define PARENT_READ_PIPE   1

int pipes[NUM_PIPES][2];

/* always in a pipe[], pipe[0] is for read and 
   pipe[1] is for write */
#define READ_FD  0
#define WRITE_FD 1

#define PARENT_READ_FD  ( pipes[PARENT_READ_PIPE][READ_FD]   )
#define PARENT_WRITE_FD ( pipes[PARENT_WRITE_PIPE][WRITE_FD] )

#define CHILD_READ_FD   ( pipes[PARENT_WRITE_PIPE][READ_FD]  )
#define CHILD_WRITE_FD  ( pipes[PARENT_READ_PIPE][WRITE_FD]  )

void
main()
{
    int outfd[2];
    int infd[2];
    
    // pipes for parent to write and read
    pipe(pipes[PARENT_READ_PIPE]);
    pipe(pipes[PARENT_WRITE_PIPE]);
    
    if(!fork()) {
        char *argv[]={ "/usr/bin/bc", "-q", 0};

        dup2(CHILD_READ_FD, STDIN_FILENO);
        dup2(CHILD_WRITE_FD, STDOUT_FILENO);

        /* Close fds not required by child. Also, we don't
           want the exec'ed program to know these existed */
        close(CHILD_READ_FD);
        close(CHILD_WRITE_FD);
        close(PARENT_READ_FD);
        close(PARENT_WRITE_FD);
         
        execv(argv[0], argv);
    } else {
        char buffer[100];
        int count;

        /* close fds not required by parent */        
        close(CHILD_READ_FD);
        close(CHILD_WRITE_FD);

        // Write to child’s stdin
        write(PARENT_WRITE_FD, "2^32\n", 5);
 
        // Read from child’s stdout
        count = read(PARENT_READ_FD, buffer, sizeof(buffer)-1);
        if (count >= 0) {
            buffer[count] = 0;
            printf("%s", buffer);
        } else {
            printf("IO Error\n");
        }
    }
}

I apologize for such a lengthy post.

bye for now.

Advertisements