Search code examples
cpipeeoffile-descriptor

C - How to detect EOF in pipe when reading only single char?


As explained in this answer, I'd be expecting the reader process to catch the EOF right after the writer process closes all related file descriptors.

But that doesn't happen and this program ends up stuck in an endless loop. Parent waits for it's child to finish & child waits for EOF signalizing closed pipe.

Why the reader process doesn't receive EOF?

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <wait.h>

#define STRING_TO_SEND "Hello, world!\n"

int main() {
    int fd[2], i = 0;
    __pid_t pid;
    char _char;
    ssize_t nbytes;

    pipe(fd);
    pid = fork();

    if (pid == -1) {
        // Error
        perror("Error forking!");
        return EXIT_FAILURE;

    } else if (pid == 0) {

        // Child
        close(fd[1]);
        while ((nbytes = read(fd[0], &_char, 1)) != EOF) {
            if (nbytes == 0)
                continue;
            putchar(_char);
        }
        close(fd[0]);

    } else {

        // Parent
        close(fd[0]);

        for(;;) {
            _char = STRING_TO_SEND[i++];
            write(fd[1], &_char, 1);
            if (_char == '\0')
                break;
        }

        close(fd[1]);
        close(STDOUT_FILENO);

        while (wait(NULL)>0) {}
    }
    return 0;
}

Solution

  • You simply misunderstood the "end of file" indication of read() which simply means nothing more to read for read() (read() returns 0 in that case). But read() doesn't actually return the value EOF. So your condition should be:

    while ((nbytes = read(fd[0], &_char, 1)) > 0) {
    

    Also __pid_t is an internal type of your C library. You shouldn't use that; just use pid_t.

    See read(2)'s man page for details.