Search code examples
clinuxshellpolling

Poll works on stdin when entering manually, but not when input is piped and not redirected


Consider this C program:

#include <poll.h>
#include <stdio.h>
#include <unistd.h>

#define TIMEOUT 500 // 0.5 s
#define BUF_SIZE 512

int fd_can_read(int fd, int timeout) {
    struct pollfd pfd;

    pfd.fd = fd;
    pfd.events = POLLIN;

    if (poll(&pfd, 1, timeout)) {
        if (pfd.revents & POLLIN) {
            return 1;
        }
    }

    return 0;
}

int main(int argv, char **argc) {
    int fd;
    size_t bytes_read;
    char buffer[BUF_SIZE];

    fd = STDIN_FILENO;

    while (1) {
        if (fd_can_read(fd, TIMEOUT)) {
            printf("Can read\n");
            bytes_read = read(fd, buffer, sizeof(buffer));

            printf("Bytes read: %zu\n", bytes_read);
        }
        else {
            printf("Can't read\n");
        }
    }
}

It tries to poll given file descriptor (which is the fd of stdin in this case), and tries to read from it when it's available for reading. Here's an example input file called "input":

stuff to be read

So let's say I run the program, give a few inputs and close it:

./a.out
test
Can read
Bytes read: 5
Can't read
Can't read
...

So lets try reading the input from a file by piping/redirecting the contents of it to stdin of my program:

cat input | ./a.out # Or ./a.out < input
Bytes read: 0
Can read
Bytes read: 0
Can read
...

Now, the poll returns instantly (does not wait for the timeout to run out), and gives the results I was not expecting. I do know poll() does not work on files correctly, but if I'm not mistaken, I'm not reading from a file.


Solution

  • The problem is that poll (just like select) only tell you that a call to e.g. read will not block. It doesn't tell you if there's actually anything to read.

    And if you read the read manual page you will see that when it returns 0 it means end of file (or connection closed for sockets).

    What poll is telling you is that read can be called without blocking, and what read tells you by returning 0 is that there is nothing more to read.

    You will get a similar "false positive" by pressing the end-of-file shortcut key (by default Ctrl-D on POSIX systems like Linux) for the non-piped or -redirected input example.