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