2
* Copyright 2001 by the Massachusetts Institute of Technology.
4
* Permission to use, copy, modify, and distribute this software and
5
* its documentation for any purpose and without fee is hereby
6
* granted, provided that the above copyright notice appear in all
7
* copies and that both that copyright notice and this permission
8
* notice appear in supporting documentation, and that the name of
9
* M.I.T. not be used in advertising or publicity pertaining to
10
* distribution of the software without specific, written prior
11
* permission. Furthermore if you modify this software you must label
12
* your software as modified software and not distribute it in such a
13
* fashion that it might be confused with the original M.I.T. software.
14
* M.I.T. makes no representations about the suitability
15
* of this software for any purpose. It is provided "as is" without
16
* express or implied warranty.
20
* A rant on the nature of pseudo-terminals:
21
* -----------------------------------------
23
* Controlling terminals and job control:
25
* First, some explanation of job control and controlling terminals is
26
* necessary for background. This discussion applies to hardwired
27
* terminals as well as ptys. On most modern systems, all processes
28
* belong to a process group. A process whose process group id (pgid)
29
* is the sames as its pid is the process group leader of its process
30
* group. Process groups belong to sessions. On a modern system, a
31
* process that is not currently a process group leader may create a
32
* new session by calling setsid(), which makes it a session leader as
33
* well as a process group leader, and also removes any existing
34
* controlling terminal (ctty) association. Only a session leader may
35
* acquire a ctty. It's not clear how systems that don't have
36
* setsid() handle ctty acquisition, though probably any process group
37
* leader that doesn't have a ctty may acquire one that way.
39
* A terminal that is a ctty has an associated foreground process
40
* group, which is a member of the terminal's associated session.
41
* This process group gets read/write access to the terminal and will
42
* receive terminal-generated signals (e.g. SIGINT, SIGTSTP). Process
43
* groups belonging to the session but not in the foreground may get
44
* signals that suspend them if they try to read/write from the ctty,
45
* depending on various terminal settings.
47
* On many systems, the controlling process (the session leader
48
* associated with a ctty) exiting will cause the session to lose its
49
* ctty, even though some processes may continue to have open file
50
* descriptors on the former ctty. It is possible for a process to
51
* have no file descriptors open on its controlling tty, but to
52
* reacquire such by opening /dev/tty, as long as its session still
57
* Ptys have a slave side and a master side. The slave side looks
58
* like a hardwired serial line to the application that opens it;
59
* usually, telnetd or rlogind, etc. opens the slave and hands it to
60
* the login program as stdin/stdout/stderr. The master side usually
61
* gets the actual network traffic written to/from it. Roughly, the
62
* master and slave are two ends of a bidirectional pair of FIFOs,
63
* though this can get complicated by other things.
65
* The master side of a pty is theoretically a single-open device.
66
* This MUST be true on systems that have BSD-style ptys, since there
67
* is usually no way to allocate an unused pty except by attempting to
68
* open all the master pty nodes in the system.
70
* Often, but not always, the last close of a slave device will cause
71
* the master to get an EOF. Closing the master device will sometimes
72
* cause the foreground process group of the slave to get a SIGHUP,
73
* but that may depend on terminal settings.
77
* On a BSD-derived system, the master nodes are named like
78
* /dev/ptyp0, and the slave nodes are named like /dev/ttyp0. The
79
* last two characters are the variable ones, and a shell-glob type
80
* pattern for a slave device is usually of the form
81
* /dev/tty[p-z][0-9a-f], though variants are known to exist.
83
* System V cloning ptys:
85
* There is a cloning master device (usually /dev/ptmx, but the name
86
* can vary) that gets opened. Each open of the cloning master
87
* results in an open file descriptor of a unique master device. The
88
* application calls ptsname() to find the pathname to the slave node.
90
* In theory, the slave side of the pty is locked out until the
91
* process opening the master calls grantpt() to adjust permissions
92
* and unlockpt() to unlock the slave. It turns out that Unix98
93
* doesn't require that the slave actually get locked out, or that
94
* unlockpt() actually do anything on such systems. At least AIX
95
* allows the slave to be opened prior to calling unlockpt(), but most
96
* other SysV-ish systems seem to actually lock out the slave.
100
* It's not guaranteed on a BSD-ish system that a slave can't be
101
* opened when the master isn't open. It's even possible to acquire
102
* the slave as a ctty (!) if the open is done as non-blocking. It's
103
* possible to open the master corresponding to an open slave, which
104
* creates some security issues: once this master is open, data
105
* written to the slave will actually pass to the master.
107
* On a SysV-ish system, the close of the master will invalidate any
108
* open file descriptors on the slave.
110
* In general, there are two functions that can be used to "clean" a
111
* pty slave, revoke() and vhangup(). revoke() will invalidate all
112
* file descriptors open on a particular pathname (often this only
113
* works on terminal devices), usually by invalidating the underlying
114
* vnode. vhangup() will send a SIGHUP to the foreground process
115
* group of the control terminal. On many systems, it also has
116
* revoke() semantics.
118
* If a process acquires a controlling terminal in order to perform a
119
* vhangup(), the reopen of the controlling terminal after the
120
* vhangup() call should be done prior to the close of the file
121
* descriptor used to initially acquire the controlling terminal,
122
* since that will likely prevent the process on the master side from
123
* reading a spurious EOF due to all file descriptors to the slave
126
* Known quirks of various OSes:
130
* If the environment variable XPG_SUS_ENV is not equal to "ON", then
131
* it's possible to open the slave prior to calling unlockpt().
135
* NOTE: this program will get reworked at some point to actually test
136
* passing of data between master and slave, and to do general cleanup.
138
* This is rather complex, so it bears some explanation.
140
* There are multiple child processes and a parent process. These
141
* communicate via pipes (which we assume here to be unidirectional).
144
* pp1 - parent -> any children
146
* p1p - any children -> parent
148
* p21 - only child2 -> child1
150
* A parent process will acquire a pty master and slave via
151
* pty_getpty(). It will then fork a process, child1. It then does a
152
* waitpid() for child1, and then writes to child2 via syncpipe pp1.
153
* It then reads from child3 via syncpipe p1p, then closes the
154
* master. It writes to child3 via syncpipe pp1 to indicate that it
155
* has closed the master. It then reads from child3 via syncpipe p1p
156
* and exits with a value appropriate to what it read from child3.
158
* child1 will acquire the slave as its ctty and fork child2; child1
159
* will exit once it reads from the syncpipe p21 from child2.
161
* child2 will set a signal handler for SIGHUP and then write to
162
* child1 via syncpipe p21 to indicate that child2 has set up the
163
* handler. It will then read from the syncpipe pp1 from the parent
164
* to confirm that the parent has seen child1 exit, and then checks to
165
* see if it still has a ctty. Under Unix98, and likely earlier
166
* System V derivatives, the exiting of the session leader associated
167
* with a ctty (in this case, child1) will cause the entire session to
170
* child2 will then check to see if it can reopen the slave, and
171
* whether it has a ctty after reopening it. This should fail on most
174
* child2 will then fork child3 and immediately exit.
176
* child3 will write to the syncpipe p1p and read from the syncpipe
177
* pp1. It will then check if it has a ctty and then attempt to
178
* reopen the slave. This should fail. It will then write to the
179
* parent via syncpipe p1p and exit.
181
* If this doesn't fail, child3 will attempt to write to the open
182
* slave fd. This should fail unless a prior call to revoke(),
183
* etc. failed due to lack of permissions, e.g. NetBSD when running as
190
#include <sys/wait.h>
194
int masterfd, slavefd;
195
char slave[64], slave2[64];
196
pid_t pid1, pid2, pid3;
197
int status1, status2;
198
int pp1[2], p1p[2], p21[2];
201
void rdsync(int, int *, const char *);
202
void wrsync(int, int, const char *);
203
void testctty(const char *);
204
void testex(int, const char *);
205
void testwr(int, const char *);
213
printf("pid %ld got signal %d\n", (long)getpid(), sig);
219
rdsync(int fd, int *status, const char *caller)
225
printf("rdsync: %s: starting\n", caller);
228
while ((n = read(fd, &c, 1)) < 0) {
229
if (errno != EINTR) {
230
fprintf(stderr, "rdsync: %s", caller);
234
printf("rdsync: %s: got EINTR; looping\n", caller);
239
fprintf(stderr, "rdsync: %s: unexpected EOF\n", caller);
242
printf("rdsync: %s: got sync byte\n", caller);
249
wrsync(int fd, int status, const char *caller)
255
while ((n = write(fd, &c, 1)) < 0) {
256
if (errno != EINTR) {
257
fprintf(stderr, "wrsync: %s", caller);
261
printf("wrsync: %s: got EINTR; looping\n", caller);
266
printf("wrsync: %s: sent sync byte\n", caller);
272
testctty(const char *caller)
276
fd = open("/dev/tty", O_RDWR|O_NONBLOCK);
278
printf("%s: no ctty\n", caller);
280
printf("%s: have ctty\n", caller);
285
testex(int fd, const char *caller)
288
struct timeval timeout;
299
n = select(fd + 1, &rfds, NULL, &xfds, &timeout);
301
fprintf(stderr, "testex: %s: ", caller);
305
if (FD_ISSET(fd, &rfds) || FD_ISSET(fd, &xfds)) {
308
printf("testex: %s: got EOF\n", caller);
311
} else if (n == -1) {
312
printf("testex: %s: got errno=%ld (%s)\n",
313
caller, (long)errno, strerror(errno));
315
printf("testex: %s: read 1 byte!?\n", caller);
319
printf("testex: %s: no exceptions or readable fds\n", caller);
324
testwr(int fd, const char *caller)
327
struct timeval timeout;
335
n = select(fd + 1, NULL, &wfds, NULL, &timeout);
337
fprintf(stderr, "testwr: %s: ", caller);
341
if (FD_ISSET(fd, &wfds)) {
342
printf("testwr: %s: is writable\n", caller);
354
ptyint_void_association();
355
slavefd = open(slave, O_RDWR|O_NONBLOCK);
357
wrsync(p1p[1], 1, "[02] child3->parent");
358
printf("child3: failed reopen of slave\n");
363
ioctl(slavefd, TIOCSCTTY, 0);
366
printf("child3: reopened slave\n");
367
testctty("child3: after reopen of slave");
368
testwr(slavefd, "child3: after reopen of slave");
369
testex(slavefd, "child3: after reopen of slave");
371
testctty("child3: after close of slave");
374
* Sync for parent to close master.
376
wrsync(p1p[1], 0, "[02] child3->parent");
377
rdsync(pp1[0], NULL, "[03] parent->child3");
379
testctty("child3: after close of master");
380
printf("child3: attempting reopen of slave\n");
382
slavefd = open(slave, O_RDWR|O_NONBLOCK);
384
printf("child3: failed reopen of slave after master close: "
385
"errno=%ld (%s)\n", (long)errno, strerror(errno));
386
wrsync(p1p[1], 0, "[04] child3->parent");
390
if (fcntl(slavefd, F_SETFL, 0) == -1) {
391
perror("child3: fcntl");
392
wrsync(p1p[1], 2, "[04] child3->parent");
396
ioctl(slavefd, TIOCSCTTY, 0);
398
printf("child3: reopened slave after master close\n");
399
testctty("child3: after reopen of slave after master close");
400
testwr(slavefd, "child3: after reopen of slave after master close");
401
testex(slavefd, "child3: after reopen of slave after master close");
402
n = write(slavefd, "foo", 4);
404
printf("child3: writing to slave of closed master: errno=%ld (%s)\n",
405
(long)errno, strerror(errno));
406
wrsync(p1p[1], 1, "[04] child3->parent");
408
printf("child3: wrote %d byes to slave of closed master\n", n);
410
wrsync(p1p[1], 2, "[04] child3->parent");
412
rdsync(pp1[0], NULL, "[05] parent->child3");
413
testex(slavefd, "child3: after parent reopen of master");
414
testwr(slavefd, "child3: after parent reopen of master");
416
n = write(slavefd, "bar", 4);
418
perror("child3: writing to slave");
420
printf("child3: wrote %d bytes to slave\n", n);
423
wrsync(p1p[1], 0, "[06] child3->parent");
424
rdsync(pp1[0], NULL, "[07] parent->child3");
425
wrsync(p1p[1], 0, "[08] child3->parent");
437
sigemptyset(&sa.sa_mask);
438
sa.sa_handler = handler;
439
if (sigaction(SIGHUP, &sa, NULL) < 0) {
440
wrsync(p21[1], 1, "[00] child2->child1");
441
perror("child2: sigaction");
445
printf("child2: set up signal handler\n");
446
testctty("child2: after start");
447
testwr(slavefd, "child2: after start");
448
wrsync(p21[1], 0, "[00] child2->child1");
449
rdsync(pp1[0], NULL, "[01] parent->child2");
451
testctty("child2: after child1 exit");
452
testex(slavefd, "child2: after child1 exit");
453
testwr(slavefd, "child2: after child1 exit");
455
testctty("child2: after close of slavefd");
456
slavefd = open(slave, O_RDWR|O_NONBLOCK);
458
wrsync(p1p[1], 1, "[02] child2->parent");
459
printf("child2: failed reopen of slave\n");
464
ioctl(slavefd, TIOCSCTTY, 0);
466
printf("child2: reopened slave\n");
467
testctty("child2: after reopen of slave");
473
} else if (pid3 == -1) {
474
wrsync(p1p[1], 1, "[02] child2->parent");
475
perror("child2: fork of child3");
478
printf("child2: forked child3=%ld\n", (long)pid3);
494
ptyint_void_association();
495
slavefd = open(slave, O_RDWR|O_NONBLOCK);
497
perror("child1: open slave");
501
ioctl(slavefd, TIOCSCTTY, 0);
504
printf("child1: opened slave\n");
505
testctty("child1: after slave open");
508
perror("pipe child2->child1");
514
} else if (pid2 == -1) {
515
perror("child1: fork child2");
519
printf("child1: forked child2=%ld\n", (long)pid2);
521
rdsync(p21[0], &status, "[00] child2->child1");
526
main(int argc, char *argv[])
535
printf("parent: pid=%ld\n", (long)getpid());
537
retval = ptyint_getpty_ext(&masterfd, slave, sizeof(slave), 0);
540
com_err(prog, retval, "open master");
546
printf("parent: master opened; slave=%s\n", slave);
549
#if defined(HAVE_GRANTPT) && defined(HAVE_STREAMS)
551
printf("parent: attempting to open slave before unlockpt\n");
553
slavefd = open(slave, O_RDWR|O_NONBLOCK|O_NOCTTY);
555
printf("parent: failed slave open before unlockpt errno=%ld (%s)\n",
556
(long)errno, strerror(errno));
558
printf("parent: WARNING: "
559
"succeeded in opening slave before unlockpt\n");
563
if (grantpt(masterfd) < 0) {
564
perror("parent: grantpt");
567
if (unlockpt(masterfd) < 0) {
568
perror("parent: unlockpt");
571
#endif /* HAVE_GRANTPT && HAVE_STREAMS */
574
perror("pipe parent->child1");
578
perror("pipe child1->parent");
585
} else if (pid1 == -1) {
586
perror("fork of child1");
589
printf("parent: forked child1=%ld\n", (long)pid1);
591
if (waitpid(pid1, &status1, 0) < 0) {
592
perror("waitpid for child1");
595
printf("parent: child1 exited, status=%d\n", status1);
599
wrsync(pp1[1], 0, "[01] parent->child2");
600
rdsync(p1p[0], &status, "[02] child3->parent");
602
fprintf(stderr, "child2 or child3 got an error\n");
606
printf("parent: closing master\n");
610
printf("parent: closed master\n");
611
wrsync(pp1[1], 0, "[03] parent->child3");
613
rdsync(p1p[0], &status, "[04] child3->parent");
620
fprintf(stderr, "child3 got an error\n");
625
retval = pty_getpty(&masterfd, slave2, sizeof(slave2));
626
printf("parent: new master opened; slave=%s\n", slave2);
629
printf("parent: revoking\n");
634
wrsync(pp1[1], 0, "[05] parent->child3");
635
rdsync(p1p[0], NULL, "[06] child3->parent");
637
n = read(masterfd, buf, 4);
639
perror("parent: reading from master");
641
printf("parent: read %d bytes (%.*s) from master\n", n, n, buf);
646
wrsync(pp1[1], 0, "[07] parent->child3");
647
rdsync(p1p[0], NULL, "[08] child3->parent");