How to Handle the Linux Socket Revents Pollerr, Pollhup and Pollnval

How to handle the Linux socket revents POLLERR, POLLHUP and POLLNVAL?

A POLLHUP means the socket is no longer connected. In TCP, this means FIN has been received and sent.

A POLLERR means the socket got an asynchronous error. In TCP, this typically means a RST has been received or sent. If the file descriptor is not a socket, POLLERR might mean the device does not support polling.

For both of the conditions above, the socket file descriptor is still open, and has not yet been closed (but shutdown() may have already been called). A close() on the file descriptor will release resources that are still being reserved on behalf of the socket. In theory, it should be possible to reuse the socket immediately (e.g., with another connect() call).

A POLLNVAL means the socket file descriptor is not open. It would be an error to close() it.

when does poll() return POLLERR

It's implementation dependent. Most applications just treat POLLERR the same as normal readiness, allowing the subsequent operation to fail.

POLLHUP vs POLLNVAL, or what is POLLHUP?

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.

check revents into struct pollfd

According to man(2) poll indeed...

The field revents is an output parameter, filled by the kernel
with the events that actually occurred. The bits returned in
revents can include any of those specified in events, or one of
the values POLLERR, POLLHUP, or POLLNVAL. (These three bits are
meaningless in the events field, and will be set in the revents
field whenever the corresponding condition is true.)

poll.h defines those as

#define POLLIN      0x001       /* There is data to read.  */
#define POLLPRI 0x002 /* There is urgent data to read. */
#define POLLOUT 0x004 /* Writing now will not block. */
// etc...

so armed with this knowledge,

if(!(fds.revents & 1))

is the same as

if(!(fds.revents & POLLIN))

which means "if the "there is data to read" bit is not set", i.e. "if there is no data to read".

Unexpected revents value in poll for non blocking socket connect

The value of poll.revents is a bitfield.

From the header file poll.h

#define POLLIN          0x0001
#define POLLPRI 0x0002
#define POLLOUT 0x0004
#define POLLERR 0x0008
#define POLLHUP 0x0010
#define POLLNVAL 0x0020

So your value 0x28 is comprised of POLLERR|POLLNVAL, which can occur, when the filedescriptor polled for is not open:

  POLLNVAL
Invalid request: fd not open (only returned in revents; ignored in events).

These values can always occur, even when not asked for in poll.events.

As you can see, the socket error is 111 (first call to getsockopt()), which means Connection refused. After this, the filedescriptor is invalid to poll for, because it is no longer connected.

POLLHUP vs. POLLRDHUP?

No, when poll()ing a socket, POLLHUP will signal that the connection was closed in both directions.

POLLRDHUP will be set when the other end has called shutdown(SHUT_WR) or when this end has called shutdown(SHUT_RD), but the connection may still be alive in the other direction.

You can have a look at net/ipv4/tcp.c the kernel source:

        if (sk->sk_shutdown == SHUTDOWN_MASK || state == TCP_CLOSE)
mask |= EPOLLHUP;
if (sk->sk_shutdown & RCV_SHUTDOWN)
mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP;

SHUTDOWN_MASK is RCV_SHUTDOWN|SEND_SHUTDOWN. RCV_SHUTDOWN is set when a FIN packet is received, and SEND_SHUTDOWN is set when a FIN packet is acknowledged by the other end, and the socket moves to the FIN-WAIT2 state.

[except for the TCP_CLOSE part, that snippet is replicated by all protocols; and the whole thing works similarly for unix sockets, etc]

There are other important differences -- POLLRDHUP (unlike POLLHUP) has to be set explicitly in .events in order to be returned in .revents.

And POLLRDHUP only works on sockets, not on fifos/pipes or ttys.



Related Topics



Leave a reply



Submit