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 
     41 char *TCID = "pty01";		/* Test program identifier.    */
     42 int TST_TOTAL = 5;		/* Total number of test cases. */
     43 /**************/
     44 
     45 /*
     46  * pty master clone device
     47  */
     48 #define MASTERCLONE "/dev/ptmx"
     49 
     50 /*
     51  * string for testing read/write on ptys
     52  */
     53 #define STRING "Linux Test Project\n"
     54 
     55 /*
     56  * test buffer size
     57  */
     58 #define TESTSIZE 1024
     59 
     60 /*
     61  * mode we expect grantpt() to leave pty as
     62  */
     63 #define PTY_MODE 020622
     64 
     65 /*
     66  * number of procs for parallel test
     67  */
     68 #define NUMPROCS 15
     69 
     70 /*
     71  * test slave locking
     72  */
     73 static int test1(void)
     74 {
     75 	int masterfd;		/* master pty fd */
     76 	int slavefd;		/* slave pty fd */
     77 	char *slavename;
     78 	struct stat st;
     79 	char buf[TESTSIZE];
     80 
     81 	masterfd = open(MASTERCLONE, O_RDWR);
     82 	if (masterfd < 0) {
     83 		tst_brkm(TBROK | TERRNO, NULL, MASTERCLONE);
     84 	}
     85 
     86 	slavename = ptsname(masterfd);
     87 	if (slavename == NULL) {
     88 		tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed");
     89 	}
     90 
     91 	if (grantpt(masterfd) != 0) {
     92 		tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed");
     93 	}
     94 
     95 	if (stat(slavename, &st) != 0) {
     96 		tst_brkm(TBROK | TERRNO, NULL, "stat(%s) failed", slavename);
     97 	}
     98 	if (st.st_uid != getuid()) {
     99 		tst_brkm(TBROK, NULL, "uid mismatch");
    100 	}
    101 
    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 
    106 	slavefd = open(slavename, O_RDWR);
    107 	if (slavefd >= 0) {
    108 		tst_brkm(TBROK, NULL, "open didn't fail as expected!");
    109 	}
    110 
    111 	if (unlockpt(masterfd) != 0) {
    112 		tst_brkm(TBROK | TERRNO, NULL, "unlockpt() failed");
    113 	}
    114 
    115 	slavefd = open(slavename, O_RDWR);
    116 	if (slavefd < 0) {
    117 		tst_brkm(TBROK, NULL, "Could not open %s", slavename);
    118 	}
    119 
    120 	/*
    121 	 * test writing to the master / reading from the slave
    122 	 */
    123 	if (write(masterfd, STRING, strlen(STRING)) != strlen(STRING)) {
    124 		/*
    125 		 * XXX: the errno printout might be garbage, but better to be
    126 		 * safe than sorry..
    127 		 */
    128 		tst_brkm(TFAIL | TERRNO, NULL, "write to master");
    129 	}
    130 
    131 	if (read(slavefd, buf, strlen(STRING)) != strlen(STRING)) {
    132 		/* XXX: Same as write above.. */
    133 		tst_brkm(TFAIL | TERRNO, NULL, "read from slave");
    134 	}
    135 	if (strncmp(STRING, buf, strlen(STRING) - 1) != 0) {
    136 		tst_brkm(TFAIL, NULL,
    137 			 "strings are different (STRING = '%s' != buf = '%s')",
    138 			 STRING, buf);
    139 	}
    140 
    141 	/*
    142 	 * test writing to the slave / reading from the master
    143 	 */
    144 	if (write(slavefd, STRING, strlen(STRING)) != strlen(STRING)) {
    145 		/* XXX: Same as write above.. */
    146 		tst_brkm(TFAIL | TERRNO, NULL, "write to slave");
    147 	}
    148 
    149 	if (read(masterfd, buf, strlen(STRING)) != strlen(STRING)) {
    150 		/* XXX: Same as write above.. */
    151 		tst_brkm(TFAIL | TERRNO, NULL, "read from master");
    152 	}
    153 	if (strncmp(STRING, buf, strlen(STRING) - 1) != 0) {
    154 		tst_brkm(TFAIL, NULL,
    155 			 "strings are different (STRING = '%s' != buf = '%s').",
    156 			 STRING, buf);
    157 	}
    158 
    159 	/*
    160 	 * try an invalid ioctl on the slave...
    161 	 */
    162 	if (ioctl(slavefd, TIOCGWINSZ, NULL) == 0) {
    163 		tst_brkm(TFAIL, NULL,
    164 			 "invalid slave TIOCGWINSZ ioctl succeeded.. it should "
    165 			 "have failed");
    166 	}
    167 
    168 	/*
    169 	 * try an invalid ioctl on the master...
    170 	 */
    171 	if (ioctl(masterfd, TIOCGWINSZ, NULL) == 0) {
    172 		tst_brkm(TFAIL, NULL,
    173 			 "invalid master TIOCGWINSZ ioctl succeeded.. it should "
    174 			 "have failed");
    175 	}
    176 
    177 	/*
    178 	 * close pty fds
    179 	 */
    180 	if (close(slavefd) != 0) {
    181 		tst_brkm(TBROK | TERRNO, NULL, "close of slave");
    182 	}
    183 	if (close(masterfd) != 0) {
    184 		tst_brkm(TBROK | TERRNO, NULL, "close of master");
    185 	}
    186 	tst_resm(TPASS, "test1");
    187 	/** NOTREACHED **/
    188 	return 0;
    189 }
    190 
    191 /*
    192  * test slave operations with closed master
    193  */
    194 static void test2(void)
    195 {
    196 	int masterfd;		/* master pty fd */
    197 	int slavefd;		/* slave pty fd */
    198 	int i;
    199 	char *slavename;
    200 	char c;
    201 
    202 	masterfd = open(MASTERCLONE, O_RDWR);
    203 	if (masterfd < 0) {
    204 		tst_brkm(TBROK | TERRNO, NULL, MASTERCLONE);
    205 	}
    206 
    207 	slavename = ptsname(masterfd);
    208 	if (slavename == NULL) {
    209 		tst_brkm(TBROK | TERRNO, NULL, "ptsname() call failed");
    210 	}
    211 
    212 	if (grantpt(masterfd) != 0) {
    213 		tst_brkm(TBROK | TERRNO, NULL, "grantpt() call failed");
    214 	}
    215 
    216 	if (unlockpt(masterfd) != 0) {
    217 		tst_brkm(TBROK | TERRNO, NULL, "unlockpt() call failed");
    218 	}
    219 
    220 	slavefd = open(slavename, O_RDWR);
    221 	if (slavefd < 0) {
    222 		tst_brkm(TBROK | TERRNO, NULL, "Could not open %s", slavename);
    223 	}
    224 
    225 	/*
    226 	 * close pty fds.  See what happens when we close the master
    227 	 * first.
    228 	 */
    229 	if (close(masterfd) != 0) {
    230 		tst_brkm(TBROK | TERRNO, NULL, "close()");
    231 	}
    232 
    233 	errno = 0;
    234 	if ((i = read(slavefd, &c, 1)) == 1) {
    235 		tst_brkm(TFAIL, NULL,
    236 			 "reading from slave fd should have failed, but didn't"
    237 			 "(read '%c')", c);
    238 	}
    239 
    240 	if ((i = write(slavefd, &c, 1)) == 1) {
    241 		tst_brkm(TFAIL, NULL,
    242 			 "writing to slave fd should have failed, but didn't");
    243 	}
    244 
    245 	if (ioctl(slavefd, TIOCGWINSZ, NULL) == 0) {
    246 		tst_brkm(TFAIL, NULL,
    247 			 "trying TIOCGWINSZ on slave fd should have failed, "
    248 			 "but didn't");
    249 	}
    250 
    251 	if (close(slavefd) != 0) {
    252 		tst_brkm(TBROK, NULL, "close");
    253 	}
    254 	tst_resm(TPASS, "test2");
    255 }
    256 
    257 /*
    258  * test operations on master with closed slave
    259  */
    260 static void test3(void)
    261 {
    262 	int masterfd;		/* master pty fd */
    263 
    264 	masterfd = open(MASTERCLONE, O_RDWR);
    265 	if (masterfd < 0) {
    266 		tst_brkm(TBROK, NULL, MASTERCLONE);
    267 	}
    268 
    269 	if (ioctl(masterfd, TIOCGWINSZ, NULL) == 0) {
    270 		tst_brkm(TFAIL | TERRNO, NULL,
    271 			 "trying TIOCGWINSZ on master with no open slave "
    272 			 "succeeded unexpectedly");
    273 	}
    274 	tst_resm(TPASS, "test3");
    275 }
    276 
    277 /*
    278  * test multiple opens on slave side of pty
    279  */
    280 static void test4(void)
    281 {
    282 	int masterfd;		/* master pty fd */
    283 	int slavefd;		/* slave pty fd */
    284 	int slavefd2;
    285 	int slavefd3;
    286 	char *slavename;
    287 
    288 	masterfd = open(MASTERCLONE, O_RDWR);
    289 	if (masterfd < 0) {
    290 		tst_brkm(TBROK, NULL, "%s", MASTERCLONE);
    291 	}
    292 
    293 	slavename = ptsname(masterfd);
    294 	if (slavename == NULL) {
    295 		tst_brkm(TBROK, NULL, "ptsname() call failed");
    296 	}
    297 
    298 	if (grantpt(masterfd) != 0) {
    299 		tst_brkm(TBROK, NULL, "grantpt() call failed");
    300 	}
    301 
    302 	if (unlockpt(masterfd) != 0) {
    303 		tst_brkm(TBROK | TERRNO, NULL, "unlockpt() call failed");
    304 	}
    305 
    306 	slavefd = open(slavename, O_RDWR);
    307 	if (slavefd < 0) {
    308 		tst_brkm(TBROK | TERRNO, NULL, "Could not open %s", slavename);
    309 	}
    310 
    311 	slavefd2 = open(slavename, O_RDWR);
    312 	if (slavefd < 0) {
    313 		tst_brkm(TFAIL | TERRNO, NULL, "Could not open %s (again)",
    314 			 slavename);
    315 	}
    316 
    317 	slavefd3 = open(slavename, O_RDWR);
    318 	if (slavefd < 0) {
    319 		tst_brkm(TFAIL | TERRNO, NULL, "Could not open %s (once more)",
    320 			 slavename);
    321 	}
    322 
    323 	/*
    324 	 * close pty fds.
    325 	 */
    326 	if (close(slavefd) != 0) {
    327 		tst_brkm(TBROK | TERRNO, NULL, "close slave");
    328 	}
    329 
    330 	if (close(slavefd2) != 0) {
    331 		tst_brkm(TBROK, NULL, "close slave again");
    332 	}
    333 
    334 	if (close(slavefd3) != 0) {
    335 		tst_brkm(TBROK, NULL, "close slave once more");
    336 	}
    337 
    338 	if (close(masterfd) != 0) {
    339 		tst_brkm(TBROK, NULL, "close master");
    340 	}
    341 	tst_resm(TPASS, "test4");
    342 }
    343 
    344 /*
    345  * test opening/closing lots of ptys in parallel.  We may run out
    346  * of ptys for this test depending on how the system is configured,
    347  * but that's not a fatal error.
    348  */
    349 static void test5(void)
    350 {
    351 	int masterfd;		/* master pty fd */
    352 	char *slavename;
    353 	int status;
    354 	int i;
    355 
    356 	for (i = 0; i < NUMPROCS; ++i) {
    357 		switch (fork()) {
    358 		case -1:
    359 			tst_brkm(TBROK, NULL, "fork()");
    360 			break;
    361 		case 0:
    362 			masterfd = open(MASTERCLONE, O_RDWR);
    363 			if (masterfd < 0) {
    364 				printf("proc %d: opening %s failed: %s",
    365 				       i, MASTERCLONE, strerror(errno));
    366 				exit(1);
    367 			}
    368 			if (grantpt(masterfd) != 0) {
    369 				printf("proc %d: grantpt() call failed: %s",
    370 				       i, strerror(errno));
    371 				exit(1);
    372 			}
    373 			slavename = ptsname(masterfd);
    374 			if (slavename == NULL) {
    375 				printf("proc %d: ptsname() call failed: %s",
    376 				       i, strerror(errno));
    377 				exit(1);
    378 			}
    379 			sleep(10);
    380 			if (close(masterfd) != 0) {
    381 				printf("proc %d: close failed: %s",
    382 				       i, strerror(errno));
    383 				exit(1);
    384 			}
    385 			exit(0);
    386 		default:
    387 			break;
    388 		}
    389 	}
    390 	while (wait(&status) > 0) {
    391 		if (status) {
    392 			tst_brkm(TFAIL, NULL,
    393 				 "child exited with non-zero status %d",
    394 				 status);
    395 		}
    396 	}
    397 	tst_resm(TPASS, "test5");
    398 }
    399 
    400 /*
    401  * main test driver
    402  */
    403 int main(int argc, char **argv)
    404 {
    405 	test1();
    406 	test2();
    407 	test3();
    408 	test4();
    409 	test5();
    410 
    411 	/*
    412 	 * all done
    413 	 */
    414 	tst_exit();
    415 }
    416