/*
ptysh.c  "Pseudo Terminal Shell"

For UNIX System V variants (Sun Microsystem's Solaris 2.5).
Compile with "cc -o ptysh ptysh.c"


Author: Andrew Sun
Revision: January 10, 1998


Description:

This program enables the /bin/sh command interpreter to
emulate a "terminal device".  Tip, cu, the UUCP environment,
and PPP normally interact with serial interfaces via terminal
device files, such as /dev/cua/b.  Rather than limiting these
applications to physical serial interfaces, "ptysh" enables them
to interact with the shell (/bin/sh), and with TCP/IP communication
programs, including telnet and rlogin.


Usage:

Once ptysh is active, an application accessing the serial pty
device it reports will interact with /bin/sh.  At this point,
an application's chat script can "exec telnet" or "exec rlogin"
to establish communications over a network.
*/

#include <stdio.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <stropts.h>
#include <poll.h>

char *ptsname(int fildes);

main(argc,argv)
int argc;
char *argv[];
{

int shellpid;

char iobuf12[2000], iobuf21[2000];
char *iobuf12p, *iobuf21p;
int iofdcnt, iowrcnt, iordcnt12, iordcnt21;

int fd1m, fd1s, fd2m, fd2s;
char *slavename;

struct pollfd fds[2];
unsigned long nfds;
int timeout;

for (;;) {

/* Create serial interface ptys */
if ( (fd1m = open("/dev/ptmx", O_RDWR)) < 0 ) {
	perror("open serial pty master"); exit(1);
}
	grantpt(fd1m); unlockpt(fd1m);
if ( (slavename = ptsname(fd1m)) == NULL ) {
	perror("can't determine serial pty"); exit(1);
}
	printf("serial pty: %s\n", slavename);
if ( (fd1s = open(slavename,O_RDWR)) < 0 ) {
	perror("open serial pty slave"); exit(1);
}
	ioctl(fd1s, I_PUSH, "ptem");
	ioctl(fd1s, I_PUSH, "ldterm");

/* Detect "open" for serial pty slave */
fds[0].fd = fd1m;
fds[0].events = POLLIN;
nfds = 1; timeout = -1;
poll(fds, nfds, timeout);
close (fd1s);	/* Application has serial pty slave now */

/* Create shell ptys */
if ( (fd2m = open("/dev/ptmx", O_RDWR)) < 0 ) {
	perror("open shell pty master"); exit(1);
}
	grantpt(fd2m); unlockpt(fd2m);
if ( (slavename = ptsname(fd2m)) == NULL ) {
	perror("can't determine shell pty"); exit(1);
}
	/* printf("shell pty: %s\n", slavename); */

/* Invoke /bin/sh as a subprocess */
if (! (shellpid = fork()) ) {
	close (fd1m);
	close (fd2m);
	setsid();
	if ( (fd2s = open(slavename,O_RDWR)) < 0 ) {
		perror("open shell pty slave"); exit(1);
	}
	ioctl(fd2s, I_PUSH, "ptem");
	ioctl(fd2s, I_PUSH, "ldterm");
	dup2 (fd2s, 0);		/* stdin */
	dup2 (fd2s, 1);		/* stdout */
	dup2 (fd2s, 2);		/* stderr */
	system ("stty sane");
	execl ("/bin/sh", "sh", (char *) 0);
}

iordcnt12 = 0;	/* I/O read character count, fd1m to fd2m */
iordcnt21 = 0;	/* I/O read character count, fd2m to fd1m */

for (;;) {

fds[0].fd = fd1m; fds[1].fd = fd2m;
fds[0].events = fds[1].events = 0;
nfds = 2; timeout = -1;
if (iordcnt12) {
	fds[1].events |= POLLOUT;
} else {
	fds[0].events |= POLLIN;
}
if (iordcnt21) {
	fds[0].events |= POLLOUT;
} else {
	fds[1].events |= POLLIN;
}

iofdcnt = poll(fds, nfds, timeout);
if ( iofdcnt < 0 )
	if ( errno == EINTR ) continue;
	else {
		perror("poll"); exit(1);
	}

if ( fds[0].revents & POLLIN ) {

	/* Read from fd1m data to be written to fd2m */
	iobuf12p = iobuf12;
	iordcnt12 = read (fd1m, iobuf12, 2000);
	if ( iordcnt12 <= 0 ) break;
}
if ( fds[0].revents & POLLOUT ) {

	/* Write to fd1m with data read from fd2m */
	iowrcnt = write (fd1m, iobuf21p, iordcnt21);
	if ( iowrcnt <= 0 ) break;
	iordcnt21 -= iowrcnt;
	iobuf21p += iowrcnt;
}
if ( fds[1].revents & POLLIN ) {

	/* Read from fd2m data to be written to fd1m */
	iobuf21p = iobuf21;
	iordcnt21 = read (fd2m, iobuf21, 2000);
	if ( iordcnt21 <= 0 ) break;
}
if ( fds[1].revents & POLLOUT ) {

	/* Write to fd2m with data read from fd1m */
	iowrcnt = write (fd2m, iobuf12p, iordcnt12);
	if ( iowrcnt <= 0 ) break;
	iordcnt12 -= iowrcnt;
	iobuf12p += iowrcnt;
}

}
close (fd1m);
close (fd2m);
sleep(3);	/* Delay for pty close */
wait();
}

}

