1/* $FreeBSD$ */
2
3#include <sys/poll.h>
4#include <sys/socket.h>
5#include <sys/stat.h>
6
7#include <err.h>
8#include <fcntl.h>
9#include <signal.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <unistd.h>
13
14#define	FIFONAME	"fifo.tmp"
15#define	FT_END		3
16#define	FT_FIFO		2
17#define	FT_PIPE		0
18#define	FT_SOCKETPAIR	1
19
20static int filetype;
21
22static const char *
23decode_events(int events)
24{
25	char *ncresult;
26	const char *result;
27
28	switch (events) {
29	case POLLIN:
30		result = "POLLIN";
31		break;
32	case POLLHUP:
33		result = "POLLHUP";
34		break;
35	case POLLIN | POLLHUP:
36		result = "POLLIN | POLLHUP";
37		break;
38	default:
39		asprintf(&ncresult, "%#x", events);
40		result = ncresult;
41		break;
42	}
43	return (result);
44}
45
46static void
47report_state(const char *state)
48{
49
50	printf(" %s state %s: ",
51	    filetype == FT_PIPE ? "Pipe" :
52	    filetype == FT_SOCKETPAIR ? "Sock" : "FIFO",
53	    state);
54}
55
56static void
57report(int num, const char *state, int expected, int got, int res,
58    int res_expected)
59{
60
61	if (res != res_expected) {
62		printf("not ok %-2d", num);
63		report_state(state);
64		printf("poll result %d expected %d. ",
65		    res, res_expected);
66	} else {
67		if (expected == got)
68			printf("ok %-2d    ", num);
69		else
70			printf("not ok %-2d", num);
71		report_state(state);
72	}
73	printf("expected %s; got %s\n", decode_events(expected),
74	    decode_events(got));
75	fflush(stdout);
76}
77
78static pid_t cpid;
79static pid_t ppid;
80static volatile sig_atomic_t state;
81
82static void
83catch(int sig __unused)
84{
85
86	state++;
87}
88
89static void
90child(int fd, int num)
91{
92	struct pollfd pfd;
93	int fd2, res;
94	char buf[256];
95
96	if (filetype == FT_FIFO) {
97		fd = open(FIFONAME, O_RDONLY | O_NONBLOCK);
98		if (fd < 0)
99			err(1, "open for read");
100	}
101	pfd.fd = fd;
102	pfd.events = POLLIN;
103
104	if (filetype == FT_FIFO) {
105		if ((res = poll(&pfd, 1, 0)) < 0)
106			err(1, "poll");
107		report(num++, "0", 0, pfd.revents, res, 0);
108	}
109	kill(ppid, SIGUSR1);
110
111	usleep(1);
112	while (state != 1)
113		;
114	if (filetype != FT_FIFO) {
115		/*
116		 * The connection cannot be reestablished.  Use the code that
117		 * delays the read until after the writer disconnects since
118		 * that case is more interesting.
119		 */
120		state = 4;
121		goto state4;
122	}
123	if ((res = poll(&pfd, 1, 0)) < 0)
124		err(1, "poll");
125	report(num++, "1", 0, pfd.revents, res, 0);
126	kill(ppid, SIGUSR1);
127
128	usleep(1);
129	while (state != 2)
130		;
131	if ((res = poll(&pfd, 1, 0)) < 0)
132		err(1, "poll");
133	report(num++, "2", POLLIN, pfd.revents, res, 1);
134	if (read(fd, buf, sizeof buf) != 1)
135		err(1, "read");
136	if ((res = poll(&pfd, 1, 0)) < 0)
137		err(1, "poll");
138	report(num++, "2a", 0, pfd.revents, res, 0);
139	kill(ppid, SIGUSR1);
140
141	usleep(1);
142	while (state != 3)
143		;
144	if ((res = poll(&pfd, 1, 0)) < 0)
145		err(1, "poll");
146	report(num++, "3", POLLHUP, pfd.revents, res, 1);
147	kill(ppid, SIGUSR1);
148
149	/*
150	 * Now we expect a new writer, and a new connection too since
151	 * we read all the data.  The only new point is that we didn't
152	 * start quite from scratch since the read fd is not new.  Check
153	 * startup state as above, but don't do the read as above.
154	 */
155	usleep(1);
156	while (state != 4)
157		;
158state4:
159	if ((res = poll(&pfd, 1, 0)) < 0)
160		err(1, "poll");
161	report(num++, "4", 0, pfd.revents, res, 0);
162	kill(ppid, SIGUSR1);
163
164	usleep(1);
165	while (state != 5)
166		;
167	if ((res = poll(&pfd, 1, 0)) < 0)
168		err(1, "poll");
169	report(num++, "5", POLLIN, pfd.revents, res, 1);
170	kill(ppid, SIGUSR1);
171
172	usleep(1);
173	while (state != 6)
174		;
175	/*
176	 * Now we have no writer, but should still have data from the old
177	 * writer.  Check that we have both a data-readable condition and a
178	 * hangup condition, and that the data can be read in the usual way.
179	 * Since Linux does this, programs must not quit reading when they
180	 * see POLLHUP; they must see POLLHUP without POLLIN (or another
181	 * input condition) before they decide that there is EOF.  gdb-6.1.1
182	 * is an example of a broken program that quits on POLLHUP only --
183	 * see its event-loop.c.
184	 */
185	if ((res = poll(&pfd, 1, 0)) < 0)
186		err(1, "poll");
187	report(num++, "6", POLLIN | POLLHUP, pfd.revents, res, 1);
188	if (read(fd, buf, sizeof buf) != 1)
189		err(1, "read");
190	if ((res = poll(&pfd, 1, 0)) < 0)
191		err(1, "poll");
192	report(num++, "6a", POLLHUP, pfd.revents, res, 1);
193	if (filetype == FT_FIFO) {
194		/*
195		 * Check that POLLHUP is sticky for a new reader and for
196		 * the old reader.
197		 */
198		fd2 = open(FIFONAME, O_RDONLY | O_NONBLOCK);
199		if (fd2 < 0)
200			err(1, "open for read");
201		pfd.fd = fd2;
202		if ((res = poll(&pfd, 1, 0)) < 0)
203			err(1, "poll");
204		report(num++, "6b", POLLHUP, pfd.revents, res, 1);
205		pfd.fd = fd;
206		if ((res = poll(&pfd, 1, 0)) < 0)
207			err(1, "poll");
208		report(num++, "6c", POLLHUP, pfd.revents, res, 1);
209		close(fd2);
210		if ((res = poll(&pfd, 1, 0)) < 0)
211			err(1, "poll");
212		report(num++, "6d", POLLHUP, pfd.revents, res, 1);
213	}
214	close(fd);
215	kill(ppid, SIGUSR1);
216
217	exit(0);
218}
219
220static void
221parent(int fd)
222{
223	usleep(1);
224	while (state != 1)
225		;
226	if (filetype == FT_FIFO) {
227		fd = open(FIFONAME, O_WRONLY | O_NONBLOCK);
228		if (fd < 0)
229			err(1, "open for write");
230	}
231	kill(cpid, SIGUSR1);
232
233	usleep(1);
234	while (state != 2)
235		;
236	if (write(fd, "", 1) != 1)
237		err(1, "write");
238	kill(cpid, SIGUSR1);
239
240	usleep(1);
241	while (state != 3)
242		;
243	if (close(fd) != 0)
244		err(1, "close for write");
245	kill(cpid, SIGUSR1);
246
247	usleep(1);
248	while (state != 4)
249		;
250	if (filetype != FT_FIFO)
251		return;
252	fd = open(FIFONAME, O_WRONLY | O_NONBLOCK);
253	if (fd < 0)
254		err(1, "open for write");
255	kill(cpid, SIGUSR1);
256
257	usleep(1);
258	while (state != 5)
259		;
260	if (write(fd, "", 1) != 1)
261		err(1, "write");
262	kill(cpid, SIGUSR1);
263
264	usleep(1);
265	while (state != 6)
266		;
267	if (close(fd) != 0)
268		err(1, "close for write");
269	kill(cpid, SIGUSR1);
270
271	usleep(1);
272	while (state != 7)
273		;
274}
275
276int
277main(void)
278{
279	int fd[2], num;
280
281	num = 1;
282	printf("1..20\n");
283	fflush(stdout);
284	signal(SIGUSR1, catch);
285	ppid = getpid();
286	for (filetype = 0; filetype < FT_END; filetype++) {
287		switch (filetype) {
288		case FT_FIFO:
289			if (mkfifo(FIFONAME, 0666) != 0)
290				err(1, "mkfifo");
291			fd[0] = -1;
292			fd[1] = -1;
293			break;
294		case FT_SOCKETPAIR:
295			if (socketpair(AF_UNIX, SOCK_STREAM, AF_UNSPEC,
296			    fd) != 0)
297				err(1, "socketpair");
298			break;
299		case FT_PIPE:
300			if (pipe(fd) != 0)
301				err(1, "pipe");
302			break;
303		}
304		state = 0;
305		switch (cpid = fork()) {
306		case -1:
307			err(1, "fork");
308		case 0:
309			(void)close(fd[1]);
310			child(fd[0], num);
311			break;
312		default:
313			(void)close(fd[0]);
314			parent(fd[1]);
315			break;
316		}
317		num += filetype == FT_FIFO ? 12 : 4;
318	}
319	(void)unlink(FIFONAME);
320	return (0);
321}
322