xref: /minix3/minix/tests/test77.c (revision da21d850255e4f11bb2023f69a0b3aad4eab62e2)
1433d6423SLionel Sambuc /* Tests for opening/closing pseudo terminals - by D.C. van Moolenbroek */
2*da21d850SDavid van Moolenbroek /*
3*da21d850SDavid van Moolenbroek  * As of the introduction of Unix98 PTY support, this test set actually relies
4*da21d850SDavid van Moolenbroek  * on the ability to create Unix98 PTYs.  The system still supports old-style
5*da21d850SDavid van Moolenbroek  * PTYs but there is no way to force openpty(3) to use them.  However, part of
6*da21d850SDavid van Moolenbroek  * this test set can still be used to test old-style PTYs: first disable Unix98
7*da21d850SDavid van Moolenbroek  * PTYs, for example by unmounting PTYFS or temporarily removing /dev/ptmx, and
8*da21d850SDavid van Moolenbroek  * then run the a-f subtests from this test set as root.
9*da21d850SDavid van Moolenbroek  */
10433d6423SLionel Sambuc #include <stdlib.h>
11433d6423SLionel Sambuc #include <stdio.h>
12433d6423SLionel Sambuc #include <string.h>
13433d6423SLionel Sambuc #include <signal.h>
14433d6423SLionel Sambuc #include <termios.h>
15433d6423SLionel Sambuc #include <sys/wait.h>
16433d6423SLionel Sambuc #include <sys/syslimits.h>
17433d6423SLionel Sambuc #include <paths.h>
18*da21d850SDavid van Moolenbroek #include <dirent.h>
19*da21d850SDavid van Moolenbroek #include <grp.h>
20433d6423SLionel Sambuc #include <fcntl.h>
21433d6423SLionel Sambuc #include <util.h>
22433d6423SLionel Sambuc 
23433d6423SLionel Sambuc #define ITERATIONS 10
24433d6423SLionel Sambuc 
25*da21d850SDavid van Moolenbroek #define MIN_PTYS 4
26*da21d850SDavid van Moolenbroek 
27433d6423SLionel Sambuc #include "common.h"
28433d6423SLionel Sambuc 
29433d6423SLionel Sambuc static int sighups;		/* number of SIGHUP signals received */
30433d6423SLionel Sambuc 
31433d6423SLionel Sambuc /*
32433d6423SLionel Sambuc  * Signal handler for SIGHUP and SIGUSR1.
33433d6423SLionel Sambuc  */
34673c4e01SDavid van Moolenbroek static void
signal_handler(int sig)35433d6423SLionel Sambuc signal_handler(int sig)
36433d6423SLionel Sambuc {
37433d6423SLionel Sambuc 	if (sig == SIGHUP)
38433d6423SLionel Sambuc 		sighups++;
39433d6423SLionel Sambuc }
40433d6423SLionel Sambuc 
41433d6423SLionel Sambuc /*
42433d6423SLionel Sambuc  * Set the slave side of the pseudo terminal to raw mode.  This simplifies
43433d6423SLionel Sambuc  * testing communication.
44433d6423SLionel Sambuc  */
45433d6423SLionel Sambuc static void
make_raw(int slavefd)46433d6423SLionel Sambuc make_raw(int slavefd)
47433d6423SLionel Sambuc {
48433d6423SLionel Sambuc 	struct termios tios;
49433d6423SLionel Sambuc 
50*da21d850SDavid van Moolenbroek 	if (tcgetattr(slavefd, &tios) < 0) e(0);
51433d6423SLionel Sambuc 
52433d6423SLionel Sambuc 	cfmakeraw(&tios);
53433d6423SLionel Sambuc 
54*da21d850SDavid van Moolenbroek 	if (tcsetattr(slavefd, TCSANOW, &tios) < 0) e(0);
55433d6423SLionel Sambuc }
56433d6423SLionel Sambuc 
57433d6423SLionel Sambuc /*
58433d6423SLionel Sambuc  * See if the given pseudo terminal can successfully perform basic
59433d6423SLionel Sambuc  * communication between master and slave.
60433d6423SLionel Sambuc  */
61433d6423SLionel Sambuc static void
test_comm(int masterfd,int slavefd)62433d6423SLionel Sambuc test_comm(int masterfd, int slavefd)
63433d6423SLionel Sambuc {
64433d6423SLionel Sambuc 	char c;
65433d6423SLionel Sambuc 
66433d6423SLionel Sambuc 	make_raw(slavefd);
67433d6423SLionel Sambuc 
68433d6423SLionel Sambuc 	c = 'A';
69*da21d850SDavid van Moolenbroek 	if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
70*da21d850SDavid van Moolenbroek 	if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
71*da21d850SDavid van Moolenbroek 	if (c != 'A') e(0);
72433d6423SLionel Sambuc 
73433d6423SLionel Sambuc 	c = 'B';
74*da21d850SDavid van Moolenbroek 	if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
75*da21d850SDavid van Moolenbroek 	if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
76*da21d850SDavid van Moolenbroek 	if (c != 'B') e(0);
77433d6423SLionel Sambuc 
78433d6423SLionel Sambuc 	c = 'C';
79*da21d850SDavid van Moolenbroek 	if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
80*da21d850SDavid van Moolenbroek 	if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
81*da21d850SDavid van Moolenbroek 	if (c != 'C') e(0);
82433d6423SLionel Sambuc 
83433d6423SLionel Sambuc 	c = 'D';
84*da21d850SDavid van Moolenbroek 	if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
85*da21d850SDavid van Moolenbroek 	if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
86*da21d850SDavid van Moolenbroek 	if (c != 'D') e(0);
87433d6423SLionel Sambuc }
88433d6423SLionel Sambuc 
89433d6423SLionel Sambuc /*
90*da21d850SDavid van Moolenbroek  * Obtain a pseudo terminal.  The master end is opened and its file descriptor
91*da21d850SDavid van Moolenbroek  * stored in 'pfd'.  The slave path name is stored in 'tname'.  For old-style
92*da21d850SDavid van Moolenbroek  * PTYs, the function returns 1 and stores the master name in 'pname' if not
93*da21d850SDavid van Moolenbroek  * NULL.  For Unix98 PTYs, the function returns 0, in which case no master name
94*da21d850SDavid van Moolenbroek  * is available.  For old-style PTYs, the caller may close and reopen the
95*da21d850SDavid van Moolenbroek  * master.  In that case, we make the assumption that nobody snatches the pair
96*da21d850SDavid van Moolenbroek  * while we are running.  For Unix98 PTYs, the master must be kept open.
97433d6423SLionel Sambuc  */
98*da21d850SDavid van Moolenbroek static int
get_pty(int * pfd,char pname[PATH_MAX],char tname[PATH_MAX])99*da21d850SDavid van Moolenbroek get_pty(int *pfd, char pname[PATH_MAX], char tname[PATH_MAX])
100433d6423SLionel Sambuc {
101*da21d850SDavid van Moolenbroek 	char *name;
102433d6423SLionel Sambuc 	int len, masterfd, slavefd;
103433d6423SLionel Sambuc 
104433d6423SLionel Sambuc 	/*
105*da21d850SDavid van Moolenbroek 	 * First try Unix98 PTY allocation, mainly to avoid opening the slave
106*da21d850SDavid van Moolenbroek 	 * end immediately.  If this fails, try openpty(3) as well.
107433d6423SLionel Sambuc 	 */
108*da21d850SDavid van Moolenbroek 	if ((masterfd = posix_openpt(O_RDWR | O_NOCTTY)) != -1) {
109*da21d850SDavid van Moolenbroek 		if (grantpt(masterfd) != -1 && unlockpt(masterfd) != -1 &&
110*da21d850SDavid van Moolenbroek 		    (name = ptsname(masterfd)) != NULL) {
111*da21d850SDavid van Moolenbroek 			*pfd = masterfd;
112*da21d850SDavid van Moolenbroek 			strlcpy(tname, name, PATH_MAX);
113433d6423SLionel Sambuc 
114*da21d850SDavid van Moolenbroek 			return 0;
115*da21d850SDavid van Moolenbroek 		}
116*da21d850SDavid van Moolenbroek 		if (close(masterfd) < 0) e(0);
117*da21d850SDavid van Moolenbroek 	}
118433d6423SLionel Sambuc 
119*da21d850SDavid van Moolenbroek 	if (openpty(&masterfd, &slavefd, tname, NULL, NULL) < 0) e(0);
120433d6423SLionel Sambuc 
121433d6423SLionel Sambuc 	test_comm(masterfd, slavefd);
122433d6423SLionel Sambuc 
123*da21d850SDavid van Moolenbroek 	*pfd = masterfd;
124*da21d850SDavid van Moolenbroek 
125*da21d850SDavid van Moolenbroek 	if (close(slavefd) < 0) e(0);
126*da21d850SDavid van Moolenbroek 
127*da21d850SDavid van Moolenbroek 	/*
128*da21d850SDavid van Moolenbroek 	 * openpty(3) gives us only the slave name, but we also want the master
129*da21d850SDavid van Moolenbroek 	 * name.
130*da21d850SDavid van Moolenbroek 	 */
131*da21d850SDavid van Moolenbroek 	len = strlen(_PATH_DEV);
132*da21d850SDavid van Moolenbroek 	if (strncmp(tname, _PATH_DEV, len)) e(0);
133*da21d850SDavid van Moolenbroek 
134*da21d850SDavid van Moolenbroek 	if (strncmp(&tname[len], "tty", 3))
135*da21d850SDavid van Moolenbroek 		return 0; /* Unix98 after all?  Well okay, whatever.. */
136*da21d850SDavid van Moolenbroek 
137*da21d850SDavid van Moolenbroek 	if (pname != NULL) {
138*da21d850SDavid van Moolenbroek 		strlcpy(pname, tname, PATH_MAX);
139*da21d850SDavid van Moolenbroek 		pname[len] = 'p';
140*da21d850SDavid van Moolenbroek 	}
141*da21d850SDavid van Moolenbroek 
142*da21d850SDavid van Moolenbroek 	return 1;
143433d6423SLionel Sambuc }
144433d6423SLionel Sambuc 
145433d6423SLionel Sambuc /*
146433d6423SLionel Sambuc  * Test various orders of opening and closing the master and slave sides of a
147433d6423SLionel Sambuc  * pseudo terminal, as well as opening/closing one side without ever opening
148*da21d850SDavid van Moolenbroek  * the other.  This test is meaningful mainly for old-style pseudoterminals.
149433d6423SLionel Sambuc  */
150433d6423SLionel Sambuc static void
test77a(void)151433d6423SLionel Sambuc test77a(void)
152433d6423SLionel Sambuc {
153433d6423SLionel Sambuc 	struct sigaction act, oact;
154433d6423SLionel Sambuc 	char pname[PATH_MAX], tname[PATH_MAX];
155*da21d850SDavid van Moolenbroek 	int oldstyle, masterfd, slavefd;
156433d6423SLionel Sambuc 
157433d6423SLionel Sambuc 	subtest = 1;
158433d6423SLionel Sambuc 
159433d6423SLionel Sambuc 	/* We do not want to get SIGHUP signals in this test. */
160433d6423SLionel Sambuc 	memset(&act, 0, sizeof(act));
161433d6423SLionel Sambuc 	act.sa_handler = SIG_IGN;
162*da21d850SDavid van Moolenbroek 	if (sigaction(SIGHUP, &act, &oact) < 0) e(0);
163433d6423SLionel Sambuc 
164*da21d850SDavid van Moolenbroek 	/* Obtain a pseudo terminal. */
165*da21d850SDavid van Moolenbroek 	oldstyle = get_pty(&masterfd, pname, tname);
166433d6423SLionel Sambuc 
167*da21d850SDavid van Moolenbroek 	if (oldstyle) {
168*da21d850SDavid van Moolenbroek 		/* Try closing the master. */
169*da21d850SDavid van Moolenbroek 		if (close(masterfd) < 0) e(0);
170433d6423SLionel Sambuc 
171*da21d850SDavid van Moolenbroek 		/* See if we can reopen the master. */
172*da21d850SDavid van Moolenbroek 		if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
173*da21d850SDavid van Moolenbroek 	}
174433d6423SLionel Sambuc 
175*da21d850SDavid van Moolenbroek 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
176433d6423SLionel Sambuc 
177433d6423SLionel Sambuc 	test_comm(masterfd, slavefd);
178433d6423SLionel Sambuc 
179433d6423SLionel Sambuc 	/* In the meantime, test different closing orders. This is order A. */
180*da21d850SDavid van Moolenbroek 	if (close(slavefd) < 0) e(0);
181*da21d850SDavid van Moolenbroek 	if (close(masterfd) < 0) e(0);
182433d6423SLionel Sambuc 
183*da21d850SDavid van Moolenbroek 	/* Now try opening the pair (or a new pair) again. */
184*da21d850SDavid van Moolenbroek 	if (!oldstyle)
185*da21d850SDavid van Moolenbroek 		oldstyle = get_pty(&masterfd, pname, tname);
186*da21d850SDavid van Moolenbroek 	else
187*da21d850SDavid van Moolenbroek 		if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
188*da21d850SDavid van Moolenbroek 
189*da21d850SDavid van Moolenbroek 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
190433d6423SLionel Sambuc 
191433d6423SLionel Sambuc 	test_comm(masterfd, slavefd);
192433d6423SLionel Sambuc 
193*da21d850SDavid van Moolenbroek 	if (close(slavefd) < 0) e(0);
194433d6423SLionel Sambuc 
195433d6423SLionel Sambuc 	/*
196433d6423SLionel Sambuc 	 * Try reopening the slave after closing it.  It is not very important
197433d6423SLionel Sambuc 	 * that this works, but the TTY driver should currently support it.
198433d6423SLionel Sambuc 	 */
199*da21d850SDavid van Moolenbroek 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
200433d6423SLionel Sambuc 
201433d6423SLionel Sambuc 	test_comm(masterfd, slavefd);
202433d6423SLionel Sambuc 
203433d6423SLionel Sambuc 	/* This is closing order B. This may or may not cause a SIGHUP. */
204*da21d850SDavid van Moolenbroek 	if (close(masterfd) < 0) e(0);
205*da21d850SDavid van Moolenbroek 	if (close(slavefd) < 0) e(0);
206433d6423SLionel Sambuc 
207433d6423SLionel Sambuc 	/* Try the normal open procedure. */
208*da21d850SDavid van Moolenbroek 	if (!oldstyle)
209*da21d850SDavid van Moolenbroek 		oldstyle = get_pty(&masterfd, pname, tname);
210*da21d850SDavid van Moolenbroek 	else
211*da21d850SDavid van Moolenbroek 		if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
212*da21d850SDavid van Moolenbroek 
213*da21d850SDavid van Moolenbroek 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
214433d6423SLionel Sambuc 
215433d6423SLionel Sambuc 	test_comm(masterfd, slavefd);
216433d6423SLionel Sambuc 
217*da21d850SDavid van Moolenbroek 	if (close(slavefd) < 0) e(0);
218*da21d850SDavid van Moolenbroek 	if (close(masterfd) < 0) e(0);
219433d6423SLionel Sambuc 
220*da21d850SDavid van Moolenbroek 	/*
221*da21d850SDavid van Moolenbroek 	 * Try reopening and closing the slave, without opening the master.
222*da21d850SDavid van Moolenbroek 	 * This should work on old-style PTYS, but not on Unix98 PTYs.
223*da21d850SDavid van Moolenbroek 	 */
224*da21d850SDavid van Moolenbroek 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) >= 0) {
225*da21d850SDavid van Moolenbroek 		if (!oldstyle) e(0);
226433d6423SLionel Sambuc 
227*da21d850SDavid van Moolenbroek 		if (close(slavefd) < 0) e(0);
228*da21d850SDavid van Moolenbroek 	} else
229*da21d850SDavid van Moolenbroek 		if (oldstyle) e(0);
230433d6423SLionel Sambuc 
231433d6423SLionel Sambuc 	/* Again, try the normal open procedure. */
232*da21d850SDavid van Moolenbroek 	if (!oldstyle)
233*da21d850SDavid van Moolenbroek 		oldstyle = get_pty(&masterfd, pname, tname);
234*da21d850SDavid van Moolenbroek 	else
235*da21d850SDavid van Moolenbroek 		if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
236*da21d850SDavid van Moolenbroek 
237*da21d850SDavid van Moolenbroek 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
238433d6423SLionel Sambuc 
239433d6423SLionel Sambuc 	test_comm(masterfd, slavefd);
240433d6423SLionel Sambuc 
241*da21d850SDavid van Moolenbroek 	if (close(slavefd) < 0) e(0);
242*da21d850SDavid van Moolenbroek 	if (close(masterfd) < 0) e(0);
243433d6423SLionel Sambuc 
244*da21d850SDavid van Moolenbroek 	/*
245*da21d850SDavid van Moolenbroek 	 * Finally, try opening the slave first.  This does not work with
246*da21d850SDavid van Moolenbroek 	 * Unix98 PTYs.
247*da21d850SDavid van Moolenbroek 	 */
248*da21d850SDavid van Moolenbroek 	if (oldstyle) {
249*da21d850SDavid van Moolenbroek 		if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
250*da21d850SDavid van Moolenbroek 		if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
251433d6423SLionel Sambuc 
252433d6423SLionel Sambuc 		test_comm(masterfd, slavefd);
253433d6423SLionel Sambuc 
254*da21d850SDavid van Moolenbroek 		if (close(slavefd) < 0) e(0);
255*da21d850SDavid van Moolenbroek 		if (close(masterfd) < 0) e(0);
256*da21d850SDavid van Moolenbroek 	}
257433d6423SLionel Sambuc 
258*da21d850SDavid van Moolenbroek 	if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
259433d6423SLionel Sambuc }
260433d6423SLionel Sambuc 
261433d6423SLionel Sambuc /*
262433d6423SLionel Sambuc  * Test opening a single side multiple times.
263433d6423SLionel Sambuc  */
264433d6423SLionel Sambuc static void
test77b(void)265433d6423SLionel Sambuc test77b(void)
266433d6423SLionel Sambuc {
267433d6423SLionel Sambuc 	char pname[PATH_MAX], tname[PATH_MAX];
268*da21d850SDavid van Moolenbroek 	int oldstyle, masterfd, slavefd, extrafd;
269433d6423SLionel Sambuc 
270433d6423SLionel Sambuc 	subtest = 2;
271433d6423SLionel Sambuc 
272*da21d850SDavid van Moolenbroek 	/* Obtain a pseudo terminal. */
273*da21d850SDavid van Moolenbroek 	oldstyle = get_pty(&masterfd, pname, tname);
274433d6423SLionel Sambuc 
275*da21d850SDavid van Moolenbroek 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
276*da21d850SDavid van Moolenbroek 
277*da21d850SDavid van Moolenbroek 	/*
278*da21d850SDavid van Moolenbroek 	 * It must not be possible to open the master multiple times.  Doing so
279*da21d850SDavid van Moolenbroek 	 * is possible only if we have a named master, i.e., an old-style PTY.
280*da21d850SDavid van Moolenbroek 	 */
281*da21d850SDavid van Moolenbroek 	test_comm(masterfd, slavefd);
282*da21d850SDavid van Moolenbroek 
283*da21d850SDavid van Moolenbroek 	if (oldstyle) {
284*da21d850SDavid van Moolenbroek 		if ((extrafd = open(pname, O_RDWR | O_NOCTTY)) >= 0) e(0);
285*da21d850SDavid van Moolenbroek 		if (errno != EIO) e(0);
286*da21d850SDavid van Moolenbroek 	}
287433d6423SLionel Sambuc 
288433d6423SLionel Sambuc 	test_comm(masterfd, slavefd);
289433d6423SLionel Sambuc 
290*da21d850SDavid van Moolenbroek 	if (close(slavefd) < 0) e(0);
291*da21d850SDavid van Moolenbroek 	if (close(masterfd) < 0) e(0);
292433d6423SLionel Sambuc 
293433d6423SLionel Sambuc 	/* The slave can be opened multiple times, though. */
294*da21d850SDavid van Moolenbroek 	oldstyle = get_pty(&masterfd, pname, tname);
295*da21d850SDavid van Moolenbroek 
296*da21d850SDavid van Moolenbroek 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
297433d6423SLionel Sambuc 
298433d6423SLionel Sambuc 	test_comm(masterfd, slavefd);
299433d6423SLionel Sambuc 
300*da21d850SDavid van Moolenbroek 	if ((extrafd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
301433d6423SLionel Sambuc 
302433d6423SLionel Sambuc 	test_comm(masterfd, extrafd);
303433d6423SLionel Sambuc 	test_comm(masterfd, slavefd);
304433d6423SLionel Sambuc 
305*da21d850SDavid van Moolenbroek 	if (close(slavefd) < 0) e(0);
306*da21d850SDavid van Moolenbroek 	if (close(extrafd) < 0) e(0);
307*da21d850SDavid van Moolenbroek 	if (close(masterfd) < 0) e(0);
308433d6423SLionel Sambuc }
309433d6423SLionel Sambuc 
310433d6423SLionel Sambuc /*
311433d6423SLionel Sambuc  * Test communication on half-open pseudo terminals.
312433d6423SLionel Sambuc  */
313433d6423SLionel Sambuc static void
test77c(void)314433d6423SLionel Sambuc test77c(void)
315433d6423SLionel Sambuc {
316433d6423SLionel Sambuc 	struct sigaction act, oact;
317433d6423SLionel Sambuc 	char pname[PATH_MAX], tname[PATH_MAX];
318*da21d850SDavid van Moolenbroek 	int oldstyle, masterfd, slavefd;
319433d6423SLionel Sambuc 	char c;
320433d6423SLionel Sambuc 
321433d6423SLionel Sambuc 	subtest = 3;
322433d6423SLionel Sambuc 
323433d6423SLionel Sambuc 	/* We do not want to get SIGHUP signals in this test. */
324433d6423SLionel Sambuc 	memset(&act, 0, sizeof(act));
325433d6423SLionel Sambuc 	act.sa_handler = SIG_IGN;
326*da21d850SDavid van Moolenbroek 	if (sigaction(SIGHUP, &act, &oact) < 0) e(0);
327433d6423SLionel Sambuc 
328*da21d850SDavid van Moolenbroek 	/* Obtain a pseudo terminal. */
329*da21d850SDavid van Moolenbroek 	oldstyle = get_pty(&masterfd, pname, tname);
330433d6423SLionel Sambuc 
331*da21d850SDavid van Moolenbroek 	/*
332*da21d850SDavid van Moolenbroek 	 * For old-style pseudo terminals, we have just opened and closed the
333*da21d850SDavid van Moolenbroek 	 * slave end, which alters the behavior we are testing below.  Close
334*da21d850SDavid van Moolenbroek 	 * and reopen the master to start fresh.
335*da21d850SDavid van Moolenbroek 	 */
336*da21d850SDavid van Moolenbroek 	if (oldstyle) {
337*da21d850SDavid van Moolenbroek 		if (close(masterfd) < 0) e(0);
338*da21d850SDavid van Moolenbroek 
339*da21d850SDavid van Moolenbroek 		if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
340*da21d850SDavid van Moolenbroek 	}
341433d6423SLionel Sambuc 
342433d6423SLionel Sambuc 	/* Writes to the master should be buffered until there is a slave. */
343433d6423SLionel Sambuc 	c = 'E';
344*da21d850SDavid van Moolenbroek 	if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
345433d6423SLionel Sambuc 
346*da21d850SDavid van Moolenbroek 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
347433d6423SLionel Sambuc 
348433d6423SLionel Sambuc 	make_raw(slavefd);
349433d6423SLionel Sambuc 
350*da21d850SDavid van Moolenbroek 	if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
351*da21d850SDavid van Moolenbroek 	if (c != 'E') e(0);
352433d6423SLionel Sambuc 
353433d6423SLionel Sambuc 	/* Discard the echo on the master. */
354*da21d850SDavid van Moolenbroek 	if (tcflush(slavefd, TCOFLUSH) != 0) e(0);
355433d6423SLionel Sambuc 
356433d6423SLionel Sambuc 	test_comm(masterfd, slavefd);
357433d6423SLionel Sambuc 
358*da21d850SDavid van Moolenbroek 	if (close(slavefd) < 0) e(0);
359433d6423SLionel Sambuc 
360433d6423SLionel Sambuc 	/* Writes to the master after the slave has been closed should fail. */
361*da21d850SDavid van Moolenbroek 	if (write(masterfd, &c, sizeof(c)) >= 0) e(0);
362*da21d850SDavid van Moolenbroek 	if (errno != EIO) e(0);
363433d6423SLionel Sambuc 
364*da21d850SDavid van Moolenbroek 	if (oldstyle)
365*da21d850SDavid van Moolenbroek 		if (close(masterfd) < 0) e(0);
366433d6423SLionel Sambuc 
367*da21d850SDavid van Moolenbroek 	/*
368*da21d850SDavid van Moolenbroek 	 * Writes to the slave should be buffered until there is a master.
369*da21d850SDavid van Moolenbroek 	 * This applies to old-style PTYs only.
370*da21d850SDavid van Moolenbroek 	 */
371*da21d850SDavid van Moolenbroek 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
372433d6423SLionel Sambuc 
373*da21d850SDavid van Moolenbroek 	if (oldstyle) {
374433d6423SLionel Sambuc 		make_raw(slavefd);
375433d6423SLionel Sambuc 
376433d6423SLionel Sambuc 		c = 'F';
377*da21d850SDavid van Moolenbroek 		if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
378433d6423SLionel Sambuc 
379*da21d850SDavid van Moolenbroek 		if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
380433d6423SLionel Sambuc 
381*da21d850SDavid van Moolenbroek 		if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
382*da21d850SDavid van Moolenbroek 		if (c != 'F') e(0);
383*da21d850SDavid van Moolenbroek 	}
384433d6423SLionel Sambuc 
385433d6423SLionel Sambuc 	test_comm(masterfd, slavefd);
386433d6423SLionel Sambuc 
387*da21d850SDavid van Moolenbroek 	if (close(masterfd) < 0) e(0);
388433d6423SLionel Sambuc 
389*da21d850SDavid van Moolenbroek 	if (write(slavefd, &c, sizeof(c)) >= 0) e(0);
390*da21d850SDavid van Moolenbroek 	if (errno != EIO) e(0);
391433d6423SLionel Sambuc 
392673c4e01SDavid van Moolenbroek 	/* Reads from the slave should return EOF if the master is gone. */
393*da21d850SDavid van Moolenbroek 	if (read(slavefd, &c, sizeof(c)) != 0) e(0);
394433d6423SLionel Sambuc 
395*da21d850SDavid van Moolenbroek 	if (close(slavefd) < 0) e(0);
396673c4e01SDavid van Moolenbroek 
397*da21d850SDavid van Moolenbroek 	if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
398673c4e01SDavid van Moolenbroek }
399673c4e01SDavid van Moolenbroek 
400673c4e01SDavid van Moolenbroek /*
401673c4e01SDavid van Moolenbroek  * Wait for a child process to terminate.  Return 0 if the child exited without
402673c4e01SDavid van Moolenbroek  * errors, -1 otherwise.
403673c4e01SDavid van Moolenbroek  */
404673c4e01SDavid van Moolenbroek static int
waitchild(void)405673c4e01SDavid van Moolenbroek waitchild(void)
406673c4e01SDavid van Moolenbroek {
407673c4e01SDavid van Moolenbroek 	int status;
408673c4e01SDavid van Moolenbroek 
409673c4e01SDavid van Moolenbroek 	if (wait(&status) <= 0) return -1;
410673c4e01SDavid van Moolenbroek 	if (!WIFEXITED(status)) return -1;
411673c4e01SDavid van Moolenbroek 	if (WEXITSTATUS(status) != 0) return -1;
412673c4e01SDavid van Moolenbroek 
413673c4e01SDavid van Moolenbroek 	return 0;
414433d6423SLionel Sambuc }
415433d6423SLionel Sambuc 
416433d6423SLionel Sambuc /*
417433d6423SLionel Sambuc  * Test opening the slave side with and without the O_NOCTTY flag.
418433d6423SLionel Sambuc  */
419433d6423SLionel Sambuc static void
test77d(void)420433d6423SLionel Sambuc test77d(void)
421433d6423SLionel Sambuc {
422433d6423SLionel Sambuc 	char pname[PATH_MAX], tname[PATH_MAX];
423433d6423SLionel Sambuc 	int masterfd, slavefd;
424433d6423SLionel Sambuc 
425433d6423SLionel Sambuc 	subtest = 4;
426433d6423SLionel Sambuc 
427433d6423SLionel Sambuc 	/* Make ourselves process group leader if we aren't already. */
428433d6423SLionel Sambuc 	(void)setsid();
429433d6423SLionel Sambuc 
430*da21d850SDavid van Moolenbroek 	/* Obtain a pseudo terminal. */
431*da21d850SDavid van Moolenbroek 	(void)get_pty(&masterfd, NULL, tname);
432433d6423SLionel Sambuc 
433433d6423SLionel Sambuc 	/*
434433d6423SLionel Sambuc 	 * Opening the slave with O_NOCTTY should not change its controlling
435433d6423SLionel Sambuc 	 * terminal.
436433d6423SLionel Sambuc 	 */
437433d6423SLionel Sambuc 	switch (fork()) {
438433d6423SLionel Sambuc 	case 0:
439*da21d850SDavid van Moolenbroek 		if (close(masterfd) < 0) e(0);
440433d6423SLionel Sambuc 
441*da21d850SDavid van Moolenbroek 		if (setsid() < 0) e(0);
442433d6423SLionel Sambuc 
443*da21d850SDavid van Moolenbroek 		if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
444*da21d850SDavid van Moolenbroek 
445*da21d850SDavid van Moolenbroek 		if (open("/dev/tty", O_RDWR) >= 0) e(0);
446*da21d850SDavid van Moolenbroek 		if (errno != ENXIO) e(0);
447433d6423SLionel Sambuc 
448673c4e01SDavid van Moolenbroek 		exit(errct);
449433d6423SLionel Sambuc 	case -1:
450*da21d850SDavid van Moolenbroek 		e(0);
451433d6423SLionel Sambuc 	default:
452433d6423SLionel Sambuc 		break;
453433d6423SLionel Sambuc 	}
454433d6423SLionel Sambuc 
455*da21d850SDavid van Moolenbroek 	if (waitchild() < 0) e(0);
456433d6423SLionel Sambuc 
457*da21d850SDavid van Moolenbroek 	if (close(masterfd) < 0) e(0);
458433d6423SLionel Sambuc 
459*da21d850SDavid van Moolenbroek 	(void)get_pty(&masterfd, pname, tname);
460433d6423SLionel Sambuc 
461433d6423SLionel Sambuc 	/*
462433d6423SLionel Sambuc 	 * Opening the slave without O_NOCTTY should change its controlling
463433d6423SLionel Sambuc 	 * terminal, though.
464433d6423SLionel Sambuc 	 */
465433d6423SLionel Sambuc 	switch (fork()) {
466433d6423SLionel Sambuc 	case 0:
467*da21d850SDavid van Moolenbroek 		if (close(masterfd) < 0) e(0);
468433d6423SLionel Sambuc 
469*da21d850SDavid van Moolenbroek 		if (setsid() < 0) e(0);
470433d6423SLionel Sambuc 
471*da21d850SDavid van Moolenbroek 		if ((slavefd = open(tname, O_RDWR)) < 0) e(0);
472*da21d850SDavid van Moolenbroek 
473*da21d850SDavid van Moolenbroek 		if (open("/dev/tty", O_RDWR) < 0) e(0);
474433d6423SLionel Sambuc 
475673c4e01SDavid van Moolenbroek 		exit(errct);
476433d6423SLionel Sambuc 	case -1:
477*da21d850SDavid van Moolenbroek 		e(0);
478433d6423SLionel Sambuc 	default:
479433d6423SLionel Sambuc 		break;
480433d6423SLionel Sambuc 	}
481433d6423SLionel Sambuc 
482*da21d850SDavid van Moolenbroek 	if (waitchild() < 0) e(0);
483433d6423SLionel Sambuc 
484*da21d850SDavid van Moolenbroek 	if (close(masterfd) < 0) e(0);
485433d6423SLionel Sambuc }
486433d6423SLionel Sambuc 
487433d6423SLionel Sambuc /*
488433d6423SLionel Sambuc  * Test receiving of SIGHUP on master hang-up.  All of the tests so far have
489433d6423SLionel Sambuc  * ignored SIGHUP, and probably would not have received one anyway, since the
490433d6423SLionel Sambuc  * process was not its own session leader.  Time to test this aspect.
491433d6423SLionel Sambuc  */
492433d6423SLionel Sambuc static void
test77e(void)493433d6423SLionel Sambuc test77e(void)
494433d6423SLionel Sambuc {
495433d6423SLionel Sambuc 	struct sigaction act, hup_oact, usr_oact;
496433d6423SLionel Sambuc 	sigset_t set, oset;
497*da21d850SDavid van Moolenbroek 	char tname[PATH_MAX];
498433d6423SLionel Sambuc 	int masterfd, slavefd;
499433d6423SLionel Sambuc 
500433d6423SLionel Sambuc 	subtest = 5;
501433d6423SLionel Sambuc 
502*da21d850SDavid van Moolenbroek 	memset(&act, 0, sizeof(act));
503*da21d850SDavid van Moolenbroek 	act.sa_handler = signal_handler;
504*da21d850SDavid van Moolenbroek 	if (sigaction(SIGHUP, &act, &hup_oact) < 0) e(0);
505433d6423SLionel Sambuc 
506433d6423SLionel Sambuc 	memset(&act, 0, sizeof(act));
507433d6423SLionel Sambuc 	act.sa_handler = signal_handler;
508*da21d850SDavid van Moolenbroek 	if (sigaction(SIGUSR1, &act, &usr_oact) < 0) e(0);
509433d6423SLionel Sambuc 
510433d6423SLionel Sambuc 	sigemptyset(&set);
511433d6423SLionel Sambuc 	sigaddset(&set, SIGHUP);
512433d6423SLionel Sambuc 	sigaddset(&set, SIGUSR1);
513*da21d850SDavid van Moolenbroek 	if (sigprocmask(SIG_BLOCK, &set, &oset) < 0) e(0);
514433d6423SLionel Sambuc 
515433d6423SLionel Sambuc 	sighups = 0;
516433d6423SLionel Sambuc 
517433d6423SLionel Sambuc 	/* Make ourselves process group leader if we aren't already. */
518433d6423SLionel Sambuc 	(void)setsid();
519433d6423SLionel Sambuc 
520*da21d850SDavid van Moolenbroek 	/* Obtain a pseudo terminal. */
521*da21d850SDavid van Moolenbroek 	(void)get_pty(&masterfd, NULL, tname);
522433d6423SLionel Sambuc 
523433d6423SLionel Sambuc 	switch (fork()) {
524433d6423SLionel Sambuc 	case 0:
525*da21d850SDavid van Moolenbroek 		if (close(masterfd) < 0) e(0);
526433d6423SLionel Sambuc 
527433d6423SLionel Sambuc 		/* Become session leader. */
528*da21d850SDavid van Moolenbroek 		if (setsid() < 0) e(0);
529433d6423SLionel Sambuc 
530*da21d850SDavid van Moolenbroek 		if ((slavefd = open(tname, O_RDWR)) < 0) e(0);
531433d6423SLionel Sambuc 
532433d6423SLionel Sambuc 		/* Tell the parent we are ready. */
533433d6423SLionel Sambuc 		kill(getppid(), SIGUSR1);
534433d6423SLionel Sambuc 
535433d6423SLionel Sambuc 		/* We should now get a SIGHUP. */
536433d6423SLionel Sambuc 		set = oset;
537*da21d850SDavid van Moolenbroek 		if (sigsuspend(&set) >= 0) e(0);
538433d6423SLionel Sambuc 
539*da21d850SDavid van Moolenbroek 		if (sighups != 1) e(0);
540433d6423SLionel Sambuc 
541673c4e01SDavid van Moolenbroek 		exit(errct);
542433d6423SLionel Sambuc 	case -1:
543*da21d850SDavid van Moolenbroek 		e(0);
544433d6423SLionel Sambuc 	default:
545433d6423SLionel Sambuc 		break;
546433d6423SLionel Sambuc 	}
547433d6423SLionel Sambuc 
548433d6423SLionel Sambuc 	/* Wait for SIGUSR1 from the child. */
549433d6423SLionel Sambuc 	set = oset;
550*da21d850SDavid van Moolenbroek 	if (sigsuspend(&set) >= 0) e(0);
551433d6423SLionel Sambuc 
552433d6423SLionel Sambuc 	/* Closing the master should now raise a SIGHUP signal in the child. */
553*da21d850SDavid van Moolenbroek 	if (close(masterfd) < 0) e(0);
554433d6423SLionel Sambuc 
555*da21d850SDavid van Moolenbroek 	if (waitchild() < 0) e(0);
556433d6423SLionel Sambuc 
557*da21d850SDavid van Moolenbroek 	if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0) e(0);
558433d6423SLionel Sambuc 
559*da21d850SDavid van Moolenbroek 	if (sigaction(SIGHUP, &hup_oact, NULL) < 0) e(0);
560*da21d850SDavid van Moolenbroek 	if (sigaction(SIGUSR1, &usr_oact, NULL) < 0) e(0);
561433d6423SLionel Sambuc }
562433d6423SLionel Sambuc 
56327d0ecdbSDavid van Moolenbroek /*
56427d0ecdbSDavid van Moolenbroek  * Test basic select functionality on /dev/tty.  While this test should not be
56527d0ecdbSDavid van Moolenbroek  * part of this test set, we already have all the infrastructure we need here.
56627d0ecdbSDavid van Moolenbroek  */
56727d0ecdbSDavid van Moolenbroek static void
test77f(void)56827d0ecdbSDavid van Moolenbroek test77f(void)
56927d0ecdbSDavid van Moolenbroek {
57027d0ecdbSDavid van Moolenbroek 	struct sigaction act, oact;
571*da21d850SDavid van Moolenbroek 	char c, tname[PATH_MAX];
57227d0ecdbSDavid van Moolenbroek 	struct timeval tv;
57327d0ecdbSDavid van Moolenbroek 	fd_set fd_set;
57427d0ecdbSDavid van Moolenbroek 	int fd, maxfd, masterfd, slavefd;
57527d0ecdbSDavid van Moolenbroek 
57627d0ecdbSDavid van Moolenbroek 	subtest = 6;
57727d0ecdbSDavid van Moolenbroek 
57827d0ecdbSDavid van Moolenbroek 	/* We do not want to get SIGHUP signals in this test. */
57927d0ecdbSDavid van Moolenbroek 	memset(&act, 0, sizeof(act));
58027d0ecdbSDavid van Moolenbroek 	act.sa_handler = SIG_IGN;
581*da21d850SDavid van Moolenbroek 	if (sigaction(SIGHUP, &act, &oact) < 0) e(0);
58227d0ecdbSDavid van Moolenbroek 
583*da21d850SDavid van Moolenbroek 	/* Obtain a pseudo terminal. */
584*da21d850SDavid van Moolenbroek 	(void)get_pty(&masterfd, NULL, tname);
58527d0ecdbSDavid van Moolenbroek 
58627d0ecdbSDavid van Moolenbroek 	switch (fork()) {
58727d0ecdbSDavid van Moolenbroek 	case 0:
588*da21d850SDavid van Moolenbroek 		if (close(masterfd) < 0) e(0);
58927d0ecdbSDavid van Moolenbroek 
590*da21d850SDavid van Moolenbroek 		if (setsid() < 0) e(0);
59127d0ecdbSDavid van Moolenbroek 
592*da21d850SDavid van Moolenbroek 		if ((slavefd = open(tname, O_RDWR)) < 0) e(0);
59327d0ecdbSDavid van Moolenbroek 
594*da21d850SDavid van Moolenbroek 		if ((fd = open("/dev/tty", O_RDWR)) < 0) e(0);
59527d0ecdbSDavid van Moolenbroek 
59627d0ecdbSDavid van Moolenbroek 		make_raw(fd);
59727d0ecdbSDavid van Moolenbroek 
59827d0ecdbSDavid van Moolenbroek 		/* Without slave input, /dev/tty is not ready for reading. */
59927d0ecdbSDavid van Moolenbroek 		FD_ZERO(&fd_set);
60027d0ecdbSDavid van Moolenbroek 		FD_SET(fd, &fd_set);
60127d0ecdbSDavid van Moolenbroek 		tv.tv_sec = 0;
60227d0ecdbSDavid van Moolenbroek 		tv.tv_usec = 0;
60327d0ecdbSDavid van Moolenbroek 
604*da21d850SDavid van Moolenbroek 		if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0);
605*da21d850SDavid van Moolenbroek 		if (FD_ISSET(fd, &fd_set)) e(0);
60627d0ecdbSDavid van Moolenbroek 
60727d0ecdbSDavid van Moolenbroek 		FD_SET(fd, &fd_set);
60827d0ecdbSDavid van Moolenbroek 		tv.tv_sec = 0;
60927d0ecdbSDavid van Moolenbroek 		tv.tv_usec = 10000;
61027d0ecdbSDavid van Moolenbroek 
611*da21d850SDavid van Moolenbroek 		if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0);
612*da21d850SDavid van Moolenbroek 		if (FD_ISSET(fd, &fd_set)) e(0);
61327d0ecdbSDavid van Moolenbroek 
61427d0ecdbSDavid van Moolenbroek 		/* It will be ready for writing, though. */
61527d0ecdbSDavid van Moolenbroek 		FD_SET(fd, &fd_set);
61627d0ecdbSDavid van Moolenbroek 
617*da21d850SDavid van Moolenbroek 		if (select(fd + 1, NULL, &fd_set, NULL, NULL) != 1) e(0);
618*da21d850SDavid van Moolenbroek 		if (!FD_ISSET(fd, &fd_set)) e(0);
61927d0ecdbSDavid van Moolenbroek 
62027d0ecdbSDavid van Moolenbroek 		/* Test mixing file descriptors to the same terminal. */
62127d0ecdbSDavid van Moolenbroek 		FD_ZERO(&fd_set);
62227d0ecdbSDavid van Moolenbroek 		FD_SET(fd, &fd_set);
62327d0ecdbSDavid van Moolenbroek 		FD_SET(slavefd, &fd_set);
62427d0ecdbSDavid van Moolenbroek 		tv.tv_sec = 0;
62527d0ecdbSDavid van Moolenbroek 		tv.tv_usec = 10000;
62627d0ecdbSDavid van Moolenbroek 
62727d0ecdbSDavid van Moolenbroek 		maxfd = fd > slavefd ? fd : slavefd;
628*da21d850SDavid van Moolenbroek 		if (select(maxfd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0);
629*da21d850SDavid van Moolenbroek 		if (FD_ISSET(fd, &fd_set)) e(0);
630*da21d850SDavid van Moolenbroek 		if (FD_ISSET(slavefd, &fd_set)) e(0);
63127d0ecdbSDavid van Moolenbroek 
63227d0ecdbSDavid van Moolenbroek 		/* The delayed echo on the master must wake up our select. */
63327d0ecdbSDavid van Moolenbroek 		c = 'A';
634*da21d850SDavid van Moolenbroek 		if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
63527d0ecdbSDavid van Moolenbroek 
63627d0ecdbSDavid van Moolenbroek 		FD_ZERO(&fd_set);
63727d0ecdbSDavid van Moolenbroek 		FD_SET(fd, &fd_set);
63827d0ecdbSDavid van Moolenbroek 
639*da21d850SDavid van Moolenbroek 		if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0);
640*da21d850SDavid van Moolenbroek 		if (!FD_ISSET(fd, &fd_set)) e(0);
64127d0ecdbSDavid van Moolenbroek 
64227d0ecdbSDavid van Moolenbroek 		/* Select must now still flag readiness for reading. */
64327d0ecdbSDavid van Moolenbroek 		tv.tv_sec = 0;
64427d0ecdbSDavid van Moolenbroek 		tv.tv_usec = 0;
64527d0ecdbSDavid van Moolenbroek 
646*da21d850SDavid van Moolenbroek 		if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 1) e(0);
647*da21d850SDavid van Moolenbroek 		if (!FD_ISSET(fd, &fd_set)) e(0);
64827d0ecdbSDavid van Moolenbroek 
64927d0ecdbSDavid van Moolenbroek 		/* That is, until we read the byte. */
650*da21d850SDavid van Moolenbroek 		if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
651*da21d850SDavid van Moolenbroek 		if (c != 'B') e(0);
65227d0ecdbSDavid van Moolenbroek 
653*da21d850SDavid van Moolenbroek 		if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0);
654*da21d850SDavid van Moolenbroek 		if (FD_ISSET(fd, &fd_set)) e(0);
65527d0ecdbSDavid van Moolenbroek 
65627d0ecdbSDavid van Moolenbroek 		/* Ask the parent to close the master. */
65727d0ecdbSDavid van Moolenbroek 		c = 'C';
658*da21d850SDavid van Moolenbroek 		if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
65927d0ecdbSDavid van Moolenbroek 
66027d0ecdbSDavid van Moolenbroek 		FD_SET(fd, &fd_set);
66127d0ecdbSDavid van Moolenbroek 
66227d0ecdbSDavid van Moolenbroek 		/* The closure must cause an EOF condition on the slave. */
663*da21d850SDavid van Moolenbroek 		if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0);
664*da21d850SDavid van Moolenbroek 		if (!FD_ISSET(fd, &fd_set)) e(0);
66527d0ecdbSDavid van Moolenbroek 
666*da21d850SDavid van Moolenbroek 		if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0);
667*da21d850SDavid van Moolenbroek 		if (!FD_ISSET(fd, &fd_set)) e(0);
66827d0ecdbSDavid van Moolenbroek 
669*da21d850SDavid van Moolenbroek 		if (read(slavefd, &c, sizeof(c)) != 0) e(0);
67027d0ecdbSDavid van Moolenbroek 
67127d0ecdbSDavid van Moolenbroek 		exit(errct);
67227d0ecdbSDavid van Moolenbroek 	case -1:
673*da21d850SDavid van Moolenbroek 		e(0);
67427d0ecdbSDavid van Moolenbroek 	default:
67527d0ecdbSDavid van Moolenbroek 		/* Wait for the child to write something to the slave. */
67627d0ecdbSDavid van Moolenbroek 		FD_ZERO(&fd_set);
67727d0ecdbSDavid van Moolenbroek 		FD_SET(masterfd, &fd_set);
67827d0ecdbSDavid van Moolenbroek 
67927d0ecdbSDavid van Moolenbroek 		if (select(masterfd + 1, &fd_set, NULL, NULL, NULL) != 1)
680*da21d850SDavid van Moolenbroek 			e(0);
681*da21d850SDavid van Moolenbroek 		if (!FD_ISSET(masterfd, &fd_set)) e(0);
68227d0ecdbSDavid van Moolenbroek 
683*da21d850SDavid van Moolenbroek 		if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
684*da21d850SDavid van Moolenbroek 		if (c != 'A') e(0);
68527d0ecdbSDavid van Moolenbroek 
68627d0ecdbSDavid van Moolenbroek 		/* Write a reply once the child is blocked in its select. */
68727d0ecdbSDavid van Moolenbroek 		tv.tv_sec = 1;
68827d0ecdbSDavid van Moolenbroek 		tv.tv_usec = 0;
68927d0ecdbSDavid van Moolenbroek 		if (select(masterfd + 1, &fd_set, NULL, NULL, &tv) != 0)
690*da21d850SDavid van Moolenbroek 			e(0);
69127d0ecdbSDavid van Moolenbroek 
69227d0ecdbSDavid van Moolenbroek 		c = 'B';
693*da21d850SDavid van Moolenbroek 		if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
69427d0ecdbSDavid van Moolenbroek 
69527d0ecdbSDavid van Moolenbroek 		/* Wait for the child to request closing the master. */
696*da21d850SDavid van Moolenbroek 		if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
697*da21d850SDavid van Moolenbroek 		if (c != 'C') e(0);
69827d0ecdbSDavid van Moolenbroek 
69927d0ecdbSDavid van Moolenbroek 		/* Close the master once the child is blocked in its select. */
70027d0ecdbSDavid van Moolenbroek 		sleep(1);
70127d0ecdbSDavid van Moolenbroek 
70227d0ecdbSDavid van Moolenbroek 		close(masterfd);
70327d0ecdbSDavid van Moolenbroek 
70427d0ecdbSDavid van Moolenbroek 		break;
70527d0ecdbSDavid van Moolenbroek 	}
70627d0ecdbSDavid van Moolenbroek 
707*da21d850SDavid van Moolenbroek 	if (waitchild() < 0) e(0);
70827d0ecdbSDavid van Moolenbroek 
709*da21d850SDavid van Moolenbroek 	if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
710*da21d850SDavid van Moolenbroek }
711*da21d850SDavid van Moolenbroek 
712*da21d850SDavid van Moolenbroek /*
713*da21d850SDavid van Moolenbroek  * See if the directory contents of /dev/pts are as we expect.  We have to keep
714*da21d850SDavid van Moolenbroek  * in mind that other programs may have pseudo terminals open while we are
715*da21d850SDavid van Moolenbroek  * running, although we assume that those programs do not open or close PTYs
716*da21d850SDavid van Moolenbroek  * while we are running.
717*da21d850SDavid van Moolenbroek  */
718*da21d850SDavid van Moolenbroek static void
test_getdents(int nindex,int array[3],int present[3])719*da21d850SDavid van Moolenbroek test_getdents(int nindex, int array[3], int present[3])
720*da21d850SDavid van Moolenbroek {
721*da21d850SDavid van Moolenbroek 	struct group *group;
722*da21d850SDavid van Moolenbroek 	DIR *dirp;
723*da21d850SDavid van Moolenbroek 	struct dirent *dp;
724*da21d850SDavid van Moolenbroek 	struct stat buf;
725*da21d850SDavid van Moolenbroek 	char path[PATH_MAX], *endp;
726*da21d850SDavid van Moolenbroek 	gid_t tty_gid;
727*da21d850SDavid van Moolenbroek 	int i, n, seen_dot, seen_dotdot, seen_index[3], *seen;
728*da21d850SDavid van Moolenbroek 
729*da21d850SDavid van Moolenbroek 	seen_dot = seen_dotdot = 0;
730*da21d850SDavid van Moolenbroek 	for (i = 0; i < nindex; i++)
731*da21d850SDavid van Moolenbroek 		seen_index[i] = 0;
732*da21d850SDavid van Moolenbroek 
733*da21d850SDavid van Moolenbroek 	if ((group = getgrnam("tty")) == NULL) e(0);
734*da21d850SDavid van Moolenbroek 	tty_gid = group->gr_gid;
735*da21d850SDavid van Moolenbroek 
736*da21d850SDavid van Moolenbroek 	if ((dirp = opendir(_PATH_DEV_PTS)) == NULL) e(0);
737*da21d850SDavid van Moolenbroek 
738*da21d850SDavid van Moolenbroek 	while ((dp = readdir(dirp)) != NULL) {
739*da21d850SDavid van Moolenbroek 		snprintf(path, sizeof(path), _PATH_DEV_PTS "%s", dp->d_name);
740*da21d850SDavid van Moolenbroek 		if (stat(path, &buf) < 0) e(0);
741*da21d850SDavid van Moolenbroek 
742*da21d850SDavid van Moolenbroek 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) {
743*da21d850SDavid van Moolenbroek 			seen =
744*da21d850SDavid van Moolenbroek 			    (dp->d_name[1] == '.') ? &seen_dot : &seen_dotdot;
745*da21d850SDavid van Moolenbroek 			if (*seen) e(0);
746*da21d850SDavid van Moolenbroek 			*seen = 1;
747*da21d850SDavid van Moolenbroek 
748*da21d850SDavid van Moolenbroek 			/* Check basic dirent and stat fields. */
749*da21d850SDavid van Moolenbroek 			if (dp->d_type != DT_DIR) e(0);
750*da21d850SDavid van Moolenbroek 			if (dp->d_name[1] == '\0' &&
751*da21d850SDavid van Moolenbroek 			    buf.st_ino != dp->d_fileno) e(0);
752*da21d850SDavid van Moolenbroek 			if (!S_ISDIR(buf.st_mode)) e(0);
753*da21d850SDavid van Moolenbroek 			if (buf.st_nlink < 2) e(0);
754*da21d850SDavid van Moolenbroek 		} else {
755*da21d850SDavid van Moolenbroek 			/* The file name must be a number. */
756*da21d850SDavid van Moolenbroek 			errno = 0;
757*da21d850SDavid van Moolenbroek 			n = strtol(dp->d_name, &endp, 10);
758*da21d850SDavid van Moolenbroek 			if (errno != 0) e(0);
759*da21d850SDavid van Moolenbroek 			if (dp->d_name[0] == '\0' || *endp != '\0') e(0);
760*da21d850SDavid van Moolenbroek 			if (n < 0) e(0);
761*da21d850SDavid van Moolenbroek 
762*da21d850SDavid van Moolenbroek 			/* Check basic dirent and stat fields. */
763*da21d850SDavid van Moolenbroek 			if (dp->d_type != DT_CHR) e(0);
764*da21d850SDavid van Moolenbroek 			if (buf.st_ino != dp->d_fileno) e(0);
765*da21d850SDavid van Moolenbroek 			if (!S_ISCHR(buf.st_mode)) e(0);
766*da21d850SDavid van Moolenbroek 			if (buf.st_nlink != 1) e(0);
767*da21d850SDavid van Moolenbroek 			if (buf.st_size != 0) e(0);
768*da21d850SDavid van Moolenbroek 			if (buf.st_rdev == 0) e(0);
769*da21d850SDavid van Moolenbroek 
770*da21d850SDavid van Moolenbroek 			/* Is this one of the PTYs we created? */
771*da21d850SDavid van Moolenbroek 			for (i = 0; i < nindex; i++) {
772*da21d850SDavid van Moolenbroek 				if (array[i] == n) {
773*da21d850SDavid van Moolenbroek 					if (seen_index[i]) e(0);
774*da21d850SDavid van Moolenbroek 					seen_index[i] = 1;
775*da21d850SDavid van Moolenbroek 
776*da21d850SDavid van Moolenbroek 					break;
777*da21d850SDavid van Moolenbroek 				}
778*da21d850SDavid van Moolenbroek 			}
779*da21d850SDavid van Moolenbroek 
780*da21d850SDavid van Moolenbroek 			/* If so, perform some extra tests. */
781*da21d850SDavid van Moolenbroek 			if (i < nindex) {
782*da21d850SDavid van Moolenbroek 				if ((buf.st_mode & ALLPERMS) != 0620) e(0);
783*da21d850SDavid van Moolenbroek 				if (buf.st_uid != getuid()) e(0);
784*da21d850SDavid van Moolenbroek 				if (buf.st_gid != tty_gid) e(0);
785*da21d850SDavid van Moolenbroek 			}
786*da21d850SDavid van Moolenbroek 		}
787*da21d850SDavid van Moolenbroek 	}
788*da21d850SDavid van Moolenbroek 
789*da21d850SDavid van Moolenbroek 	if (closedir(dirp) < 0) e(0);
790*da21d850SDavid van Moolenbroek 
791*da21d850SDavid van Moolenbroek 	if (!seen_dot) e(0);
792*da21d850SDavid van Moolenbroek 	if (!seen_dotdot) e(0);
793*da21d850SDavid van Moolenbroek 	for (i = 0; i < nindex; i++)
794*da21d850SDavid van Moolenbroek 		if (seen_index[i] != present[i]) e(0);
795*da21d850SDavid van Moolenbroek }
796*da21d850SDavid van Moolenbroek 
797*da21d850SDavid van Moolenbroek /*
798*da21d850SDavid van Moolenbroek  * Obtain a Unix98 PTY.  Return an open file descriptor for the master side,
799*da21d850SDavid van Moolenbroek  * and store the name of the slave side in 'tptr'.
800*da21d850SDavid van Moolenbroek  */
801*da21d850SDavid van Moolenbroek static int
get_unix98_pty(char ** tptr)802*da21d850SDavid van Moolenbroek get_unix98_pty(char ** tptr)
803*da21d850SDavid van Moolenbroek {
804*da21d850SDavid van Moolenbroek 	int masterfd;
805*da21d850SDavid van Moolenbroek 
806*da21d850SDavid van Moolenbroek 	if ((masterfd = posix_openpt(O_RDWR | O_NOCTTY)) < 0) e(0);
807*da21d850SDavid van Moolenbroek 
808*da21d850SDavid van Moolenbroek 	if (grantpt(masterfd) < 0) e(0);
809*da21d850SDavid van Moolenbroek 
810*da21d850SDavid van Moolenbroek 	/* This call is a no-op on MINIX3. */
811*da21d850SDavid van Moolenbroek 	if (unlockpt(masterfd) < 0) e(0);
812*da21d850SDavid van Moolenbroek 
813*da21d850SDavid van Moolenbroek 	if ((*tptr = ptsname(masterfd)) == NULL) e(0);
814*da21d850SDavid van Moolenbroek 
815*da21d850SDavid van Moolenbroek 	return masterfd;
816*da21d850SDavid van Moolenbroek }
817*da21d850SDavid van Moolenbroek 
818*da21d850SDavid van Moolenbroek /*
819*da21d850SDavid van Moolenbroek  * Test for Unix98 PTY support and PTYFS.
820*da21d850SDavid van Moolenbroek  */
821*da21d850SDavid van Moolenbroek static void
test77g(void)822*da21d850SDavid van Moolenbroek test77g(void)
823*da21d850SDavid van Moolenbroek {
824*da21d850SDavid van Moolenbroek 	char *tname;
825*da21d850SDavid van Moolenbroek 	struct stat buf;
826*da21d850SDavid van Moolenbroek 	size_t len;
827*da21d850SDavid van Moolenbroek 	int i, masterfd, slavefd, fd[3], array[3], present[3];
828*da21d850SDavid van Moolenbroek 
829*da21d850SDavid van Moolenbroek 	subtest = 7;
830*da21d850SDavid van Moolenbroek 
831*da21d850SDavid van Moolenbroek 	/*
832*da21d850SDavid van Moolenbroek 	 * Test basic operation, and verify that the slave node disappears
833*da21d850SDavid van Moolenbroek 	 * after both sides of the pseudo terminal have been closed.  We check
834*da21d850SDavid van Moolenbroek 	 * different combinations of open master and slave ends (with 'i'):
835*da21d850SDavid van Moolenbroek 	 * 0) opening and closing the master only, 1) closing a slave before
836*da21d850SDavid van Moolenbroek 	 * the master, and 2) closing the slave after the master.
837*da21d850SDavid van Moolenbroek 	 */
838*da21d850SDavid van Moolenbroek 	for (i = 0; i <= 2; i++) {
839*da21d850SDavid van Moolenbroek 		masterfd = get_unix98_pty(&tname);
840*da21d850SDavid van Moolenbroek 
841*da21d850SDavid van Moolenbroek 		if (access(tname, R_OK | W_OK) < 0) e(0);
842*da21d850SDavid van Moolenbroek 
843*da21d850SDavid van Moolenbroek 		if (i > 0) {
844*da21d850SDavid van Moolenbroek 			if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0)
845*da21d850SDavid van Moolenbroek 				e(0);
846*da21d850SDavid van Moolenbroek 
847*da21d850SDavid van Moolenbroek 			if (access(tname, R_OK | W_OK) < 0) e(0);
848*da21d850SDavid van Moolenbroek 
849*da21d850SDavid van Moolenbroek 			if (i > 1) {
850*da21d850SDavid van Moolenbroek 				if (close(masterfd) < 0) e(0);
851*da21d850SDavid van Moolenbroek 
852*da21d850SDavid van Moolenbroek 				masterfd = slavefd; /* ugly but saving code */
853*da21d850SDavid van Moolenbroek 			} else
854*da21d850SDavid van Moolenbroek 				if (close(slavefd) < 0) e(0);
855*da21d850SDavid van Moolenbroek 		}
856*da21d850SDavid van Moolenbroek 
857*da21d850SDavid van Moolenbroek 		if (access(tname, R_OK | W_OK) < 0) e(0);
858*da21d850SDavid van Moolenbroek 
859*da21d850SDavid van Moolenbroek 		if (close(masterfd) < 0) e(0);
860*da21d850SDavid van Moolenbroek 
861*da21d850SDavid van Moolenbroek 		if (access(tname, R_OK | W_OK) == 0) e(0);
862*da21d850SDavid van Moolenbroek 	}
863*da21d850SDavid van Moolenbroek 
864*da21d850SDavid van Moolenbroek 	/*
865*da21d850SDavid van Moolenbroek 	 * Test whether we can open multiple pseudo terminals.  We need to be
866*da21d850SDavid van Moolenbroek 	 * able to open three PTYs.  Verify that they are properly listed in
867*da21d850SDavid van Moolenbroek 	 * the /dev/pts directory contents, and have proper attributes set.
868*da21d850SDavid van Moolenbroek 	 */
869*da21d850SDavid van Moolenbroek 	test_getdents(0, NULL, NULL);
870*da21d850SDavid van Moolenbroek 
871*da21d850SDavid van Moolenbroek 	for (i = 0; i < 3; i++) {
872*da21d850SDavid van Moolenbroek 		fd[i] = get_unix98_pty(&tname);
873*da21d850SDavid van Moolenbroek 
874*da21d850SDavid van Moolenbroek 		/* Figure out the slave index number. */
875*da21d850SDavid van Moolenbroek 		len = strlen(_PATH_DEV_PTS);
876*da21d850SDavid van Moolenbroek 		if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0);
877*da21d850SDavid van Moolenbroek 		array[i] = atoi(&tname[len]);
878*da21d850SDavid van Moolenbroek 		present[i] = 1;
879*da21d850SDavid van Moolenbroek 	}
880*da21d850SDavid van Moolenbroek 
881*da21d850SDavid van Moolenbroek 	test_getdents(3, array, present);
882*da21d850SDavid van Moolenbroek 
883*da21d850SDavid van Moolenbroek 	if (close(fd[0]) < 0) e(0);
884*da21d850SDavid van Moolenbroek 	present[0] = 0;
885*da21d850SDavid van Moolenbroek 
886*da21d850SDavid van Moolenbroek 	test_getdents(3, array, present);
887*da21d850SDavid van Moolenbroek 
888*da21d850SDavid van Moolenbroek 	if (close(fd[2]) < 0) e(0);
889*da21d850SDavid van Moolenbroek 	present[2] = 0;
890*da21d850SDavid van Moolenbroek 
891*da21d850SDavid van Moolenbroek 	test_getdents(3, array, present);
892*da21d850SDavid van Moolenbroek 
893*da21d850SDavid van Moolenbroek 	if (close(fd[1]) < 0) e(0);
894*da21d850SDavid van Moolenbroek 	present[1] = 0;
895*da21d850SDavid van Moolenbroek 
896*da21d850SDavid van Moolenbroek 	test_getdents(3, array, present);
897*da21d850SDavid van Moolenbroek 
898*da21d850SDavid van Moolenbroek 	/*
899*da21d850SDavid van Moolenbroek 	 * Test chmod(2) on a slave node, and multiple calls to grantpt(3).
900*da21d850SDavid van Moolenbroek 	 * The first grantpt(3) call should create the slave node (we currently
901*da21d850SDavid van Moolenbroek 	 * can not test this: the slave node may be created earlier as well,
902*da21d850SDavid van Moolenbroek 	 * but we do not know its name), whereas subsequent grantpt(3) calls
903*da21d850SDavid van Moolenbroek 	 * should reset its mode, uid, and gid.  Testing the latter two and
904*da21d850SDavid van Moolenbroek 	 * chown(2) on the slave node requires root, so we skip that part.
905*da21d850SDavid van Moolenbroek 	 *
906*da21d850SDavid van Moolenbroek 	 * Finally, NetBSD revokes access to existing slave file descriptors
907*da21d850SDavid van Moolenbroek 	 * upon a call to grantpt(3).  This is not a POSIX requirement, but
908*da21d850SDavid van Moolenbroek 	 * NetBSD needs this for security reasons because it already creates
909*da21d850SDavid van Moolenbroek 	 * the slave node when the master is opened (and it does not lock the
910*da21d850SDavid van Moolenbroek 	 * slave until a call to unlockpt(3)).  MINIX3 does not implement
911*da21d850SDavid van Moolenbroek 	 * revocation this way, because the slave node is created only upon the
912*da21d850SDavid van Moolenbroek 	 * call to grantpt(3), thus leaving no insecure window for the slave
913*da21d850SDavid van Moolenbroek 	 * side between posix_openpt(3) and grantpt(3).  While this behavior
914*da21d850SDavid van Moolenbroek 	 * may be changed later, we test for the lack of revocation here now.
915*da21d850SDavid van Moolenbroek 	 */
916*da21d850SDavid van Moolenbroek 	masterfd = get_unix98_pty(&tname);
917*da21d850SDavid van Moolenbroek 
918*da21d850SDavid van Moolenbroek 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
919*da21d850SDavid van Moolenbroek 
920*da21d850SDavid van Moolenbroek 	if (stat(tname, &buf) != 0) e(0);
921*da21d850SDavid van Moolenbroek 	if (buf.st_mode != (S_IFCHR | 0620)) e(0);
922*da21d850SDavid van Moolenbroek 
923*da21d850SDavid van Moolenbroek 	if (chmod(tname, S_IFCHR | 0630) != 0) e(0);
924*da21d850SDavid van Moolenbroek 
925*da21d850SDavid van Moolenbroek 	if (stat(tname, &buf) != 0) e(0);
926*da21d850SDavid van Moolenbroek 	if (buf.st_mode != (S_IFCHR | 0630)) e(0);
927*da21d850SDavid van Moolenbroek 
928*da21d850SDavid van Moolenbroek 	if (grantpt(masterfd) != 0) e(0);
929*da21d850SDavid van Moolenbroek 
930*da21d850SDavid van Moolenbroek 	if (stat(tname, &buf) != 0) e(0);
931*da21d850SDavid van Moolenbroek 	if (buf.st_mode != (S_IFCHR | 0620)) e(0);
932*da21d850SDavid van Moolenbroek 
933*da21d850SDavid van Moolenbroek 	test_comm(masterfd, slavefd);
934*da21d850SDavid van Moolenbroek 
935*da21d850SDavid van Moolenbroek 	if (close(slavefd) < 0) e(0);
936*da21d850SDavid van Moolenbroek 	if (close(masterfd) < 0) e(0);
937*da21d850SDavid van Moolenbroek 
938*da21d850SDavid van Moolenbroek 	test_getdents(0, NULL, NULL);
939*da21d850SDavid van Moolenbroek }
940*da21d850SDavid van Moolenbroek 
941*da21d850SDavid van Moolenbroek /*
942*da21d850SDavid van Moolenbroek  * Check that the given PTY index, which is in use for an old-style PTY, is not
943*da21d850SDavid van Moolenbroek  * allocated through Unix98 PTY allocation.  This test is not foolproof, but it
944*da21d850SDavid van Moolenbroek  * does the job well enough.
945*da21d850SDavid van Moolenbroek  */
946*da21d850SDavid van Moolenbroek static void
test_overlap(int m)947*da21d850SDavid van Moolenbroek test_overlap(int m)
948*da21d850SDavid van Moolenbroek {
949*da21d850SDavid van Moolenbroek 	char *tname;
950*da21d850SDavid van Moolenbroek 	size_t len;
951*da21d850SDavid van Moolenbroek 	int i, n, fd[MIN_PTYS];
952*da21d850SDavid van Moolenbroek 
953*da21d850SDavid van Moolenbroek 	for (i = 0; i < MIN_PTYS; i++) {
954*da21d850SDavid van Moolenbroek 		if ((fd[i] = posix_openpt(O_RDWR | O_NOCTTY)) < 0)
955*da21d850SDavid van Moolenbroek 			break; /* out of PTYs */
956*da21d850SDavid van Moolenbroek 		if (grantpt(fd[i]) < 0) e(0);
957*da21d850SDavid van Moolenbroek 		if (unlockpt(fd[i]) < 0) e(0);
958*da21d850SDavid van Moolenbroek 		if ((tname = ptsname(fd[i])) == NULL) e(0);
959*da21d850SDavid van Moolenbroek 
960*da21d850SDavid van Moolenbroek 		len = strlen(_PATH_DEV_PTS);
961*da21d850SDavid van Moolenbroek 		if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0);
962*da21d850SDavid van Moolenbroek 		n = atoi(&tname[len]);
963*da21d850SDavid van Moolenbroek 		if (n < 0 || n > 9) e(0);
964*da21d850SDavid van Moolenbroek 
965*da21d850SDavid van Moolenbroek 		if (m == n) e(0);
966*da21d850SDavid van Moolenbroek 	}
967*da21d850SDavid van Moolenbroek 
968*da21d850SDavid van Moolenbroek 	for (i--; i >= 0; i--)
969*da21d850SDavid van Moolenbroek 		if (close(fd[i]) < 0) e(0);
970*da21d850SDavid van Moolenbroek }
971*da21d850SDavid van Moolenbroek 
972*da21d850SDavid van Moolenbroek /*
973*da21d850SDavid van Moolenbroek  * Test for mixing access to old-style and Unix98 PTYs.  Since the PTY service
974*da21d850SDavid van Moolenbroek  * internally shares the set of pseudo terminals between the two types, it has
975*da21d850SDavid van Moolenbroek  * to implement checks to prevent that a PTY opened as one type is also
976*da21d850SDavid van Moolenbroek  * accessed through the other type.  We test some of those checks here.
977*da21d850SDavid van Moolenbroek  */
978*da21d850SDavid van Moolenbroek static void
test77h(void)979*da21d850SDavid van Moolenbroek test77h(void)
980*da21d850SDavid van Moolenbroek {
981*da21d850SDavid van Moolenbroek 	char *tname, ptest[PATH_MAX], ttest[PATH_MAX];
982*da21d850SDavid van Moolenbroek 	struct sigaction act, oact;
983*da21d850SDavid van Moolenbroek 	size_t len;
984*da21d850SDavid van Moolenbroek 	int i, n, masterfd, slavefd;
985*da21d850SDavid van Moolenbroek 
986*da21d850SDavid van Moolenbroek 	subtest = 8;
987*da21d850SDavid van Moolenbroek 
988*da21d850SDavid van Moolenbroek 	/* We do not want to get SIGHUP signals in this test. */
989*da21d850SDavid van Moolenbroek 	memset(&act, 0, sizeof(act));
990*da21d850SDavid van Moolenbroek 	act.sa_handler = SIG_IGN;
991*da21d850SDavid van Moolenbroek 	if (sigaction(SIGHUP, &act, &oact) < 0) e(0);
992*da21d850SDavid van Moolenbroek 
993*da21d850SDavid van Moolenbroek 	/*
994*da21d850SDavid van Moolenbroek 	 * Check that Unix98 PTYs cannot be accessed through old-style device
995*da21d850SDavid van Moolenbroek 	 * nodes.  We check different combinations of open master and
996*da21d850SDavid van Moolenbroek 	 * slave ends for the Unix98 side (with 'i'): 0) opening and closing
997*da21d850SDavid van Moolenbroek 	 * the master only, 1) closing a slave before the master, and 2)
998*da21d850SDavid van Moolenbroek 	 * closing the slave after the master.
999*da21d850SDavid van Moolenbroek 	 *
1000*da21d850SDavid van Moolenbroek 	 * This test relies on the implementation aspect that /dev/ttypN and
1001*da21d850SDavid van Moolenbroek 	 * /dev/pts/N (with N in the range 0..9) map to the same PTY.  It also
1002*da21d850SDavid van Moolenbroek 	 * relies on lack of concurrent PTY allocation outside the test.
1003*da21d850SDavid van Moolenbroek 	 */
1004*da21d850SDavid van Moolenbroek 	for (i = 0; i <= 2; i++) {
1005*da21d850SDavid van Moolenbroek 		/* Open a Unix98 PTY and get the slave name. */
1006*da21d850SDavid van Moolenbroek 		masterfd = get_unix98_pty(&tname);
1007*da21d850SDavid van Moolenbroek 
1008*da21d850SDavid van Moolenbroek 		/* Figure out the slave index number. */
1009*da21d850SDavid van Moolenbroek 		len = strlen(_PATH_DEV_PTS);
1010*da21d850SDavid van Moolenbroek 		if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0);
1011*da21d850SDavid van Moolenbroek 		n = atoi(&tname[len]);
1012*da21d850SDavid van Moolenbroek 		if (n < 0 || n > 9) e(0);
1013*da21d850SDavid van Moolenbroek 
1014*da21d850SDavid van Moolenbroek 		/* Use this index number to create old-style device names. */
1015*da21d850SDavid van Moolenbroek 		snprintf(ptest, sizeof(ptest), _PATH_DEV "ptyp%u", n);
1016*da21d850SDavid van Moolenbroek 		snprintf(ttest, sizeof(ttest), _PATH_DEV "ttyp%u", n);
1017*da21d850SDavid van Moolenbroek 
1018*da21d850SDavid van Moolenbroek 		/*
1019*da21d850SDavid van Moolenbroek 		 * Now make sure that opening the old-style master and slave
1020*da21d850SDavid van Moolenbroek 		 * fails as long as either side of the Unix98 PTY is open.
1021*da21d850SDavid van Moolenbroek 		 */
1022*da21d850SDavid van Moolenbroek 		if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0);
1023*da21d850SDavid van Moolenbroek 		if (errno != EACCES && errno != EIO) e(0);
1024*da21d850SDavid van Moolenbroek 		if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0);
1025*da21d850SDavid van Moolenbroek 		if (errno != EACCES && errno != EIO) e(0);
1026*da21d850SDavid van Moolenbroek 
1027*da21d850SDavid van Moolenbroek 		if (i > 0) {
1028*da21d850SDavid van Moolenbroek 			if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0)
1029*da21d850SDavid van Moolenbroek 				e(0);
1030*da21d850SDavid van Moolenbroek 
1031*da21d850SDavid van Moolenbroek 			if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0);
1032*da21d850SDavid van Moolenbroek 			if (errno != EACCES && errno != EIO) e(0);
1033*da21d850SDavid van Moolenbroek 			if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0);
1034*da21d850SDavid van Moolenbroek 			if (errno != EACCES && errno != EIO) e(0);
1035*da21d850SDavid van Moolenbroek 
1036*da21d850SDavid van Moolenbroek 			if (close(slavefd) < 0) e(0);
1037*da21d850SDavid van Moolenbroek 
1038*da21d850SDavid van Moolenbroek 			if (i > 1) {
1039*da21d850SDavid van Moolenbroek 				if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0);
1040*da21d850SDavid van Moolenbroek 				if (errno != EACCES && errno != EIO) e(0);
1041*da21d850SDavid van Moolenbroek 				if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0);
1042*da21d850SDavid van Moolenbroek 				if (errno != EACCES && errno != EIO) e(0);
1043*da21d850SDavid van Moolenbroek 
1044*da21d850SDavid van Moolenbroek 				if ((slavefd =
1045*da21d850SDavid van Moolenbroek 				    open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
1046*da21d850SDavid van Moolenbroek 
1047*da21d850SDavid van Moolenbroek 				if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0);
1048*da21d850SDavid van Moolenbroek 				if (errno != EACCES && errno != EIO) e(0);
1049*da21d850SDavid van Moolenbroek 				if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0);
1050*da21d850SDavid van Moolenbroek 				if (errno != EACCES && errno != EIO) e(0);
1051*da21d850SDavid van Moolenbroek 
1052*da21d850SDavid van Moolenbroek 				if (close(masterfd) < 0) e(0);
1053*da21d850SDavid van Moolenbroek 
1054*da21d850SDavid van Moolenbroek 				masterfd = slavefd; /* ugly but saving code */
1055*da21d850SDavid van Moolenbroek 			}
1056*da21d850SDavid van Moolenbroek 
1057*da21d850SDavid van Moolenbroek 			if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0);
1058*da21d850SDavid van Moolenbroek 			if (errno != EACCES && errno != EIO) e(0);
1059*da21d850SDavid van Moolenbroek 			if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0);
1060*da21d850SDavid van Moolenbroek 			if (errno != EACCES && errno != EIO) e(0);
1061*da21d850SDavid van Moolenbroek 		}
1062*da21d850SDavid van Moolenbroek 
1063*da21d850SDavid van Moolenbroek 		if (close(masterfd) < 0) e(0);
1064*da21d850SDavid van Moolenbroek 
1065*da21d850SDavid van Moolenbroek 		/*
1066*da21d850SDavid van Moolenbroek 		 * Once both Unix98 sides are closed, the pseudo terminal can
1067*da21d850SDavid van Moolenbroek 		 * be reused.  Thus, opening the old-style master should now
1068*da21d850SDavid van Moolenbroek 		 * succeed.  However, it is possible that we do not have
1069*da21d850SDavid van Moolenbroek 		 * permission to open the master at all.
1070*da21d850SDavid van Moolenbroek 		 */
1071*da21d850SDavid van Moolenbroek 		if ((masterfd = open(ptest, O_RDWR | O_NOCTTY)) < 0 &&
1072*da21d850SDavid van Moolenbroek 		    errno != EACCES) e(0);
1073*da21d850SDavid van Moolenbroek 
1074*da21d850SDavid van Moolenbroek 		if (masterfd >= 0 && close(masterfd) < 0) e(0);
1075*da21d850SDavid van Moolenbroek 	}
1076*da21d850SDavid van Moolenbroek 
1077*da21d850SDavid van Moolenbroek 	/*
1078*da21d850SDavid van Moolenbroek 	 * The reverse test, which would check that old-style PTYs cannot be
1079*da21d850SDavid van Moolenbroek 	 * accessed through Unix98 device nodes, is impossible to perform
1080*da21d850SDavid van Moolenbroek 	 * properly without root privileges, as we would have to create device
1081*da21d850SDavid van Moolenbroek 	 * nodes manually with mknod(2).  All we can do here is ensure that if
1082*da21d850SDavid van Moolenbroek 	 * an old-style PTY is opened, it will not also be allocated as a
1083*da21d850SDavid van Moolenbroek 	 * Unix98 PTY.  We do a rather basic check, but only if we can open an
1084*da21d850SDavid van Moolenbroek 	 * old-style master at all.  We check two closing orders (with 'i'):
1085*da21d850SDavid van Moolenbroek 	 * 0) the slave first, 1) the master first.  Here, we make the hard
1086*da21d850SDavid van Moolenbroek 	 * assumption that the system supports at least four pseudo terminals,
1087*da21d850SDavid van Moolenbroek 	 * of which at least one is currently free.
1088*da21d850SDavid van Moolenbroek 	 */
1089*da21d850SDavid van Moolenbroek 	for (i = 0; i <= 1; i++) {
1090*da21d850SDavid van Moolenbroek 		for (n = 0; n < MIN_PTYS; n++) {
1091*da21d850SDavid van Moolenbroek 			snprintf(ptest, sizeof(ptest), _PATH_DEV "ptyp%u", n);
1092*da21d850SDavid van Moolenbroek 
1093*da21d850SDavid van Moolenbroek 			if ((masterfd = open(ptest, O_RDWR | O_NOCTTY)) >= 0)
1094*da21d850SDavid van Moolenbroek 				break;
1095*da21d850SDavid van Moolenbroek 		}
1096*da21d850SDavid van Moolenbroek 
1097*da21d850SDavid van Moolenbroek 		if (n >= MIN_PTYS)
1098*da21d850SDavid van Moolenbroek 			break;
1099*da21d850SDavid van Moolenbroek 
1100*da21d850SDavid van Moolenbroek 		test_overlap(n);
1101*da21d850SDavid van Moolenbroek 
1102*da21d850SDavid van Moolenbroek 		snprintf(ttest, sizeof(ttest), _PATH_DEV "ttyp%u", n);
1103*da21d850SDavid van Moolenbroek 
1104*da21d850SDavid van Moolenbroek 		/* We can do part of the test only if we can open the slave. */
1105*da21d850SDavid van Moolenbroek 		if ((slavefd = open(ttest, O_RDWR | O_NOCTTY)) >= 0) {
1106*da21d850SDavid van Moolenbroek 			test_overlap(n);
1107*da21d850SDavid van Moolenbroek 
1108*da21d850SDavid van Moolenbroek 			if (i > 0) {
1109*da21d850SDavid van Moolenbroek 				if (close(masterfd) < 0) e(0);
1110*da21d850SDavid van Moolenbroek 
1111*da21d850SDavid van Moolenbroek 				masterfd = slavefd; /* again, ugly */
1112*da21d850SDavid van Moolenbroek 			} else
1113*da21d850SDavid van Moolenbroek 				if (close(slavefd) < 0) e(0);
1114*da21d850SDavid van Moolenbroek 
1115*da21d850SDavid van Moolenbroek 			test_overlap(n);
1116*da21d850SDavid van Moolenbroek 		}
1117*da21d850SDavid van Moolenbroek 
1118*da21d850SDavid van Moolenbroek 		if (close(masterfd) < 0) e(0);
1119*da21d850SDavid van Moolenbroek 	}
1120*da21d850SDavid van Moolenbroek 
1121*da21d850SDavid van Moolenbroek 	if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
112227d0ecdbSDavid van Moolenbroek }
112327d0ecdbSDavid van Moolenbroek 
1124433d6423SLionel Sambuc int
main(int argc,char ** argv)1125433d6423SLionel Sambuc main(int argc, char **argv)
1126433d6423SLionel Sambuc {
1127433d6423SLionel Sambuc 	int i, m;
1128433d6423SLionel Sambuc 
1129433d6423SLionel Sambuc 	start(77);
1130433d6423SLionel Sambuc 
1131433d6423SLionel Sambuc 	if (argc == 2)
1132433d6423SLionel Sambuc 		m = atoi(argv[1]);
1133433d6423SLionel Sambuc 	else
1134433d6423SLionel Sambuc 		m = 0xFF;
1135433d6423SLionel Sambuc 
1136433d6423SLionel Sambuc 	for (i = 0; i < ITERATIONS; i++) {
1137433d6423SLionel Sambuc 		if (m & 0x01) test77a();
1138433d6423SLionel Sambuc 		if (m & 0x02) test77b();
1139433d6423SLionel Sambuc 		if (m & 0x04) test77c();
1140433d6423SLionel Sambuc 		if (m & 0x08) test77d();
1141433d6423SLionel Sambuc 		if (m & 0x10) test77e();
114227d0ecdbSDavid van Moolenbroek 		if (m & 0x20) test77f();
1143*da21d850SDavid van Moolenbroek 		if (m & 0x40) test77g();
1144*da21d850SDavid van Moolenbroek 		if (m & 0x80) test77h();
1145433d6423SLionel Sambuc 	}
1146433d6423SLionel Sambuc 
1147433d6423SLionel Sambuc 	quit();
1148433d6423SLionel Sambuc }
1149