/* * 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 opening a single side multiple times. */ static void test77b(void) { char pname[PATH_MAX], tname[PATH_MAX]; int oldstyle, masterfd, slavefd, extrafd; subtest = 2; /* Obtain a pseudo terminal. */ oldstyle = get_pty(&masterfd, pname, tname); if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); /* * It must not be possible to open the master multiple times. Doing so * is possible only if we have a named master, i.e., an old-style PTY. */ test_comm(masterfd, slavefd); if (oldstyle) { if ((extrafd = open(pname, O_RDWR | O_NOCTTY)) >= 0) e(0); if (errno != EIO) e(0); } test_comm(masterfd, slavefd); if (close(slavefd) < 0) e(0); if (close(masterfd) < 0) e(0); /* The slave can be opened multiple times, though. */ oldstyle = get_pty(&masterfd, pname, tname); if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); test_comm(masterfd, slavefd); if ((extrafd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); test_comm(masterfd, extrafd); test_comm(masterfd, slavefd); if (close(slavefd) < 0) e(0); if (close(extrafd) < 0) e(0); if (close(masterfd) < 0) e(0); }
/* * 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); }
/* * Obtain a pseudo terminal. The master end is opened and its file descriptor * stored in 'pfd'. The slave path name is stored in 'tname'. For old-style * PTYs, the function returns 1 and stores the master name in 'pname' if not * NULL. For Unix98 PTYs, the function returns 0, in which case no master name * is available. For old-style PTYs, the caller may close and reopen the * master. In that case, we make the assumption that nobody snatches the pair * while we are running. For Unix98 PTYs, the master must be kept open. */ static int get_pty(int *pfd, char pname[PATH_MAX], char tname[PATH_MAX]) { char *name; int len, masterfd, slavefd; /* * First try Unix98 PTY allocation, mainly to avoid opening the slave * end immediately. If this fails, try openpty(3) as well. */ if ((masterfd = posix_openpt(O_RDWR | O_NOCTTY)) != -1) { if (grantpt(masterfd) != -1 && unlockpt(masterfd) != -1 && (name = ptsname(masterfd)) != NULL) { *pfd = masterfd; strlcpy(tname, name, PATH_MAX); return 0; } if (close(masterfd) < 0) e(0); } if (openpty(&masterfd, &slavefd, tname, NULL, NULL) < 0) e(0); test_comm(masterfd, slavefd); *pfd = masterfd; if (close(slavefd) < 0) e(0); /* * openpty(3) gives us only the slave name, but we also want the master * name. */ len = strlen(_PATH_DEV); if (strncmp(tname, _PATH_DEV, len)) e(0); if (strncmp(&tname[len], "tty", 3)) return 0; /* Unix98 after all? Well okay, whatever.. */ if (pname != NULL) { strlcpy(pname, tname, PATH_MAX); pname[len] = 'p'; } return 1; }
/* * 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); /* Reads from the slave should return EOF if the master is gone. */ if (read(slavefd, &c, sizeof(c)) != 0) e(20); if (close(slavefd) < 0) e(21); if (sigaction(SIGHUP, &oact, NULL) < 0) e(22); }
/* * 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 for Unix98 PTY support and PTYFS. */ static void test77g(void) { char *tname; struct stat buf; size_t len; int i, masterfd, slavefd, fd[3], array[3], present[3]; subtest = 7; /* * Test basic operation, and verify that the slave node disappears * after both sides of the pseudo terminal have been closed. We check * different combinations of open master and slave ends (with 'i'): * 0) opening and closing the master only, 1) closing a slave before * the master, and 2) closing the slave after the master. */ for (i = 0; i <= 2; i++) { masterfd = get_unix98_pty(&tname); if (access(tname, R_OK | W_OK) < 0) e(0); if (i > 0) { if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); if (access(tname, R_OK | W_OK) < 0) e(0); if (i > 1) { if (close(masterfd) < 0) e(0); masterfd = slavefd; /* ugly but saving code */ } else if (close(slavefd) < 0) e(0); } if (access(tname, R_OK | W_OK) < 0) e(0); if (close(masterfd) < 0) e(0); if (access(tname, R_OK | W_OK) == 0) e(0); } /* * Test whether we can open multiple pseudo terminals. We need to be * able to open three PTYs. Verify that they are properly listed in * the /dev/pts directory contents, and have proper attributes set. */ test_getdents(0, NULL, NULL); for (i = 0; i < 3; i++) { fd[i] = get_unix98_pty(&tname); /* Figure out the slave index number. */ len = strlen(_PATH_DEV_PTS); if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0); array[i] = atoi(&tname[len]); present[i] = 1; } test_getdents(3, array, present); if (close(fd[0]) < 0) e(0); present[0] = 0; test_getdents(3, array, present); if (close(fd[2]) < 0) e(0); present[2] = 0; test_getdents(3, array, present); if (close(fd[1]) < 0) e(0); present[1] = 0; test_getdents(3, array, present); /* * Test chmod(2) on a slave node, and multiple calls to grantpt(3). * The first grantpt(3) call should create the slave node (we currently * can not test this: the slave node may be created earlier as well, * but we do not know its name), whereas subsequent grantpt(3) calls * should reset its mode, uid, and gid. Testing the latter two and * chown(2) on the slave node requires root, so we skip that part. * * Finally, NetBSD revokes access to existing slave file descriptors * upon a call to grantpt(3). This is not a POSIX requirement, but * NetBSD needs this for security reasons because it already creates * the slave node when the master is opened (and it does not lock the * slave until a call to unlockpt(3)). MINIX3 does not implement * revocation this way, because the slave node is created only upon the * call to grantpt(3), thus leaving no insecure window for the slave * side between posix_openpt(3) and grantpt(3). While this behavior * may be changed later, we test for the lack of revocation here now. */ masterfd = get_unix98_pty(&tname); if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); if (stat(tname, &buf) != 0) e(0); if (buf.st_mode != (S_IFCHR | 0620)) e(0); if (chmod(tname, S_IFCHR | 0630) != 0) e(0); if (stat(tname, &buf) != 0) e(0); if (buf.st_mode != (S_IFCHR | 0630)) e(0); if (grantpt(masterfd) != 0) e(0); if (stat(tname, &buf) != 0) e(0); if (buf.st_mode != (S_IFCHR | 0620)) e(0); test_comm(masterfd, slavefd); if (close(slavefd) < 0) e(0); if (close(masterfd) < 0) e(0); test_getdents(0, NULL, NULL); }
/* * Test communication on half-open pseudo terminals. */ static void test77c(void) { struct sigaction act, oact; char pname[PATH_MAX], tname[PATH_MAX]; int oldstyle, 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(0); /* Obtain a pseudo terminal. */ oldstyle = get_pty(&masterfd, pname, tname); /* * For old-style pseudo terminals, we have just opened and closed the * slave end, which alters the behavior we are testing below. Close * and reopen the master to start fresh. */ if (oldstyle) { if (close(masterfd) < 0) e(0); if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); } /* Writes to the master should be buffered until there is a slave. */ c = 'E'; if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); make_raw(slavefd); if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); if (c != 'E') e(0); /* Discard the echo on the master. */ if (tcflush(slavefd, TCOFLUSH) != 0) e(0); test_comm(masterfd, slavefd); if (close(slavefd) < 0) e(0); /* Writes to the master after the slave has been closed should fail. */ if (write(masterfd, &c, sizeof(c)) >= 0) e(0); if (errno != EIO) e(0); if (oldstyle) if (close(masterfd) < 0) e(0); /* * Writes to the slave should be buffered until there is a master. * This applies to old-style PTYs only. */ if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); if (oldstyle) { make_raw(slavefd); c = 'F'; if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); if (c != 'F') e(0); } test_comm(masterfd, slavefd); if (close(masterfd) < 0) e(0); if (write(slavefd, &c, sizeof(c)) >= 0) e(0); if (errno != EIO) e(0); /* Reads from the slave should return EOF if the master is gone. */ if (read(slavefd, &c, sizeof(c)) != 0) e(0); if (close(slavefd) < 0) e(0); if (sigaction(SIGHUP, &oact, NULL) < 0) e(0); }
/* * 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. This test is meaningful mainly for old-style pseudoterminals. */ static void test77a(void) { struct sigaction act, oact; char pname[PATH_MAX], tname[PATH_MAX]; int oldstyle, 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(0); /* Obtain a pseudo terminal. */ oldstyle = get_pty(&masterfd, pname, tname); if (oldstyle) { /* Try closing the master. */ if (close(masterfd) < 0) e(0); /* See if we can reopen the master. */ if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); } if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); test_comm(masterfd, slavefd); /* In the meantime, test different closing orders. This is order A. */ if (close(slavefd) < 0) e(0); if (close(masterfd) < 0) e(0); /* Now try opening the pair (or a new pair) again. */ if (!oldstyle) oldstyle = get_pty(&masterfd, pname, tname); else if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); test_comm(masterfd, slavefd); if (close(slavefd) < 0) e(0); /* * 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(0); test_comm(masterfd, slavefd); /* This is closing order B. This may or may not cause a SIGHUP. */ if (close(masterfd) < 0) e(0); if (close(slavefd) < 0) e(0); /* Try the normal open procedure. */ if (!oldstyle) oldstyle = get_pty(&masterfd, pname, tname); else if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); test_comm(masterfd, slavefd); if (close(slavefd) < 0) e(0); if (close(masterfd) < 0) e(0); /* * Try reopening and closing the slave, without opening the master. * This should work on old-style PTYS, but not on Unix98 PTYs. */ if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) >= 0) { if (!oldstyle) e(0); if (close(slavefd) < 0) e(0); } else if (oldstyle) e(0); /* Again, try the normal open procedure. */ if (!oldstyle) oldstyle = get_pty(&masterfd, pname, tname); else if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); test_comm(masterfd, slavefd); if (close(slavefd) < 0) e(0); if (close(masterfd) < 0) e(0); /* * Finally, try opening the slave first. This does not work with * Unix98 PTYs. */ if (oldstyle) { if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); test_comm(masterfd, slavefd); if (close(slavefd) < 0) e(0); if (close(masterfd) < 0) e(0); } if (sigaction(SIGHUP, &oact, NULL) < 0) e(0); }