Search code examples
cpipefork

Closing a pipe does not send EOF to other end


I would like to run an external command from a C program. Let's say, as minimal working example, that I want to run the 'cat' command. I use use fork() and execl() to spawn the new process, and I communicate with it via pipes.

Now that's where my problem is. In a terminal I would tell 'cat' that I am done with my input by pressing CTRL-D. Here I am trying to do so by closing the file descriptor -- see the line with close(outpipefd[1]) in the code below -- but this does not seem to work. My code stalls as 'cat' is waiting for more input.

My code is as follows... What am I doing wrong? Thanks in advance!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>

int main(void)
{
    pid_t pid=0;
    int inpipefd[2];
    int outpipefd[2];

    /*
        We create the pipes for communicating with the child process
    */

    pipe(inpipefd);
    pipe(outpipefd);

    if((pid=fork())==0)
    {
        /*
            Child
        */

        dup2(outpipefd[0],STDIN_FILENO);
        dup2(inpipefd[1],STDOUT_FILENO);
        dup2(inpipefd[1],STDERR_FILENO);

        /*
            We spawn the process
        */

        execl("/bin/cat","cat",(char *)(NULL));

        /*
            Nothing below this line should be executed by child process.
            If so, it means that the execl function wasn't successfull, so lets exit!
        */

        exit(1);
    }

    /*
        Parent.
        Close unused pipe ends.
    */
    
    close(outpipefd[0]);
    close(inpipefd[1]);

    /*
        Now we can write to outpipefd[1] and read from inpipefd[0]
    */

    char *greeting="Hello world!\n";
    write(outpipefd[1],greeting,strlen(greeting));

    /*
        Here I believe that closing the pipe should be equivalent to
        pressing CTRL-D in a terminal, therefore terminating the cat command...
        This is unfortunately not the case!
    */

    close(outpipefd[1]);

    while(1)
    {
        char buf[256];

        for(int c=0;c<256;c++)
            buf[c]=0;
        
        if(read(inpipefd[0], buf, 256)<=0)
            break;

        printf("OUTPUT: %s\n", buf);
    }

    /*
        Send SIGKILL signal to the child process
    */

    int status;

    kill(pid, SIGKILL);
    waitpid(pid, &status, 0);
    
    return 0;
}

Solution

  • The child still has both ends of both pipes opened, because you never closed any of your FDs in it. Until every FD referring to the write end of a pipe is closed, it won't return EOF.