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@us.ibm.com */
21/* 06/30/2001	Port to Linux	nsharoff@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
42char *TCID = "pty01";		/* Test program identifier.    */
43int 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 */
74static 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 */
192static 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 */
252static 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 */
269static 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 */
332static 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 */
386int 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