]> Zhao Yanbai Git Server - minix.git/commitdiff
tests: add test77 for opening/closing PTYs 58/958/2
authorDavid van Moolenbroek <david@minix3.org>
Sat, 31 Aug 2013 14:13:37 +0000 (16:13 +0200)
committerLionel Sambuc <lionel@minix3.org>
Tue, 18 Feb 2014 10:25:03 +0000 (11:25 +0100)
Change-Id: I30e3418f75137aa08037fa6581ff3d4cce32a114

distrib/sets/lists/minix/mi
test/Makefile
test/run
test/test77.c [new file with mode: 0644]

index 7cc7ada044d94c66f4f692ab6c05626f96d3bec0..fe1621c5e78ac7853d876d4cad036f8d82a785b0 100644 (file)
 ./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
index f399692c16010434f9d0986bb68fc73fdb700acf..c3cef47ed036be16d40c2f2bb2695189b8cdb333 100644 (file)
@@ -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+= \
index 8ddf28b722a7a232a6e52665aee4ca4de9b7645b..2084ded895d73421cf24d7a6cf3ffd57ffee2ca1 100755 (executable)
--- 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 (file)
index 0000000..b634a00
--- /dev/null
@@ -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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/wait.h>
+#include <paths.h>
+#include <fcntl.h>
+#include <util.h>
+
+#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();
+}