/* Test case written by Bharat Joshi */
#include <sys/cdefs.h>
__RCSID("$NetBSD: t_fifo.c,v 1.2 2017/01/10 22:36:29 christos Exp $");
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <err.h>
#include <signal.h>
#ifndef STANDALONE
#include <atf-c.h>
#endif
#define FIFO_FILE_PATH "./fifo_file"
#define NUM_MESSAGES 20
#define MSG_SIZE 240
#define MESSAGE "I am fine"
static int verbose = 0;
/*
* child_writer
*
* Function that runs in child context and opens and write to the FIFO.
*/
static void
child_writer(void)
{
ssize_t rv;
int fd;
size_t count;
char message[MSG_SIZE] = MESSAGE;
static const struct timespec ts = { 0, 10000 };
/* Open the fifo in write-mode */
for (;;) {
fd = open(FIFO_FILE_PATH, O_WRONLY, 0);
if (fd == -1) {
if (errno == EINTR)
continue;
err(1, "Child: can't open fifo in write mode");
}
break;
}
for (count = 0; count < NUM_MESSAGES; count++) {
rv = write(fd, message, MSG_SIZE);
if (rv == -1) {
warn("Child: Failed to write");
break;
}
if (rv != MSG_SIZE)
warnx("Child: wrote only %zd", rv);
nanosleep(&ts, NULL);
}
close(fd);
if (verbose) {
printf("Child: Closed the fifo file\n");
fflush(stdout);
}
}
/*
* _sigchild_handler
*
* Called when a sigchild is delivered
*/
static void
sigchild_handler(int signo)
{
if (verbose) {
if (signo == SIGCHLD) {
printf("Got sigchild\n");
} else {
printf("Got %d signal\n", signo);
}
fflush(stdout);
}
}
static int
run(void)
{
pid_t pid;
ssize_t rv;
int fd, status;
size_t buf_size = MSG_SIZE;
char buf[MSG_SIZE];
struct sigaction action;
static const struct timespec ts = { 0, 500000000 };
/* Catch sigchild Signal */
memset(&action, 0, sizeof(action));
action.sa_handler = sigchild_handler;
sigemptyset(&action.sa_mask);
if (sigaction(SIGCHLD, &action, NULL) == -1)
err(1, "sigaction");
(void)unlink(FIFO_FILE_PATH);
/* First create a fifo */
if (mkfifo(FIFO_FILE_PATH, S_IRUSR | S_IWUSR) == -1)
err(1, "mkfifo");
switch ((pid = fork())) {
case -1:
err(1, "fork");
case 0:
/* Open the file in write mode so that subsequent read
* from parent side does not block the parent..
*/
if ((fd = open(FIFO_FILE_PATH, O_WRONLY, 0)) == -1)
err(1, "failed to open fifo");
/* In child */
child_writer();
return 0;
default:
break;
}
if (verbose) {
printf("Child pid is %d\n", pid );
fflush(stdout);
}
/* In parent */
for (;;) {
if ((fd = open(FIFO_FILE_PATH, O_RDONLY, 0)) == -1) {
if (errno == EINTR)
continue;
else
err(1, "Failed to open the fifo in read mode");
}
/* Read mode is opened */
break;
}
nanosleep(&ts, NULL);
if (verbose) {
printf("Was sleeping...\n");
fflush(stdout);
}
for (;;) {
rv = read(fd, buf, buf_size);
if (rv == -1) {
warn("Failed to read");
if (errno == EINTR) {
if (verbose) {
printf("Parent interrupted, "
"continuing...\n");
fflush(stdout);
}
continue;
}
break;
}
if (rv == 0) {
if (verbose) {
printf("Writers have closed, looks like we "
"are done\n");
fflush(stdout);
}
break;
}
if (verbose) {
printf("Received %zd bytes message '%s'\n", rv, buf);
fflush(stdout);
}
}
close(fd);
if (verbose) {
printf("We are done.. now reap the child");
fflush(stdout);
}
// Read the child...
while (waitpid(pid, &status, 0) == -1)
if (errno != EINTR) {
warn("Failed to reap the child");
return 1;
}
if (verbose) {
printf("We are done completely\n");
fflush(stdout);
}
return 0;
}
#ifndef STANDALONE
ATF_TC(parent_child);
ATF_TC_HEAD(parent_child, tc)
{
atf_tc_set_md_var(tc, "descr", "Checks that when a fifo is shared "
"between a reader parent and a writer child, that read will "
"return EOF, and not get stuck after the child exits");
}
ATF_TC_BODY(parent_child, tc)
{
ATF_REQUIRE(run() == 0);
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, parent_child);
return atf_no_error();
}
#else
int
main(void)
{
verbose = 1;
return run();
}
#endif