1/*++
2/* NAME
3/*	fifo_rdonly_bug 1
4/* SUMMARY
5/*	fifo server test program
6/* SYNOPSIS
7/*	fifo_rdonly_bug
8/* DESCRIPTION
9/*	fifo_rdonly_bug creates a FIFO and opens it read only. It
10/*	then opens the FIFO for writing, writes one byte, and closes
11/*	the writing end. On Linux Redhat 4.2 and 5.0, and HP-UX 9.05
12/*	and 10.20, select() will report that the FIFO remains readable
13/*	even after multiple read operations.
14/* DIAGNOSTICS
15/*	Problems are reported to the standard error stream.
16/* LICENSE
17/* .ad
18/* .fi
19/*	The Secure Mailer license must be distributed with this software.
20/* AUTHOR(S)
21/*	Wietse Venema
22/*	IBM T.J. Watson Research
23/*	P.O. Box 704
24/*	Yorktown Heights, NY 10598, USA
25/*--*/
26
27#include <sys_defs.h>
28#include <sys/stat.h>
29#include <sys/time.h>
30#include <stdio.h>
31#include <unistd.h>
32#include <stdlib.h>
33#include <fcntl.h>
34#include <string.h>
35
36#define FIFO_PATH	"test-fifo"
37#define TRIGGER_DELAY	5
38
39#define perrorexit(s)   { perror(s); exit(1); }
40
41static void cleanup(void)
42{
43    printf("Removing fifo %s...\n", FIFO_PATH);
44    if (unlink(FIFO_PATH))
45	perrorexit("unlink");
46    printf("Done.\n");
47}
48
49static void perrorcleanup(char *str)
50{
51    perror(str);
52    cleanup();
53    exit(0);
54}
55
56static void readable_event(int fd)
57{
58    char    ch;
59    static int count = 0;
60
61    if (read(fd, &ch, 1) < 0) {
62	perror("read");
63	sleep(1);
64    }
65    if (count++ > 5) {
66	printf("FIFO remains readable after multiple reads.\n");
67	cleanup();
68	exit(1);
69    }
70}
71
72int     main(int unused_argc, char **unused_argv)
73{
74    struct timeval tv;
75    fd_set  read_fds;
76    fd_set  except_fds;
77    int     fd;
78    int     fd2;
79
80    (void) unlink(FIFO_PATH);
81
82    printf("Create fifo %s...\n", FIFO_PATH);
83    if (mkfifo(FIFO_PATH, 0600) < 0)
84	perrorexit("mkfifo");
85
86    printf("Open fifo %s, read-only mode...\n", FIFO_PATH);
87    if ((fd = open(FIFO_PATH, O_RDONLY | O_NONBLOCK, 0)) < 0)
88	perrorcleanup("open");
89
90    printf("Write one byte to the fifo, then close it...\n");
91    if ((fd2 = open(FIFO_PATH, O_WRONLY, 0)) < 0)
92	perrorcleanup("open fifo O_WRONLY");
93    if (write(fd2, "", 1) < 1)
94	perrorcleanup("write one byte to fifo");
95    if (close(fd2) < 0)
96	perrorcleanup("close fifo");
97
98    printf("Selecting the fifo for readability...\n");
99
100    for (;;) {
101	FD_ZERO(&read_fds);
102	FD_SET(fd, &read_fds);
103	FD_ZERO(&except_fds);
104	FD_SET(fd, &except_fds);
105	tv.tv_sec = 1;
106	tv.tv_usec = 0;
107
108	switch (select(fd + 1, &read_fds, (fd_set *) 0, &except_fds, &tv)) {
109	case -1:
110	    perrorexit("select");
111	default:
112	    if (FD_ISSET(fd, &except_fds)) {
113		printf("Exceptional fifo condition! You are not normal!\n");
114		readable_event(fd);
115	    } else if (FD_ISSET(fd, &read_fds)) {
116		printf("Readable fifo condition\n");
117		readable_event(fd);
118	    }
119	    break;
120	case 0:
121	    printf("The fifo is not readable. You're normal.\n");
122	    cleanup();
123	    exit(0);
124	    break;
125	}
126    }
127}
128