xref: /minix/minix/tests/test77.c (revision 83133719)
1 /* Tests for opening/closing pseudo terminals - by D.C. van Moolenbroek */
2 /* This test needs to be run as root; otherwise, openpty() won't work. */
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <signal.h>
7 #include <termios.h>
8 #include <sys/wait.h>
9 #include <sys/syslimits.h>
10 #include <paths.h>
11 #include <fcntl.h>
12 #include <util.h>
13 
14 #define ITERATIONS 10
15 
16 #include "common.h"
17 
18 static int sighups;		/* number of SIGHUP signals received */
19 
20 /*
21  * Signal handler for SIGHUP and SIGUSR1.
22  */
23 static void
24 signal_handler(int sig)
25 {
26 	if (sig == SIGHUP)
27 		sighups++;
28 }
29 
30 /*
31  * Set the slave side of the pseudo terminal to raw mode.  This simplifies
32  * testing communication.
33  */
34 static void
35 make_raw(int slavefd)
36 {
37 	struct termios tios;
38 
39 	if (tcgetattr(slavefd, &tios) < 0) e(100);
40 
41 	cfmakeraw(&tios);
42 
43 	if (tcsetattr(slavefd, TCSANOW, &tios) < 0) e(101);
44 }
45 
46 /*
47  * See if the given pseudo terminal can successfully perform basic
48  * communication between master and slave.
49  */
50 static void
51 test_comm(int masterfd, int slavefd)
52 {
53 	char c;
54 
55 	make_raw(slavefd);
56 
57 	c = 'A';
58 	if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(200);
59 	if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(201);
60 	if (c != 'A') e(202);
61 
62 	c = 'B';
63 	if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(203);
64 	if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(204);
65 	if (c != 'B') e(205);
66 
67 	c = 'C';
68 	if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(206);
69 	if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(207);
70 	if (c != 'C') e(208);
71 
72 	c = 'D';
73 	if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(209);
74 	if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(210);
75 	if (c != 'D') e(211);
76 }
77 
78 /*
79  * Get device node names for the master and slave end of a free pseudo
80  * terminal.  We don't want to replicate the entire openpty(3) logic here, so
81  * start by letting openpty(3) do the work for us.  We make the assumption that
82  * nobody snatches the pair while we are running.
83  */
84 static void
85 get_names(char pname[PATH_MAX], char tname[PATH_MAX])
86 {
87 	int len, masterfd, slavefd;
88 
89 	if (openpty(&masterfd, &slavefd, tname, NULL, NULL) < 0) e(300);
90 
91 	/*
92 	 * openpty(3) gives us only the slave name, but we also need the master
93 	 * name.
94 	 */
95 	strlcpy(pname, tname, PATH_MAX);
96 	len = strlen(_PATH_DEV);
97 
98 	if (strncmp(pname, _PATH_DEV, len)) e(301);
99 
100 	/* If this fails, this test needs to be updated. */
101 	if (pname[len] != 't') e(302);
102 
103 	pname[len] = 'p';
104 
105 	test_comm(masterfd, slavefd);
106 
107 	if (close(masterfd) < 0) e(303);
108 	if (close(slavefd) < 0) e(304);
109 }
110 
111 /*
112  * Test various orders of opening and closing the master and slave sides of a
113  * pseudo terminal, as well as opening/closing one side without ever opening
114  * the other.
115  */
116 static void
117 test77a(void)
118 {
119 	struct sigaction act, oact;
120 	char pname[PATH_MAX], tname[PATH_MAX];
121 	int masterfd, slavefd;
122 
123 	subtest = 1;
124 
125 	/* We do not want to get SIGHUP signals in this test. */
126 	memset(&act, 0, sizeof(act));
127 	act.sa_handler = SIG_IGN;
128 	if (sigaction(SIGHUP, &act, &oact) < 0) e(1);
129 
130 	/* Get master and slave device names for a free pseudo terminal. */
131 	get_names(pname, tname);
132 
133 	/* Try opening and then closing the master. */
134 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(2);
135 
136 	if (close(masterfd) < 0) e(3);
137 
138 	/* Now see if we can reopen the master as well as the slave. */
139 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(4);
140 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(5);
141 
142 	test_comm(masterfd, slavefd);
143 
144 	/* In the meantime, test different closing orders. This is order A. */
145 	if (close(slavefd) < 0) e(6);
146 	if (close(masterfd) < 0) e(7);
147 
148 	/* Now try opening the pair again. */
149 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(8);
150 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(9);
151 
152 	test_comm(masterfd, slavefd);
153 
154 	if (close(slavefd) < 0) e(10);
155 
156 	/*
157 	 * Try reopening the slave after closing it.  It is not very important
158 	 * that this works, but the TTY driver should currently support it.
159 	 */
160 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(11);
161 
162 	test_comm(masterfd, slavefd);
163 
164 	/* This is closing order B. This may or may not cause a SIGHUP. */
165 	if (close(masterfd) < 0) e(12);
166 	if (close(slavefd) < 0) e(13);
167 
168 	/* Try the normal open procedure. */
169 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(14);
170 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(15);
171 
172 	test_comm(masterfd, slavefd);
173 
174 	if (close(slavefd) < 0) e(16);
175 	if (close(masterfd) < 0) e(17);
176 
177 	/* Try reopening and closing the slave, without opening the master. */
178 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(18);
179 
180 	if (close(slavefd) < 0) e(19);
181 
182 	/* Again, try the normal open procedure. */
183 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(20);
184 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(21);
185 
186 	test_comm(masterfd, slavefd);
187 
188 	if (close(slavefd) < 0) e(22);
189 	if (close(masterfd) < 0) e(23);
190 
191 	/* Finally, try opening the slave first. */
192 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(24);
193 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(25);
194 
195 	test_comm(masterfd, slavefd);
196 
197 	if (close(slavefd) < 0) e(26);
198 	if (close(masterfd) < 0) e(27);
199 
200 	if (sigaction(SIGHUP, &oact, NULL) < 0) e(28);
201 }
202 
203 /*
204  * Test opening a single side multiple times.
205  */
206 static void
207 test77b(void)
208 {
209 	char pname[PATH_MAX], tname[PATH_MAX];
210 	int masterfd, slavefd, extrafd;
211 
212 	subtest = 2;
213 
214 	/* Get master and slave device names for a free pseudo terminal. */
215 	get_names(pname, tname);
216 
217 	/* It must not be possible to open the master multiple times. */
218 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(1);
219 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(2);
220 
221 	test_comm(masterfd, slavefd);
222 
223 	if ((extrafd = open(pname, O_RDWR | O_NOCTTY)) >= 0) e(3);
224 	if (errno != EIO) e(4);
225 
226 	test_comm(masterfd, slavefd);
227 
228 	if (close(slavefd) < 0) e(5);
229 	if (close(masterfd) < 0) e(6);
230 
231 	/* The slave can be opened multiple times, though. */
232 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(7);
233 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(8);
234 
235 	test_comm(masterfd, slavefd);
236 
237 	if ((extrafd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(9);
238 
239 	test_comm(masterfd, extrafd);
240 	test_comm(masterfd, slavefd);
241 
242 	if (close(slavefd) < 0) e(10);
243 	if (close(extrafd) < 0) e(11);
244 	if (close(masterfd) < 0) e(12);
245 }
246 
247 /*
248  * Test communication on half-open pseudo terminals.
249  */
250 static void
251 test77c(void)
252 {
253 	struct sigaction act, oact;
254 	char pname[PATH_MAX], tname[PATH_MAX];
255 	int masterfd, slavefd;
256 	char c;
257 
258 	subtest = 3;
259 
260 	/* We do not want to get SIGHUP signals in this test. */
261 	memset(&act, 0, sizeof(act));
262 	act.sa_handler = SIG_IGN;
263 	if (sigaction(SIGHUP, &act, &oact) < 0) e(1);
264 
265 	/* Get master and slave device names for a free pseudo terminal. */
266 	get_names(pname, tname);
267 
268 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(2);
269 
270 	/* Writes to the master should be buffered until there is a slave. */
271 	c = 'E';
272 	if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(3);
273 
274 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(4);
275 
276 	make_raw(slavefd);
277 
278 	if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(5);
279 	if (c != 'E') e(6);
280 
281 	/* Discard the echo on the master. */
282 	if (tcflush(slavefd, TCOFLUSH) != 0) e(7);
283 
284 	test_comm(masterfd, slavefd);
285 
286 	if (close(slavefd) < 0) e(8);
287 
288 	/* Writes to the master after the slave has been closed should fail. */
289 	if (write(masterfd, &c, sizeof(c)) >= 0) e(9);
290 	if (errno != EIO) e(10);
291 
292 	if (close(masterfd) < 0) e(11);
293 
294 	/* Writes to the slave should be buffered until there is a master. */
295 	if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(12);
296 
297 	make_raw(slavefd);
298 
299 	c = 'F';
300 	if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(13);
301 
302 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(14);
303 
304 	if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(15);
305 	if (c != 'F') e(16);
306 
307 	test_comm(masterfd, slavefd);
308 
309 	if (close(masterfd) < 0) e(17);
310 
311 	if (write(slavefd, &c, sizeof(c)) >= 0) e(18);
312 	if (errno != EIO) e(19);
313 
314 	/* Reads from the slave should return EOF if the master is gone. */
315 	if (read(slavefd, &c, sizeof(c)) != 0) e(20);
316 
317 	if (close(slavefd) < 0) e(21);
318 
319 	if (sigaction(SIGHUP, &oact, NULL) < 0) e(22);
320 }
321 
322 /*
323  * Wait for a child process to terminate.  Return 0 if the child exited without
324  * errors, -1 otherwise.
325  */
326 static int
327 waitchild(void)
328 {
329 	int status;
330 
331 	if (wait(&status) <= 0) return -1;
332 	if (!WIFEXITED(status)) return -1;
333 	if (WEXITSTATUS(status) != 0) return -1;
334 
335 	return 0;
336 }
337 
338 /*
339  * Test opening the slave side with and without the O_NOCTTY flag.
340  */
341 static void
342 test77d(void)
343 {
344 	char pname[PATH_MAX], tname[PATH_MAX];
345 	int masterfd, slavefd;
346 
347 	subtest = 4;
348 
349 	/* Get master and slave device names for a free pseudo terminal. */
350 	get_names(pname, tname);
351 
352 	/* Make ourselves process group leader if we aren't already. */
353 	(void) setsid();
354 
355 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(1);
356 
357 	/*
358 	 * Opening the slave with O_NOCTTY should not change its controlling
359 	 * terminal.
360 	 */
361 	switch (fork()) {
362 	case 0:
363 		if (setsid() < 0) e(2);
364 
365 		if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(3);
366 
367 		if (open("/dev/tty", O_RDWR) >= 0) e(4);
368 		if (errno != ENXIO) e(5);
369 
370 		exit(errct);
371 	case -1:
372 		e(6);
373 	default:
374 		break;
375 	}
376 
377 	if (waitchild() < 0) e(7);
378 
379 	if (close(masterfd) < 0) e(8);
380 
381 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(9);
382 
383 	/*
384 	 * Opening the slave without O_NOCTTY should change its controlling
385 	 * terminal, though.
386 	 */
387 	switch (fork()) {
388 	case 0:
389 		if (setsid() < 0) e(10);
390 
391 		if ((slavefd = open(tname, O_RDWR)) < 0) e(11);
392 
393 		if (open("/dev/tty", O_RDWR) < 0) e(12);
394 
395 		exit(errct);
396 	case -1:
397 		e(13);
398 	default:
399 		break;
400 	}
401 
402 	if (waitchild() < 0) e(14);
403 
404 	if (close(masterfd) < 0) e(15);
405 }
406 
407 /*
408  * Test receiving of SIGHUP on master hang-up.  All of the tests so far have
409  * ignored SIGHUP, and probably would not have received one anyway, since the
410  * process was not its own session leader.  Time to test this aspect.
411  */
412 static void
413 test77e(void)
414 {
415 	struct sigaction act, hup_oact, usr_oact;
416 	sigset_t set, oset;
417 	char pname[PATH_MAX], tname[PATH_MAX];
418 	int masterfd, slavefd;
419 
420 	subtest = 5;
421 
422 	/* Get master and slave device names for a free pseudo terminal. */
423 	get_names(pname, tname);
424 
425 	memset(&act, 0, sizeof(act));
426 	act.sa_handler = signal_handler;
427 	if (sigaction(SIGHUP, &act, &hup_oact) < 0) e(1);
428 
429 	memset(&act, 0, sizeof(act));
430 	act.sa_handler = signal_handler;
431 	if (sigaction(SIGUSR1, &act, &usr_oact) < 0) e(2);
432 
433 	sigemptyset(&set);
434 	sigaddset(&set, SIGHUP);
435 	sigaddset(&set, SIGUSR1);
436 	if (sigprocmask(SIG_BLOCK, &set, &oset) < 0) e(3);
437 
438 	sighups = 0;
439 
440 	/* Make ourselves process group leader if we aren't already. */
441 	(void) setsid();
442 
443 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(4);
444 
445 	switch (fork()) {
446 	case 0:
447 		if (close(masterfd) < 0) e(5);
448 
449 		/* Become session leader. */
450 		if (setsid() < 0) e(6);
451 
452 		if ((slavefd = open(tname, O_RDWR)) < 0) e(7);
453 
454 		/* Tell the parent we are ready. */
455 		kill(getppid(), SIGUSR1);
456 
457 		/* We should now get a SIGHUP. */
458 		set = oset;
459 		if (sigsuspend(&set) >= 0) e(8);
460 
461 		if (sighups != 1) e(9);
462 
463 		exit(errct);
464 	case -1:
465 		e(10);
466 	default:
467 		break;
468 	}
469 
470 	/* Wait for SIGUSR1 from the child. */
471 	set = oset;
472 	if (sigsuspend(&set) >= 0) e(11);
473 
474 	/* Closing the master should now raise a SIGHUP signal in the child. */
475 	if (close(masterfd) < 0) e(12);
476 
477 	if (waitchild() < 0) e(13);
478 
479 	if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0) e(14);
480 
481 	if (sigaction(SIGHUP, &hup_oact, NULL) < 0) e(15);
482 	if (sigaction(SIGUSR1, &usr_oact, NULL) < 0) e(16);
483 }
484 
485 /*
486  * Test basic select functionality on /dev/tty.  While this test should not be
487  * part of this test set, we already have all the infrastructure we need here.
488  */
489 static void
490 test77f(void)
491 {
492 	struct sigaction act, oact;
493 	char c, pname[PATH_MAX], tname[PATH_MAX];
494 	struct timeval tv;
495 	fd_set fd_set;
496 	int fd, maxfd, masterfd, slavefd;
497 
498 	subtest = 6;
499 
500 	/* We do not want to get SIGHUP signals in this test. */
501 	memset(&act, 0, sizeof(act));
502 	act.sa_handler = SIG_IGN;
503 	if (sigaction(SIGHUP, &act, &oact) < 0) e(1);
504 
505 	/* Get master and slave device names for a free pseudo terminal. */
506 	get_names(pname, tname);
507 
508 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(2);
509 
510 	switch (fork()) {
511 	case 0:
512 		if (setsid() < 0) e(3);
513 
514 		close(masterfd);
515 
516 		if ((slavefd = open(tname, O_RDWR)) < 0) e(4);
517 
518 		if ((fd = open("/dev/tty", O_RDWR)) < 0) e(5);
519 
520 		make_raw(fd);
521 
522 		/* Without slave input, /dev/tty is not ready for reading. */
523 		FD_ZERO(&fd_set);
524 		FD_SET(fd, &fd_set);
525 		tv.tv_sec = 0;
526 		tv.tv_usec = 0;
527 
528 		if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(6);
529 		if (FD_ISSET(fd, &fd_set)) e(7);
530 
531 		FD_SET(fd, &fd_set);
532 		tv.tv_sec = 0;
533 		tv.tv_usec = 10000;
534 
535 		if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(8);
536 		if (FD_ISSET(fd, &fd_set)) e(9);
537 
538 		/* It will be ready for writing, though. */
539 		FD_SET(fd, &fd_set);
540 
541 		if (select(fd + 1, NULL, &fd_set, NULL, NULL) != 1) e(10);
542 		if (!FD_ISSET(fd, &fd_set)) e(11);
543 
544 		/* Test mixing file descriptors to the same terminal. */
545 		FD_ZERO(&fd_set);
546 		FD_SET(fd, &fd_set);
547 		FD_SET(slavefd, &fd_set);
548 		tv.tv_sec = 0;
549 		tv.tv_usec = 10000;
550 
551 		maxfd = fd > slavefd ? fd : slavefd;
552 		if (select(maxfd + 1, &fd_set, NULL, NULL, &tv) != 0) e(12);
553 		if (FD_ISSET(fd, &fd_set)) e(13);
554 		if (FD_ISSET(slavefd, &fd_set)) e(14);
555 
556 		/* The delayed echo on the master must wake up our select. */
557 		c = 'A';
558 		if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(15);
559 
560 		FD_ZERO(&fd_set);
561 		FD_SET(fd, &fd_set);
562 
563 		if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(16);
564 		if (!FD_ISSET(fd, &fd_set)) e(17);
565 
566 		/* Select must now still flag readiness for reading. */
567 		tv.tv_sec = 0;
568 		tv.tv_usec = 0;
569 
570 		if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 1) e(18);
571 		if (!FD_ISSET(fd, &fd_set)) e(19);
572 
573 		/* That is, until we read the byte. */
574 		if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(20);
575 		if (c != 'B') e(21);
576 
577 		if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(22);
578 		if (FD_ISSET(fd, &fd_set)) e(23);
579 
580 		/* Ask the parent to close the master. */
581 		c = 'C';
582 		if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(24);
583 
584 		FD_SET(fd, &fd_set);
585 
586 		/* The closure must cause an EOF condition on the slave. */
587 		if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(25);
588 		if (!FD_ISSET(fd, &fd_set)) e(26);
589 
590 		if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(27);
591 		if (!FD_ISSET(fd, &fd_set)) e(28);
592 
593 		if (read(slavefd, &c, sizeof(c)) != 0) e(29);
594 
595 		exit(errct);
596 	case -1:
597 		e(30);
598 	default:
599 		/* Wait for the child to write something to the slave. */
600 		FD_ZERO(&fd_set);
601 		FD_SET(masterfd, &fd_set);
602 
603 		if (select(masterfd + 1, &fd_set, NULL, NULL, NULL) != 1)
604 			e(31);
605 		if (!FD_ISSET(masterfd, &fd_set)) e(32);
606 
607 		if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(33);
608 		if (c != 'A') e(34);
609 
610 		/* Write a reply once the child is blocked in its select. */
611 		tv.tv_sec = 1;
612 		tv.tv_usec = 0;
613 		if (select(masterfd + 1, &fd_set, NULL, NULL, &tv) != 0)
614 			e(35);
615 
616 		c = 'B';
617 		if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(36);
618 
619 		/* Wait for the child to request closing the master. */
620 		if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(37);
621 		if (c != 'C') e(38);
622 
623 		/* Close the master once the child is blocked in its select. */
624 		sleep(1);
625 
626 		close(masterfd);
627 
628 		break;
629 	}
630 
631 	if (waitchild() < 0) e(39);
632 
633 	if (sigaction(SIGHUP, &oact, NULL) < 0) e(28);
634 }
635 
636 int
637 main(int argc, char **argv)
638 {
639 	int i, m;
640 
641 	start(77);
642 
643 	if (argc == 2)
644 		m = atoi(argv[1]);
645 	else
646 		m = 0xFF;
647 
648 	for (i = 0; i < ITERATIONS; i++) {
649 		if (m & 0x01) test77a();
650 		if (m & 0x02) test77b();
651 		if (m & 0x04) test77c();
652 		if (m & 0x08) test77d();
653 		if (m & 0x10) test77e();
654 		if (m & 0x20) test77f();
655 	}
656 
657 	quit();
658 }
659