Home | History | Annotate | Download | only in pty
      1 /*
      2  *
      3  *   Copyright (c) International Business Machines  Corp., 2002
      4  *
      5  *   This program is free software;  you can redistribute it and/or modify
      6  *   it under the terms of the GNU General Public License as published by
      7  *   the Free Software Foundation; either version 2 of the License, or
      8  *   (at your option) any later version.
      9  *
     10  *   This program is distributed in the hope that it will be useful,
     11  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
     12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
     13  *   the GNU General Public License for more details.
     14  *
     15  *   You should have received a copy of the GNU General Public License
     16  *   along with this program;  if not, write to the Free Software
     17  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     18  */
     19 
     20 /* 12/23/2002	Port to LTP	robbiew (at) us.ibm.com */
     21 /* 06/30/2001	Port to Linux	nsharoff (at) us.ibm.com */
     22 
     23 #ifndef _GNU_SOURCE
     24 #define _GNU_SOURCE 1
     25 #endif
     26 
     27 #include <sys/types.h>
     28 #include <sys/stat.h>
     29 #include <sys/wait.h>
     30 #include <errno.h>
     31 #include <fcntl.h>
     32 #include <stdio.h>
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #include <termio.h>
     36 #include <unistd.h>
     37 
     38 /** LTP Port **/
     39 #include "test.h"
     40 #include "safe_macros.h"
     41 
     42 char *TCID = "pty01";		/* Test program identifier.    */
     43 int TST_TOTAL = 5;		/* Total number of test cases. */
     44 /**************/
     45 
     46 /*
     47  * pty master clone device
     48  */
     49 #define MASTERCLONE "/dev/ptmx"
     50 
     51 /*
     52  * string for testing read/write on ptys
     53  */
     54 #define STRING "Linux Test Project\n"
     55 
     56 /*
     57  * test buffer size
     58  */
     59 #define TESTSIZE 1024
     60 
     61 /*
     62  * mode we expect grantpt() to leave pty as
     63  */
     64 #define PTY_MODE 020622
     65 
     66 /*
     67  * number of procs for parallel test
     68  */
     69 #define NUMPROCS 15
     70 
     71 /*
     72  * test slave locking
     73  */
     74 static int test1(void)
     75 {
     76 	int masterfd;		/* master pty fd */
     77 	int slavefd;		/* slave pty fd */
     78 	char *slavename;
     79 	struct stat st;
     80 	char buf[TESTSIZE];
     81 
     82 	masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR);
     83 
     84 	slavename = ptsname(masterfd);
     85 	if (slavename == NULL) {
     86 		tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed");
     87 	}
     88 
     89 	if (grantpt(masterfd) != 0) {
     90 		tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed");
     91 	}
     92 
     93 	if (stat(slavename, &st) != 0) {
     94 		tst_brkm(TBROK | TERRNO, NULL, "stat(%s) failed", slavename);
     95 	}
     96 	if (st.st_uid != getuid()) {
     97 		tst_brkm(TBROK, NULL, "uid mismatch");
     98 	}
     99 
    100 	 /* grantpt() is a no-op in bionic. */
    101 #ifndef __BIONIC__
    102 	if (st.st_mode != (S_IFCHR | S_IRUSR | S_IWUSR | S_IWGRP)) {
    103 		tst_brkm(TBROK, NULL, "mode mismatch (mode=%o)", st.st_mode);
    104 	}
    105 #endif
    106 
    107 	slavefd = open(slavename, O_RDWR);
    108 	if (slavefd >= 0) {
    109 		tst_brkm(TBROK, NULL, "open didn't fail as expected!");
    110 	}
    111 
    112 	if (unlockpt(masterfd) != 0) {
    113 		tst_brkm(TBROK | TERRNO, NULL, "unlockpt() failed");
    114 	}
    115 
    116 	slavefd = SAFE_OPEN(NULL, slavename, O_RDWR);
    117 
    118 	/*
    119 	 * test writing to the master / reading from the slave
    120 	 */
    121 	if (write(masterfd, STRING, strlen(STRING)) != strlen(STRING)) {
    122 		/*
    123 		 * XXX: the errno printout might be garbage, but better to be
    124 		 * safe than sorry..
    125 		 */
    126 		tst_brkm(TFAIL | TERRNO, NULL, "write to master");
    127 	}
    128 
    129 	if (read(slavefd, buf, strlen(STRING)) != strlen(STRING)) {
    130 		/* XXX: Same as write above.. */
    131 		tst_brkm(TFAIL | TERRNO, NULL, "read from slave");
    132 	}
    133 	if (strncmp(STRING, buf, strlen(STRING) - 1) != 0) {
    134 		tst_brkm(TFAIL, NULL,
    135 			 "strings are different (STRING = '%s' != buf = '%s')",
    136 			 STRING, buf);
    137 	}
    138 
    139 	/*
    140 	 * test writing to the slave / reading from the master
    141 	 */
    142 	if (write(slavefd, STRING, strlen(STRING)) != strlen(STRING)) {
    143 		/* XXX: Same as write above.. */
    144 		tst_brkm(TFAIL | TERRNO, NULL, "write to slave");
    145 	}
    146 
    147 	if (read(masterfd, buf, strlen(STRING)) != strlen(STRING)) {
    148 		/* XXX: Same as write above.. */
    149 		tst_brkm(TFAIL | TERRNO, NULL, "read from master");
    150 	}
    151 	if (strncmp(STRING, buf, strlen(STRING) - 1) != 0) {
    152 		tst_brkm(TFAIL, NULL,
    153 			 "strings are different (STRING = '%s' != buf = '%s').",
    154 			 STRING, buf);
    155 	}
    156 
    157 	/*
    158 	 * try an invalid ioctl on the slave...
    159 	 */
    160 	if (ioctl(slavefd, TIOCGWINSZ, NULL) == 0) {
    161 		tst_brkm(TFAIL, NULL,
    162 			 "invalid slave TIOCGWINSZ ioctl succeeded.. it should "
    163 			 "have failed");
    164 	}
    165 
    166 	/*
    167 	 * try an invalid ioctl on the master...
    168 	 */
    169 	if (ioctl(masterfd, TIOCGWINSZ, NULL) == 0) {
    170 		tst_brkm(TFAIL, NULL,
    171 			 "invalid master TIOCGWINSZ ioctl succeeded.. it should "
    172 			 "have failed");
    173 	}
    174 
    175 	/*
    176 	 * close pty fds
    177 	 */
    178 	if (close(slavefd) != 0) {
    179 		tst_brkm(TBROK | TERRNO, NULL, "close of slave");
    180 	}
    181 	if (close(masterfd) != 0) {
    182 		tst_brkm(TBROK | TERRNO, NULL, "close of master");
    183 	}
    184 	tst_resm(TPASS, "test1");
    185 	/** NOTREACHED **/
    186 	return 0;
    187 }
    188 
    189 /*
    190  * test slave operations with closed master
    191  */
    192 static void test2(void)
    193 {
    194 	int masterfd;		/* master pty fd */
    195 	int slavefd;		/* slave pty fd */
    196 	int i;
    197 	char *slavename;
    198 	char c;
    199 
    200 	masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR);
    201 
    202 	slavename = ptsname(masterfd);
    203 	if (slavename == NULL) {
    204 		tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed");
    205 	}
    206 
    207 	if (grantpt(masterfd) != 0) {
    208 		tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed");
    209 	}
    210 
    211 	if (unlockpt(masterfd) != 0) {
    212 		tst_brkm(TBROK | TERRNO, NULL, "unlockpt() call failed");
    213 	}
    214 
    215 	slavefd = SAFE_OPEN(NULL, slavename, O_RDWR);
    216 
    217 	/*
    218 	 * close pty fds.  See what happens when we close the master
    219 	 * first.
    220 	 */
    221 	if (close(masterfd) != 0) {
    222 		tst_brkm(TBROK | TERRNO, NULL, "close()");
    223 	}
    224 
    225 	errno = 0;
    226 	if ((i = read(slavefd, &c, 1)) == 1) {
    227 		tst_brkm(TFAIL, NULL,
    228 			 "reading from slave fd should have failed, but didn't"
    229 			 "(read '%c')", c);
    230 	}
    231 
    232 	if ((i = write(slavefd, &c, 1)) == 1) {
    233 		tst_brkm(TFAIL, NULL,
    234 			 "writing to slave fd should have failed, but didn't");
    235 	}
    236 
    237 	if (ioctl(slavefd, TIOCGWINSZ, NULL) == 0) {
    238 		tst_brkm(TFAIL, NULL,
    239 			 "trying TIOCGWINSZ on slave fd should have failed, "
    240 			 "but didn't");
    241 	}
    242 
    243 	if (close(slavefd) != 0) {
    244 		tst_brkm(TBROK, NULL, "close");
    245 	}
    246 	tst_resm(TPASS, "test2");
    247 }
    248 
    249 /*
    250  * test operations on master with closed slave
    251  */
    252 static void test3(void)
    253 {
    254 	int masterfd;		/* master pty fd */
    255 
    256 	masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR);
    257 
    258 	if (ioctl(masterfd, TIOCGWINSZ, NULL) == 0) {
    259 		tst_brkm(TFAIL | TERRNO, NULL,
    260 			 "trying TIOCGWINSZ on master with no open slave "
    261 			 "succeeded unexpectedly");
    262 	}
    263 	tst_resm(TPASS, "test3");
    264 }
    265 
    266 /*
    267  * test multiple opens on slave side of pty
    268  */
    269 static void test4(void)
    270 {
    271 	int masterfd;		/* master pty fd */
    272 	int slavefd;		/* slave pty fd */
    273 	int slavefd2;
    274 	int slavefd3;
    275 	char *slavename;
    276 
    277 	masterfd = SAFE_OPEN(NULL, MASTERCLONE, O_RDWR);
    278 
    279 	slavename = ptsname(masterfd);
    280 	if (slavename == NULL) {
    281 		tst_brkm(TBROK, NULL, "ptsname() call failed");
    282 	}
    283 
    284 	if (grantpt(masterfd) != 0) {
    285 		tst_brkm(TBROK, NULL, "grantpt() call failed");
    286 	}
    287 
    288 	if (unlockpt(masterfd) != 0) {
    289 		tst_brkm(TBROK | TERRNO, NULL, "unlockpt() call failed");
    290 	}
    291 
    292 	slavefd = SAFE_OPEN(NULL, slavename, O_RDWR);
    293 
    294 	slavefd2 = open(slavename, O_RDWR);
    295 	if (slavefd < 0) {
    296 		tst_brkm(TFAIL | TERRNO, NULL, "Could not open %s (again)",
    297 			 slavename);
    298 	}
    299 
    300 	slavefd3 = open(slavename, O_RDWR);
    301 	if (slavefd < 0) {
    302 		tst_brkm(TFAIL | TERRNO, NULL, "Could not open %s (once more)",
    303 			 slavename);
    304 	}
    305 
    306 	/*
    307 	 * close pty fds.
    308 	 */
    309 	if (close(slavefd) != 0) {
    310 		tst_brkm(TBROK | TERRNO, NULL, "close slave");
    311 	}
    312 
    313 	if (close(slavefd2) != 0) {
    314 		tst_brkm(TBROK, NULL, "close slave again");
    315 	}
    316 
    317 	if (close(slavefd3) != 0) {
    318 		tst_brkm(TBROK, NULL, "close slave once more");
    319 	}
    320 
    321 	if (close(masterfd) != 0) {
    322 		tst_brkm(TBROK, NULL, "close master");
    323 	}
    324 	tst_resm(TPASS, "test4");
    325 }
    326 
    327 /*
    328  * test opening/closing lots of ptys in parallel.  We may run out
    329  * of ptys for this test depending on how the system is configured,
    330  * but that's not a fatal error.
    331  */
    332 static void test5(void)
    333 {
    334 	int masterfd;		/* master pty fd */
    335 	char *slavename;
    336 	int status;
    337 	int i;
    338 
    339 	for (i = 0; i < NUMPROCS; ++i) {
    340 		switch (fork()) {
    341 		case -1:
    342 			tst_brkm(TBROK, NULL, "fork()");
    343 			break;
    344 		case 0:
    345 			masterfd = open(MASTERCLONE, O_RDWR);
    346 			if (masterfd < 0) {
    347 				printf("proc %d: opening %s failed: %s",
    348 				       i, MASTERCLONE, strerror(errno));
    349 				exit(1);
    350 			}
    351 			if (grantpt(masterfd) != 0) {
    352 				printf("proc %d: grantpt() call failed: %s",
    353 				       i, strerror(errno));
    354 				exit(1);
    355 			}
    356 			slavename = ptsname(masterfd);
    357 			if (slavename == NULL) {
    358 				printf("proc %d: ptsname() call failed: %s",
    359 				       i, strerror(errno));
    360 				exit(1);
    361 			}
    362 			sleep(10);
    363 			if (close(masterfd) != 0) {
    364 				printf("proc %d: close failed: %s",
    365 				       i, strerror(errno));
    366 				exit(1);
    367 			}
    368 			exit(0);
    369 		default:
    370 			break;
    371 		}
    372 	}
    373 	while (wait(&status) > 0) {
    374 		if (status) {
    375 			tst_brkm(TFAIL, NULL,
    376 				 "child exited with non-zero status %d",
    377 				 status);
    378 		}
    379 	}
    380 	tst_resm(TPASS, "test5");
    381 }
    382 
    383 /*
    384  * main test driver
    385  */
    386 int main(int argc, char **argv)
    387 {
    388 	test1();
    389 	test2();
    390 	test3();
    391 	test4();
    392 	test5();
    393 
    394 	/*
    395 	 * all done
    396 	 */
    397 	tst_exit();
    398 }
    399