9

The manpages say for poll(2):

POLLHUP - Hang up (output only)

POLLNVAL - Invalid request: fd not open (output only)

What exactly is the difference? Writing a simple program shows that POLLNVAL will trigger if I close a file descriptor, then try reading from the closed fd. However, I can't figure out a way to return a POLLHUP.

Community
  • 1
  • 1
Lycus
  • 410
  • 1
  • 6
  • 15
  • 2
    This is what my `man` page says: `POLLHUP The device or socket has been disconnected. This flag is output only, and ignored if present in the input events bitmask. Note that POLLHUP and POLLOUT are mutually exclusive and should never be present in the revents bitmask at the same time.` – Fiddling Bits Aug 05 '14 at 20:02

3 Answers3

18

POLLNVAL is equivalent to EBADF: it means the file descriptor does not actually refer to any open file, i.e. it was closed or never open to begin with. This can never happen except as a result of a programming error or intentional attempt to query whether a file descriptor is invalid. External conditions, such as a peer closing its end of a network socket or pipe, can never close your file descriptor to your end of the socket or pipe. If it could, this would lead to massive vulnerabilities in basically any program using sockets/pipes/etc.

POLLHUP, on the other hand, indicates that your file descriptor is valid, but that it's in a state where:

A device has been disconnected, or a pipe or FIFO has been closed by the last process that had it open for writing. Once set, the hangup state of a FIFO shall persist until some process opens the FIFO for writing or until all read-only file descriptors for the FIFO are closed. This event and POLLOUT are mutually-exclusive; a stream can never be writable if a hangup has occurred. However, this event and POLLIN, POLLRDNORM, POLLRDBAND, or POLLPRI are not mutually-exclusive. This flag is only valid in the revents bitmask; it shall be ignored in the events member.

Source: http://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html

If you want to see POLLHUP, simply open a pipe, close the reading end, and query the writing end with poll.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 1
    On (at least) Linux, closing the read end of a pipe and polling the write end will get you a `POLLERR`, not a `POLLHUP`. I think the reason is that trying to write would cause an error (EPIPE, if the SIGPIPE was ignored). – Jander Jan 29 '15 at 02:12
  • Maybe I should have reading/writing switched then? – R.. GitHub STOP HELPING ICE Jan 29 '15 at 04:00
  • 3
    I'm seeing POLLHUP with reading and writing "switched" in that sense. I open a FIFO for reading and then I poll; this blocks gracefully until something else writes to the FIFO. Then I get POLLIN and read the data. The writer closes its end and I poll again, intending to wait for another writer to open the FIFO and write to it--but instead I immediately receive a POLLHUP. (Or a POLLHUP | POLLIN, if I repeatedly poll until writing does happen.) Guess I need to refresh my file descriptor before polling again, so as to avoid busy-waiting. – mjwach Nov 17 '15 at 21:31
6

If your goal is to write a program that triggers POLLHUP, try something like opening a pipe, closing the write end of it and then poll()ing the read end (code modified from http://www.greenend.org.uk/rjk/tech/poll.html):

#include <unistd.h>
#include <stdio.h>
#include <poll.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int main(void)
{
    int p[2];
    struct pollfd ufd;

    if (pipe(p) < 0) {
        perror("pipe");
        return EXIT_FAILURE;
    }
    if (close(p[1]) < 0) { /* close the write fd */
        perror("close");
        return EXIT_FAILURE;
    }

    memset(&ufd, 0, sizeof ufd);
    ufd.fd = p[0]; /* poll the read fd after the write fd is closed */
    ufd.events = POLLIN;
    if (poll(&ufd, 1, 1000) < 0) {
        perror("poll");
        return EXIT_FAILURE;
    }

    switch(ufd.revents & (POLLIN|POLLHUP)) {
        case POLLIN: printf("POLLIN\n"); break;
        case POLLHUP: printf("POLLHUP\n"); break;
        case POLLIN|POLLHUP: printf("POLLIN|POLLHUP\n"); break;
        case POLLERR: printf("POLLERR\n"); break;
        default: printf("%#x\n", (unsigned)ufd.revents); break;
    }

    return EXIT_SUCCESS;
}

The above prints POLLHUP for me.

Alok Singhal
  • 93,253
  • 21
  • 125
  • 158
  • 2
    The mask in the switch statement means the `POLLERR` and `POLLHUP` cases will never be taken. – Pod Mar 14 '19 at 09:12
  • @Pod you're right. I have no idea what I was thinking when I wrote the above code :(. – Alok Singhal Mar 15 '19 at 04:32
  • Actually, I think I need to change the mask to `(POLLIN|POLLHUP|POLLERR)` in the `switch` statement. – Alok Singhal Mar 15 '19 at 04:35
  • 2
    That won't work either, as if multiple bits are set then a lot of the cases will fail. The options are either 1) enumerate all of the cases, or 2) just have 3 ifs checking for each relevant bit – Pod Mar 18 '19 at 10:54
2

POLLNVAL means that the file descriptor value is invalid. It usually indicates an error in your program, but you can rely on poll returning POLLNVAL if you've closed a file descriptor and you haven't opened any file since then that might have reused the descriptor.

POLLHUP basically means that what's at the other end of the connection has closed its end of the connection. POSIX describes it as

The device has been disconnected. This event and POLLOUT are mutually-exclusive; a stream can never be writable if a hangup has occurred.

This is clear enough for a terminal: the terminal has gone away (same event that generates a SIGHUP: the modem session has been terminated, the terminal emulator window has been closed, etc.). POLLHUP is never sent for a regular file. For pipes and sockets, it depends. Linux sets POLLHUP when the program on the writing end of a pipe closes the pipe, and sets POLLIN|POLLHUP when the other end of a socket closed the socket, but POLLIN only for a socket shutdown. Recent *BSD set POLLIN|POLLUP when the writing end of a pipe closes the pipe, and the behavior for sockets is more variable.

To observe POLLHUP, have your program read from a terminal and close the terminal. Or, on Linux, have your program read from a pipe and close the writing end (e.g. sleep 1 | yourprogram).

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254