Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144341690
D29757.id88159.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D29757.id88159.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D29757: POLLRDHUP
Attached
Detach File
Event Timeline
Log In to Comment