#include "com_err.h"
#include "libpty.h"
#include "pty-int.h"
#include <sys/wait.h>
#include <stdlib.h>
char *prog;
int masterfd, slavefd;
char slave[64], slave2[64];
pid_t pid1, pid2, pid3;
int status1, status2;
int pp1[2], p1p[2], p21[2];
void handler(int);
void rdsync(int, int *, const char *);
void wrsync(int, int, const char *);
void testctty(const char *);
void testex(int, const char *);
void testwr(int, const char *);
void child1(void);
void child2(void);
void child3(void);
void
handler(int sig)
{
printf("pid %ld got signal %d\n", (long)getpid(), sig);
fflush(stdout);
return;
}
void
rdsync(int fd, int *status, const char *caller)
{
int n;
char c;
#if 0
printf("rdsync: %s: starting\n", caller);
fflush(stdout);
#endif
while ((n = read(fd, &c, 1)) < 0) {
if (errno != EINTR) {
fprintf(stderr, "rdsync: %s", caller);
perror("");
exit(1);
} else {
printf("rdsync: %s: got EINTR; looping\n", caller);
fflush(stdout);
}
}
if (!n) {
fprintf(stderr, "rdsync: %s: unexpected EOF\n", caller);
exit(1);
}
printf("rdsync: %s: got sync byte\n", caller);
fflush(stdout);
if (status != NULL)
*status = c;
}
void
wrsync(int fd, int status, const char *caller)
{
int n;
char c;
c = status;
while ((n = write(fd, &c, 1)) < 0) {
if (errno != EINTR) {
fprintf(stderr, "wrsync: %s", caller);
perror("");
exit(1);
} else {
printf("wrsync: %s: got EINTR; looping\n", caller);
fflush(stdout);
}
}
#if 0
printf("wrsync: %s: sent sync byte\n", caller);
#endif
fflush(stdout);
}
void
testctty(const char *caller)
{
int fd;
fd = open("/dev/tty", O_RDWR|O_NONBLOCK);
if (fd < 0) {
printf("%s: no ctty\n", caller);
} else {
printf("%s: have ctty\n", caller);
}
}
void
testex(int fd, const char *caller)
{
fd_set rfds, xfds;
struct timeval timeout;
int n;
char c;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
FD_ZERO(&rfds);
FD_ZERO(&xfds);
FD_SET(fd, &rfds);
FD_SET(fd, &xfds);
n = select(fd + 1, &rfds, NULL, &xfds, &timeout);
if (n < 0) {
fprintf(stderr, "testex: %s: ", caller);
perror("select");
}
if (n) {
if (FD_ISSET(fd, &rfds) || FD_ISSET(fd, &xfds)) {
n = read(fd, &c, 1);
if (!n) {
printf("testex: %s: got EOF\n", caller);
fflush(stdout);
return;
} else if (n == -1) {
printf("testex: %s: got errno=%ld (%s)\n",
caller, (long)errno, strerror(errno));
} else {
printf("testex: %s: read 1 byte!?\n", caller);
}
}
} else {
printf("testex: %s: no exceptions or readable fds\n", caller);
}
}
void
testwr(int fd, const char *caller)
{
fd_set wfds;
struct timeval timeout;
int n;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
n = select(fd + 1, NULL, &wfds, NULL, &timeout);
if (n < 0) {
fprintf(stderr, "testwr: %s: ", caller);
perror("select");
}
if (n) {
if (FD_ISSET(fd, &wfds)) {
printf("testwr: %s: is writable\n", caller);
fflush(stdout);
}
}
}
void
child3(void)
{
int n;
ptyint_void_association();
slavefd = open(slave, O_RDWR|O_NONBLOCK);
if (slavefd < 0) {
wrsync(p1p[1], 1, "[02] child3->parent");
printf("child3: failed reopen of slave\n");
fflush(stdout);
exit(1);
}
#ifdef TIOCSCTTY
ioctl(slavefd, TIOCSCTTY, 0);
#endif
printf("child3: reopened slave\n");
testctty("child3: after reopen of slave");
testwr(slavefd, "child3: after reopen of slave");
testex(slavefd, "child3: after reopen of slave");
close(slavefd);
testctty("child3: after close of slave");
wrsync(p1p[1], 0, "[02] child3->parent");
rdsync(pp1[0], NULL, "[03] parent->child3");
testctty("child3: after close of master");
printf("child3: attempting reopen of slave\n");
fflush(stdout);
slavefd = open(slave, O_RDWR|O_NONBLOCK);
if (slavefd < 0) {
printf("child3: failed reopen of slave after master close: "
"errno=%ld (%s)\n", (long)errno, strerror(errno));
wrsync(p1p[1], 0, "[04] child3->parent");
fflush(stdout);
exit(0);
}
if (fcntl(slavefd, F_SETFL, 0) == -1) {
perror("child3: fcntl");
wrsync(p1p[1], 2, "[04] child3->parent");
exit(1);
}
#ifdef TIOCSCTTY
ioctl(slavefd, TIOCSCTTY, 0);
#endif
printf("child3: reopened slave after master close\n");
testctty("child3: after reopen of slave after master close");
testwr(slavefd, "child3: after reopen of slave after master close");
testex(slavefd, "child3: after reopen of slave after master close");
n = write(slavefd, "foo", 4);
if (n < 0) {
printf("child3: writing to slave of closed master: errno=%ld (%s)\n",
(long)errno, strerror(errno));
wrsync(p1p[1], 1, "[04] child3->parent");
} else {
printf("child3: wrote %d byes to slave of closed master\n", n);
fflush(stdout);
wrsync(p1p[1], 2, "[04] child3->parent");
}
rdsync(pp1[0], NULL, "[05] parent->child3");
testex(slavefd, "child3: after parent reopen of master");
testwr(slavefd, "child3: after parent reopen of master");
fflush(stdout);
n = write(slavefd, "bar", 4);
if (n < 0) {
perror("child3: writing to slave");
} else {
printf("child3: wrote %d bytes to slave\n", n);
fflush(stdout);
}
wrsync(p1p[1], 0, "[06] child3->parent");
rdsync(pp1[0], NULL, "[07] parent->child3");
wrsync(p1p[1], 0, "[08] child3->parent");
exit(0);
}
void
child2(void)
{
struct sigaction sa;
close(p21[0]);
setpgid(0, 0);
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sa.sa_handler = handler;
if (sigaction(SIGHUP, &sa, NULL) < 0) {
wrsync(p21[1], 1, "[00] child2->child1");
perror("child2: sigaction");
fflush(stdout);
exit(1);
}
printf("child2: set up signal handler\n");
testctty("child2: after start");
testwr(slavefd, "child2: after start");
wrsync(p21[1], 0, "[00] child2->child1");
rdsync(pp1[0], NULL, "[01] parent->child2");
testctty("child2: after child1 exit");
testex(slavefd, "child2: after child1 exit");
testwr(slavefd, "child2: after child1 exit");
close(slavefd);
testctty("child2: after close of slavefd");
slavefd = open(slave, O_RDWR|O_NONBLOCK);
if (slavefd < 0) {
wrsync(p1p[1], 1, "[02] child2->parent");
printf("child2: failed reopen of slave\n");
fflush(stdout);
exit(1);
}
#ifdef TIOCSCTTY
ioctl(slavefd, TIOCSCTTY, 0);
#endif
printf("child2: reopened slave\n");
testctty("child2: after reopen of slave");
fflush(stdout);
close(slavefd);
pid3 = fork();
if (!pid3) {
child3();
} else if (pid3 == -1) {
wrsync(p1p[1], 1, "[02] child2->parent");
perror("child2: fork of child3");
exit(1);
}
printf("child2: forked child3=%ld\n", (long)pid3);
fflush(stdout);
exit(0);
}
void
child1(void)
{
int status;
#if 0
setuid(1);
#endif
close(pp1[1]);
close(p1p[0]);
close(masterfd);
ptyint_void_association();
slavefd = open(slave, O_RDWR|O_NONBLOCK);
if (slavefd < 0) {
perror("child1: open slave");
exit(1);
}
#ifdef TIOCSCTTY
ioctl(slavefd, TIOCSCTTY, 0);
#endif
printf("child1: opened slave\n");
testctty("child1: after slave open");
if (pipe(p21) < 0) {
perror("pipe child2->child1");
exit(1);
}
pid2 = fork();
if (!pid2) {
child2();
} else if (pid2 == -1) {
perror("child1: fork child2");
exit(1);
}
close(p21[1]);
printf("child1: forked child2=%ld\n", (long)pid2);
fflush(stdout);
rdsync(p21[0], &status, "[00] child2->child1");
exit(status);
}
int
main(int argc, char *argv[])
{
long retval;
int status;
char buf[4];
int n;
prog = argv[0];
printf("parent: pid=%ld\n", (long)getpid());
retval = ptyint_getpty_ext(&masterfd, slave, sizeof(slave), 0);
if (retval) {
com_err(prog, retval, "open master");
exit(1);
}
#if 0
chown(slave, 1, -1);
#endif
printf("parent: master opened; slave=%s\n", slave);
fflush(stdout);
#if defined(HAVE_GRANTPT) && defined(HAVE_STREAMS)
#ifdef O_NOCTTY
printf("parent: attempting to open slave before unlockpt\n");
fflush(stdout);
slavefd = open(slave, O_RDWR|O_NONBLOCK|O_NOCTTY);
if (slavefd < 0) {
printf("parent: failed slave open before unlockpt errno=%ld (%s)\n",
(long)errno, strerror(errno));
} else {
printf("parent: WARNING: "
"succeeded in opening slave before unlockpt\n");
}
close(slavefd);
#endif
if (grantpt(masterfd) < 0) {
perror("parent: grantpt");
exit(1);
}
if (unlockpt(masterfd) < 0) {
perror("parent: unlockpt");
exit(1);
}
#endif
if (pipe(pp1) < 0) {
perror("pipe parent->child1");
exit(1);
}
if (pipe(p1p) < 0) {
perror("pipe child1->parent");
exit(1);
}
pid1 = fork();
if (!pid1) {
child1();
} else if (pid1 == -1) {
perror("fork of child1");
exit(1);
}
printf("parent: forked child1=%ld\n", (long)pid1);
fflush(stdout);
if (waitpid(pid1, &status1, 0) < 0) {
perror("waitpid for child1");
exit(1);
}
printf("parent: child1 exited, status=%d\n", status1);
if (status1)
exit(status1);
wrsync(pp1[1], 0, "[01] parent->child2");
rdsync(p1p[0], &status, "[02] child3->parent");
if (status) {
fprintf(stderr, "child2 or child3 got an error\n");
exit(1);
}
printf("parent: closing master\n");
fflush(stdout);
close(masterfd);
chmod(slave, 0666);
printf("parent: closed master\n");
wrsync(pp1[1], 0, "[03] parent->child3");
rdsync(p1p[0], &status, "[04] child3->parent");
switch (status) {
case 1:
break;
case 0:
exit(0);
default:
fprintf(stderr, "child3 got an error\n");
fflush(stdout);
exit(1);
}
retval = pty_getpty(&masterfd, slave2, sizeof(slave2));
printf("parent: new master opened; slave=%s\n", slave2);
#if 0
#ifdef HAVE_REVOKE
printf("parent: revoking\n");
revoke(slave2);
#endif
#endif
fflush(stdout);
wrsync(pp1[1], 0, "[05] parent->child3");
rdsync(p1p[0], NULL, "[06] child3->parent");
n = read(masterfd, buf, 4);
if (n < 0) {
perror("parent: reading from master");
} else {
printf("parent: read %d bytes (%.*s) from master\n", n, n, buf);
fflush(stdout);
}
chmod(slave2, 0666);
close(masterfd);
wrsync(pp1[1], 0, "[07] parent->child3");
rdsync(p1p[0], NULL, "[08] child3->parent");
fflush(stdout);
exit(0);
}