1 /* Test case written by Bharat Joshi */
2 #include <sys/cdefs.h>
3 __RCSID("$NetBSD: t_fifo.c,v 1.2 2017/01/10 22:36:29 christos Exp $");
4 
5 #include <sys/types.h>
6 #include <sys/wait.h>
7 #include <sys/stat.h>
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <err.h>
16 #include <signal.h>
17 
18 #ifndef STANDALONE
19 #include <atf-c.h>
20 #endif
21 
22 #define FIFO_FILE_PATH       "./fifo_file"
23 #define NUM_MESSAGES         20
24 #define MSG_SIZE             240
25 #define MESSAGE              "I am fine"
26 
27 static int verbose = 0;
28 
29 /*
30  * child_writer
31  *
32  * Function that runs in child context and opens and write to the FIFO.
33  */
34 static void
35 child_writer(void)
36 {
37 	ssize_t rv;
38 	int fd;
39 	size_t count;
40 	char message[MSG_SIZE] = MESSAGE;
41 	static const struct timespec ts = { 0, 10000 };
42 
43 	/* Open the fifo in write-mode */
44 	for (;;) {
45 		fd = open(FIFO_FILE_PATH, O_WRONLY, 0);
46 		if (fd == -1) {
47 			if (errno == EINTR)
48 				continue;
49 			err(1, "Child: can't open fifo in write mode");
50 		}
51 		break;
52 	}
53 
54 	for (count = 0; count < NUM_MESSAGES; count++) {
55 		rv = write(fd, message, MSG_SIZE);
56 		if (rv == -1) {
57 			warn("Child: Failed to write");
58 			break;
59 		}
60 		if (rv != MSG_SIZE)
61 			warnx("Child: wrote only %zd", rv);
62 		nanosleep(&ts, NULL);
63 	}
64 
65 	close(fd);
66 	if (verbose) {
67 		printf("Child: Closed the fifo file\n");
68 		fflush(stdout);
69 	}
70 }
71 
72 /*
73  * _sigchild_handler
74  *
75  * Called when a sigchild is delivered
76  */
77 static void
78 sigchild_handler(int signo)
79 {
80 	if (verbose) {
81 		if (signo == SIGCHLD) {
82 			printf("Got sigchild\n");
83 		} else {
84 			printf("Got %d signal\n", signo);
85 		}
86 		fflush(stdout);
87 	}
88 
89 }
90 
91 static int
92 run(void)
93 {
94 	pid_t pid;
95 	ssize_t rv;
96 	int fd, status;
97 	size_t buf_size = MSG_SIZE;
98 	char buf[MSG_SIZE];
99 	struct sigaction action;
100 	static const struct timespec ts = { 0, 500000000 };
101 
102 	/* Catch sigchild Signal */
103 	memset(&action, 0, sizeof(action));
104 	action.sa_handler = sigchild_handler;
105 	sigemptyset(&action.sa_mask);
106 
107 	if (sigaction(SIGCHLD, &action, NULL) == -1)
108 		err(1, "sigaction");
109 
110 	(void)unlink(FIFO_FILE_PATH);
111 	/* First create a fifo */
112 	if (mkfifo(FIFO_FILE_PATH, S_IRUSR | S_IWUSR) == -1)
113 		err(1, "mkfifo");
114 
115 	switch ((pid = fork())) {
116 	case -1:
117 		err(1, "fork");
118 	case 0:
119 		/* Open the file in write mode so that subsequent read
120 		 * from parent side does not block the parent..
121 		 */
122 		if ((fd = open(FIFO_FILE_PATH, O_WRONLY, 0)) == -1)
123 			err(1, "failed to open fifo");
124 
125 		/* In child */
126 		child_writer();
127 		return 0;
128 
129 	default:
130 		break;
131 	}
132 
133 	if (verbose) {
134 		printf("Child pid is %d\n", pid );
135 		fflush(stdout);
136 	}
137 
138 	/* In parent */
139 	for (;;) {
140 		if ((fd = open(FIFO_FILE_PATH, O_RDONLY, 0)) == -1) {
141 			if (errno == EINTR)
142 				continue;
143 			else
144 				err(1, "Failed to open the fifo in read mode");
145 		}
146 		/* Read mode is opened */
147 		break;
148 
149 	}
150 
151 	nanosleep(&ts, NULL);
152 	if (verbose) {
153 		printf("Was sleeping...\n");
154 		fflush(stdout);
155 	}
156 
157 	for (;;) {
158 		rv = read(fd, buf, buf_size);
159 
160 		if (rv == -1) {
161 			warn("Failed to read");
162 			if (errno == EINTR) {
163 				if (verbose) {
164 					printf("Parent interrupted, "
165 					    "continuing...\n");
166 					fflush(stdout);
167 				}
168 				continue;
169 			}
170 
171 			break;
172 		}
173 
174 		if (rv == 0) {
175 			if (verbose) {
176 				printf("Writers have closed, looks like we "
177 				    "are done\n");
178 				fflush(stdout);
179 			}
180 			break;
181 		}
182 
183 		if (verbose) {
184 			printf("Received %zd bytes message '%s'\n", rv, buf);
185 			fflush(stdout);
186 		}
187 	}
188 
189 	close(fd);
190 
191 	if (verbose) {
192 		printf("We are done.. now reap the child");
193 		fflush(stdout);
194 	}
195 
196 	// Read the child...
197 	while (waitpid(pid, &status, 0) == -1)
198 		if (errno != EINTR) {
199 			warn("Failed to reap the child");
200 			return 1;
201 		}
202 
203 	if (verbose) {
204 		printf("We are done completely\n");
205 		fflush(stdout);
206 	}
207 	return 0;
208 }
209 
210 #ifndef STANDALONE
211 ATF_TC(parent_child);
212 
213 ATF_TC_HEAD(parent_child, tc)
214 {
215         atf_tc_set_md_var(tc, "descr", "Checks that when a fifo is shared "
216 	    "between a reader parent and a writer child, that read will "
217 	    "return EOF, and not get stuck after the child exits");
218 }
219 
220 ATF_TC_BODY(parent_child, tc)
221 {
222         ATF_REQUIRE(run() == 0);
223 }
224 
225 ATF_TP_ADD_TCS(tp)
226 {
227         ATF_TP_ADD_TC(tp, parent_child);
228 
229         return atf_no_error();
230 }
231 #else
232 int
233 main(void)
234 {
235 	verbose = 1;
236 	return run();
237 }
238 #endif
239