xref: /minix/minix/tests/test77.c (revision 9f988b79)
1 /* Tests for opening/closing pseudo terminals - by D.C. van Moolenbroek */
2 /*
3  * As of the introduction of Unix98 PTY support, this test set actually relies
4  * on the ability to create Unix98 PTYs.  The system still supports old-style
5  * PTYs but there is no way to force openpty(3) to use them.  However, part of
6  * this test set can still be used to test old-style PTYs: first disable Unix98
7  * PTYs, for example by unmounting PTYFS or temporarily removing /dev/ptmx, and
8  * then run the a-f subtests from this test set as root.
9  */
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <signal.h>
14 #include <termios.h>
15 #include <sys/wait.h>
16 #include <sys/syslimits.h>
17 #include <paths.h>
18 #include <dirent.h>
19 #include <grp.h>
20 #include <fcntl.h>
21 #include <util.h>
22 
23 #define ITERATIONS 10
24 
25 #define MIN_PTYS 4
26 
27 #include "common.h"
28 
29 static int sighups;		/* number of SIGHUP signals received */
30 
31 /*
32  * Signal handler for SIGHUP and SIGUSR1.
33  */
34 static void
35 signal_handler(int sig)
36 {
37 	if (sig == SIGHUP)
38 		sighups++;
39 }
40 
41 /*
42  * Set the slave side of the pseudo terminal to raw mode.  This simplifies
43  * testing communication.
44  */
45 static void
46 make_raw(int slavefd)
47 {
48 	struct termios tios;
49 
50 	if (tcgetattr(slavefd, &tios) < 0) e(0);
51 
52 	cfmakeraw(&tios);
53 
54 	if (tcsetattr(slavefd, TCSANOW, &tios) < 0) e(0);
55 }
56 
57 /*
58  * See if the given pseudo terminal can successfully perform basic
59  * communication between master and slave.
60  */
61 static void
62 test_comm(int masterfd, int slavefd)
63 {
64 	char c;
65 
66 	make_raw(slavefd);
67 
68 	c = 'A';
69 	if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
70 	if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
71 	if (c != 'A') e(0);
72 
73 	c = 'B';
74 	if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
75 	if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
76 	if (c != 'B') e(0);
77 
78 	c = 'C';
79 	if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
80 	if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
81 	if (c != 'C') e(0);
82 
83 	c = 'D';
84 	if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
85 	if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
86 	if (c != 'D') e(0);
87 }
88 
89 /*
90  * Obtain a pseudo terminal.  The master end is opened and its file descriptor
91  * stored in 'pfd'.  The slave path name is stored in 'tname'.  For old-style
92  * PTYs, the function returns 1 and stores the master name in 'pname' if not
93  * NULL.  For Unix98 PTYs, the function returns 0, in which case no master name
94  * is available.  For old-style PTYs, the caller may close and reopen the
95  * master.  In that case, we make the assumption that nobody snatches the pair
96  * while we are running.  For Unix98 PTYs, the master must be kept open.
97  */
98 static int
99 get_pty(int *pfd, char pname[PATH_MAX], char tname[PATH_MAX])
100 {
101 	char *name;
102 	int len, masterfd, slavefd;
103 
104 	/*
105 	 * First try Unix98 PTY allocation, mainly to avoid opening the slave
106 	 * end immediately.  If this fails, try openpty(3) as well.
107 	 */
108 	if ((masterfd = posix_openpt(O_RDWR | O_NOCTTY)) != -1) {
109 		if (grantpt(masterfd) != -1 && unlockpt(masterfd) != -1 &&
110 		    (name = ptsname(masterfd)) != NULL) {
111 			*pfd = masterfd;
112 			strlcpy(tname, name, PATH_MAX);
113 
114 			return 0;
115 		}
116 		if (close(masterfd) < 0) e(0);
117 	}
118 
119 	if (openpty(&masterfd, &slavefd, tname, NULL, NULL) < 0) e(0);
120 
121 	test_comm(masterfd, slavefd);
122 
123 	*pfd = masterfd;
124 
125 	if (close(slavefd) < 0) e(0);
126 
127 	/*
128 	 * openpty(3) gives us only the slave name, but we also want the master
129 	 * name.
130 	 */
131 	len = strlen(_PATH_DEV);
132 	if (strncmp(tname, _PATH_DEV, len)) e(0);
133 
134 	if (strncmp(&tname[len], "tty", 3))
135 		return 0; /* Unix98 after all?  Well okay, whatever.. */
136 
137 	if (pname != NULL) {
138 		strlcpy(pname, tname, PATH_MAX);
139 		pname[len] = 'p';
140 	}
141 
142 	return 1;
143 }
144 
145 /*
146  * Test various orders of opening and closing the master and slave sides of a
147  * pseudo terminal, as well as opening/closing one side without ever opening
148  * the other.  This test is meaningful mainly for old-style pseudoterminals.
149  */
150 static void
151 test77a(void)
152 {
153 	struct sigaction act, oact;
154 	char pname[PATH_MAX], tname[PATH_MAX];
155 	int oldstyle, masterfd, slavefd;
156 
157 	subtest = 1;
158 
159 	/* We do not want to get SIGHUP signals in this test. */
160 	memset(&act, 0, sizeof(act));
161 	act.sa_handler = SIG_IGN;
162 	if (sigaction(SIGHUP, &act, &oact) < 0) e(0);
163 
164 	/* Obtain a pseudo terminal. */
165 	oldstyle = get_pty(&masterfd, pname, tname);
166 
167 	if (oldstyle) {
168 		/* Try closing the master. */
169 		if (close(masterfd) < 0) e(0);
170 
171 		/* See if we can reopen the master. */
172 		if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
173 	}
174 
175 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
176 
177 	test_comm(masterfd, slavefd);
178 
179 	/* In the meantime, test different closing orders. This is order A. */
180 	if (close(slavefd) < 0) e(0);
181 	if (close(masterfd) < 0) e(0);
182 
183 	/* Now try opening the pair (or a new pair) again. */
184 	if (!oldstyle)
185 		oldstyle = get_pty(&masterfd, pname, tname);
186 	else
187 		if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
188 
189 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
190 
191 	test_comm(masterfd, slavefd);
192 
193 	if (close(slavefd) < 0) e(0);
194 
195 	/*
196 	 * Try reopening the slave after closing it.  It is not very important
197 	 * that this works, but the TTY driver should currently support it.
198 	 */
199 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
200 
201 	test_comm(masterfd, slavefd);
202 
203 	/* This is closing order B. This may or may not cause a SIGHUP. */
204 	if (close(masterfd) < 0) e(0);
205 	if (close(slavefd) < 0) e(0);
206 
207 	/* Try the normal open procedure. */
208 	if (!oldstyle)
209 		oldstyle = get_pty(&masterfd, pname, tname);
210 	else
211 		if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
212 
213 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
214 
215 	test_comm(masterfd, slavefd);
216 
217 	if (close(slavefd) < 0) e(0);
218 	if (close(masterfd) < 0) e(0);
219 
220 	/*
221 	 * Try reopening and closing the slave, without opening the master.
222 	 * This should work on old-style PTYS, but not on Unix98 PTYs.
223 	 */
224 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) >= 0) {
225 		if (!oldstyle) e(0);
226 
227 		if (close(slavefd) < 0) e(0);
228 	} else
229 		if (oldstyle) e(0);
230 
231 	/* Again, try the normal open procedure. */
232 	if (!oldstyle)
233 		oldstyle = get_pty(&masterfd, pname, tname);
234 	else
235 		if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
236 
237 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
238 
239 	test_comm(masterfd, slavefd);
240 
241 	if (close(slavefd) < 0) e(0);
242 	if (close(masterfd) < 0) e(0);
243 
244 	/*
245 	 * Finally, try opening the slave first.  This does not work with
246 	 * Unix98 PTYs.
247 	 */
248 	if (oldstyle) {
249 		if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
250 		if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
251 
252 		test_comm(masterfd, slavefd);
253 
254 		if (close(slavefd) < 0) e(0);
255 		if (close(masterfd) < 0) e(0);
256 	}
257 
258 	if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
259 }
260 
261 /*
262  * Test opening a single side multiple times.
263  */
264 static void
265 test77b(void)
266 {
267 	char pname[PATH_MAX], tname[PATH_MAX];
268 	int oldstyle, masterfd, slavefd, extrafd;
269 
270 	subtest = 2;
271 
272 	/* Obtain a pseudo terminal. */
273 	oldstyle = get_pty(&masterfd, pname, tname);
274 
275 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
276 
277 	/*
278 	 * It must not be possible to open the master multiple times.  Doing so
279 	 * is possible only if we have a named master, i.e., an old-style PTY.
280 	 */
281 	test_comm(masterfd, slavefd);
282 
283 	if (oldstyle) {
284 		if ((extrafd = open(pname, O_RDWR | O_NOCTTY)) >= 0) e(0);
285 		if (errno != EIO) e(0);
286 	}
287 
288 	test_comm(masterfd, slavefd);
289 
290 	if (close(slavefd) < 0) e(0);
291 	if (close(masterfd) < 0) e(0);
292 
293 	/* The slave can be opened multiple times, though. */
294 	oldstyle = get_pty(&masterfd, pname, tname);
295 
296 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
297 
298 	test_comm(masterfd, slavefd);
299 
300 	if ((extrafd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
301 
302 	test_comm(masterfd, extrafd);
303 	test_comm(masterfd, slavefd);
304 
305 	if (close(slavefd) < 0) e(0);
306 	if (close(extrafd) < 0) e(0);
307 	if (close(masterfd) < 0) e(0);
308 }
309 
310 /*
311  * Test communication on half-open pseudo terminals.
312  */
313 static void
314 test77c(void)
315 {
316 	struct sigaction act, oact;
317 	char pname[PATH_MAX], tname[PATH_MAX];
318 	int oldstyle, masterfd, slavefd;
319 	char c;
320 
321 	subtest = 3;
322 
323 	/* We do not want to get SIGHUP signals in this test. */
324 	memset(&act, 0, sizeof(act));
325 	act.sa_handler = SIG_IGN;
326 	if (sigaction(SIGHUP, &act, &oact) < 0) e(0);
327 
328 	/* Obtain a pseudo terminal. */
329 	oldstyle = get_pty(&masterfd, pname, tname);
330 
331 	/*
332 	 * For old-style pseudo terminals, we have just opened and closed the
333 	 * slave end, which alters the behavior we are testing below.  Close
334 	 * and reopen the master to start fresh.
335 	 */
336 	if (oldstyle) {
337 		if (close(masterfd) < 0) e(0);
338 
339 		if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
340 	}
341 
342 	/* Writes to the master should be buffered until there is a slave. */
343 	c = 'E';
344 	if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
345 
346 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
347 
348 	make_raw(slavefd);
349 
350 	if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
351 	if (c != 'E') e(0);
352 
353 	/* Discard the echo on the master. */
354 	if (tcflush(slavefd, TCOFLUSH) != 0) e(0);
355 
356 	test_comm(masterfd, slavefd);
357 
358 	if (close(slavefd) < 0) e(0);
359 
360 	/* Writes to the master after the slave has been closed should fail. */
361 	if (write(masterfd, &c, sizeof(c)) >= 0) e(0);
362 	if (errno != EIO) e(0);
363 
364 	if (oldstyle)
365 		if (close(masterfd) < 0) e(0);
366 
367 	/*
368 	 * Writes to the slave should be buffered until there is a master.
369 	 * This applies to old-style PTYs only.
370 	 */
371 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
372 
373 	if (oldstyle) {
374 		make_raw(slavefd);
375 
376 		c = 'F';
377 		if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
378 
379 		if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
380 
381 		if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
382 		if (c != 'F') e(0);
383 	}
384 
385 	test_comm(masterfd, slavefd);
386 
387 	if (close(masterfd) < 0) e(0);
388 
389 	if (write(slavefd, &c, sizeof(c)) >= 0) e(0);
390 	if (errno != EIO) e(0);
391 
392 	/* Reads from the slave should return EOF if the master is gone. */
393 	if (read(slavefd, &c, sizeof(c)) != 0) e(0);
394 
395 	if (close(slavefd) < 0) e(0);
396 
397 	if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
398 }
399 
400 /*
401  * Wait for a child process to terminate.  Return 0 if the child exited without
402  * errors, -1 otherwise.
403  */
404 static int
405 waitchild(void)
406 {
407 	int status;
408 
409 	if (wait(&status) <= 0) return -1;
410 	if (!WIFEXITED(status)) return -1;
411 	if (WEXITSTATUS(status) != 0) return -1;
412 
413 	return 0;
414 }
415 
416 /*
417  * Test opening the slave side with and without the O_NOCTTY flag.
418  */
419 static void
420 test77d(void)
421 {
422 	char pname[PATH_MAX], tname[PATH_MAX];
423 	int masterfd, slavefd;
424 
425 	subtest = 4;
426 
427 	/* Make ourselves process group leader if we aren't already. */
428 	(void)setsid();
429 
430 	/* Obtain a pseudo terminal. */
431 	(void)get_pty(&masterfd, NULL, tname);
432 
433 	/*
434 	 * Opening the slave with O_NOCTTY should not change its controlling
435 	 * terminal.
436 	 */
437 	switch (fork()) {
438 	case 0:
439 		if (close(masterfd) < 0) e(0);
440 
441 		if (setsid() < 0) e(0);
442 
443 		if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
444 
445 		if (open("/dev/tty", O_RDWR) >= 0) e(0);
446 		if (errno != ENXIO) e(0);
447 
448 		exit(errct);
449 	case -1:
450 		e(0);
451 	default:
452 		break;
453 	}
454 
455 	if (waitchild() < 0) e(0);
456 
457 	if (close(masterfd) < 0) e(0);
458 
459 	(void)get_pty(&masterfd, pname, tname);
460 
461 	/*
462 	 * Opening the slave without O_NOCTTY should change its controlling
463 	 * terminal, though.
464 	 */
465 	switch (fork()) {
466 	case 0:
467 		if (close(masterfd) < 0) e(0);
468 
469 		if (setsid() < 0) e(0);
470 
471 		if ((slavefd = open(tname, O_RDWR)) < 0) e(0);
472 
473 		if (open("/dev/tty", O_RDWR) < 0) e(0);
474 
475 		exit(errct);
476 	case -1:
477 		e(0);
478 	default:
479 		break;
480 	}
481 
482 	if (waitchild() < 0) e(0);
483 
484 	if (close(masterfd) < 0) e(0);
485 }
486 
487 /*
488  * Test receiving of SIGHUP on master hang-up.  All of the tests so far have
489  * ignored SIGHUP, and probably would not have received one anyway, since the
490  * process was not its own session leader.  Time to test this aspect.
491  */
492 static void
493 test77e(void)
494 {
495 	struct sigaction act, hup_oact, usr_oact;
496 	sigset_t set, oset;
497 	char tname[PATH_MAX];
498 	int masterfd, slavefd;
499 
500 	subtest = 5;
501 
502 	memset(&act, 0, sizeof(act));
503 	act.sa_handler = signal_handler;
504 	if (sigaction(SIGHUP, &act, &hup_oact) < 0) e(0);
505 
506 	memset(&act, 0, sizeof(act));
507 	act.sa_handler = signal_handler;
508 	if (sigaction(SIGUSR1, &act, &usr_oact) < 0) e(0);
509 
510 	sigemptyset(&set);
511 	sigaddset(&set, SIGHUP);
512 	sigaddset(&set, SIGUSR1);
513 	if (sigprocmask(SIG_BLOCK, &set, &oset) < 0) e(0);
514 
515 	sighups = 0;
516 
517 	/* Make ourselves process group leader if we aren't already. */
518 	(void)setsid();
519 
520 	/* Obtain a pseudo terminal. */
521 	(void)get_pty(&masterfd, NULL, tname);
522 
523 	switch (fork()) {
524 	case 0:
525 		if (close(masterfd) < 0) e(0);
526 
527 		/* Become session leader. */
528 		if (setsid() < 0) e(0);
529 
530 		if ((slavefd = open(tname, O_RDWR)) < 0) e(0);
531 
532 		/* Tell the parent we are ready. */
533 		kill(getppid(), SIGUSR1);
534 
535 		/* We should now get a SIGHUP. */
536 		set = oset;
537 		if (sigsuspend(&set) >= 0) e(0);
538 
539 		if (sighups != 1) e(0);
540 
541 		exit(errct);
542 	case -1:
543 		e(0);
544 	default:
545 		break;
546 	}
547 
548 	/* Wait for SIGUSR1 from the child. */
549 	set = oset;
550 	if (sigsuspend(&set) >= 0) e(0);
551 
552 	/* Closing the master should now raise a SIGHUP signal in the child. */
553 	if (close(masterfd) < 0) e(0);
554 
555 	if (waitchild() < 0) e(0);
556 
557 	if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0) e(0);
558 
559 	if (sigaction(SIGHUP, &hup_oact, NULL) < 0) e(0);
560 	if (sigaction(SIGUSR1, &usr_oact, NULL) < 0) e(0);
561 }
562 
563 /*
564  * Test basic select functionality on /dev/tty.  While this test should not be
565  * part of this test set, we already have all the infrastructure we need here.
566  */
567 static void
568 test77f(void)
569 {
570 	struct sigaction act, oact;
571 	char c, tname[PATH_MAX];
572 	struct timeval tv;
573 	fd_set fd_set;
574 	int fd, maxfd, masterfd, slavefd;
575 
576 	subtest = 6;
577 
578 	/* We do not want to get SIGHUP signals in this test. */
579 	memset(&act, 0, sizeof(act));
580 	act.sa_handler = SIG_IGN;
581 	if (sigaction(SIGHUP, &act, &oact) < 0) e(0);
582 
583 	/* Obtain a pseudo terminal. */
584 	(void)get_pty(&masterfd, NULL, tname);
585 
586 	switch (fork()) {
587 	case 0:
588 		if (close(masterfd) < 0) e(0);
589 
590 		if (setsid() < 0) e(0);
591 
592 		if ((slavefd = open(tname, O_RDWR)) < 0) e(0);
593 
594 		if ((fd = open("/dev/tty", O_RDWR)) < 0) e(0);
595 
596 		make_raw(fd);
597 
598 		/* Without slave input, /dev/tty is not ready for reading. */
599 		FD_ZERO(&fd_set);
600 		FD_SET(fd, &fd_set);
601 		tv.tv_sec = 0;
602 		tv.tv_usec = 0;
603 
604 		if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0);
605 		if (FD_ISSET(fd, &fd_set)) e(0);
606 
607 		FD_SET(fd, &fd_set);
608 		tv.tv_sec = 0;
609 		tv.tv_usec = 10000;
610 
611 		if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0);
612 		if (FD_ISSET(fd, &fd_set)) e(0);
613 
614 		/* It will be ready for writing, though. */
615 		FD_SET(fd, &fd_set);
616 
617 		if (select(fd + 1, NULL, &fd_set, NULL, NULL) != 1) e(0);
618 		if (!FD_ISSET(fd, &fd_set)) e(0);
619 
620 		/* Test mixing file descriptors to the same terminal. */
621 		FD_ZERO(&fd_set);
622 		FD_SET(fd, &fd_set);
623 		FD_SET(slavefd, &fd_set);
624 		tv.tv_sec = 0;
625 		tv.tv_usec = 10000;
626 
627 		maxfd = fd > slavefd ? fd : slavefd;
628 		if (select(maxfd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0);
629 		if (FD_ISSET(fd, &fd_set)) e(0);
630 		if (FD_ISSET(slavefd, &fd_set)) e(0);
631 
632 		/* The delayed echo on the master must wake up our select. */
633 		c = 'A';
634 		if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
635 
636 		FD_ZERO(&fd_set);
637 		FD_SET(fd, &fd_set);
638 
639 		if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0);
640 		if (!FD_ISSET(fd, &fd_set)) e(0);
641 
642 		/* Select must now still flag readiness for reading. */
643 		tv.tv_sec = 0;
644 		tv.tv_usec = 0;
645 
646 		if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 1) e(0);
647 		if (!FD_ISSET(fd, &fd_set)) e(0);
648 
649 		/* That is, until we read the byte. */
650 		if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
651 		if (c != 'B') e(0);
652 
653 		if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0);
654 		if (FD_ISSET(fd, &fd_set)) e(0);
655 
656 		/* Ask the parent to close the master. */
657 		c = 'C';
658 		if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
659 
660 		FD_SET(fd, &fd_set);
661 
662 		/* The closure must cause an EOF condition on the slave. */
663 		if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0);
664 		if (!FD_ISSET(fd, &fd_set)) e(0);
665 
666 		if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0);
667 		if (!FD_ISSET(fd, &fd_set)) e(0);
668 
669 		if (read(slavefd, &c, sizeof(c)) != 0) e(0);
670 
671 		exit(errct);
672 	case -1:
673 		e(0);
674 	default:
675 		/* Wait for the child to write something to the slave. */
676 		FD_ZERO(&fd_set);
677 		FD_SET(masterfd, &fd_set);
678 
679 		if (select(masterfd + 1, &fd_set, NULL, NULL, NULL) != 1)
680 			e(0);
681 		if (!FD_ISSET(masterfd, &fd_set)) e(0);
682 
683 		if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
684 		if (c != 'A') e(0);
685 
686 		/* Write a reply once the child is blocked in its select. */
687 		tv.tv_sec = 1;
688 		tv.tv_usec = 0;
689 		if (select(masterfd + 1, &fd_set, NULL, NULL, &tv) != 0)
690 			e(0);
691 
692 		c = 'B';
693 		if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
694 
695 		/* Wait for the child to request closing the master. */
696 		if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
697 		if (c != 'C') e(0);
698 
699 		/* Close the master once the child is blocked in its select. */
700 		sleep(1);
701 
702 		close(masterfd);
703 
704 		break;
705 	}
706 
707 	if (waitchild() < 0) e(0);
708 
709 	if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
710 }
711 
712 /*
713  * See if the directory contents of /dev/pts are as we expect.  We have to keep
714  * in mind that other programs may have pseudo terminals open while we are
715  * running, although we assume that those programs do not open or close PTYs
716  * while we are running.
717  */
718 static void
719 test_getdents(int nindex, int array[3], int present[3])
720 {
721 	struct group *group;
722 	DIR *dirp;
723 	struct dirent *dp;
724 	struct stat buf;
725 	char path[PATH_MAX], *endp;
726 	gid_t tty_gid;
727 	int i, n, seen_dot, seen_dotdot, seen_index[3], *seen;
728 
729 	seen_dot = seen_dotdot = 0;
730 	for (i = 0; i < nindex; i++)
731 		seen_index[i] = 0;
732 
733 	if ((group = getgrnam("tty")) == NULL) e(0);
734 	tty_gid = group->gr_gid;
735 
736 	if ((dirp = opendir(_PATH_DEV_PTS)) == NULL) e(0);
737 
738 	while ((dp = readdir(dirp)) != NULL) {
739 		snprintf(path, sizeof(path), _PATH_DEV_PTS "%s", dp->d_name);
740 		if (stat(path, &buf) < 0) e(0);
741 
742 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) {
743 			seen =
744 			    (dp->d_name[1] == '.') ? &seen_dot : &seen_dotdot;
745 			if (*seen) e(0);
746 			*seen = 1;
747 
748 			/* Check basic dirent and stat fields. */
749 			if (dp->d_type != DT_DIR) e(0);
750 			if (dp->d_name[1] == '\0' &&
751 			    buf.st_ino != dp->d_fileno) e(0);
752 			if (!S_ISDIR(buf.st_mode)) e(0);
753 			if (buf.st_nlink < 2) e(0);
754 		} else {
755 			/* The file name must be a number. */
756 			errno = 0;
757 			n = strtol(dp->d_name, &endp, 10);
758 			if (errno != 0) e(0);
759 			if (dp->d_name[0] == '\0' || *endp != '\0') e(0);
760 			if (n < 0) e(0);
761 
762 			/* Check basic dirent and stat fields. */
763 			if (dp->d_type != DT_CHR) e(0);
764 			if (buf.st_ino != dp->d_fileno) e(0);
765 			if (!S_ISCHR(buf.st_mode)) e(0);
766 			if (buf.st_nlink != 1) e(0);
767 			if (buf.st_size != 0) e(0);
768 			if (buf.st_rdev == 0) e(0);
769 
770 			/* Is this one of the PTYs we created? */
771 			for (i = 0; i < nindex; i++) {
772 				if (array[i] == n) {
773 					if (seen_index[i]) e(0);
774 					seen_index[i] = 1;
775 
776 					break;
777 				}
778 			}
779 
780 			/* If so, perform some extra tests. */
781 			if (i < nindex) {
782 				if ((buf.st_mode & ALLPERMS) != 0620) e(0);
783 				if (buf.st_uid != getuid()) e(0);
784 				if (buf.st_gid != tty_gid) e(0);
785 			}
786 		}
787 	}
788 
789 	if (closedir(dirp) < 0) e(0);
790 
791 	if (!seen_dot) e(0);
792 	if (!seen_dotdot) e(0);
793 	for (i = 0; i < nindex; i++)
794 		if (seen_index[i] != present[i]) e(0);
795 }
796 
797 /*
798  * Obtain a Unix98 PTY.  Return an open file descriptor for the master side,
799  * and store the name of the slave side in 'tptr'.
800  */
801 static int
802 get_unix98_pty(char ** tptr)
803 {
804 	int masterfd;
805 
806 	if ((masterfd = posix_openpt(O_RDWR | O_NOCTTY)) < 0) e(0);
807 
808 	if (grantpt(masterfd) < 0) e(0);
809 
810 	/* This call is a no-op on MINIX3. */
811 	if (unlockpt(masterfd) < 0) e(0);
812 
813 	if ((*tptr = ptsname(masterfd)) == NULL) e(0);
814 
815 	return masterfd;
816 }
817 
818 /*
819  * Test for Unix98 PTY support and PTYFS.
820  */
821 static void
822 test77g(void)
823 {
824 	char *tname;
825 	struct stat buf;
826 	size_t len;
827 	int i, masterfd, slavefd, fd[3], array[3], present[3];
828 
829 	subtest = 7;
830 
831 	/*
832 	 * Test basic operation, and verify that the slave node disappears
833 	 * after both sides of the pseudo terminal have been closed.  We check
834 	 * different combinations of open master and slave ends (with 'i'):
835 	 * 0) opening and closing the master only, 1) closing a slave before
836 	 * the master, and 2) closing the slave after the master.
837 	 */
838 	for (i = 0; i <= 2; i++) {
839 		masterfd = get_unix98_pty(&tname);
840 
841 		if (access(tname, R_OK | W_OK) < 0) e(0);
842 
843 		if (i > 0) {
844 			if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0)
845 				e(0);
846 
847 			if (access(tname, R_OK | W_OK) < 0) e(0);
848 
849 			if (i > 1) {
850 				if (close(masterfd) < 0) e(0);
851 
852 				masterfd = slavefd; /* ugly but saving code */
853 			} else
854 				if (close(slavefd) < 0) e(0);
855 		}
856 
857 		if (access(tname, R_OK | W_OK) < 0) e(0);
858 
859 		if (close(masterfd) < 0) e(0);
860 
861 		if (access(tname, R_OK | W_OK) == 0) e(0);
862 	}
863 
864 	/*
865 	 * Test whether we can open multiple pseudo terminals.  We need to be
866 	 * able to open three PTYs.  Verify that they are properly listed in
867 	 * the /dev/pts directory contents, and have proper attributes set.
868 	 */
869 	test_getdents(0, NULL, NULL);
870 
871 	for (i = 0; i < 3; i++) {
872 		fd[i] = get_unix98_pty(&tname);
873 
874 		/* Figure out the slave index number. */
875 		len = strlen(_PATH_DEV_PTS);
876 		if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0);
877 		array[i] = atoi(&tname[len]);
878 		present[i] = 1;
879 	}
880 
881 	test_getdents(3, array, present);
882 
883 	if (close(fd[0]) < 0) e(0);
884 	present[0] = 0;
885 
886 	test_getdents(3, array, present);
887 
888 	if (close(fd[2]) < 0) e(0);
889 	present[2] = 0;
890 
891 	test_getdents(3, array, present);
892 
893 	if (close(fd[1]) < 0) e(0);
894 	present[1] = 0;
895 
896 	test_getdents(3, array, present);
897 
898 	/*
899 	 * Test chmod(2) on a slave node, and multiple calls to grantpt(3).
900 	 * The first grantpt(3) call should create the slave node (we currently
901 	 * can not test this: the slave node may be created earlier as well,
902 	 * but we do not know its name), whereas subsequent grantpt(3) calls
903 	 * should reset its mode, uid, and gid.  Testing the latter two and
904 	 * chown(2) on the slave node requires root, so we skip that part.
905 	 *
906 	 * Finally, NetBSD revokes access to existing slave file descriptors
907 	 * upon a call to grantpt(3).  This is not a POSIX requirement, but
908 	 * NetBSD needs this for security reasons because it already creates
909 	 * the slave node when the master is opened (and it does not lock the
910 	 * slave until a call to unlockpt(3)).  MINIX3 does not implement
911 	 * revocation this way, because the slave node is created only upon the
912 	 * call to grantpt(3), thus leaving no insecure window for the slave
913 	 * side between posix_openpt(3) and grantpt(3).  While this behavior
914 	 * may be changed later, we test for the lack of revocation here now.
915 	 */
916 	masterfd = get_unix98_pty(&tname);
917 
918 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
919 
920 	if (stat(tname, &buf) != 0) e(0);
921 	if (buf.st_mode != (S_IFCHR | 0620)) e(0);
922 
923 	if (chmod(tname, S_IFCHR | 0630) != 0) e(0);
924 
925 	if (stat(tname, &buf) != 0) e(0);
926 	if (buf.st_mode != (S_IFCHR | 0630)) e(0);
927 
928 	if (grantpt(masterfd) != 0) e(0);
929 
930 	if (stat(tname, &buf) != 0) e(0);
931 	if (buf.st_mode != (S_IFCHR | 0620)) e(0);
932 
933 	test_comm(masterfd, slavefd);
934 
935 	if (close(slavefd) < 0) e(0);
936 	if (close(masterfd) < 0) e(0);
937 
938 	test_getdents(0, NULL, NULL);
939 }
940 
941 /*
942  * Check that the given PTY index, which is in use for an old-style PTY, is not
943  * allocated through Unix98 PTY allocation.  This test is not foolproof, but it
944  * does the job well enough.
945  */
946 static void
947 test_overlap(int m)
948 {
949 	char *tname;
950 	size_t len;
951 	int i, n, fd[MIN_PTYS];
952 
953 	for (i = 0; i < MIN_PTYS; i++) {
954 		if ((fd[i] = posix_openpt(O_RDWR | O_NOCTTY)) < 0)
955 			break; /* out of PTYs */
956 		if (grantpt(fd[i]) < 0) e(0);
957 		if (unlockpt(fd[i]) < 0) e(0);
958 		if ((tname = ptsname(fd[i])) == NULL) e(0);
959 
960 		len = strlen(_PATH_DEV_PTS);
961 		if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0);
962 		n = atoi(&tname[len]);
963 		if (n < 0 || n > 9) e(0);
964 
965 		if (m == n) e(0);
966 	}
967 
968 	for (i--; i >= 0; i--)
969 		if (close(fd[i]) < 0) e(0);
970 }
971 
972 /*
973  * Test for mixing access to old-style and Unix98 PTYs.  Since the PTY service
974  * internally shares the set of pseudo terminals between the two types, it has
975  * to implement checks to prevent that a PTY opened as one type is also
976  * accessed through the other type.  We test some of those checks here.
977  */
978 static void
979 test77h(void)
980 {
981 	char *tname, ptest[PATH_MAX], ttest[PATH_MAX];
982 	struct sigaction act, oact;
983 	size_t len;
984 	int i, n, masterfd, slavefd;
985 
986 	subtest = 8;
987 
988 	/* We do not want to get SIGHUP signals in this test. */
989 	memset(&act, 0, sizeof(act));
990 	act.sa_handler = SIG_IGN;
991 	if (sigaction(SIGHUP, &act, &oact) < 0) e(0);
992 
993 	/*
994 	 * Check that Unix98 PTYs cannot be accessed through old-style device
995 	 * nodes.  We check different combinations of open master and
996 	 * slave ends for the Unix98 side (with 'i'): 0) opening and closing
997 	 * the master only, 1) closing a slave before the master, and 2)
998 	 * closing the slave after the master.
999 	 *
1000 	 * This test relies on the implementation aspect that /dev/ttypN and
1001 	 * /dev/pts/N (with N in the range 0..9) map to the same PTY.  It also
1002 	 * relies on lack of concurrent PTY allocation outside the test.
1003 	 */
1004 	for (i = 0; i <= 2; i++) {
1005 		/* Open a Unix98 PTY and get the slave name. */
1006 		masterfd = get_unix98_pty(&tname);
1007 
1008 		/* Figure out the slave index number. */
1009 		len = strlen(_PATH_DEV_PTS);
1010 		if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0);
1011 		n = atoi(&tname[len]);
1012 		if (n < 0 || n > 9) e(0);
1013 
1014 		/* Use this index number to create old-style device names. */
1015 		snprintf(ptest, sizeof(ptest), _PATH_DEV "ptyp%u", n);
1016 		snprintf(ttest, sizeof(ttest), _PATH_DEV "ttyp%u", n);
1017 
1018 		/*
1019 		 * Now make sure that opening the old-style master and slave
1020 		 * fails as long as either side of the Unix98 PTY is open.
1021 		 */
1022 		if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0);
1023 		if (errno != EACCES && errno != EIO) e(0);
1024 		if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0);
1025 		if (errno != EACCES && errno != EIO) e(0);
1026 
1027 		if (i > 0) {
1028 			if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0)
1029 				e(0);
1030 
1031 			if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0);
1032 			if (errno != EACCES && errno != EIO) e(0);
1033 			if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0);
1034 			if (errno != EACCES && errno != EIO) e(0);
1035 
1036 			if (close(slavefd) < 0) e(0);
1037 
1038 			if (i > 1) {
1039 				if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0);
1040 				if (errno != EACCES && errno != EIO) e(0);
1041 				if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0);
1042 				if (errno != EACCES && errno != EIO) e(0);
1043 
1044 				if ((slavefd =
1045 				    open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
1046 
1047 				if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0);
1048 				if (errno != EACCES && errno != EIO) e(0);
1049 				if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0);
1050 				if (errno != EACCES && errno != EIO) e(0);
1051 
1052 				if (close(masterfd) < 0) e(0);
1053 
1054 				masterfd = slavefd; /* ugly but saving code */
1055 			}
1056 
1057 			if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0);
1058 			if (errno != EACCES && errno != EIO) e(0);
1059 			if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0);
1060 			if (errno != EACCES && errno != EIO) e(0);
1061 		}
1062 
1063 		if (close(masterfd) < 0) e(0);
1064 
1065 		/*
1066 		 * Once both Unix98 sides are closed, the pseudo terminal can
1067 		 * be reused.  Thus, opening the old-style master should now
1068 		 * succeed.  However, it is possible that we do not have
1069 		 * permission to open the master at all.
1070 		 */
1071 		if ((masterfd = open(ptest, O_RDWR | O_NOCTTY)) < 0 &&
1072 		    errno != EACCES) e(0);
1073 
1074 		if (masterfd >= 0 && close(masterfd) < 0) e(0);
1075 	}
1076 
1077 	/*
1078 	 * The reverse test, which would check that old-style PTYs cannot be
1079 	 * accessed through Unix98 device nodes, is impossible to perform
1080 	 * properly without root privileges, as we would have to create device
1081 	 * nodes manually with mknod(2).  All we can do here is ensure that if
1082 	 * an old-style PTY is opened, it will not also be allocated as a
1083 	 * Unix98 PTY.  We do a rather basic check, but only if we can open an
1084 	 * old-style master at all.  We check two closing orders (with 'i'):
1085 	 * 0) the slave first, 1) the master first.  Here, we make the hard
1086 	 * assumption that the system supports at least four pseudo terminals,
1087 	 * of which at least one is currently free.
1088 	 */
1089 	for (i = 0; i <= 1; i++) {
1090 		for (n = 0; n < MIN_PTYS; n++) {
1091 			snprintf(ptest, sizeof(ptest), _PATH_DEV "ptyp%u", n);
1092 
1093 			if ((masterfd = open(ptest, O_RDWR | O_NOCTTY)) >= 0)
1094 				break;
1095 		}
1096 
1097 		if (n >= MIN_PTYS)
1098 			break;
1099 
1100 		test_overlap(n);
1101 
1102 		snprintf(ttest, sizeof(ttest), _PATH_DEV "ttyp%u", n);
1103 
1104 		/* We can do part of the test only if we can open the slave. */
1105 		if ((slavefd = open(ttest, O_RDWR | O_NOCTTY)) >= 0) {
1106 			test_overlap(n);
1107 
1108 			if (i > 0) {
1109 				if (close(masterfd) < 0) e(0);
1110 
1111 				masterfd = slavefd; /* again, ugly */
1112 			} else
1113 				if (close(slavefd) < 0) e(0);
1114 
1115 			test_overlap(n);
1116 		}
1117 
1118 		if (close(masterfd) < 0) e(0);
1119 	}
1120 
1121 	if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
1122 }
1123 
1124 int
1125 main(int argc, char **argv)
1126 {
1127 	int i, m;
1128 
1129 	start(77);
1130 
1131 	if (argc == 2)
1132 		m = atoi(argv[1]);
1133 	else
1134 		m = 0xFF;
1135 
1136 	for (i = 0; i < ITERATIONS; i++) {
1137 		if (m & 0x01) test77a();
1138 		if (m & 0x02) test77b();
1139 		if (m & 0x04) test77c();
1140 		if (m & 0x08) test77d();
1141 		if (m & 0x10) test77e();
1142 		if (m & 0x20) test77f();
1143 		if (m & 0x40) test77g();
1144 		if (m & 0x80) test77h();
1145 	}
1146 
1147 	quit();
1148 }
1149