root/tests/fs/fifofs/t_fifo.c
/* 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