xref: /minix/minix/tests/test77.c (revision 7f5f010b)
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 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 	if (close(slavefd) < 0) e(20);
315 
316 	if (sigaction(SIGHUP, &oact, NULL) < 0) e(21);
317 }
318 
319 /*
320  * Test opening the slave side with and without the O_NOCTTY flag.
321  */
322 static void
323 test77d(void)
324 {
325 	char pname[PATH_MAX], tname[PATH_MAX];
326 	int masterfd, slavefd;
327 
328 	subtest = 4;
329 
330 	/* Get master and slave device names for a free pseudo terminal. */
331 	get_names(pname, tname);
332 
333 	/* Make ourselves process group leader if we aren't already. */
334 	(void) setsid();
335 
336 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(1);
337 
338 	/*
339 	 * Opening the slave with O_NOCTTY should not change its controlling
340 	 * terminal.
341 	 */
342 	switch (fork()) {
343 	case 0:
344 		if (setsid() < 0) e(2);
345 
346 		if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(3);
347 
348 		if (open("/dev/tty", O_RDWR) >= 0) e(4);
349 		if (errno != ENXIO) e(5);
350 
351 		exit(0);
352 	case -1:
353 		e(6);
354 	default:
355 		break;
356 	}
357 
358 	if (wait(NULL) <= 0) e(7);
359 
360 	if (close(masterfd) < 0) e(8);
361 
362 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(9);
363 
364 	/*
365 	 * Opening the slave without O_NOCTTY should change its controlling
366 	 * terminal, though.
367 	 */
368 	switch (fork()) {
369 	case 0:
370 		if (setsid() < 0) e(10);
371 
372 		if ((slavefd = open(tname, O_RDWR)) < 0) e(11);
373 
374 		if (open("/dev/tty", O_RDWR) < 0) e(12);
375 
376 		exit(0);
377 	case -1:
378 		e(13);
379 	default:
380 		break;
381 	}
382 
383 	if (wait(NULL) <= 0) e(14);
384 
385 	if (close(masterfd) < 0) e(15);
386 }
387 
388 /*
389  * Test receiving of SIGHUP on master hang-up.  All of the tests so far have
390  * ignored SIGHUP, and probably would not have received one anyway, since the
391  * process was not its own session leader.  Time to test this aspect.
392  */
393 static void
394 test77e(void)
395 {
396 	struct sigaction act, hup_oact, usr_oact;
397 	sigset_t set, oset;
398 	char pname[PATH_MAX], tname[PATH_MAX];
399 	int masterfd, slavefd;
400 
401 	subtest = 5;
402 
403 	/* Get master and slave device names for a free pseudo terminal. */
404 	get_names(pname, tname);
405 
406 	memset(&act, 0, sizeof(act));
407 	act.sa_handler = signal_handler;
408 	if (sigaction(SIGHUP, &act, &hup_oact) < 0) e(1);
409 
410 	memset(&act, 0, sizeof(act));
411 	act.sa_handler = signal_handler;
412 	if (sigaction(SIGUSR1, &act, &usr_oact) < 0) e(2);
413 
414 	sigemptyset(&set);
415 	sigaddset(&set, SIGHUP);
416 	sigaddset(&set, SIGUSR1);
417 	if (sigprocmask(SIG_BLOCK, &set, &oset) < 0) e(3);
418 
419 	sighups = 0;
420 
421 	/* Make ourselves process group leader if we aren't already. */
422 	(void) setsid();
423 
424 	if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(4);
425 
426 	switch (fork()) {
427 	case 0:
428 		if (close(masterfd) < 0) e(5);
429 
430 		/* Become session leader. */
431 		if (setsid() < 0) e(6);
432 
433 		if ((slavefd = open(tname, O_RDWR)) < 0) e(7);
434 
435 		/* Tell the parent we are ready. */
436 		kill(getppid(), SIGUSR1);
437 
438 		/* We should now get a SIGHUP. */
439 		set = oset;
440 		if (sigsuspend(&set) >= 0) e(8);
441 
442 		if (sighups != 1) e(9);
443 
444 		exit(0);
445 	case -1:
446 		e(10);
447 	default:
448 		break;
449 	}
450 
451 	/* Wait for SIGUSR1 from the child. */
452 	set = oset;
453 	if (sigsuspend(&set) >= 0) e(11);
454 
455 	/* Closing the master should now raise a SIGHUP signal in the child. */
456 	if (close(masterfd) < 0) e(12);
457 
458 	if (wait(NULL) <= 0) e(13);
459 
460 	if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0) e(14);
461 
462 	if (sigaction(SIGHUP, &hup_oact, NULL) < 0) e(15);
463 	if (sigaction(SIGUSR1, &usr_oact, NULL) < 0) e(16);
464 }
465 
466 int
467 main(int argc, char **argv)
468 {
469 	int i, m;
470 
471 	start(77);
472 
473 	if (argc == 2)
474 		m = atoi(argv[1]);
475 	else
476 		m = 0xFF;
477 
478 	for (i = 0; i < ITERATIONS; i++) {
479 		if (m & 0x01) test77a();
480 		if (m & 0x02) test77b();
481 		if (m & 0x04) test77c();
482 		if (m & 0x08) test77d();
483 		if (m & 0x10) test77e();
484 	}
485 
486 	quit();
487 }
488