3
votes

I am trying to run ls|wc using execvp. So I create a pipe and then fork to create a child. I close the appropriate(read./write) end in parent/child and then map the other end to stdout/stdin. Then I run the ls in parent using execvp and wc in child. When I run the program it says

 wc:standard input:bad file descriptor.
 0 0 0
 wc: -:Bad file descriptor

Here is my code:

int main()
{
//int nbBytes = 0; //stream length
int pfd_1[2]; //file descriptor 
//char buffer[MAX_FILE_LENGTH]; 
char* arg[MAX_FILE_LENGTH];
pid_t processPid;

//Create a pipe

if(pipe(pfd_1) == -1)
{
    printf("Error in creating pipe");
    return 0;
}

//Create a child
processPid = fork();

if(processPid == -1)
{
    printf("Erro in fork");
    exit(1);
}   
else if(processPid == 0) //Child
{               
    //redirect read end file descriptor to standard input
    dup2(pfd_1[0],0);
    //Close the write end
    if(close(pfd_1[1] == -1))
    {
        printf("Error in closing the write end file descriptor");
        exit(1);
    }
    arg[0] = "wc";
    //arg[1] = "-l";
    arg[1] = '\0';

    if(execvp(arg[0],arg) == -1)
    {
        printf("Error in executing ls");
    }       

}
else //Parent
{               
    //redirect standard output to the file descriptor
    dup2(pfd_1[1],1);
    //Close the read end
    if(close(pfd_1[0] == -1))
    {
        printf("Error in closing the read end from parent");
        exit(1);
    }
    //Command 
    arg[0] = "ls";
    arg[1] = "/proc/1/status";
    arg[2] = '\0';

    if(execvp(arg[0],arg) == -1)
    {
        printf("Error in executing ls");
    }       
}

}

Any idea what might be wrong? Why would it consider standard input as bad file descriptor? My understanding was since the stdin and read end file descriptor are aliases so the wc -l would read whatever the output is from the parent process. Do I need to do scanf to read from the stdin?

2
Error messages belong on stderr. Do not use printf to display them. Instead, use perror. This will also display the system error messages telling you the reason for the error. Also, you are leaving file descriptors open. Try: dup2( pfd_1[0], 0 ); close( pfd_1[0]); close( pfd_1[1]); And check that dup2 succeeds.William Pursell

2 Answers

1
votes

The problem is in this line:

if(close(pfd_1[1] == -1))

You are closing the result of pfd_1[1] == -1, which is by necessity equal to 0 (as they will never be equal). The correct line would probably be:

if (close(pfd_1[1]) == -1)

Note that you do this again later in attempting to close the read end in the parent process.

0
votes

If you're going to fork children, you have to call wait() in the parent process in order to avoid "zombie" child processes. So you don't want to overlay the parent process that did the original process forking with another executable via exec.

One quick way to setup a series of pipes in the way you want would be to fork a child for each executable you want to run, and read that data back into a buffer in the parent. Then feed that data from the first child into a new child process that the parent forks off. So each child is fed data from the parent, processes the data, and writes the data back to the parent process, which stores the transformed data in a buffer. That buffer is then fed to the next child, etc., etc. The final results of the data in the buffer are the final output of the pipe.

Here's a little pseudo-code:

//allocate buffer
unsigned char buffer[SIZE];

for (each executable to run in pipeline)
{
    pipes[2];
    pipe(pipes);

    pid_t pid = fork();

    if (pid == 0)
    {
        //setup the pipe in the child process
        //call exec
    }
    else
    {
        //setup the pipe in the parent process

        if (child executable is not the first in the pipeline)
        {
            //write contents of buffer to child process
        }

        //read from the pipe until the child exits
        //store the results in buffer

        //call wait, and maybe also check the return value to make sure the 
        //child returned successfully
        wait(NULL);

        //clean up the pipe
    }
}