diff --git a/test/Makefile b/test/Makefile index bdb06e088..623ee8675 100644 --- a/test/Makefile +++ b/test/Makefile @@ -23,11 +23,12 @@ OBJS.test57=test57loop.o 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \ 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \ 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 \ -61 62 64 65 66 67 +61 62 64 65 66 67 68 PROG+= test$(t) .endfor -PROG+= t10a t11a t11b t40a t40b t40c t40d t40e t40f t60a t60b t67a t67b +PROG+= t10a t11a t11b t40a t40b t40c t40d t40e t40f t60a t60b \ + t67a t67b t68a t68b .include @@ -53,5 +54,5 @@ clean: .PHONY .MAKE $(MAKE) -C select clean rm -rf *.o *.s *.bak test? test?? t10a t11a t11b \ t40a t40b t40c t40d t40e t40f \ - t60a t60b t67a t67b \ + t60a t60b t67a t67b t68a t68b \ DIR* diff --git a/test/run b/test/run index df2ca9912..4c81951a4 100755 --- a/test/run +++ b/test/run @@ -14,7 +14,7 @@ badones= # list of tests that failed tests=" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \ 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \ 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 \ - 61 62 63 64 65 66 67\ + 61 62 63 64 65 66 67 68\ sh1.sh sh2.sh interp.sh" tests_no=`expr 0` diff --git a/test/t68a.c b/test/t68a.c new file mode 100644 index 000000000..d39835856 --- /dev/null +++ b/test/t68a.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + int fd, fd_parent; + char buf[1]; + + if (argc != 2) { + return 1; + } + + fd_parent = atoi(argv[1]); + + /* If we open a new file, the fd we obtain should be fd_parent + 1 */ + fd = open("open_plusplus_fd", O_CREAT|O_RDWR, 0660); + if (fd != fd_parent + 1) { + return 2; + } + + /* Also, writing to fd_parent should succeed */ + if (write(fd_parent, buf, sizeof(buf)) <= 0) { + return 3; + } + + return 0; +} + diff --git a/test/t68b.c b/test/t68b.c new file mode 100644 index 000000000..ccdebd5ce --- /dev/null +++ b/test/t68b.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + int fd, fd_parent; + char buf[1]; + + if (argc != 2) { + return 1; + } + + fd_parent = atoi(argv[1]); + + /* Writing to fd_parent should fail as it has to be closed at this + * point */ + if (write(fd_parent, buf, sizeof(buf)) != -1) { + return 2; + } + if (errno != EBADF) { + return 3; + } + + /* If we open a new file, the fd we obtain should be identical to + * fd_parent */ + fd = open("open_identical_fd", O_CREAT|O_RDWR, 0660); + if (fd != fd_parent) { + return 4; + } + + return 0; +} + diff --git a/test/test68.c b/test/test68.c new file mode 100644 index 000000000..cdc5e6f61 --- /dev/null +++ b/test/test68.c @@ -0,0 +1,298 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 */ +} +