POLLHUP vs POLLNVAL, or what is POLLHUP? [duplicate]
Asked Answered
S

3

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.

Sponger answered 5/8, 2014 at 19:53 Comment(1)
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.Gerik
C
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.

Carvey answered 5/8, 2014 at 20:13 Comment(3)
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).Kodok
Maybe I should have reading/writing switched then?Carvey
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.Acinus
H
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.

Hekate answered 5/8, 2014 at 20:15 Comment(4)
The mask in the switch statement means the POLLERR and POLLHUP cases will never be taken.Venomous
@Venomous you're right. I have no idea what I was thinking when I wrote the above code :(.Hekate
Actually, I think I need to change the mask to (POLLIN|POLLHUP|POLLERR) in the switch statement.Hekate
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 bitVenomous
E
3

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).

Eldreda answered 5/8, 2014 at 20:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.