Page MenuHomeFreeBSD

D29757.id88159.diff
No OneTemporary

D29757.id88159.diff

diff --git a/lib/libc/sys/poll.2 b/lib/libc/sys/poll.2
--- a/lib/libc/sys/poll.2
+++ b/lib/libc/sys/poll.2
@@ -126,6 +126,15 @@
should never be present in the
.Fa revents
bitmask at the same time.
+.It POLLRDHUP
+Remote peer closed connection, or shut down writing.
+Unlike
+POLLHUP,
+POLLRDHUP
+must be present in the
+.Fa events
+bitmask to be reported.
+Applies only to stream sockets.
.It POLLNVAL
The file descriptor is not open,
or in capability mode the file descriptor has insufficient rights.
@@ -261,6 +270,9 @@
The
.Fn ppoll
is not specified by POSIX.
+The
+POLLRDHUP
+flag is not specified by POSIX, but is compatible with Linux and illumos.
.Sh HISTORY
The
.Fn poll
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -3567,9 +3567,11 @@
revents |= POLLHUP;
}
}
+ if (so->so_rcv.sb_state & SBS_CANTRCVMORE)
+ revents |= events & POLLRDHUP;
if (revents == 0) {
if (events &
- (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) {
+ (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND | POLLRDHUP)) {
selrecord(td, &so->so_rdsel);
so->so_rcv.sb_flags |= SB_SEL;
}
diff --git a/sys/sys/poll.h b/sys/sys/poll.h
--- a/sys/sys/poll.h
+++ b/sys/sys/poll.h
@@ -71,6 +71,7 @@
#if __BSD_VISIBLE
/* General FreeBSD extension (currently only supported for sockets): */
#define POLLINIGNEOF 0x2000 /* like POLLIN, except ignore EOF */
+#define POLLRDHUP 0x4000 /* half shut down */
#endif
/*
diff --git a/tests/sys/netinet/socket_afinet.c b/tests/sys/netinet/socket_afinet.c
--- a/tests/sys/netinet/socket_afinet.c
+++ b/tests/sys/netinet/socket_afinet.c
@@ -31,6 +31,7 @@
#include <sys/errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <poll.h>
#include <atf-c.h>
@@ -89,12 +90,152 @@
close(sd);
}
+ATF_TC_WITHOUT_HEAD(socket_afinet_poll_no_rdhup);
+ATF_TC_BODY(socket_afinet_poll_no_rdhup, tc)
+{
+ int ss, ss2, cs, rc;
+ struct sockaddr_in sin;
+ struct pollfd pfd;
+ int one = 1;
+
+ /* Verify that we don't expose POLLRDHUP if not requested. */
+
+ /* Server setup. */
+ ss = socket(PF_INET, SOCK_STREAM, 0);
+ ATF_CHECK(ss >= 0);
+ rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
+ ATF_CHECK_EQ(0, rc);
+ bzero(&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_port = htons(6666);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ rc = bind(ss, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_CHECK_EQ(0, rc);
+ rc = listen(ss, 1);
+ ATF_CHECK_EQ(0, rc);
+
+ /* Client connects, server accepts. */
+ cs = socket(PF_INET, SOCK_STREAM, 0);
+ ATF_CHECK(cs >= 0);
+ rc = connect(cs, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_CHECK_EQ(0, rc);
+ ss2 = accept(ss, NULL, NULL);
+ ATF_CHECK(ss2 >= 0);
+
+ /* Server can write, sees only POLLOUT. */
+ pfd.fd = ss2;
+ pfd.events = POLLIN | POLLOUT;
+ rc = poll(&pfd, 1, 0);
+ ATF_CHECK_EQ(1, rc);
+ ATF_CHECK_EQ(POLLOUT, pfd.revents);
+
+ /* Client closes socket! */
+ rc = close(cs);
+ ATF_CHECK_EQ(0, rc);
+
+ /*
+ * Server now sees POLLIN, but not POLLRDHUP because we didn't ask.
+ * Need non-zero timeout to wait for the FIN to arrive and trigger the
+ * socket to become readable.
+ */
+ pfd.fd = ss2;
+ pfd.events = POLLIN;
+ rc = poll(&pfd, 1, 60000);
+ ATF_CHECK_EQ(1, rc);
+ ATF_CHECK_EQ(POLLIN, pfd.revents);
+
+ close(ss2);
+ close(ss);
+}
+
+ATF_TC_WITHOUT_HEAD(socket_afinet_poll_rdhup);
+ATF_TC_BODY(socket_afinet_poll_rdhup, tc)
+{
+ int ss, ss2, cs, rc;
+ struct sockaddr_in sin;
+ struct pollfd pfd;
+ char buffer;
+ int one = 1;
+
+ /* Verify that server sees POLLRDHUP if it asks for it. */
+
+ /* Server setup. */
+ ss = socket(PF_INET, SOCK_STREAM, 0);
+ ATF_CHECK(ss >= 0);
+ rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
+ ATF_CHECK_EQ(0, rc);
+ bzero(&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_port = htons(6666);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ rc = bind(ss, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_CHECK_EQ(0, rc);
+ rc = listen(ss, 1);
+ ATF_CHECK_EQ(0, rc);
+
+ /* Client connects, server accepts. */
+ cs = socket(PF_INET, SOCK_STREAM, 0);
+ ATF_CHECK(cs >= 0);
+ rc = connect(cs, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_CHECK_EQ(0, rc);
+ ss2 = accept(ss, NULL, NULL);
+ ATF_CHECK(ss2 >= 0);
+
+ /* Server can write, so sees POLLOUT. */
+ pfd.fd = ss2;
+ pfd.events = POLLIN | POLLOUT | POLLRDHUP;
+ rc = poll(&pfd, 1, 0);
+ ATF_CHECK_EQ(1, rc);
+ ATF_CHECK_EQ(POLLOUT, pfd.revents);
+
+ /* Client writes two bytes, server reads only one of them. */
+ rc = write(cs, "xx", 2);
+ ATF_CHECK_EQ(2, rc);
+ rc = read(ss2, &buffer, 1);
+ ATF_CHECK_EQ(1, rc);
+
+ /* Server can read, so sees POLLIN. */
+ pfd.fd = ss2;
+ pfd.events = POLLIN | POLLOUT | POLLRDHUP;
+ rc = poll(&pfd, 1, 0);
+ ATF_CHECK_EQ(1, rc);
+ ATF_CHECK_EQ(POLLIN | POLLOUT, pfd.revents);
+
+ /* Client closes socket! */
+ rc = close(cs);
+ ATF_CHECK_EQ(0, rc);
+
+ /*
+ * Server sees Linux-style POLLRDHUP. Note that this is the case even
+ * though one byte of data remains unread.
+ *
+ * This races against the delivery of FIN caused by the close() above.
+ * Sometimes (more likely when run under truss or if another system
+ * call is added in between) it hits the path where sopoll_generic()
+ * immediately sees SBS_CANTRCVMORE, and sometimes it sleeps with flag
+ * SB_SEL so that it's woken up almost immediately and runs again,
+ * which is why we need a non-zero timeout here.
+ */
+ pfd.fd = ss2;
+ pfd.events = POLLRDHUP;
+ rc = poll(&pfd, 1, 60000);
+ ATF_CHECK_EQ(1, rc);
+ ATF_CHECK_EQ(POLLRDHUP, pfd.revents);
+
+ close(ss2);
+ close(ss);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, socket_afinet);
ATF_TP_ADD_TC(tp, socket_afinet_bind_zero);
ATF_TP_ADD_TC(tp, socket_afinet_bind_ok);
+ ATF_TP_ADD_TC(tp, socket_afinet_poll_no_rdhup);
+ ATF_TP_ADD_TC(tp, socket_afinet_poll_rdhup);
return atf_no_error();
}
diff --git a/tools/regression/poll/14/sockpoll.out b/tools/regression/poll/14/sockpoll.out
new file mode 100644
--- /dev/null
+++ b/tools/regression/poll/14/sockpoll.out
@@ -0,0 +1,22 @@
+1..18
+ok 1 state initial 0: expected POLLOUT; got POLLOUT
+ok 2 state initial 1: expected POLLOUT; got POLLOUT
+ok 3 state after large write: expected 0; got 0
+ok 4 state other side after large write: expected POLLIN | POLLOUT; got POLLIN | POLLOUT
+ok 5 state other side after close: expected POLLIN | POLLHUP; got POLLIN | POLLHUP
+not ok 6 state other side after reading input: expected POLLHUP; got POLLIN | POLLHUP
+ok 7 state after shutdown(SHUT_WR): expected POLLOUT; got POLLOUT
+ok 8 state other side after shutdown(SHUT_WR): expected POLLIN | POLLOUT; got POLLIN | POLLOUT
+ok 9 state other side after reading EOF: expected POLLIN | POLLOUT; got POLLIN | POLLOUT
+ok 10 state after data from other side: expected POLLIN | POLLOUT; got POLLIN | POLLOUT
+ok 11 state after writing: expected POLLIN; got POLLIN
+ok 12 state after second shutdown: expected POLLIN | POLLHUP; got POLLIN | POLLHUP
+not ok 13 state after second shutdown: expected POLLHUP; got POLLIN | POLLHUP
+not ok 14 state after close: expected POLLHUP; got POLLIN | POLLHUP
+ok 15 state after shutdown(SHUT_RD): expected POLLIN | POLLOUT; got POLLIN | POLLOUT
+ok 16 state other side after shutdown(SHUT_RD): expected POLLOUT; got POLLOUT
+not ok 17 state after shutdown(SHUT_WR): expected POLLHUP; got POLLIN | POLLHUP
+ok 18 state other side after shutdown(SHUT_WR): expected POLLIN | POLLOUT; got POLLIN | POLLOUT
+ok 19 state other side after shutdown(SHUT_RD): expected POLLOUT; got POLLOUT
+ok 20 state other side after write: expected POLLIN | POLLOUT; got POLLIN | POLLOUT
+ok 21 state other side after shutdown(SHUT_WR): expected POLLIN | POLLOUT | POLLRDHUP; got POLLIN | POLLOUT | POLLRDHUP
diff --git a/tools/regression/poll/l/sockpoll.out b/tools/regression/poll/l/sockpoll.out
new file mode 100644
--- /dev/null
+++ b/tools/regression/poll/l/sockpoll.out
@@ -0,0 +1,22 @@
+1..18
+ok 1 state initial 0: expected POLLOUT; got POLLOUT
+ok 2 state initial 1: expected POLLOUT; got POLLOUT
+ok 3 state after large write: expected 0; got 0
+ok 4 state other side after large write: expected POLLIN | POLLOUT; got POLLIN | POLLOUT
+not ok 5 state other side after close: expected POLLIN | POLLHUP; got POLLIN | POLLOUT | POLLHUP
+not ok 6 state other side after reading input: expected POLLHUP; got POLLIN | POLLOUT | POLLHUP
+ok 7 state after shutdown(SHUT_WR): expected POLLOUT; got POLLOUT
+ok 8 state other side after shutdown(SHUT_WR): expected POLLIN | POLLOUT; got POLLIN | POLLOUT
+ok 9 state other side after reading EOF: expected POLLIN | POLLOUT; got POLLIN | POLLOUT
+ok 10 state after data from other side: expected POLLIN | POLLOUT; got POLLIN | POLLOUT
+ok 11 state after writing: expected POLLIN; got POLLIN
+not ok 12 state after second shutdown: expected POLLIN | POLLHUP; got POLLIN | POLLOUT | POLLHUP
+not ok 13 state after second shutdown: expected POLLHUP; got POLLIN | POLLHUP
+not ok 14 state after close: expected POLLHUP; got POLLIN | POLLOUT | POLLHUP | 8
+ok 15 state after shutdown(SHUT_RD): expected POLLIN | POLLOUT; got POLLIN | POLLOUT
+ok 16 state other side after shutdown(SHUT_RD): expected POLLOUT; got POLLOUT
+not ok 17 state after shutdown(SHUT_WR): expected POLLHUP; got POLLIN | POLLOUT | POLLHUP
+not ok 18 state other side after shutdown(SHUT_WR): expected POLLIN | POLLOUT; got POLLIN | POLLOUT | POLLHUP
+ok 19 state other side after shutdown(SHUT_RD): expected POLLOUT; got POLLOUT
+ok 20 state other side after write: expected POLLIN | POLLOUT; got POLLIN | POLLOUT
+not ok 21 state other side after shutdown(SHUT_WR): expected POLLIN | POLLOUT | POLLRDHUP; got POLLIN | POLLOUT | POLLHUP | POLLRDHUP
diff --git a/tools/regression/poll/sockpoll.c b/tools/regression/poll/sockpoll.c
--- a/tools/regression/poll/sockpoll.c
+++ b/tools/regression/poll/sockpoll.c
@@ -1,61 +1,82 @@
/* $FreeBSD$ */
-#include <sys/poll.h>
+#define _GNU_SOURCE /* expose POLLRDHUP when testing on Linux */
+
#include <sys/socket.h>
#include <sys/stat.h>
#include <err.h>
#include <fcntl.h>
+#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
-static const char *
-decode_events(int events)
+static void
+append(char *out, size_t out_size, const char *s)
+{
+ size_t size = strlen(out);
+
+ snprintf(out + size, out_size - size, "%s", s);
+}
+
+static void
+decode_events(int events, char *out, size_t out_size)
{
- char *ncresult;
- const char *result;
-
- switch (events) {
- case POLLIN:
- result = "POLLIN";
- break;
- case POLLOUT:
- result = "POLLOUT";
- break;
- case POLLIN | POLLOUT:
- result = "POLLIN | POLLOUT";
- break;
- case POLLHUP:
- result = "POLLHUP";
- break;
- case POLLIN | POLLHUP:
- result = "POLLIN | POLLHUP";
- break;
- case POLLOUT | POLLHUP:
- result = "POLLOUT | POLLHUP";
- break;
- case POLLIN | POLLOUT | POLLHUP:
- result = "POLLIN | POLLOUT | POLLHUP";
- break;
- default:
- asprintf(&ncresult, "%#x", events);
- result = ncresult;
- break;
+ int unknown;
+
+ out[0] = 0;
+
+ if (events == 0) {
+ append(out, out_size, "0");
+ return;
+ }
+
+#define DECODE_FLAG(x) \
+ if (events & (x)) { \
+ if (out[0] != 0) \
+ append(out, out_size, " | "); \
+ append(out, out_size, #x); \
+ }
+
+ /* Show the expected flags by name. */
+ DECODE_FLAG(POLLIN);
+ DECODE_FLAG(POLLOUT);
+ DECODE_FLAG(POLLHUP);
+#ifndef POLLRDHUP
+#define KNOWN_FLAGS (POLLIN | POLLOUT | POLLHUP)
+#else
+ DECODE_FLAG(POLLRDHUP);
+#define KNOWN_FLAGS (POLLIN | POLLOUT | POLLHUP | POLLRDHUP);
+#endif
+
+ /* Show any unexpected bits as hex. */
+ unknown = events & ~KNOWN_FLAGS;
+ if (unknown != 0) {
+ char buf[80];
+
+ snprintf(buf, sizeof(buf), "%s%x", out[0] != 0 ? " | " : "",
+ unknown);
+ append(out, out_size, buf);
}
- return (result);
}
static void
report(int num, const char *state, int expected, int got)
{
+ char expected_str[80];
+ char got_str[80];
+
+ decode_events(expected, expected_str, sizeof(expected_str));
+ decode_events(got, got_str, sizeof(got_str));
if (expected == got)
printf("ok %-2d ", num);
else
printf("not ok %-2d", num);
printf(" state %s: expected %s; got %s\n",
- state, decode_events(expected), decode_events(got));
+ state, expected_str, got_str);
fflush(stdout);
}
@@ -198,5 +219,27 @@
close(fd[0]);
close(fd[1]);
+#ifdef POLLRDHUP
+ setup();
+ pfd1.events |= POLLRDHUP;
+ if (shutdown(fd[0], SHUT_RD) == -1)
+ err(1, "shutdown");
+ if (poll(&pfd1, 1, 0) == -1)
+ err(1, "poll");
+ report(num++, "other side after shutdown(SHUT_RD)", POLLOUT, pfd1.revents);
+ if (write(fd[0], "x", 1) != 1)
+ err(1, "write");
+ if (poll(&pfd1, 1, 0) == -1)
+ err(1, "poll");
+ report(num++, "other side after write", POLLIN | POLLOUT, pfd1.revents);
+ if (shutdown(fd[0], SHUT_WR) == -1)
+ err(1, "shutdown");
+ if (poll(&pfd1, 1, 0) == -1)
+ err(1, "poll");
+ report(num++, "other side after shutdown(SHUT_WR)", POLLIN | POLLOUT | POLLRDHUP, pfd1.revents);
+ close(fd[0]);
+ close(fd[1]);
+#endif
+
return (0);
}
diff --git a/usr.bin/truss/syscalls.c b/usr.bin/truss/syscalls.c
--- a/usr.bin/truss/syscalls.c
+++ b/usr.bin/truss/syscalls.c
@@ -726,7 +726,7 @@
static struct xlat poll_flags[] = {
X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR)
X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND)
- X(POLLWRBAND) X(POLLINIGNEOF) XEND
+ X(POLLWRBAND) X(POLLINIGNEOF) X(POLLRDHUP) XEND
};
static struct xlat sigaction_flags[] = {

File Metadata

Mime Type
text/plain
Expires
Sun, Feb 8, 10:50 PM (15 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28507417
Default Alt Text
D29757.id88159.diff (13 KB)

Event Timeline