64-bit VFS_LSEEK_OFF
[minix3.git] / test / test77.c
blob2b8c68d095e04828a16ca6594af99a2832114b2d
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>
14 #define ITERATIONS 10
16 #include "common.h"
18 static int sighups; /* number of SIGHUP signals received */
21 * Signal handler for SIGHUP and SIGUSR1.
23 void
24 signal_handler(int sig)
26 if (sig == SIGHUP)
27 sighups++;
31 * Set the slave side of the pseudo terminal to raw mode. This simplifies
32 * testing communication.
34 static void
35 make_raw(int slavefd)
37 struct termios tios;
39 if (tcgetattr(slavefd, &tios) < 0) e(100);
41 cfmakeraw(&tios);
43 if (tcsetattr(slavefd, TCSANOW, &tios) < 0) e(101);
47 * See if the given pseudo terminal can successfully perform basic
48 * communication between master and slave.
50 static void
51 test_comm(int masterfd, int slavefd)
53 char c;
55 make_raw(slavefd);
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);
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);
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);
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);
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.
84 static void
85 get_names(char pname[PATH_MAX], char tname[PATH_MAX])
87 int len, masterfd, slavefd;
89 if (openpty(&masterfd, &slavefd, tname, NULL, NULL) < 0) e(300);
92 * openpty(3) gives us only the slave name, but we also need the master
93 * name.
95 strlcpy(pname, tname, PATH_MAX);
96 len = strlen(_PATH_DEV);
98 if (strncmp(pname, _PATH_DEV, len)) e(301);
100 /* If this fails, this test needs to be updated. */
101 if (pname[len] != 't') e(302);
103 pname[len] = 'p';
105 test_comm(masterfd, slavefd);
107 if (close(masterfd) < 0) e(303);
108 if (close(slavefd) < 0) e(304);
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.
116 static void
117 test77a(void)
119 struct sigaction act, oact;
120 char pname[PATH_MAX], tname[PATH_MAX];
121 int masterfd, slavefd;
123 subtest = 1;
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);
130 /* Get master and slave device names for a free pseudo terminal. */
131 get_names(pname, tname);
133 /* Try opening and then closing the master. */
134 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(2);
136 if (close(masterfd) < 0) e(3);
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);
142 test_comm(masterfd, slavefd);
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);
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);
152 test_comm(masterfd, slavefd);
154 if (close(slavefd) < 0) e(10);
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.
160 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(11);
162 test_comm(masterfd, slavefd);
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);
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);
172 test_comm(masterfd, slavefd);
174 if (close(slavefd) < 0) e(16);
175 if (close(masterfd) < 0) e(17);
177 /* Try reopening and closing the slave, without opening the master. */
178 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(18);
180 if (close(slavefd) < 0) e(19);
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);
186 test_comm(masterfd, slavefd);
188 if (close(slavefd) < 0) e(22);
189 if (close(masterfd) < 0) e(23);
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);
195 test_comm(masterfd, slavefd);
197 if (close(slavefd) < 0) e(26);
198 if (close(masterfd) < 0) e(27);
200 if (sigaction(SIGHUP, &oact, NULL) < 0) e(28);
204 * Test opening a single side multiple times.
206 static void
207 test77b(void)
209 char pname[PATH_MAX], tname[PATH_MAX];
210 int masterfd, slavefd, extrafd;
212 subtest = 2;
214 /* Get master and slave device names for a free pseudo terminal. */
215 get_names(pname, tname);
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);
221 test_comm(masterfd, slavefd);
223 if ((extrafd = open(pname, O_RDWR | O_NOCTTY)) >= 0) e(3);
224 if (errno != EIO) e(4);
226 test_comm(masterfd, slavefd);
228 if (close(slavefd) < 0) e(5);
229 if (close(masterfd) < 0) e(6);
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);
235 test_comm(masterfd, slavefd);
237 if ((extrafd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(9);
239 test_comm(masterfd, extrafd);
240 test_comm(masterfd, slavefd);
242 if (close(slavefd) < 0) e(10);
243 if (close(extrafd) < 0) e(11);
244 if (close(masterfd) < 0) e(12);
248 * Test communication on half-open pseudo terminals.
250 static void
251 test77c(void)
253 struct sigaction act, oact;
254 char pname[PATH_MAX], tname[PATH_MAX];
255 int masterfd, slavefd;
256 char c;
258 subtest = 3;
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);
265 /* Get master and slave device names for a free pseudo terminal. */
266 get_names(pname, tname);
268 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(2);
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);
274 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(4);
276 make_raw(slavefd);
278 if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(5);
279 if (c != 'E') e(6);
281 /* Discard the echo on the master. */
282 if (tcflush(slavefd, TCOFLUSH) != 0) e(7);
284 test_comm(masterfd, slavefd);
286 if (close(slavefd) < 0) e(8);
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);
292 if (close(masterfd) < 0) e(11);
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);
297 make_raw(slavefd);
299 c = 'F';
300 if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(13);
302 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(14);
304 if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(15);
305 if (c != 'F') e(16);
307 test_comm(masterfd, slavefd);
309 if (close(masterfd) < 0) e(17);
311 if (write(slavefd, &c, sizeof(c)) >= 0) e(18);
312 if (errno != EIO) e(19);
314 if (close(slavefd) < 0) e(20);
316 if (sigaction(SIGHUP, &oact, NULL) < 0) e(21);
320 * Test opening the slave side with and without the O_NOCTTY flag.
322 static void
323 test77d(void)
325 char pname[PATH_MAX], tname[PATH_MAX];
326 int masterfd, slavefd;
328 subtest = 4;
330 /* Get master and slave device names for a free pseudo terminal. */
331 get_names(pname, tname);
333 /* Make ourselves process group leader if we aren't already. */
334 (void) setsid();
336 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(1);
339 * Opening the slave with O_NOCTTY should not change its controlling
340 * terminal.
342 switch (fork()) {
343 case 0:
344 if (setsid() < 0) e(2);
346 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(3);
348 if (open("/dev/tty", O_RDWR) >= 0) e(4);
349 if (errno != ENXIO) e(5);
351 exit(0);
352 case -1:
353 e(6);
354 default:
355 break;
358 if (wait(NULL) <= 0) e(7);
360 if (close(masterfd) < 0) e(8);
362 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(9);
365 * Opening the slave without O_NOCTTY should change its controlling
366 * terminal, though.
368 switch (fork()) {
369 case 0:
370 if (setsid() < 0) e(10);
372 if ((slavefd = open(tname, O_RDWR)) < 0) e(11);
374 if (open("/dev/tty", O_RDWR) < 0) e(12);
376 exit(0);
377 case -1:
378 e(13);
379 default:
380 break;
383 if (wait(NULL) <= 0) e(14);
385 if (close(masterfd) < 0) e(15);
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.
393 static void
394 test77e(void)
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;
401 subtest = 5;
403 /* Get master and slave device names for a free pseudo terminal. */
404 get_names(pname, tname);
406 memset(&act, 0, sizeof(act));
407 act.sa_handler = signal_handler;
408 if (sigaction(SIGHUP, &act, &hup_oact) < 0) e(1);
410 memset(&act, 0, sizeof(act));
411 act.sa_handler = signal_handler;
412 if (sigaction(SIGUSR1, &act, &usr_oact) < 0) e(2);
414 sigemptyset(&set);
415 sigaddset(&set, SIGHUP);
416 sigaddset(&set, SIGUSR1);
417 if (sigprocmask(SIG_BLOCK, &set, &oset) < 0) e(3);
419 sighups = 0;
421 /* Make ourselves process group leader if we aren't already. */
422 (void) setsid();
424 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(4);
426 switch (fork()) {
427 case 0:
428 if (close(masterfd) < 0) e(5);
430 /* Become session leader. */
431 if (setsid() < 0) e(6);
433 if ((slavefd = open(tname, O_RDWR)) < 0) e(7);
435 /* Tell the parent we are ready. */
436 kill(getppid(), SIGUSR1);
438 /* We should now get a SIGHUP. */
439 set = oset;
440 if (sigsuspend(&set) >= 0) e(8);
442 if (sighups != 1) e(9);
444 exit(0);
445 case -1:
446 e(10);
447 default:
448 break;
451 /* Wait for SIGUSR1 from the child. */
452 set = oset;
453 if (sigsuspend(&set) >= 0) e(11);
455 /* Closing the master should now raise a SIGHUP signal in the child. */
456 if (close(masterfd) < 0) e(12);
458 if (wait(NULL) <= 0) e(13);
460 if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0) e(14);
462 if (sigaction(SIGHUP, &hup_oact, NULL) < 0) e(15);
463 if (sigaction(SIGUSR1, &usr_oact, NULL) < 0) e(16);
467 main(int argc, char **argv)
469 int i, m;
471 start(77);
473 if (argc == 2)
474 m = atoi(argv[1]);
475 else
476 m = 0xFF;
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();
486 quit();