etc/services - sync with NetBSD-8
[minix.git] / minix / tests / test77.c
blob2ad24615906bf65caddf61ee61b3adcbab379503
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>
23 #define ITERATIONS 10
25 #define MIN_PTYS 4
27 #include "common.h"
29 static int sighups; /* number of SIGHUP signals received */
32 * Signal handler for SIGHUP and SIGUSR1.
34 static void
35 signal_handler(int sig)
37 if (sig == SIGHUP)
38 sighups++;
42 * Set the slave side of the pseudo terminal to raw mode. This simplifies
43 * testing communication.
45 static void
46 make_raw(int slavefd)
48 struct termios tios;
50 if (tcgetattr(slavefd, &tios) < 0) e(0);
52 cfmakeraw(&tios);
54 if (tcsetattr(slavefd, TCSANOW, &tios) < 0) e(0);
58 * See if the given pseudo terminal can successfully perform basic
59 * communication between master and slave.
61 static void
62 test_comm(int masterfd, int slavefd)
64 char c;
66 make_raw(slavefd);
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);
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);
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);
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);
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.
98 static int
99 get_pty(int *pfd, char pname[PATH_MAX], char tname[PATH_MAX])
101 char *name;
102 int len, masterfd, slavefd;
105 * First try Unix98 PTY allocation, mainly to avoid opening the slave
106 * end immediately. If this fails, try openpty(3) as well.
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);
114 return 0;
116 if (close(masterfd) < 0) e(0);
119 if (openpty(&masterfd, &slavefd, tname, NULL, NULL) < 0) e(0);
121 test_comm(masterfd, slavefd);
123 *pfd = masterfd;
125 if (close(slavefd) < 0) e(0);
128 * openpty(3) gives us only the slave name, but we also want the master
129 * name.
131 len = strlen(_PATH_DEV);
132 if (strncmp(tname, _PATH_DEV, len)) e(0);
134 if (strncmp(&tname[len], "tty", 3))
135 return 0; /* Unix98 after all? Well okay, whatever.. */
137 if (pname != NULL) {
138 strlcpy(pname, tname, PATH_MAX);
139 pname[len] = 'p';
142 return 1;
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.
150 static void
151 test77a(void)
153 struct sigaction act, oact;
154 char pname[PATH_MAX], tname[PATH_MAX];
155 int oldstyle, masterfd, slavefd;
157 subtest = 1;
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);
164 /* Obtain a pseudo terminal. */
165 oldstyle = get_pty(&masterfd, pname, tname);
167 if (oldstyle) {
168 /* Try closing the master. */
169 if (close(masterfd) < 0) e(0);
171 /* See if we can reopen the master. */
172 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
175 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
177 test_comm(masterfd, slavefd);
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);
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);
189 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
191 test_comm(masterfd, slavefd);
193 if (close(slavefd) < 0) e(0);
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.
199 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
201 test_comm(masterfd, slavefd);
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);
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);
213 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
215 test_comm(masterfd, slavefd);
217 if (close(slavefd) < 0) e(0);
218 if (close(masterfd) < 0) e(0);
221 * Try reopening and closing the slave, without opening the master.
222 * This should work on old-style PTYS, but not on Unix98 PTYs.
224 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) >= 0) {
225 if (!oldstyle) e(0);
227 if (close(slavefd) < 0) e(0);
228 } else
229 if (oldstyle) e(0);
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);
237 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
239 test_comm(masterfd, slavefd);
241 if (close(slavefd) < 0) e(0);
242 if (close(masterfd) < 0) e(0);
245 * Finally, try opening the slave first. This does not work with
246 * Unix98 PTYs.
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);
252 test_comm(masterfd, slavefd);
254 if (close(slavefd) < 0) e(0);
255 if (close(masterfd) < 0) e(0);
258 if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
262 * Test opening a single side multiple times.
264 static void
265 test77b(void)
267 char pname[PATH_MAX], tname[PATH_MAX];
268 int oldstyle, masterfd, slavefd, extrafd;
270 subtest = 2;
272 /* Obtain a pseudo terminal. */
273 oldstyle = get_pty(&masterfd, pname, tname);
275 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
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.
281 test_comm(masterfd, slavefd);
283 if (oldstyle) {
284 if ((extrafd = open(pname, O_RDWR | O_NOCTTY)) >= 0) e(0);
285 if (errno != EIO) e(0);
288 test_comm(masterfd, slavefd);
290 if (close(slavefd) < 0) e(0);
291 if (close(masterfd) < 0) e(0);
293 /* The slave can be opened multiple times, though. */
294 oldstyle = get_pty(&masterfd, pname, tname);
296 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
298 test_comm(masterfd, slavefd);
300 if ((extrafd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
302 test_comm(masterfd, extrafd);
303 test_comm(masterfd, slavefd);
305 if (close(slavefd) < 0) e(0);
306 if (close(extrafd) < 0) e(0);
307 if (close(masterfd) < 0) e(0);
311 * Test communication on half-open pseudo terminals.
313 static void
314 test77c(void)
316 struct sigaction act, oact;
317 char pname[PATH_MAX], tname[PATH_MAX];
318 int oldstyle, masterfd, slavefd;
319 char c;
321 subtest = 3;
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);
328 /* Obtain a pseudo terminal. */
329 oldstyle = get_pty(&masterfd, pname, tname);
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.
336 if (oldstyle) {
337 if (close(masterfd) < 0) e(0);
339 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
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);
346 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
348 make_raw(slavefd);
350 if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
351 if (c != 'E') e(0);
353 /* Discard the echo on the master. */
354 if (tcflush(slavefd, TCOFLUSH) != 0) e(0);
356 test_comm(masterfd, slavefd);
358 if (close(slavefd) < 0) e(0);
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);
364 if (oldstyle)
365 if (close(masterfd) < 0) e(0);
368 * Writes to the slave should be buffered until there is a master.
369 * This applies to old-style PTYs only.
371 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
373 if (oldstyle) {
374 make_raw(slavefd);
376 c = 'F';
377 if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
379 if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0);
381 if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
382 if (c != 'F') e(0);
385 test_comm(masterfd, slavefd);
387 if (close(masterfd) < 0) e(0);
389 if (write(slavefd, &c, sizeof(c)) >= 0) e(0);
390 if (errno != EIO) e(0);
392 /* Reads from the slave should return EOF if the master is gone. */
393 if (read(slavefd, &c, sizeof(c)) != 0) e(0);
395 if (close(slavefd) < 0) e(0);
397 if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
401 * Wait for a child process to terminate. Return 0 if the child exited without
402 * errors, -1 otherwise.
404 static int
405 waitchild(void)
407 int status;
409 if (wait(&status) <= 0) return -1;
410 if (!WIFEXITED(status)) return -1;
411 if (WEXITSTATUS(status) != 0) return -1;
413 return 0;
417 * Test opening the slave side with and without the O_NOCTTY flag.
419 static void
420 test77d(void)
422 char pname[PATH_MAX], tname[PATH_MAX];
423 int masterfd, slavefd;
425 subtest = 4;
427 /* Make ourselves process group leader if we aren't already. */
428 (void)setsid();
430 /* Obtain a pseudo terminal. */
431 (void)get_pty(&masterfd, NULL, tname);
434 * Opening the slave with O_NOCTTY should not change its controlling
435 * terminal.
437 switch (fork()) {
438 case 0:
439 if (close(masterfd) < 0) e(0);
441 if (setsid() < 0) e(0);
443 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
445 if (open("/dev/tty", O_RDWR) >= 0) e(0);
446 if (errno != ENXIO) e(0);
448 exit(errct);
449 case -1:
450 e(0);
451 default:
452 break;
455 if (waitchild() < 0) e(0);
457 if (close(masterfd) < 0) e(0);
459 (void)get_pty(&masterfd, pname, tname);
462 * Opening the slave without O_NOCTTY should change its controlling
463 * terminal, though.
465 switch (fork()) {
466 case 0:
467 if (close(masterfd) < 0) e(0);
469 if (setsid() < 0) e(0);
471 if ((slavefd = open(tname, O_RDWR)) < 0) e(0);
473 if (open("/dev/tty", O_RDWR) < 0) e(0);
475 exit(errct);
476 case -1:
477 e(0);
478 default:
479 break;
482 if (waitchild() < 0) e(0);
484 if (close(masterfd) < 0) e(0);
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.
492 static void
493 test77e(void)
495 struct sigaction act, hup_oact, usr_oact;
496 sigset_t set, oset;
497 char tname[PATH_MAX];
498 int masterfd, slavefd;
500 subtest = 5;
502 memset(&act, 0, sizeof(act));
503 act.sa_handler = signal_handler;
504 if (sigaction(SIGHUP, &act, &hup_oact) < 0) e(0);
506 memset(&act, 0, sizeof(act));
507 act.sa_handler = signal_handler;
508 if (sigaction(SIGUSR1, &act, &usr_oact) < 0) e(0);
510 sigemptyset(&set);
511 sigaddset(&set, SIGHUP);
512 sigaddset(&set, SIGUSR1);
513 if (sigprocmask(SIG_BLOCK, &set, &oset) < 0) e(0);
515 sighups = 0;
517 /* Make ourselves process group leader if we aren't already. */
518 (void)setsid();
520 /* Obtain a pseudo terminal. */
521 (void)get_pty(&masterfd, NULL, tname);
523 switch (fork()) {
524 case 0:
525 if (close(masterfd) < 0) e(0);
527 /* Become session leader. */
528 if (setsid() < 0) e(0);
530 if ((slavefd = open(tname, O_RDWR)) < 0) e(0);
532 /* Tell the parent we are ready. */
533 kill(getppid(), SIGUSR1);
535 /* We should now get a SIGHUP. */
536 set = oset;
537 if (sigsuspend(&set) >= 0) e(0);
539 if (sighups != 1) e(0);
541 exit(errct);
542 case -1:
543 e(0);
544 default:
545 break;
548 /* Wait for SIGUSR1 from the child. */
549 set = oset;
550 if (sigsuspend(&set) >= 0) e(0);
552 /* Closing the master should now raise a SIGHUP signal in the child. */
553 if (close(masterfd) < 0) e(0);
555 if (waitchild() < 0) e(0);
557 if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0) e(0);
559 if (sigaction(SIGHUP, &hup_oact, NULL) < 0) e(0);
560 if (sigaction(SIGUSR1, &usr_oact, NULL) < 0) e(0);
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.
567 static void
568 test77f(void)
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;
576 subtest = 6;
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);
583 /* Obtain a pseudo terminal. */
584 (void)get_pty(&masterfd, NULL, tname);
586 switch (fork()) {
587 case 0:
588 if (close(masterfd) < 0) e(0);
590 if (setsid() < 0) e(0);
592 if ((slavefd = open(tname, O_RDWR)) < 0) e(0);
594 if ((fd = open("/dev/tty", O_RDWR)) < 0) e(0);
596 make_raw(fd);
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;
604 if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0);
605 if (FD_ISSET(fd, &fd_set)) e(0);
607 FD_SET(fd, &fd_set);
608 tv.tv_sec = 0;
609 tv.tv_usec = 10000;
611 if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0);
612 if (FD_ISSET(fd, &fd_set)) e(0);
614 /* It will be ready for writing, though. */
615 FD_SET(fd, &fd_set);
617 if (select(fd + 1, NULL, &fd_set, NULL, NULL) != 1) e(0);
618 if (!FD_ISSET(fd, &fd_set)) e(0);
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;
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);
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);
636 FD_ZERO(&fd_set);
637 FD_SET(fd, &fd_set);
639 if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0);
640 if (!FD_ISSET(fd, &fd_set)) e(0);
642 /* Select must now still flag readiness for reading. */
643 tv.tv_sec = 0;
644 tv.tv_usec = 0;
646 if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 1) e(0);
647 if (!FD_ISSET(fd, &fd_set)) e(0);
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);
653 if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0);
654 if (FD_ISSET(fd, &fd_set)) e(0);
656 /* Ask the parent to close the master. */
657 c = 'C';
658 if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0);
660 FD_SET(fd, &fd_set);
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);
666 if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0);
667 if (!FD_ISSET(fd, &fd_set)) e(0);
669 if (read(slavefd, &c, sizeof(c)) != 0) e(0);
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);
679 if (select(masterfd + 1, &fd_set, NULL, NULL, NULL) != 1)
680 e(0);
681 if (!FD_ISSET(masterfd, &fd_set)) e(0);
683 if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
684 if (c != 'A') e(0);
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);
692 c = 'B';
693 if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0);
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);
699 /* Close the master once the child is blocked in its select. */
700 sleep(1);
702 close(masterfd);
704 break;
707 if (waitchild() < 0) e(0);
709 if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
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.
718 static void
719 test_getdents(int nindex, int array[3], int present[3])
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;
729 seen_dot = seen_dotdot = 0;
730 for (i = 0; i < nindex; i++)
731 seen_index[i] = 0;
733 if ((group = getgrnam("tty")) == NULL) e(0);
734 tty_gid = group->gr_gid;
736 if ((dirp = opendir(_PATH_DEV_PTS)) == NULL) e(0);
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);
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;
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);
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);
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;
776 break;
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);
789 if (closedir(dirp) < 0) e(0);
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);
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'.
801 static int
802 get_unix98_pty(char ** tptr)
804 int masterfd;
806 if ((masterfd = posix_openpt(O_RDWR | O_NOCTTY)) < 0) e(0);
808 if (grantpt(masterfd) < 0) e(0);
810 /* This call is a no-op on MINIX3. */
811 if (unlockpt(masterfd) < 0) e(0);
813 if ((*tptr = ptsname(masterfd)) == NULL) e(0);
815 return masterfd;
819 * Test for Unix98 PTY support and PTYFS.
821 static void
822 test77g(void)
824 char *tname;
825 struct stat buf;
826 size_t len;
827 int i, masterfd, slavefd, fd[3], array[3], present[3];
829 subtest = 7;
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.
838 for (i = 0; i <= 2; i++) {
839 masterfd = get_unix98_pty(&tname);
841 if (access(tname, R_OK | W_OK) < 0) e(0);
843 if (i > 0) {
844 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0)
845 e(0);
847 if (access(tname, R_OK | W_OK) < 0) e(0);
849 if (i > 1) {
850 if (close(masterfd) < 0) e(0);
852 masterfd = slavefd; /* ugly but saving code */
853 } else
854 if (close(slavefd) < 0) e(0);
857 if (access(tname, R_OK | W_OK) < 0) e(0);
859 if (close(masterfd) < 0) e(0);
861 if (access(tname, R_OK | W_OK) == 0) e(0);
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.
869 test_getdents(0, NULL, NULL);
871 for (i = 0; i < 3; i++) {
872 fd[i] = get_unix98_pty(&tname);
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;
881 test_getdents(3, array, present);
883 if (close(fd[0]) < 0) e(0);
884 present[0] = 0;
886 test_getdents(3, array, present);
888 if (close(fd[2]) < 0) e(0);
889 present[2] = 0;
891 test_getdents(3, array, present);
893 if (close(fd[1]) < 0) e(0);
894 present[1] = 0;
896 test_getdents(3, array, present);
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.
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.
916 masterfd = get_unix98_pty(&tname);
918 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
920 if (stat(tname, &buf) != 0) e(0);
921 if (buf.st_mode != (S_IFCHR | 0620)) e(0);
923 if (chmod(tname, S_IFCHR | 0630) != 0) e(0);
925 if (stat(tname, &buf) != 0) e(0);
926 if (buf.st_mode != (S_IFCHR | 0630)) e(0);
928 if (grantpt(masterfd) != 0) e(0);
930 if (stat(tname, &buf) != 0) e(0);
931 if (buf.st_mode != (S_IFCHR | 0620)) e(0);
933 test_comm(masterfd, slavefd);
935 if (close(slavefd) < 0) e(0);
936 if (close(masterfd) < 0) e(0);
938 test_getdents(0, NULL, NULL);
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.
946 static void
947 test_overlap(int m)
949 char *tname;
950 size_t len;
951 int i, n, fd[MIN_PTYS];
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);
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);
965 if (m == n) e(0);
968 for (i--; i >= 0; i--)
969 if (close(fd[i]) < 0) e(0);
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.
978 static void
979 test77h(void)
981 char *tname, ptest[PATH_MAX], ttest[PATH_MAX];
982 struct sigaction act, oact;
983 size_t len;
984 int i, n, masterfd, slavefd;
986 subtest = 8;
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);
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.
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.
1004 for (i = 0; i <= 2; i++) {
1005 /* Open a Unix98 PTY and get the slave name. */
1006 masterfd = get_unix98_pty(&tname);
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);
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);
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.
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);
1027 if (i > 0) {
1028 if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0)
1029 e(0);
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);
1036 if (close(slavefd) < 0) e(0);
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);
1044 if ((slavefd =
1045 open(tname, O_RDWR | O_NOCTTY)) < 0) e(0);
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);
1052 if (close(masterfd) < 0) e(0);
1054 masterfd = slavefd; /* ugly but saving code */
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);
1063 if (close(masterfd) < 0) e(0);
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.
1071 if ((masterfd = open(ptest, O_RDWR | O_NOCTTY)) < 0 &&
1072 errno != EACCES) e(0);
1074 if (masterfd >= 0 && close(masterfd) < 0) e(0);
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.
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);
1093 if ((masterfd = open(ptest, O_RDWR | O_NOCTTY)) >= 0)
1094 break;
1097 if (n >= MIN_PTYS)
1098 break;
1100 test_overlap(n);
1102 snprintf(ttest, sizeof(ttest), _PATH_DEV "ttyp%u", n);
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);
1108 if (i > 0) {
1109 if (close(masterfd) < 0) e(0);
1111 masterfd = slavefd; /* again, ugly */
1112 } else
1113 if (close(slavefd) < 0) e(0);
1115 test_overlap(n);
1118 if (close(masterfd) < 0) e(0);
1121 if (sigaction(SIGHUP, &oact, NULL) < 0) e(0);
1125 main(int argc, char **argv)
1127 int i, m;
1129 start(77);
1131 if (argc == 2)
1132 m = atoi(argv[1]);
1133 else
1134 m = 0xFF;
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();
1147 quit();