/*
ptysh.c  "Pseudo Terminal Shell"

For Linux Slackware 3.4
Compile with "cc -o ptysh ptysh.c"


Author: Andrew Sun
Revision: January 17, 1996, January 10, 1998, June 1, 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/cua1.  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 /dev/ttyr0 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 <fcntl.h>
#include <sys/termio.h>

#include <sys/types.h>
#include <sys/time.h>


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

int shellpid;

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

int fd1m, fd2m, fd2s;
fd_set readfds, writefds;

for (;;) {

/* Create serial interface ptys */
if ( (fd1m = open("/dev/ptyr0",O_RDWR)) < 0 ) {
	perror("open serial pty master /dev/ptyr0");
	exit(1);
}
	printf("serial pty: %s\n", "/dev/ttyr0");

/* Detect "open" for serial pty slave */
FD_ZERO (&readfds);
FD_ZERO (&writefds);
FD_SET (fd1m, &readfds);
/* FD_SET (fd1m, &writefds); */	/* Remove this if wait for open fails */
while ( select (64, &readfds , &writefds, (fd_set *) 0, NULL) < 1 )
	;

/* Create shell ptys */
if ( (fd2m = open("/dev/ptyr1",O_RDWR)) < 0 ) {
	perror("open shell pty master /dev/ptyr1");
	exit(1);
}
	/* printf("shell pty: %s\n", "/dev/ttyr1"); */

/* Invoke /bin/sh as a subprocess */
if (! (shellpid = fork()) ) {
	close (fd1m);
	close (fd2m);
	setsid();
	if ( (fd2s = open("/dev/ttyr1",O_RDWR)) < 0 ) {
		perror("open shell pty slave /dev/ttyr1");
		exit(1);
	}
	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 (;;) {

FD_ZERO ( &readfds );
FD_ZERO ( &writefds );
if (iordcnt12) {
	FD_SET (fd2m, &writefds);
} else {
	FD_SET (fd1m, &readfds);
}
if (iordcnt21) {
	FD_SET (fd1m, &writefds);
} else {
	FD_SET (fd2m, &readfds);
}

iofdcnt = select (64, &readfds, &writefds, (fd_set *) 0, NULL);
if ( iofdcnt < 0 )
	if ( errno == EINTR ) continue;
	else {
		perror("select"); exit(1);
	}

if ( FD_ISSET (fd1m, &readfds) ) {

	/* Read from fd1m data to be written to fd2m */
	iobuf12p = iobuf12;
	iordcnt12 = read (fd1m, iobuf12, 2000);
	if ( iordcnt12 <= 0 ) break;
}
if ( FD_ISSET (fd1m, &writefds) ) {

	/* Write to fd1m with data read from fd2m */
	iowrcnt = write (fd1m, iobuf21p, iordcnt21);
	if ( iowrcnt <= 0 ) break;
	iordcnt21 -= iowrcnt;
	iobuf21p += iowrcnt;
}
if ( FD_ISSET (fd2m, &readfds) ) {

	/* Read from fd2m data to be written to fd1m */
	iobuf21p = iobuf21;
	iordcnt21 = read (fd2m, iobuf21, 2000);
	if ( iordcnt21 <= 0 ) break;
}
if ( FD_ISSET (fd2m, &writefds) ) {

	/* 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(NULL);
}

}
