1
2#define _GNU_SOURCE         /* expose POLLRDHUP when testing on Linux */
3
4#include <sys/socket.h>
5#include <sys/stat.h>
6
7#include <err.h>
8#include <fcntl.h>
9#include <poll.h>
10#include <signal.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <unistd.h>
15
16static void
17append(char *out, size_t out_size, const char *s)
18{
19	size_t size = strlen(out);
20
21	snprintf(out + size, out_size - size, "%s", s);
22}
23
24static void
25decode_events(int events, char *out, size_t out_size)
26{
27	int unknown;
28
29	out[0] = 0;
30
31	if (events == 0) {
32		append(out, out_size, "0");
33		return;
34	}
35
36#define DECODE_FLAG(x) \
37	if (events & (x)) { \
38		if (out[0] != 0) \
39			append(out, out_size, " | "); \
40		append(out, out_size, #x); \
41	}
42
43	/* Show the expected flags by name. */
44	DECODE_FLAG(POLLIN);
45	DECODE_FLAG(POLLOUT);
46	DECODE_FLAG(POLLHUP);
47#ifndef POLLRDHUP
48#define KNOWN_FLAGS (POLLIN | POLLOUT | POLLHUP)
49#else
50	DECODE_FLAG(POLLRDHUP);
51#define KNOWN_FLAGS (POLLIN | POLLOUT | POLLHUP | POLLRDHUP);
52#endif
53
54	/* Show any unexpected bits as hex. */
55	unknown = events & ~KNOWN_FLAGS;
56	if (unknown != 0) {
57		char buf[80];
58
59		snprintf(buf, sizeof(buf), "%s%x", out[0] != 0 ? " | " : "",
60			unknown);
61		append(out, out_size, buf);
62	}
63}
64
65static void
66report(int num, const char *state, int expected, int got)
67{
68	char expected_str[80];
69	char got_str[80];
70
71	decode_events(expected, expected_str, sizeof(expected_str));
72	decode_events(got, got_str, sizeof(got_str));
73	if (expected == got)
74		printf("ok %-2d    ", num);
75	else
76		printf("not ok %-2d", num);
77	printf(" state %s: expected %s; got %s\n",
78	    state, expected_str, got_str);
79	fflush(stdout);
80}
81
82static int
83set_nonblocking(int sck)
84{
85	int flags;
86
87	flags = fcntl(sck, F_GETFL, 0);
88	flags |= O_NONBLOCK;
89
90	if (fcntl(sck, F_SETFL, flags))
91		return -1;
92
93	return 0;
94}
95
96static char largeblock[1048576]; /* should be more than AF_UNIX sockbuf size */
97static int fd[2];
98static struct pollfd pfd0;
99static struct pollfd pfd1;
100
101void
102setup(void)
103{
104	if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0)
105		err(1, "socketpair");
106	if (set_nonblocking(fd[0]) == -1)
107		err(1, "fcntl");
108	if (set_nonblocking(fd[1]) == -1)
109		err(1, "fcntl");
110	pfd0.fd = fd[0];
111	pfd0.events = POLLIN | POLLOUT;
112	pfd1.fd = fd[1];
113	pfd1.events = POLLIN | POLLOUT;
114}
115
116int
117main(void)
118{
119	int num;
120
121	num = 1;
122	printf("1..18\n");
123	fflush(stdout);
124
125	/* Large write with close */
126	setup();
127	if (poll(&pfd0, 1, 0) == -1)
128		err(1, "poll");
129	report(num++, "initial 0", POLLOUT, pfd0.revents);
130	if (poll(&pfd1, 1, 0) == -1)
131		err(1, "poll");
132	report(num++, "initial 1", POLLOUT, pfd1.revents);
133	if (write(fd[0], largeblock, sizeof(largeblock)) == -1)
134		err(1, "write");
135	if (poll(&pfd0, 1, 0) == -1)
136		err(1, "poll");
137	report(num++, "after large write", 0, pfd0.revents);
138	if (poll(&pfd1, 1, 0) == -1)
139		err(1, "poll");
140	report(num++, "other side after large write", POLLIN | POLLOUT, pfd1.revents);
141	close(fd[0]);
142	if (poll(&pfd1, 1, 0) == -1)
143		err(1, "poll");
144	report(num++, "other side after close", POLLIN | POLLHUP, pfd1.revents);
145	if (read(fd[1], largeblock, sizeof(largeblock)) == -1)
146		err(1, "read");
147	if (poll(&pfd1, 1, 0) == -1)
148		err(1, "poll");
149	report(num++, "other side after reading input", POLLHUP, pfd1.revents);
150	close(fd[1]);
151
152	/* With shutdown(SHUT_WR) */
153	setup();
154	if (shutdown(fd[0], SHUT_WR) == -1)
155		err(1, "shutdown");
156	if (poll(&pfd0, 1, 0) == -1)
157		err(1, "poll");
158	report(num++, "after shutdown(SHUT_WR)", POLLOUT, pfd0.revents);
159	if (poll(&pfd1, 1, 0) == -1)
160		err(1, "poll");
161	report(num++, "other side after shutdown(SHUT_WR)", POLLIN | POLLOUT, pfd1.revents);
162	switch (read(fd[1], largeblock, sizeof(largeblock))) {
163		case 0:
164			break;
165		case -1:
166			err(1, "read after other side shutdown");
167			break;
168		default:
169			errx(1, "kernel made up data that was never written");
170	}
171	if (poll(&pfd1, 1, 0) == -1)
172		err(1, "poll");
173	report(num++, "other side after reading EOF", POLLIN | POLLOUT, pfd1.revents);
174	if (write(fd[1], largeblock, sizeof(largeblock)) == -1)
175		err(1, "write");
176	if (poll(&pfd0, 1, 0) == -1)
177		err(1, "poll");
178	report(num++, "after data from other side", POLLIN | POLLOUT, pfd0.revents);
179	if (poll(&pfd1, 1, 0) == -1)
180		err(1, "poll");
181	report(num++, "after writing", POLLIN, pfd1.revents);
182	if (shutdown(fd[1], SHUT_WR) == -1)
183		err(1, "shutdown second");
184	if (poll(&pfd0, 1, 0) == -1)
185		err(1, "poll");
186	report(num++, "after second shutdown", POLLIN | POLLHUP, pfd0.revents);
187	if (poll(&pfd1, 1, 0) == -1)
188		err(1, "poll");
189	report(num++, "after second shutdown", POLLHUP, pfd1.revents);
190	close(fd[0]);
191	if (poll(&pfd1, 1, 0) == -1)
192		err(1, "poll");
193	report(num++, "after close", POLLHUP, pfd1.revents);
194	close(fd[1]);
195
196	/*
197	 * With shutdown(SHUT_RD)
198	 * Note that shutdown(SHUT_WR) is passed to the peer, but
199	 * shutdown(SHUT_RD) is not.
200	 */
201	setup();
202	if (shutdown(fd[0], SHUT_RD) == -1)
203		err(1, "shutdown");
204	if (poll(&pfd0, 1, 0) == -1)
205		err(1, "poll");
206	report(num++, "after shutdown(SHUT_RD)", POLLIN | POLLOUT, pfd0.revents);
207	if (poll(&pfd1, 1, 0) == -1)
208		err(1, "poll");
209	report(num++, "other side after shutdown(SHUT_RD)", POLLOUT, pfd1.revents);
210	if (shutdown(fd[0], SHUT_WR) == -1)
211		err(1, "shutdown");
212	if (poll(&pfd0, 1, 0) == -1)
213		err(1, "poll");
214	report(num++, "after shutdown(SHUT_WR)", POLLHUP, pfd0.revents);
215	if (poll(&pfd1, 1, 0) == -1)
216		err(1, "poll");
217	report(num++, "other side after shutdown(SHUT_WR)", POLLIN | POLLOUT, pfd1.revents);
218	close(fd[0]);
219	close(fd[1]);
220
221#ifdef POLLRDHUP
222	setup();
223	pfd1.events |= POLLRDHUP;
224	if (shutdown(fd[0], SHUT_RD) == -1)
225		err(1, "shutdown");
226	if (poll(&pfd1, 1, 0) == -1)
227		err(1, "poll");
228	report(num++, "other side after shutdown(SHUT_RD)", POLLOUT, pfd1.revents);
229	if (write(fd[0], "x", 1) != 1)
230		err(1, "write");
231	if (poll(&pfd1, 1, 0) == -1)
232		err(1, "poll");
233	report(num++, "other side after write", POLLIN | POLLOUT, pfd1.revents);
234	if (shutdown(fd[0], SHUT_WR) == -1)
235		err(1, "shutdown");
236	if (poll(&pfd1, 1, 0) == -1)
237		err(1, "poll");
238	report(num++, "other side after shutdown(SHUT_WR)", POLLIN | POLLOUT | POLLRDHUP, pfd1.revents);
239	close(fd[0]);
240	close(fd[1]);
241#endif
242
243	return (0);
244}
245