xref: /openbsd/regress/sys/fifofs/fifotest.c (revision 771fbea0)
1 /*
2  * Copyright (c) 2004, 2014-2015 Todd C. Miller <millert@openbsd.org>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include <sys/param.h>
18 #include <sys/stat.h>
19 #include <sys/wait.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <poll.h>
23 #include <signal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 
29 #ifndef INFTIM
30 #define	INFTIM	-1
31 #endif
32 
33 void usage(void);
34 void sigalrm(int);
35 void sigusr1(int);
36 void dopoll(pid_t, int, int, char *, int);
37 void doselect(pid_t, int, int, int);
38 void runtest(char *, int, int);
39 void eoftest(char *, int, int);
40 
41 #if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
42     defined(__linux__)
43 extern char *__progname;
44 #else
45 char *__progname;
46 #endif
47 
48 /*
49  * Test FIFOs and poll(2) both with an emtpy and full FIFO.
50  */
51 int
52 main(int argc, char **argv)
53 {
54 	struct sigaction sa;
55 #if !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \
56     !defined(__linux__)
57 	__progname = argv[0];
58 #endif
59 	if (argc != 2)
60 		usage();
61 
62 	/* Just want EINTR from SIGALRM */
63 	sigemptyset(&sa.sa_mask);
64 	sa.sa_flags = 0;
65 	sa.sa_handler = sigalrm;
66 	sigaction(SIGALRM, &sa, NULL);
67 
68 	/* SIGUSR1 is used for synchronization only. */
69 	sa.sa_flags = SA_RESTART;
70 	sa.sa_handler = sigusr1;
71 	sigaction(SIGUSR1, &sa, NULL);
72 
73 	runtest(argv[1], 0, 0);
74 	runtest(argv[1], 0, INFTIM);
75 	runtest(argv[1], O_NONBLOCK, 0);
76 	runtest(argv[1], O_NONBLOCK, INFTIM);
77 	eoftest(argv[1], O_NONBLOCK, INFTIM);
78 
79 	exit(0);
80 }
81 
82 void
83 runtest(char *fifo, int flags, int timeout)
84 {
85 	ssize_t nread;
86 	int fd;
87 	char buf[BUFSIZ];
88 
89 	(void)unlink(fifo);
90 	if (mkfifo(fifo, 0644) != 0) {
91 		printf("mkfifo %s: %s\n", fifo, strerror(errno));
92 		exit(1);
93 	}
94 
95 	/* Note: O_RDWR not required by POSIX */
96 	alarm(2);
97 	if ((fd = open(fifo, O_RDWR | flags, 0644)) == -1) {
98 		printf("open %s: %s\n", fifo, strerror(errno));
99 		exit(1);
100 	}
101 	alarm(0);
102 	(void)unlink(fifo);
103 	printf("\nOpened fifo %s%s\n", fifo,
104 	    (flags & O_NONBLOCK) ? " (nonblocking)" : "");
105 
106 	printf("\nTesting empty FIFO:\n");
107 	dopoll(-1, fd, POLLIN|POLLOUT, "POLLIN|POLLOUT", timeout);
108 	dopoll(-1, fd, POLLIN, "POLLIN", timeout);
109 	dopoll(-1, fd, POLLOUT, "POLLOUT", timeout);
110 	dopoll(-1, fd, 0, "(none)", timeout);
111 	doselect(-1, fd, fd, timeout);
112 	doselect(-1, fd, -1, timeout);
113 	doselect(-1, -1, fd, timeout);
114 	doselect(-1, -1, -1, timeout);
115 
116 	if (write(fd, "test", 4) != 4) {
117 		printf("write error: %s\n", strerror(errno));
118 		exit(1);
119 	}
120 
121 	printf("\nTesting full FIFO:\n");
122 	dopoll(-1, fd, POLLIN|POLLOUT, "POLLIN|POLLOUT", timeout);
123 	dopoll(-1, fd, POLLIN, "POLLIN", timeout);
124 	dopoll(-1, fd, POLLOUT, "POLLOUT", timeout);
125 	dopoll(-1, fd, 0, "(none)", timeout);
126 	doselect(-1, fd, fd, timeout);
127 	doselect(-1, fd, -1, timeout);
128 	doselect(-1, -1, fd, timeout);
129 	doselect(-1, -1, -1, timeout);
130 
131 	if ((nread = read(fd, buf, sizeof(buf))) <= 0) {
132 		printf("read error: %s\n", (nread == 0) ? "EOF" : strerror(errno));
133 		exit(1);
134 	}
135 	buf[nread] = '\0';
136 	printf("\treceived '%s' from FIFO\n", buf);
137 }
138 
139 pid_t
140 eof_writer(const char *fifo, int flags)
141 {
142 	int fd;
143 	pid_t pid;
144 	sigset_t mask, omask;
145 
146 	/* Block SIGUSR1 (in child). */
147 	sigemptyset(&mask);
148 	sigaddset(&mask, SIGUSR1);
149 	sigprocmask(SIG_BLOCK, &mask, &omask);
150 
151 	switch ((pid = fork())) {
152 	case -1:
153 		printf("fork: %s\n", strerror(errno));
154 		return -1;
155 	case 0:
156 		/* child */
157 		break;
158 	default:
159 		/* parent */
160 		sigprocmask(SIG_SETMASK, &omask, NULL);
161 		return pid;
162 	}
163 
164 	/* Wait for reader. */
165 	sigemptyset(&mask);
166 	sigsuspend(&mask);
167 	sigprocmask(SIG_SETMASK, &omask, NULL);
168 
169 	/* connect to FIFO. */
170 	alarm(2);
171 	fd = open(fifo, O_WRONLY | flags, 0644);
172 	alarm(0);
173 	if (fd == -1) {
174 		printf("open %s O_WRONLY: %s\n", fifo, strerror(errno));
175 		return -1;
176 	}
177 
178 	/*
179 	 * We need to give the reader time to call poll() or select()
180 	 * before we close the fd.  This is racey...
181 	 */
182 	usleep(100000);
183 	close(fd);
184 	_exit(0);
185 }
186 
187 void
188 eoftest(char *fifo, int flags, int timeout)
189 {
190 	ssize_t nread;
191 	int fd = -1, pass, status;
192 	pid_t writer;
193 	char buf[BUFSIZ];
194 
195 	/*
196 	 * Test all combinations of select and poll.
197 	 */
198 	for (pass = 0; pass < 16; pass++) {
199 		/*
200 		 * We run each test twice, once with a fresh fifo,
201 		 * and once with a reused one.
202 		 */
203 		if ((pass & 1) == 0) {
204 			if (fd != -1)
205 				close(fd);
206 			(void)unlink(fifo);
207 			if (mkfifo(fifo, 0644) != 0) {
208 				printf("mkfifo %s: %s\n", fifo, strerror(errno));
209 				exit(1);
210 			}
211 
212 			/* XXX - also verify that we get alarm for O_RDWR */
213 			alarm(2);
214 			if ((fd = open(fifo, O_RDONLY | flags, 0644)) == -1) {
215 				printf("open %s: %s\n", fifo, strerror(errno));
216 				exit(1);
217 			}
218 			alarm(0);
219 
220 			printf("\nOpened fifo for reading %s%s\n", fifo,
221 			    (flags & O_NONBLOCK) ? " (nonblocking)" : "");
222 		}
223 
224 		printf("\nTesting EOF FIFO behavior (pass %d):\n", pass);
225 
226 		/*
227 		 * The writer will sleep for a bit to give the reader time
228 		 * to call select() before anything has been written.
229 		 */
230 		writer = eof_writer(fifo, flags);
231 		if (writer == -1)
232 			exit(1);
233 
234 		switch (pass) {
235 		case 0:
236 		case 1:
237 		    dopoll(writer, fd, POLLIN|POLLOUT, "POLLIN|POLLOUT", timeout);
238 		    break;
239 		case 2:
240 		case 3:
241 		    dopoll(writer, fd, POLLIN, "POLLIN", timeout);
242 		    break;
243 		case 4:
244 		case 5:
245 		    dopoll(writer, fd, POLLOUT, "POLLOUT", timeout);
246 		    break;
247 		case 6:
248 		case 7:
249 		    dopoll(writer, fd, 0, "(none)", timeout);
250 		    break;
251 		case 8:
252 		case 9:
253 		    doselect(writer, fd, fd, timeout);
254 		    break;
255 		case 10:
256 		case 11:
257 		    doselect(writer, fd, -1, timeout);
258 		    break;
259 		case 12:
260 		case 13:
261 		    doselect(writer, -1, fd, timeout);
262 		    break;
263 		case 14:
264 		case 15:
265 		    doselect(writer, -1, -1, timeout);
266 		    break;
267 		}
268 		wait(&status);
269 		if ((nread = read(fd, buf, sizeof(buf))) < 0) {
270 			printf("read error: %s\n", strerror(errno));
271 			exit(1);
272 		}
273 		buf[nread] = '\0';
274 		printf("\treceived %s%s%s from FIFO\n", nread ? "'" : "",
275 		    nread ? buf : "EOF", nread ? "'" : "");
276 	}
277 	close(fd);
278 	(void)unlink(fifo);
279 }
280 
281 void
282 dopoll(pid_t writer, int fd, int events, char *str, int timeout)
283 {
284 	struct pollfd pfd;
285 	int nready;
286 
287 	pfd.fd = fd;
288 	pfd.events = events;
289 
290 	printf("\tpoll %s, timeout=%d\n", str, timeout);
291 	pfd.events = events;
292 	if (writer != -1)
293 		kill(writer, SIGUSR1);
294 	alarm(2);
295 	nready = poll(&pfd, 1, timeout);
296 	alarm(0);
297 	if (nready < 0) {
298 		printf("poll: %s\n", strerror(errno));
299 		return;
300 	}
301 	printf("\t\t%d fd(s) ready%s", nready, nready ? ", revents ==" : "");
302 	if (pfd.revents & POLLIN)
303 		printf(" POLLIN");
304 	if (pfd.revents & POLLOUT)
305 		printf(" POLLOUT");
306 	if (pfd.revents & POLLERR)
307 		printf(" POLLERR");
308 	if (pfd.revents & POLLHUP)
309 		printf(" POLLHUP");
310 	if (pfd.revents & POLLNVAL)
311 		printf(" POLLNVAL");
312 	printf("\n");
313 }
314 
315 void
316 doselect(pid_t writer, int rfd, int wfd, int timeout)
317 {
318 	struct timeval tv, *tvp;
319 	fd_set *rfds = NULL, *wfds = NULL;
320 	int nready, maxfd;
321 
322 	if (timeout == INFTIM)
323 		tvp = NULL;
324 	else {
325 		tv.tv_sec = timeout / 1000;
326 		tv.tv_usec = (timeout % 1000) * 1000;
327 		tvp = &tv;
328 	}
329 	maxfd = rfd > wfd ? rfd : wfd;
330 	if (rfd != -1) {
331 		rfds = calloc(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
332 		if (rfds == NULL) {
333 			printf("unable to allocate memory\n");
334 			exit(1);
335 		}
336 		FD_SET(rfd, rfds);
337 	}
338 	if (wfd != -1) {
339 		wfds = calloc(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
340 		if (wfds == NULL) {
341 			printf("unable to allocate memory\n");
342 			exit(1);
343 		}
344 		FD_SET(wfd, wfds);
345 	}
346 
347 	printf("\tselect%s%s, timeout=%d\n", rfds ? " read" : "",
348 	    wfds ? " write" : rfds ? "" : " (none)", timeout);
349 	if (writer != -1)
350 		kill(writer, SIGUSR1);
351 	alarm(2);
352 	nready = select(maxfd + 1, rfds, wfds, NULL, tvp);
353 	alarm(0);
354 	if (nready < 0) {
355 		printf("select: %s\n", strerror(errno));
356 		goto cleanup;
357 	}
358 	printf("\t\t%d fd(s) ready", nready);
359 	if (rfds != NULL && FD_ISSET(rfd, rfds))
360 		printf(", readable");
361 	if (wfds != NULL && FD_ISSET(wfd, wfds))
362 		printf(", writeable");
363 	printf("\n");
364 cleanup:
365 	free(rfds);
366 	free(wfds);
367 }
368 
369 void
370 sigalrm(int dummy)
371 {
372 	/* Just cause EINTR */
373 	return;
374 }
375 
376 void
377 sigusr1(int dummy)
378 {
379 	return;
380 }
381 
382 void
383 usage(void)
384 {
385 	fprintf(stderr, "usage: %s fifoname\n", __progname);
386 	exit(1);
387 }
388