/* * * Copyright (c) International Business Machines Corp., 2002 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* 12/23/2002 Port to LTP robbiew@us.ibm.com */ /* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <termio.h> #include <unistd.h> /** LTP Port **/ #include "test.h" #include "safe_macros.h" char *TCID = "pty01"; /* Test program identifier. */ int TST_TOTAL = 5; /* Total number of test cases. */ /**************/ /* * pty master clone device */ #define MASTERCLONE "/dev/ptmx" /* * string for testing read/write on ptys */ #define STRING "Linux Test Project\n" /* * test buffer size */ #define TESTSIZE 1024 /* * mode we expect grantpt() to leave pty as */ #define PTY_MODE 020622 /* * number of procs for parallel test */ #define NUMPROCS 15 /* * test slave locking */ static int test1(void) { int masterfd; /* master pty fd */ int slavefd; /* slave pty fd */ char *slavename; struct stat st; char buf[TESTSIZE]; masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR); slavename = ptsname(masterfd); if (slavename == NULL) { tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed"); } if (grantpt(masterfd) != 0) { tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed"); } if (stat(slavename, &st) != 0) { tst_brkm(TBROK | TERRNO, NULL, "stat(%s) failed", slavename); } if (st.st_uid != getuid()) { tst_brkm(TBROK, NULL, "uid mismatch"); } /* grantpt() is a no-op in bionic. */ #ifndef __BIONIC__ if (st.st_mode != (S_IFCHR | S_IRUSR | S_IWUSR | S_IWGRP)) { tst_brkm(TBROK, NULL, "mode mismatch (mode=%o)", st.st_mode); } #endif slavefd = open(slavename, O_RDWR); if (slavefd >= 0) { tst_brkm(TBROK, NULL, "open didn't fail as expected!"); } if (unlockpt(masterfd) != 0) { tst_brkm(TBROK | TERRNO, NULL, "unlockpt() failed"); } slavefd = SAFE_OPEN(NULL, slavename, O_RDWR); /* * test writing to the master / reading from the slave */ if (write(masterfd, STRING, strlen(STRING)) != strlen(STRING)) { /* * XXX: the errno printout might be garbage, but better to be * safe than sorry.. */ tst_brkm(TFAIL | TERRNO, NULL, "write to master"); } if (read(slavefd, buf, strlen(STRING)) != strlen(STRING)) { /* XXX: Same as write above.. */ tst_brkm(TFAIL | TERRNO, NULL, "read from slave"); } if (strncmp(STRING, buf, strlen(STRING) - 1) != 0) { tst_brkm(TFAIL, NULL, "strings are different (STRING = '%s' != buf = '%s')", STRING, buf); } /* * test writing to the slave / reading from the master */ if (write(slavefd, STRING, strlen(STRING)) != strlen(STRING)) { /* XXX: Same as write above.. */ tst_brkm(TFAIL | TERRNO, NULL, "write to slave"); } if (read(masterfd, buf, strlen(STRING)) != strlen(STRING)) { /* XXX: Same as write above.. */ tst_brkm(TFAIL | TERRNO, NULL, "read from master"); } if (strncmp(STRING, buf, strlen(STRING) - 1) != 0) { tst_brkm(TFAIL, NULL, "strings are different (STRING = '%s' != buf = '%s').", STRING, buf); } /* * try an invalid ioctl on the slave... */ if (ioctl(slavefd, TIOCGWINSZ, NULL) == 0) { tst_brkm(TFAIL, NULL, "invalid slave TIOCGWINSZ ioctl succeeded.. it should " "have failed"); } /* * try an invalid ioctl on the master... */ if (ioctl(masterfd, TIOCGWINSZ, NULL) == 0) { tst_brkm(TFAIL, NULL, "invalid master TIOCGWINSZ ioctl succeeded.. it should " "have failed"); } /* * close pty fds */ if (close(slavefd) != 0) { tst_brkm(TBROK | TERRNO, NULL, "close of slave"); } if (close(masterfd) != 0) { tst_brkm(TBROK | TERRNO, NULL, "close of master"); } tst_resm(TPASS, "test1"); /** NOTREACHED **/ return 0; } /* * test slave operations with closed master */ static void test2(void) { int masterfd; /* master pty fd */ int slavefd; /* slave pty fd */ int i; char *slavename; char c; masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR); slavename = ptsname(masterfd); if (slavename == NULL) { tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed"); } if (grantpt(masterfd) != 0) { tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed"); } if (unlockpt(masterfd) != 0) { tst_brkm(TBROK | TERRNO, NULL, "unlockpt() call failed"); } slavefd = SAFE_OPEN(NULL, slavename, O_RDWR); /* * close pty fds. See what happens when we close the master * first. */ if (close(masterfd) != 0) { tst_brkm(TBROK | TERRNO, NULL, "close()"); } errno = 0; if ((i = read(slavefd, &c, 1)) == 1) { tst_brkm(TFAIL, NULL, "reading from slave fd should have failed, but didn't" "(read '%c')", c); } if ((i = write(slavefd, &c, 1)) == 1) { tst_brkm(TFAIL, NULL, "writing to slave fd should have failed, but didn't"); } if (ioctl(slavefd, TIOCGWINSZ, NULL) == 0) { tst_brkm(TFAIL, NULL, "trying TIOCGWINSZ on slave fd should have failed, " "but didn't"); } if (close(slavefd) != 0) { tst_brkm(TBROK, NULL, "close"); } tst_resm(TPASS, "test2"); } /* * test operations on master with closed slave */ static void test3(void) { int masterfd; /* master pty fd */ masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR); if (ioctl(masterfd, TIOCGWINSZ, NULL) == 0) { tst_brkm(TFAIL | TERRNO, NULL, "trying TIOCGWINSZ on master with no open slave " "succeeded unexpectedly"); } tst_resm(TPASS, "test3"); } /* * test multiple opens on slave side of pty */ static void test4(void) { int masterfd; /* master pty fd */ int slavefd; /* slave pty fd */ int slavefd2; int slavefd3; char *slavename; masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR); slavename = ptsname(masterfd); if (slavename == NULL) { tst_brkm(TBROK, NULL, "ptsname() call failed"); } if (grantpt(masterfd) != 0) { tst_brkm(TBROK, NULL, "grantpt() call failed"); } if (unlockpt(masterfd) != 0) { tst_brkm(TBROK | TERRNO, NULL, "unlockpt() call failed"); } slavefd = SAFE_OPEN(NULL, slavename, O_RDWR); slavefd2 = open(slavename, O_RDWR); if (slavefd < 0) { tst_brkm(TFAIL | TERRNO, NULL, "Could not open %s (again)", slavename); } slavefd3 = open(slavename, O_RDWR); if (slavefd < 0) { tst_brkm(TFAIL | TERRNO, NULL, "Could not open %s (once more)", slavename); } /* * close pty fds. */ if (close(slavefd) != 0) { tst_brkm(TBROK | TERRNO, NULL, "close slave"); } if (close(slavefd2) != 0) { tst_brkm(TBROK, NULL, "close slave again"); } if (close(slavefd3) != 0) { tst_brkm(TBROK, NULL, "close slave once more"); } if (close(masterfd) != 0) { tst_brkm(TBROK, NULL, "close master"); } tst_resm(TPASS, "test4"); } /* * test opening/closing lots of ptys in parallel. We may run out * of ptys for this test depending on how the system is configured, * but that's not a fatal error. */ static void test5(void) { int masterfd; /* master pty fd */ char *slavename; int status; int i; for (i = 0; i < NUMPROCS; ++i) { switch (fork()) { case -1: tst_brkm(TBROK, NULL, "fork()"); break; case 0: masterfd = open(MASTERCLONE, O_RDWR); if (masterfd < 0) { printf("proc %d: opening %s failed: %s", i, MASTERCLONE, strerror(errno)); exit(1); } if (grantpt(masterfd) != 0) { printf("proc %d: grantpt() call failed: %s", i, strerror(errno)); exit(1); } slavename = ptsname(masterfd); if (slavename == NULL) { printf("proc %d: ptsname() call failed: %s", i, strerror(errno)); exit(1); } sleep(10); if (close(masterfd) != 0) { printf("proc %d: close failed: %s", i, strerror(errno)); exit(1); } exit(0); default: break; } } while (wait(&status) > 0) { if (status) { tst_brkm(TFAIL, NULL, "child exited with non-zero status %d", status); } } tst_resm(TPASS, "test5"); } /* * main test driver */ int main(int argc, char **argv) { test1(); test2(); test3(); test4(); test5(); /* * all done */ tst_exit(); }