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/types.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
main(int argc,char ** argv)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
runtest(char * fifo,int flags,int timeout)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)) == -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
eof_writer(const char * fifo,int flags)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);
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
eoftest(char * fifo,int flags,int timeout)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
dopoll(pid_t writer,int fd,int events,char * str,int timeout)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
doselect(pid_t writer,int rfd,int wfd,int timeout)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
sigalrm(int dummy)370 sigalrm(int dummy)
371 {
372 /* Just cause EINTR */
373 return;
374 }
375
376 void
sigusr1(int dummy)377 sigusr1(int dummy)
378 {
379 return;
380 }
381
382 void
usage(void)383 usage(void)
384 {
385 fprintf(stderr, "usage: %s fifoname\n", __progname);
386 exit(1);
387 }
388