--- /dev/null
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define MAX_ERROR 5
+#include "common.c"
+
+void copy_subtests(void);
+void test_pipe_cloexec(void);
+void test_pipe_flag_setting(void);
+void test_pipe_nonblock(void);
+void test_pipe_normal(void);
+void test_pipe_nosigpipe(void);
+void alarm_handler(int sig);
+void pipe_handler(int sig);
+
+static int seen_pipe_signal = 0;
+static int seen_alarm_signal = 0;
+
+void
+alarm_handler(int sig)
+{
+ if (seen_pipe_signal == 0)
+ seen_pipe_signal = -1;
+ seen_alarm_signal = 1;
+}
+
+void
+pipe_handler(int sig)
+{
+ seen_pipe_signal = 1;
+}
+
+void
+copy_subtests()
+{
+ char *subtests[] = { "t68a", "t68b" };
+ char copy_cmd[8 + PATH_MAX + 1];
+ int i, no_tests;
+
+ no_tests = sizeof(subtests) / sizeof(char *);
+
+ for (i = 0; i < no_tests; i++) {
+ snprintf(copy_cmd, 8 + PATH_MAX, "cp ../%s .", subtests[i]);
+ system(copy_cmd);
+ }
+}
+
+void
+test_pipe_normal()
+{
+/* Verify pipe2 creates pipes that behave like a normal pipe */
+
+ int pipes[2];
+ char buf_in[1], buf_out[1];
+ pid_t pid;
+
+ subtest = 2;
+
+ if (pipe2(pipes, 0) != 0) e(1);
+
+ buf_out[0] = 'T';
+ if (write(pipes[1], buf_out, sizeof(buf_out)) != sizeof(buf_out)) e(2);
+ if (read(pipes[0], buf_in, sizeof(buf_in)) != sizeof(buf_in)) e(3);
+ if (buf_out[0] != buf_in[0]) e(4);
+
+ /* When we close the write end, reading should fail */
+ if (close(pipes[1]) != 0) e(5);
+ if (read(pipes[0], buf_in, sizeof(buf_in)) != 0) e(6);
+
+ /* Let's retry that experiment the other way around. Install a signal
+ * handler to catch SIGPIPE. Install an alarm handler to make sure
+ * this test finishes in finite time. */
+ if (pipe2(pipes, 0) != 0) e(7);
+ signal(SIGPIPE, pipe_handler);
+ signal(SIGALRM, alarm_handler);
+ seen_pipe_signal = 0;
+ seen_alarm_signal = 0;
+ alarm(1);
+ if (close(pipes[0]) != 0) e(8);
+ if (write(pipes[1], buf_out, sizeof(buf_out)) != -1) e(9);
+ while (seen_pipe_signal == 0)
+ ;
+ if (seen_pipe_signal != 1) e(10);
+ if (close(pipes[1]) != 0) e(11);
+
+ /* Collect alarm signal */
+ while (seen_alarm_signal == 0)
+ ;
+
+ if (pipe2(pipes, 0) != 0) e(12);
+
+ /* Now fork and verify we can write to the pipe */
+ pid = fork();
+ if (pid < 0) e(13);
+ if (pid == 0) {
+ /* We're the child */
+ char fd_buf[2];
+
+ /* Verify we can still write a byte into the pipe */
+ if (write(pipes[1], buf_out, sizeof(buf_out)) != 1) e(14);
+
+ snprintf(fd_buf, sizeof(fd_buf), "%d", pipes[1]);
+ execl("./t68a", "t68a", fd_buf, NULL);
+
+ exit(1); /* Should not be reached */
+ } else {
+ /* We're the parent */
+ int result;
+
+ if (waitpid(pid, &result, 0) == -1) e(15);
+ if (WEXITSTATUS(result) != 0) e(16);
+ }
+
+ if (close(pipes[0]) != 0) e(17);
+ if (close(pipes[1]) != 0) e(18);
+}
+
+void
+test_pipe_cloexec()
+{
+/* Open a pipe with O_CLOEXEC */
+ int flags;
+ int pipes[2];
+ pid_t pid;
+ char buf_in[1], buf_out[1];
+
+ subtest = 3;
+
+ if (pipe2(pipes, O_CLOEXEC) != 0) e(1);
+
+ /* Verify O_CLOEXEC flag is set */
+ flags = fcntl(pipes[0], F_GETFD);
+ if (flags < 0) e(2);
+ if (!(flags & FD_CLOEXEC)) e(3);
+
+ pid = fork();
+ if (pid < 0) e(4);
+ if (pid == 0) {
+ /* We're the child */
+ char fd_buf[2];
+
+ /* Verify we can still write a byte into the pipe */
+ buf_in[0] = 0;
+ buf_out[0] = 'T';
+ if (write(pipes[1], buf_out, sizeof(buf_out)) != 1) e(5);
+ if (read(pipes[0], buf_in, sizeof(buf_in)) != 1) e(6);
+ if (buf_out[0] != buf_in[0]) e(7);
+
+ /* Verify FD_CLOEXEC flag is still set */
+ flags = fcntl(pipes[0], F_GETFD);
+ if (flags < 0) e(8);
+ if (!(flags & FD_CLOEXEC)) e(9);
+
+ snprintf(fd_buf, sizeof(fd_buf), "%d", pipes[0]);
+ execl("./t68b", "t68b", fd_buf, NULL);
+
+ exit(1); /* Should not be reached */
+ } else {
+ /* We're the parent */
+ int result;
+
+ if (waitpid(pid, &result, 0) == -1) e(10);
+ if (WEXITSTATUS(result) != 0) e(11);
+ }
+
+ /* Eventhough our child's pipe should've been closed upon exec, our
+ * pipe should still be functioning.
+ */
+ buf_in[0] = 0;
+ buf_out[0] = 't';
+ if (write(pipes[1], buf_out, sizeof(buf_out)) != sizeof(buf_out)) e(12);
+ if (read(pipes[0], buf_in, sizeof(buf_in)) != sizeof(buf_in)) e(13);
+ if (buf_out[0] != buf_in[0]) e(14);
+
+ if (close(pipes[0]) != 0) e(15);
+ if (close(pipes[1]) != 0) e(16);
+}
+
+void
+test_pipe_nonblock()
+{
+/* Open a pipe with O_NONBLOCK */
+ char *buf_in, *buf_out;
+ int pipes[2];
+ size_t pipe_size;
+
+ subtest = 4;
+
+ if (pipe2(pipes, O_NONBLOCK) != 0) e(1);
+ if ((pipe_size = fpathconf(pipes[0], _PC_PIPE_BUF)) == -1) e(2);
+ buf_in = calloc(2, pipe_size); /* Allocate twice the buffer size */
+ if (buf_in == NULL) e(3);
+ buf_out = calloc(2, pipe_size); /* Idem dito for output buffer */
+ if (buf_out == NULL) e(4);
+
+ /* According to POSIX, a pipe with O_NONBLOCK set shall never block.
+ * When we attempt to write PIPE_BUF or less bytes, and there is
+ * sufficient space available, write returns nbytes. Else write will
+ * return -1 and not transfer any data.
+ */
+ if (write(pipes[1], buf_out, 1) != 1) e(5); /* Write 1 byte */
+ if (write(pipes[1], buf_out, pipe_size) != -1) e(6); /* Can't fit */
+ if (errno != EAGAIN) e(7);
+
+ /* When writing more than PIPE_BUF bytes and when at least 1 byte can
+ * be tranferred, return the number of bytes written. We've written 1
+ * byte, so there are PIPE_BUF - 1 bytes left. */
+ if (write(pipes[1], buf_out, pipe_size + 1) != pipe_size - 1) e(8);
+
+ /* Read out all data and try again. This time we should be able to
+ * write PIPE_BUF bytes. */
+ if (read(pipes[0], buf_in, pipe_size) != pipe_size) e(9);
+ if (read(pipes[0], buf_in, 1) != -1) e(10); /* Empty, can't read */
+ if (errno != EAGAIN) e(11);
+ if (write(pipes[1], buf_out, pipe_size + 1) != pipe_size) e(12);
+ if (close(pipes[0]) != 0) e(13);
+ if (close(pipes[1]) != 0) e(14);
+ free(buf_in);
+ free(buf_out);
+}
+
+void
+test_pipe_nosigpipe(void)
+{
+/* Let's retry the writing to pipe without readers experiment. This time we set
+ * the O_NOSIGPIPE flag to prevent getting a signal. */
+ int pipes[2];
+ char buf_out[1];
+
+ subtest = 5;
+
+ if (pipe2(pipes, O_NOSIGPIPE) != 0) e(7);
+ signal(SIGPIPE, pipe_handler);
+ signal(SIGALRM, alarm_handler);
+ seen_pipe_signal = 0;
+ seen_alarm_signal = 0;
+ alarm(1);
+ if (close(pipes[0]) != 0) e(8);
+ if (write(pipes[1], buf_out, sizeof(buf_out)) != -1) e(9);
+
+ /* Collect alarm signal */
+ while (seen_alarm_signal == 0)
+ ;
+ if (errno != EPIPE) e(10);
+ if (seen_pipe_signal != -1) e(11); /* Alarm sig handler set it to -1 */
+ if (close(pipes[1]) != 0) e(12);
+}
+
+void
+test_pipe_flag_setting()
+{
+ int pipes[2];
+
+ subtest = 1;
+
+ /* Create standard pipe with no flags and verify they're off */
+ if (pipe2(pipes, 0) != 0) e(1);
+ if (fcntl(pipes[0], F_GETFD) != 0) e(2);
+ if (fcntl(pipes[1], F_GETFD) != 0) e(3);
+ if (fcntl(pipes[0], F_GETFL) & O_NONBLOCK) e(4);
+ if (fcntl(pipes[1], F_GETFL) & O_NONBLOCK) e(5);
+ if (fcntl(pipes[0], F_GETNOSIGPIPE) != -1) e(6);
+ if (fcntl(pipes[1], F_GETNOSIGPIPE) != -1) e(7);
+ if (close(pipes[0]) != 0) e(8);
+ if (close(pipes[1]) != 0) e(9);
+
+ /* Create pipe with all flags and verify they're on */
+ if (pipe2(pipes, O_CLOEXEC|O_NONBLOCK|O_NOSIGPIPE) != 0) e(10);
+ if (fcntl(pipes[0], F_GETFD) != FD_CLOEXEC) e(11);
+ if (fcntl(pipes[1], F_GETFD) != FD_CLOEXEC) e(12);
+ if (!(fcntl(pipes[0], F_GETFL) & O_NONBLOCK)) e(13);
+ if (!(fcntl(pipes[1], F_GETFL) & O_NONBLOCK)) e(14);
+ if (fcntl(pipes[0], F_GETNOSIGPIPE) == -1) e(15);
+ if (fcntl(pipes[1], F_GETNOSIGPIPE) == -1) e(16);
+ if (close(pipes[0]) != 0) e(17);
+ if (close(pipes[1]) != 0) e(18);
+}
+
+int
+main(int argc, char *argv[])
+{
+ start(68);
+ copy_subtests();
+ test_pipe_flag_setting();
+ test_pipe_normal();
+ test_pipe_cloexec();
+ test_pipe_nonblock();
+ test_pipe_nosigpipe();
+ quit();
+ return(-1); /* Unreachable */
+}
+