diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 7cc7ada04..fe1621c5e 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -5657,6 +5657,7 @@ ./usr/tests/minix-posix/test74 minix-sys ./usr/tests/minix-posix/test75 minix-sys ./usr/tests/minix-posix/test76 minix-sys +./usr/tests/minix-posix/test77 minix-sys ./usr/tests/minix-posix/test8 minix-sys ./usr/tests/minix-posix/test9 minix-sys ./usr/tests/minix-posix/testinterp minix-sys diff --git a/test/Makefile b/test/Makefile index f399692c1..c3cef47ed 100644 --- a/test/Makefile +++ b/test/Makefile @@ -32,6 +32,7 @@ COPTS.test68.c= -O0 # Some have special libraries LDADD.test59= -lmthread LDADD.test76= -lutil +LDADD.test77= -lutil # Some have an extra file OBJS.test57= test57loop.o @@ -54,7 +55,7 @@ MINIX_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 48 49 50 52 53 54 55 56 58 59 60 \ -61 64 65 66 67 68 69 70 71 72 73 74 75 76 +61 64 65 66 67 68 69 70 71 72 73 74 75 76 77 .if ${MACHINE_ARCH} == "i386" MINIX_TESTS+= \ diff --git a/test/run b/test/run index 8ddf28b72..2084ded89 100755 --- a/test/run +++ b/test/run @@ -21,14 +21,14 @@ badones= # list of tests that failed # Programs that require setuid setuids="test11 test33 test43 test44 test46 test56 test60 test61 test65 \ - test69 test76" # test73" + test69 test76 test77" # test73" # Scripts that require to be run as root rootscripts="testisofs" alltests="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 68 69 70 71 72 75 76 \ + 61 62 63 64 65 66 67 68 69 70 71 72 75 76 77 \ sh1 sh2 interp mfs isofs" tests_no=`expr 0` diff --git a/test/test77.c b/test/test77.c new file mode 100644 index 000000000..b634a0091 --- /dev/null +++ b/test/test77.c @@ -0,0 +1,486 @@ +/* Tests for opening/closing pseudo terminals - by D.C. van Moolenbroek */ +/* This test needs to be run as root; otherwise, openpty() won't work. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ITERATIONS 10 + +#include "common.h" + +static int sighups; /* number of SIGHUP signals received */ + +/* + * Signal handler for SIGHUP and SIGUSR1. + */ +void +signal_handler(int sig) +{ + if (sig == SIGHUP) + sighups++; +} + +/* + * Set the slave side of the pseudo terminal to raw mode. This simplifies + * testing communication. + */ +static void +make_raw(int slavefd) +{ + struct termios tios; + + if (tcgetattr(slavefd, &tios) < 0) e(100); + + cfmakeraw(&tios); + + if (tcsetattr(slavefd, TCSANOW, &tios) < 0) e(101); +} + +/* + * See if the given pseudo terminal can successfully perform basic + * communication between master and slave. + */ +static void +test_comm(int masterfd, int slavefd) +{ + char c; + + make_raw(slavefd); + + c = 'A'; + if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(200); + if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(201); + if (c != 'A') e(202); + + c = 'B'; + if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(203); + if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(204); + if (c != 'B') e(205); + + c = 'C'; + if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(206); + if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(207); + if (c != 'C') e(208); + + c = 'D'; + if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(209); + if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(210); + if (c != 'D') e(211); +} + +/* + * Get device node names for the master and slave end of a free pseudo + * terminal. We don't want to replicate the entire openpty(3) logic here, so + * start by letting openpty(3) do the work for us. We make the assumption that + * nobody snatches the pair while we are running. + */ +static void +get_names(char pname[PATH_MAX], char tname[PATH_MAX]) +{ + int len, masterfd, slavefd; + + if (openpty(&masterfd, &slavefd, tname, NULL, NULL) < 0) e(300); + + /* + * openpty(3) gives us only the slave name, but we also need the master + * name. + */ + strlcpy(pname, tname, PATH_MAX); + len = strlen(_PATH_DEV); + + if (strncmp(pname, _PATH_DEV, len)) e(301); + + /* If this fails, this test needs to be updated. */ + if (pname[len] != 't') e(302); + + pname[len] = 'p'; + + test_comm(masterfd, slavefd); + + if (close(masterfd) < 0) e(303); + if (close(slavefd) < 0) e(304); +} + +/* + * Test various orders of opening and closing the master and slave sides of a + * pseudo terminal, as well as opening/closing one side without ever opening + * the other. + */ +static void +test77a(void) +{ + struct sigaction act, oact; + char pname[PATH_MAX], tname[PATH_MAX]; + int masterfd, slavefd; + + subtest = 1; + + /* We do not want to get SIGHUP signals in this test. */ + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + if (sigaction(SIGHUP, &act, &oact) < 0) e(1); + + /* Get master and slave device names for a free pseudo terminal. */ + get_names(pname, tname); + + /* Try opening and then closing the master. */ + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(2); + + if (close(masterfd) < 0) e(3); + + /* Now see if we can reopen the master as well as the slave. */ + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(4); + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(5); + + test_comm(masterfd, slavefd); + + /* In the meantime, test different closing orders. This is order A. */ + if (close(slavefd) < 0) e(6); + if (close(masterfd) < 0) e(7); + + /* Now try opening the pair again. */ + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(8); + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(9); + + test_comm(masterfd, slavefd); + + if (close(slavefd) < 0) e(10); + + /* + * Try reopening the slave after closing it. It is not very important + * that this works, but the TTY driver should currently support it. + */ + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(11); + + test_comm(masterfd, slavefd); + + /* This is closing order B. This may or may not cause a SIGHUP. */ + if (close(masterfd) < 0) e(12); + if (close(slavefd) < 0) e(13); + + /* Try the normal open procedure. */ + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(14); + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(15); + + test_comm(masterfd, slavefd); + + if (close(slavefd) < 0) e(16); + if (close(masterfd) < 0) e(17); + + /* Try reopening and closing the slave, without opening the master. */ + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(18); + + if (close(slavefd) < 0) e(19); + + /* Again, try the normal open procedure. */ + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(20); + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(21); + + test_comm(masterfd, slavefd); + + if (close(slavefd) < 0) e(22); + if (close(masterfd) < 0) e(23); + + /* Finally, try opening the slave first. */ + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(24); + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(25); + + test_comm(masterfd, slavefd); + + if (close(slavefd) < 0) e(26); + if (close(masterfd) < 0) e(27); + + if (sigaction(SIGHUP, &oact, NULL) < 0) e(28); +} + +/* + * Test opening a single side multiple times. + */ +static void +test77b(void) +{ + char pname[PATH_MAX], tname[PATH_MAX]; + int masterfd, slavefd, extrafd; + + subtest = 2; + + /* Get master and slave device names for a free pseudo terminal. */ + get_names(pname, tname); + + /* It must not be possible to open the master multiple times. */ + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(1); + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(2); + + test_comm(masterfd, slavefd); + + if ((extrafd = open(pname, O_RDWR | O_NOCTTY)) >= 0) e(3); + if (errno != EIO) e(4); + + test_comm(masterfd, slavefd); + + if (close(slavefd) < 0) e(5); + if (close(masterfd) < 0) e(6); + + /* The slave can be opened multiple times, though. */ + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(7); + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(8); + + test_comm(masterfd, slavefd); + + if ((extrafd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(9); + + test_comm(masterfd, extrafd); + test_comm(masterfd, slavefd); + + if (close(slavefd) < 0) e(10); + if (close(extrafd) < 0) e(11); + if (close(masterfd) < 0) e(12); +} + +/* + * Test communication on half-open pseudo terminals. + */ +static void +test77c(void) +{ + struct sigaction act, oact; + char pname[PATH_MAX], tname[PATH_MAX]; + int masterfd, slavefd; + char c; + + subtest = 3; + + /* We do not want to get SIGHUP signals in this test. */ + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + if (sigaction(SIGHUP, &act, &oact) < 0) e(1); + + /* Get master and slave device names for a free pseudo terminal. */ + get_names(pname, tname); + + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(2); + + /* Writes to the master should be buffered until there is a slave. */ + c = 'E'; + if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(3); + + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(4); + + make_raw(slavefd); + + if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(5); + if (c != 'E') e(6); + + /* Discard the echo on the master. */ + if (tcflush(slavefd, TCOFLUSH) != 0) e(7); + + test_comm(masterfd, slavefd); + + if (close(slavefd) < 0) e(8); + + /* Writes to the master after the slave has been closed should fail. */ + if (write(masterfd, &c, sizeof(c)) >= 0) e(9); + if (errno != EIO) e(10); + + if (close(masterfd) < 0) e(11); + + /* Writes to the slave should be buffered until there is a master. */ + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(12); + + make_raw(slavefd); + + c = 'F'; + if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(13); + + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(14); + + if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(15); + if (c != 'F') e(16); + + test_comm(masterfd, slavefd); + + if (close(masterfd) < 0) e(17); + + if (write(slavefd, &c, sizeof(c)) >= 0) e(18); + if (errno != EIO) e(19); + + if (close(slavefd) < 0) e(20); + + if (sigaction(SIGHUP, &oact, NULL) < 0) e(21); +} + +/* + * Test opening the slave side with and without the O_NOCTTY flag. + */ +static void +test77d(void) +{ + char pname[PATH_MAX], tname[PATH_MAX]; + int masterfd, slavefd; + + subtest = 4; + + /* Get master and slave device names for a free pseudo terminal. */ + get_names(pname, tname); + + /* Make ourselves process group leader if we aren't already. */ + (void) setsid(); + + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(1); + + /* + * Opening the slave with O_NOCTTY should not change its controlling + * terminal. + */ + switch (fork()) { + case 0: + if (setsid() < 0) e(2); + + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(3); + + if (open("/dev/tty", O_RDWR) >= 0) e(4); + if (errno != ENXIO) e(5); + + exit(0); + case -1: + e(6); + default: + break; + } + + if (wait(NULL) <= 0) e(7); + + if (close(masterfd) < 0) e(8); + + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(9); + + /* + * Opening the slave without O_NOCTTY should change its controlling + * terminal, though. + */ + switch (fork()) { + case 0: + if (setsid() < 0) e(10); + + if ((slavefd = open(tname, O_RDWR)) < 0) e(11); + + if (open("/dev/tty", O_RDWR) < 0) e(12); + + exit(0); + case -1: + e(13); + default: + break; + } + + if (wait(NULL) <= 0) e(14); + + if (close(masterfd) < 0) e(15); +} + +/* + * Test receiving of SIGHUP on master hang-up. All of the tests so far have + * ignored SIGHUP, and probably would not have received one anyway, since the + * process was not its own session leader. Time to test this aspect. + */ +static void +test77e(void) +{ + struct sigaction act, hup_oact, usr_oact; + sigset_t set, oset; + char pname[PATH_MAX], tname[PATH_MAX]; + int masterfd, slavefd; + + subtest = 5; + + /* Get master and slave device names for a free pseudo terminal. */ + get_names(pname, tname); + + memset(&act, 0, sizeof(act)); + act.sa_handler = signal_handler; + if (sigaction(SIGHUP, &act, &hup_oact) < 0) e(1); + + memset(&act, 0, sizeof(act)); + act.sa_handler = signal_handler; + if (sigaction(SIGUSR1, &act, &usr_oact) < 0) e(2); + + sigemptyset(&set); + sigaddset(&set, SIGHUP); + sigaddset(&set, SIGUSR1); + if (sigprocmask(SIG_BLOCK, &set, &oset) < 0) e(3); + + sighups = 0; + + /* Make ourselves process group leader if we aren't already. */ + (void) setsid(); + + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(4); + + switch (fork()) { + case 0: + if (close(masterfd) < 0) e(5); + + /* Become session leader. */ + if (setsid() < 0) e(6); + + if ((slavefd = open(tname, O_RDWR)) < 0) e(7); + + /* Tell the parent we are ready. */ + kill(getppid(), SIGUSR1); + + /* We should now get a SIGHUP. */ + set = oset; + if (sigsuspend(&set) >= 0) e(8); + + if (sighups != 1) e(9); + + exit(0); + case -1: + e(10); + default: + break; + } + + /* Wait for SIGUSR1 from the child. */ + set = oset; + if (sigsuspend(&set) >= 0) e(11); + + /* Closing the master should now raise a SIGHUP signal in the child. */ + if (close(masterfd) < 0) e(12); + + if (wait(NULL) <= 0) e(13); + + if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0) e(14); + + if (sigaction(SIGHUP, &hup_oact, NULL) < 0) e(15); + if (sigaction(SIGUSR1, &usr_oact, NULL) < 0) e(16); +} + +int +main(int argc, char **argv) +{ + int i, m; + + start(77); + + if (argc == 2) + m = atoi(argv[1]); + else + m = 0xFF; + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0x01) test77a(); + if (m & 0x02) test77b(); + if (m & 0x04) test77c(); + if (m & 0x08) test77d(); + if (m & 0x10) test77e(); + } + + quit(); +}