3
votes

I am trying to create a child process, redirect its stdin and stdout to the parent process, and interact with the child process. That is, the parent process should be able to receive input from the child, process it, and provide output to the child, and the cycle repeats (like user interaction with a shell, which is the actually the end goal: to simulate user interaction).

Thus far I have been able to create and redirect pipes successfully (the code below simply echoes the input on the parent process, from user, through the child). The problem is that I can only do this once, the child terminates after first I/O cycle, and I get a SIGPIPE. How can I keep the child alive, and have it read from pipe when the parent writes to it?

P.S. I know except can be used to simulate user interaction, but I need access to the returned data to do custom analysis, and I'd like to go this way for academic purposes.

#include <iostream>
#include <string>
#include <sstream>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <limits.h>
#include <sys/types.h>
#include <cstdlib>


#define MAX_CMD_SIZE 256
#define MAX_BUFF_SIZE 512

#define NUM_PIPES 2
#define PARENT_READ_PIPE pipes[0]
#define PARENT_WRITE_PIPE pipes[1]

#define READ_END 0
#define WRITE_END 1

#define PARENT_READ_FD PARENT_READ_PIPE[READ_END]
#define PARENT_WRITE_FD PARENT_WRITE_PIPE[WRITE_END]

#define CHILD_READ_FD PARENT_WRITE_PIPE[READ_END]
#define CHILD_WRITE_FD PARENT_READ_PIPE[WRITE_END]

int pipes[NUM_PIPES][2];


int main()
{
    pipe(PARENT_READ_PIPE);
    pipe(PARENT_WRITE_PIPE);

    pid_t process_id = fork();

    if (process_id<0) //throw diag::DiagError("ERROR: error during fork()");
    {std::cerr<<"ERROR: error during fork()"; exit(1);}
    else if (process_id==0)
    {//CHILD process
        dup2(CHILD_READ_FD, STDIN_FILENO);
        dup2(CHILD_WRITE_FD, STDOUT_FILENO);

        close(CHILD_READ_FD);
        close(CHILD_WRITE_FD);
        close(PARENT_READ_FD);
        close(PARENT_WRITE_FD);

        char buffer[MAX_BUFF_SIZE];
        int count = read(STDIN_FILENO, buffer, MAX_BUFF_SIZE-1);
        if (count >= 0) 
        {
            buffer[count] = 0;
            printf("%s", buffer);
        } 
        else //{throw diag::DiagError("IO Error");}
        {std::cerr<<"ERROR: error during fork()"; exit(1);}
        exit(0);
    }
    else
    {//PARENT process
       close(CHILD_READ_FD);
        close(CHILD_WRITE_FD);

        char buffer[MAX_BUFF_SIZE];

        std::string temp="";

        while(std::getline(std::cin,temp))
        {
            write(PARENT_WRITE_FD, temp.c_str(), temp.size());

            //wait(&status);
            int count = read(PARENT_READ_FD, buffer, MAX_BUFF_SIZE-1);
            if (count >= 0) 
            {
                buffer[count] = 0;
                printf("%s", buffer);
            } 
            else //{throw diag::DiagError("IO Error");}
            {std::cerr<<"ERROR: error during fork()"; exit(1);}
        }  
    }
}
2
Sorry, it was taken from part of a larger program... changed a few things and it complies and runs now.Ramon
C++ and C are different languages. Please don't tag C++ code as C.too honest for this site

2 Answers

2
votes

Your child process exits after one read:

    if (process_id<0) throw diag::DiagError("ERROR: error during fork()");
    else if (process_id==0)
    {//CHILD process
            .....
            exit(0); /* <-- here */
    }

This of course causes the pipe(s) to be closed and your parent receives SIGPIPE while trying to write to the pipe that nobody reads from anymore. Your parent code uses a loop to read/write to/from child - do simmilar thing in your child code - run in a loop instead of just exiting. You can also use some special string of characters that would cause your child process to exit the loop and terminate.

Besides - if you want to read/write using stdin and stdout - you could invoke exec() after you duplicated descriptors with dup2 and closed the right pipe ends. In your child process you could then use normal c++ streams like std::cin and std::cout. That of course requires refactoring your child code to a separate program.

0
votes

The poll and select families monitor file descriptors, and wait for them to become ready for I/O operations.

I ended up using pseudo-terminals for the two-way communication, but they work with pipes as well.