~ubuntu-branches/ubuntu/maverick/krb5/maverick

« back to all changes in this revision

Viewing changes to src/appl/libpty/pty_paranoia.c

  • Committer: Bazaar Package Importer
  • Author(s): Sam Hartman, Russ Allbery, Sam Hartman
  • Date: 2008-08-21 10:41:41 UTC
  • mfrom: (11.1.15 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080821104141-a0f9c4o4cpo8xd0o
Tags: 1.6.dfsg.4~beta1-4
[ Russ Allbery ]
* Translation updates:
  - Swedish, thanks Martin Bagge.  (Closes: #487669, #491774)
  - Italian, thanks Luca Monducci.  (Closes: #493962)

[ Sam Hartman ]
* Translation Updates:
    - Dutch, Thanks Vincent Zweije, Closes: #495733

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright 2001 by the Massachusetts Institute of Technology.
 
3
 * 
 
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.
 
17
 */
 
18
 
 
19
/*
 
20
 * A rant on the nature of pseudo-terminals:
 
21
 * -----------------------------------------
 
22
 *
 
23
 * Controlling terminals and job control:
 
24
 *
 
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.
 
38
 *
 
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.
 
46
 *
 
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
 
53
 * has a ctty.
 
54
 *
 
55
 * On ptys in general:
 
56
 *
 
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.
 
64
 *
 
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.
 
69
 *
 
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.
 
74
 *
 
75
 * BSD ptys:
 
76
 *
 
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.
 
82
 *
 
83
 * System V cloning ptys:
 
84
 *
 
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.
 
89
 *
 
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.
 
97
 *
 
98
 * Pty security:
 
99
 *
 
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.
 
106
 *
 
107
 * On a SysV-ish system, the close of the master will invalidate any
 
108
 * open file descriptors on the slave.
 
109
 *
 
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.
 
117
 *
 
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
 
124
 * being closed.
 
125
 *
 
126
 * Known quirks of various OSes:
 
127
 *
 
128
 * AIX 4.3.3:
 
129
 *
 
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().
 
132
 */
 
133
 
 
134
/*
 
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.
 
137
 *
 
138
 * This is rather complex, so it bears some explanation.
 
139
 *
 
140
 * There are multiple child processes and a parent process.  These
 
141
 * communicate via pipes (which we assume here to be unidirectional).
 
142
 * The pipes are:
 
143
 *
 
144
 * pp1 - parent -> any children
 
145
 *
 
146
 * p1p - any children -> parent
 
147
 *
 
148
 * p21 - only child2 -> child1
 
149
 *
 
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.
 
157
 *
 
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.
 
160
 *
 
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
 
168
 * lose its ctty.
 
169
 *
 
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
 
172
 * systems.
 
173
 *
 
174
 * child2 will then fork child3 and immediately exit.
 
175
 *
 
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.
 
180
 *
 
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
 
184
 * non-root.
 
185
 */
 
186
 
 
187
#include "com_err.h"
 
188
#include "libpty.h"
 
189
#include "pty-int.h"
 
190
#include <sys/wait.h>
 
191
#include <stdlib.h>
 
192
 
 
193
char *prog;
 
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];
 
199
 
 
200
void handler(int);
 
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 *);
 
206
void child1(void);
 
207
void child2(void);
 
208
void child3(void);
 
209
 
 
210
void
 
211
handler(int sig)
 
212
{
 
213
    printf("pid %ld got signal %d\n", (long)getpid(), sig);
 
214
    fflush(stdout);
 
215
    return;
 
216
}
 
217
 
 
218
void
 
219
rdsync(int fd, int *status, const char *caller)
 
220
{
 
221
    int n;
 
222
    char c;
 
223
 
 
224
#if 0
 
225
    printf("rdsync: %s: starting\n", caller);
 
226
    fflush(stdout);
 
227
#endif
 
228
    while ((n = read(fd, &c, 1)) < 0) {
 
229
        if (errno != EINTR) {
 
230
            fprintf(stderr, "rdsync: %s", caller);
 
231
            perror("");
 
232
            exit(1);
 
233
        } else {
 
234
            printf("rdsync: %s: got EINTR; looping\n", caller);
 
235
            fflush(stdout);
 
236
        }
 
237
    }
 
238
    if (!n) {
 
239
        fprintf(stderr, "rdsync: %s: unexpected EOF\n", caller);
 
240
        exit(1);
 
241
    }
 
242
    printf("rdsync: %s: got sync byte\n", caller);
 
243
    fflush(stdout);
 
244
    if (status != NULL)
 
245
        *status = c;
 
246
}
 
247
 
 
248
void
 
249
wrsync(int fd, int status, const char *caller)
 
250
{
 
251
    int n;
 
252
    char c;
 
253
 
 
254
    c = status;
 
255
    while ((n = write(fd, &c, 1)) < 0) {
 
256
        if (errno != EINTR) {
 
257
            fprintf(stderr, "wrsync: %s", caller);
 
258
            perror("");
 
259
            exit(1);
 
260
        } else {
 
261
            printf("wrsync: %s: got EINTR; looping\n", caller);
 
262
            fflush(stdout);
 
263
        }
 
264
    }
 
265
#if 0
 
266
    printf("wrsync: %s: sent sync byte\n", caller);
 
267
#endif
 
268
    fflush(stdout);
 
269
}
 
270
 
 
271
void
 
272
testctty(const char *caller)
 
273
{
 
274
    int fd;
 
275
 
 
276
    fd = open("/dev/tty", O_RDWR|O_NONBLOCK);
 
277
    if (fd < 0) {
 
278
        printf("%s: no ctty\n", caller);
 
279
    } else {
 
280
        printf("%s: have ctty\n", caller);
 
281
    }
 
282
}
 
283
 
 
284
void
 
285
testex(int fd, const char *caller)
 
286
{
 
287
    fd_set rfds, xfds;
 
288
    struct timeval timeout;
 
289
    int n;
 
290
    char c;
 
291
 
 
292
    timeout.tv_sec = 0;
 
293
    timeout.tv_usec = 0;
 
294
    FD_ZERO(&rfds);
 
295
    FD_ZERO(&xfds);
 
296
    FD_SET(fd, &rfds);
 
297
    FD_SET(fd, &xfds);
 
298
 
 
299
    n = select(fd + 1, &rfds, NULL, &xfds, &timeout);
 
300
    if (n < 0) {
 
301
        fprintf(stderr, "testex: %s: ", caller);
 
302
        perror("select");
 
303
    }
 
304
    if (n) {
 
305
        if (FD_ISSET(fd, &rfds) || FD_ISSET(fd, &xfds)) {
 
306
            n = read(fd, &c, 1);
 
307
            if (!n) {
 
308
                printf("testex: %s: got EOF\n", caller);
 
309
                fflush(stdout);
 
310
                return;
 
311
            } else if (n == -1) {
 
312
                printf("testex: %s: got errno=%ld (%s)\n",
 
313
                       caller, (long)errno, strerror(errno));
 
314
            } else {
 
315
                printf("testex: %s: read 1 byte!?\n", caller);
 
316
            }
 
317
        }
 
318
    } else {
 
319
        printf("testex: %s: no exceptions or readable fds\n", caller);
 
320
    }
 
321
}
 
322
 
 
323
void
 
324
testwr(int fd, const char *caller)
 
325
{
 
326
    fd_set wfds;
 
327
    struct timeval timeout;
 
328
    int n;
 
329
 
 
330
    timeout.tv_sec = 0;
 
331
    timeout.tv_usec = 0;
 
332
    FD_ZERO(&wfds);
 
333
    FD_SET(fd, &wfds);
 
334
 
 
335
    n = select(fd + 1, NULL, &wfds, NULL, &timeout);
 
336
    if (n < 0) {
 
337
        fprintf(stderr, "testwr: %s: ", caller);
 
338
        perror("select");
 
339
    }
 
340
    if (n) {
 
341
        if (FD_ISSET(fd, &wfds)) {
 
342
            printf("testwr: %s: is writable\n", caller);
 
343
            fflush(stdout);
 
344
        }
 
345
    }
 
346
}
 
347
 
 
348
 
 
349
void
 
350
child3(void)
 
351
{
 
352
    int n;
 
353
 
 
354
    ptyint_void_association();
 
355
    slavefd = open(slave, O_RDWR|O_NONBLOCK);
 
356
    if (slavefd < 0) {
 
357
        wrsync(p1p[1], 1, "[02] child3->parent");
 
358
        printf("child3: failed reopen of slave\n");
 
359
        fflush(stdout);
 
360
        exit(1);
 
361
    }
 
362
#ifdef TIOCSCTTY
 
363
    ioctl(slavefd, TIOCSCTTY, 0);
 
364
#endif
 
365
 
 
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");
 
370
    close(slavefd);
 
371
    testctty("child3: after close of slave");
 
372
 
 
373
    /*
 
374
     * Sync for parent to close master.
 
375
     */
 
376
    wrsync(p1p[1], 0, "[02] child3->parent");
 
377
    rdsync(pp1[0], NULL, "[03] parent->child3");
 
378
 
 
379
    testctty("child3: after close of master");
 
380
    printf("child3: attempting reopen of slave\n");
 
381
    fflush(stdout);
 
382
    slavefd = open(slave, O_RDWR|O_NONBLOCK);
 
383
    if (slavefd < 0) {
 
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");
 
387
        fflush(stdout);
 
388
        exit(0);
 
389
    }
 
390
    if (fcntl(slavefd, F_SETFL, 0) == -1) {
 
391
        perror("child3: fcntl");
 
392
        wrsync(p1p[1], 2, "[04] child3->parent");
 
393
        exit(1);
 
394
    }
 
395
#ifdef TIOCSCTTY
 
396
    ioctl(slavefd, TIOCSCTTY, 0);
 
397
#endif
 
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);
 
403
    if (n < 0) {
 
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");
 
407
    } else {
 
408
        printf("child3: wrote %d byes to slave of closed master\n", n);
 
409
        fflush(stdout);
 
410
        wrsync(p1p[1], 2, "[04] child3->parent");
 
411
    }
 
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");
 
415
    fflush(stdout);
 
416
    n = write(slavefd, "bar", 4);
 
417
    if (n < 0) {
 
418
        perror("child3: writing to slave");
 
419
    } else {
 
420
        printf("child3: wrote %d bytes to slave\n", n);
 
421
        fflush(stdout);
 
422
    }
 
423
    wrsync(p1p[1], 0, "[06] child3->parent");
 
424
    rdsync(pp1[0], NULL, "[07] parent->child3");
 
425
    wrsync(p1p[1], 0, "[08] child3->parent");
 
426
    exit(0);
 
427
}
 
428
 
 
429
void
 
430
child2(void)
 
431
{
 
432
    struct sigaction sa;
 
433
 
 
434
    close(p21[0]);
 
435
    setpgid(0, 0);
 
436
    sa.sa_flags = 0;
 
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");
 
442
        fflush(stdout);
 
443
        exit(1);
 
444
    }
 
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");
 
450
 
 
451
    testctty("child2: after child1 exit");
 
452
    testex(slavefd, "child2: after child1 exit");
 
453
    testwr(slavefd, "child2: after child1 exit");
 
454
    close(slavefd);
 
455
    testctty("child2: after close of slavefd");
 
456
    slavefd = open(slave, O_RDWR|O_NONBLOCK);
 
457
    if (slavefd < 0) {
 
458
        wrsync(p1p[1], 1, "[02] child2->parent");
 
459
        printf("child2: failed reopen of slave\n");
 
460
        fflush(stdout);
 
461
        exit(1);
 
462
    }
 
463
#ifdef TIOCSCTTY
 
464
    ioctl(slavefd, TIOCSCTTY, 0);
 
465
#endif
 
466
    printf("child2: reopened slave\n");
 
467
    testctty("child2: after reopen of slave");
 
468
    fflush(stdout);
 
469
    close(slavefd);
 
470
    pid3 = fork();
 
471
    if (!pid3) {
 
472
        child3();
 
473
    } else if (pid3 == -1) {
 
474
        wrsync(p1p[1], 1, "[02] child2->parent");
 
475
        perror("child2: fork of child3");
 
476
        exit(1);
 
477
    }
 
478
    printf("child2: forked child3=%ld\n", (long)pid3);
 
479
    fflush(stdout);
 
480
    exit(0);
 
481
}
 
482
 
 
483
void
 
484
child1(void)
 
485
{
 
486
    int status;
 
487
 
 
488
#if 0
 
489
    setuid(1);
 
490
#endif
 
491
    close(pp1[1]);
 
492
    close(p1p[0]);
 
493
    close(masterfd);
 
494
    ptyint_void_association();
 
495
    slavefd = open(slave, O_RDWR|O_NONBLOCK);
 
496
    if (slavefd < 0) {
 
497
        perror("child1: open slave");
 
498
        exit(1);
 
499
    }
 
500
#ifdef TIOCSCTTY
 
501
    ioctl(slavefd, TIOCSCTTY, 0);
 
502
#endif
 
503
 
 
504
    printf("child1: opened slave\n");
 
505
    testctty("child1: after slave open");
 
506
 
 
507
    if (pipe(p21) < 0) {
 
508
        perror("pipe child2->child1");
 
509
        exit(1);
 
510
    }
 
511
    pid2 = fork();
 
512
    if (!pid2) {
 
513
        child2();
 
514
    } else if (pid2 == -1) {
 
515
        perror("child1: fork child2");
 
516
        exit(1);
 
517
    }
 
518
    close(p21[1]);
 
519
    printf("child1: forked child2=%ld\n", (long)pid2);
 
520
    fflush(stdout);
 
521
    rdsync(p21[0], &status, "[00] child2->child1");
 
522
    exit(status);
 
523
}
 
524
 
 
525
int
 
526
main(int argc, char *argv[])
 
527
{
 
528
    long retval;
 
529
    int status;
 
530
    char buf[4];
 
531
    int n;
 
532
 
 
533
    prog = argv[0];
 
534
 
 
535
    printf("parent: pid=%ld\n", (long)getpid());
 
536
 
 
537
    retval = ptyint_getpty_ext(&masterfd, slave, sizeof(slave), 0);
 
538
 
 
539
    if (retval) {
 
540
        com_err(prog, retval, "open master");
 
541
        exit(1);
 
542
    }
 
543
#if 0
 
544
    chown(slave, 1, -1);
 
545
#endif
 
546
    printf("parent: master opened; slave=%s\n", slave);
 
547
    fflush(stdout);
 
548
 
 
549
#if defined(HAVE_GRANTPT) && defined(HAVE_STREAMS)
 
550
#ifdef O_NOCTTY
 
551
    printf("parent: attempting to open slave before unlockpt\n");
 
552
    fflush(stdout);
 
553
    slavefd = open(slave, O_RDWR|O_NONBLOCK|O_NOCTTY);
 
554
    if (slavefd < 0) {
 
555
        printf("parent: failed slave open before unlockpt errno=%ld (%s)\n",
 
556
               (long)errno, strerror(errno));
 
557
    } else {
 
558
        printf("parent: WARNING: "
 
559
               "succeeded in opening slave before unlockpt\n");
 
560
    }
 
561
    close(slavefd);
 
562
#endif
 
563
    if (grantpt(masterfd) < 0) {
 
564
        perror("parent: grantpt");
 
565
        exit(1);
 
566
    }
 
567
    if (unlockpt(masterfd) < 0) {
 
568
        perror("parent: unlockpt");
 
569
        exit(1);
 
570
    }
 
571
#endif /* HAVE_GRANTPT && HAVE_STREAMS */
 
572
 
 
573
    if (pipe(pp1) < 0) {
 
574
        perror("pipe parent->child1");
 
575
        exit(1);
 
576
    }
 
577
    if (pipe(p1p) < 0) {
 
578
        perror("pipe child1->parent");
 
579
        exit(1);
 
580
    }
 
581
 
 
582
    pid1 = fork();
 
583
    if (!pid1) {
 
584
        child1();
 
585
    } else if (pid1 == -1) {
 
586
        perror("fork of child1");
 
587
        exit(1);
 
588
    }
 
589
    printf("parent: forked child1=%ld\n", (long)pid1);
 
590
    fflush(stdout);
 
591
    if (waitpid(pid1, &status1, 0) < 0) {
 
592
        perror("waitpid for child1");
 
593
        exit(1);
 
594
    }
 
595
    printf("parent: child1 exited, status=%d\n", status1);
 
596
    if (status1)
 
597
        exit(status1);
 
598
 
 
599
    wrsync(pp1[1], 0, "[01] parent->child2");
 
600
    rdsync(p1p[0], &status, "[02] child3->parent");
 
601
    if (status) {
 
602
        fprintf(stderr, "child2 or child3 got an error\n");
 
603
        exit(1);
 
604
    }
 
605
 
 
606
    printf("parent: closing master\n");
 
607
    fflush(stdout);
 
608
    close(masterfd);
 
609
    chmod(slave, 0666);
 
610
    printf("parent: closed master\n");
 
611
    wrsync(pp1[1], 0, "[03] parent->child3");
 
612
 
 
613
    rdsync(p1p[0], &status, "[04] child3->parent");
 
614
    switch (status) {
 
615
    case 1:
 
616
        break;
 
617
    case 0:
 
618
        exit(0);
 
619
    default:
 
620
        fprintf(stderr, "child3 got an error\n");
 
621
        fflush(stdout);
 
622
        exit(1);
 
623
    }
 
624
 
 
625
    retval = pty_getpty(&masterfd, slave2, sizeof(slave2));
 
626
    printf("parent: new master opened; slave=%s\n", slave2);
 
627
#if 0
 
628
#ifdef HAVE_REVOKE
 
629
    printf("parent: revoking\n");
 
630
    revoke(slave2);
 
631
#endif
 
632
#endif
 
633
    fflush(stdout);
 
634
    wrsync(pp1[1], 0, "[05] parent->child3");
 
635
    rdsync(p1p[0], NULL, "[06] child3->parent");
 
636
 
 
637
    n = read(masterfd, buf, 4);
 
638
    if (n < 0) {
 
639
        perror("parent: reading from master");
 
640
    } else {
 
641
        printf("parent: read %d bytes (%.*s) from master\n", n, n, buf);
 
642
        fflush(stdout);
 
643
    }
 
644
    chmod(slave2, 0666);
 
645
    close(masterfd);
 
646
    wrsync(pp1[1], 0, "[07] parent->child3");
 
647
    rdsync(p1p[0], NULL, "[08] child3->parent");
 
648
    fflush(stdout);
 
649
    exit(0);
 
650
}