4.4BSD/usr/src/contrib/xns/examples/gap/gap2d.c

Compare this file to the similar file:
Show the results in this format:

#ifndef lint
static char RCSid[] = "$Header: gap2d.c,v 2.0 85/11/21 07:23:00 jqj Exp $";
#endif
/*
 * server for GAP-style (TransportObject=server,teletype) telnet connections
 * Note that we support only GAP version 2, although a server for version 3
 * exists.  The version 2 server has not been tested as thoroughly as has the
 * version 3; it does NOT support RESERVE functionality.
 */

/* $Log:	gap2d.c,v $
 * Revision 2.0  85/11/21  07:23:00  jqj
 * 4.3BSD standard release
 * 
 * Revision 1.2  85/05/23  06:22:18  jqj
 * *** empty log message ***
 * 
 * Revision 1.2  85/05/23  06:22:18  jqj
 * *** empty log message ***
 * 
 * Revision 1.1  85/05/22  09:46:52  jqj
 * Initial revision
 */
#include <stdio.h>
#include <signal.h>
#include <sgtty.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <netns/ns.h>
#include <netns/idp.h>
#include <netns/sp.h>
#include <sys/wait.h>
#include <xnscourier/realcourierconnection.h>
#include "GAP2.h"
#include "gapcontrols.h"
#include <xnscourier/except.h>
#include <errno.h>

#define	BELL	'\07'
#define BANNER	"\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r%s"

int pty, net;
extern CourierConnection *_serverConnection;
char buf[sizeof(struct sphdr)+SPPMAXDATA];
struct sphdr our_sphdr;
struct iovec our_iovec[2] = {{((caddr_t)&our_sphdr), sizeof(our_sphdr)}};
/*
 * I/O data buffers, pointers, and counters.
 */
char	ptyibuf[512], *ptyip = ptyibuf;
char	ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
char	*netip = buf;
char	netobuf[512], *nfrontp = netobuf, *nbackp = netobuf;
int	pcc, ncc;
char	line[12];
extern	char **environ;
extern	int errno;

char *envinit[3];
char wsenv[50];

/*
 * session parameters
 */
Cardinal frametimeout;		/* 0 or time in seconds to wait */


/*
 * This modified version of the server is necessary since GAP specifies
 * that the telnet data-transfer session occurs after the RPC to create
 * it has returned!
 */
Server(skipcount,skippedwords)
	int skipcount;
	Unspecified skippedwords[];
{
	Cardinal _procedure;
	register Unspecified *_buf;
	LongCardinal programnum;
	Cardinal versionnum;
	Cardinal _n;

#ifdef DEBUG
	BUGOUT("Server: %d %d",skipcount,skippedwords);
#endif
	for (;;) {
		_buf = ReceiveCallMessage(&_procedure, skipcount, skippedwords);
		DURING switch (_procedure) {
		case 3:
			server_GAP2_Delete(_buf);
			break;
		case 2:
			server_GAP2_Create(_buf);
			net = _serverConnection->fd;
			gaptelnet(); /* returns on connection close */
			break;
		case 0:
			server_GAP2_Reset(_buf);
			break;
		default:
			NoSuchProcedureValue("GAP", _procedure);
			break;
		} HANDLER {
		    Deallocate(_buf);
		    switch (Exception.Code) {
		    case GAP2_terminalAddressInvalid:
		    case GAP2_terminalAddressInUse:
		    case GAP2_controllerDoesNotExist:
		    case GAP2_controllerAlreadyExists:
		    case GAP2_gapCommunicationError:
		    case GAP2_gapNotExported:
		    case GAP2_bugInGAPCode:
		    case GAP2_tooManyGateStreams:
		    case GAP2_inconsistentParams:
		    case GAP2_transmissionMediumUnavailable:
		    case GAP2_dialingHardwareProblem:
		    case GAP2_noDialingHardware:
		    case GAP2_badAddressFormat:
		    case GAP2_mediumConnectFailed:
		    case GAP2_illegalTransport:
		    case GAP2_noCommunicationHardware:
		    case GAP2_unimplemented:
			_buf = Allocate(0);
			SendAbortMessage(Exception.Code-ERROR_OFFSET, 0, _buf);
			break;
		    default:
			_buf = Allocate(0);
			SendRejectMessage(unspecifiedError, 0, _buf);
			break;
		    }
		} END_HANDLER;
		Deallocate(_buf);
		for (;;) {
			skipcount = LookAheadCallMsg(&programnum, &versionnum,
					skippedwords);
			if (skipcount < 0) return(0);	/* timed out */
			if (programnum != 3 || versionnum != 2)
				ExecCourierProgram(programnum, versionnum,
						skipcount, skippedwords);
		}  /* loop if can't exec that program */
	}
}

void
GAP2_Delete(session)
	GAP2_SessionHandle session; 
{
}

void
GAP2_Reset()
{
}

GAP2_CreateResults
GAP2_Create(conn, BDTproc, sessionparams, transports,
	    createTimeout)
	CourierConnection *conn;
	int BDTproc;
	GAP2_SessionParameterObject sessionparams;
	struct {Cardinal length;
		GAP2_TransportObject *sequence;
	} transports;
	GAP2_WaitTime createTimeout;
{
	GAP2_CreateResults result;
	char *c1, *c2, *host;
	int t, pid;
	struct sgttyb b;
	char *xntoa(), *wsname();
	struct sockaddr_ns who;
	int whosize = sizeof(who);
	LongCardinal servicetype;
	GAP2_CommParamObject *cp;
	GAP2_Duplexity duplexity;	/* fullDuplex, halfDuplex */

#ifdef DEBUG
	BUGOUT("CREATE");
#endif
	switch (sessionparams.designator) {
	case ttyHost:
		frametimeout = sessionparams.ttyHost_case.frameTimeout/1000;
		/* could set other parameters here */
		break;
	default:
		raise(GAP2_unimplemented, 0);
		/*NOTREACHED*/
	}
	if (transports.length != 2) {
		raise(GAP2_illegalTransport);
		/*NOTREACHED*/
	}
	switch (transports.sequence[0].designator) {
	case rs232c:	/* maybe some day */
		cp = &transports.sequence[0].rs232c_case.commParams;
		if (cp->accessDetail.designator == directConn) {
			duplexity = cp->duplex;
			servicetype = 0; /* fake it */
			break;
		}
		raise(GAP2_noCommunicationHardware, 0);
		/*NOTREACHED*/
	default:
		raise(GAP2_illegalTransport, 0);
		/*NOTREACHED*/
	}
	if (transports.sequence[1].designator != teletype)
	  raise(GAP2_illegalTransport, 0);
	/* ignore createTimeout */
	/* ignore credentials and verifier */
	
	for (c1 = "pq"; *c1 != 0; c1++)
	  for (c2 = "0123456789abcdef"; *c2 != 0; c2++) {
		  sprintf(line, "/dev/pty%c%c", *c1, *c2);
		  pty = open(line, 2);
		  if (pty < 0) continue;
		  line[strlen("/dev/")] = 't';
		  t = open(line, 2);
		  if (t > 0) goto gotpty;
		  close(pty);
	  }
	raise(GAP2_tooManyGateStreams, 0);
	/*NOTREACHED*/
 gotpty:
	getpeername(_serverConnection->fd, &who, &whosize);
	host = wsname(who.sns_addr);
#ifdef DEBUG
	BUGOUT("gotpty <%s> %d <%s>",line, pty, host);
#endif
	ioctl(t, TIOCGETP, &b);
	b.sg_flags = CRMOD|XTABS|ANYP;
	ioctl(t, TIOCSETP, &b);
	ioctl(pty, TIOCGETP, &b);
	if (duplexity == fullduplex)
	  b.sg_flags |= ECHO;
	else
	  b.sg_flags &= ~ECHO;
	ioctl(pty, TIOCSETP, &b);
	/* we do the fork now so we can return failures as REPORTS */
	pid = fork();
	if (pid < 0) {
		close(pty); close(t);
		raise(GAP2_tooManyGateStreams, 0);
		/*NOTREACHED*/
	}
	else if (pid == 0) {	/* in the execed fork */
		sleep(1);	/* let parent get ready for us */
		close(_serverConnection->fd); /* close net */
		close(pty);
		dup2(t, 0);
		dup2(t, 1);
		dup2(t, 2);
		if (t > 2) close(t);
		envinit[0] = "TERM=network";
		(void)sprintf(wsenv, "WORKSTATION=%s", xntoa(who.sns_addr));
		envinit[1] = wsenv;
		envinit[2] = (char*) 0;
#ifdef DEBUG
		BUGOUT("about to exec /bin/login");
#endif
		execl("/bin/login","login", "-h", host, 0);
#ifdef DEBUG
		BUGOUT("exec of /bin/login failed");
#endif
		perror("/bin/login");
		exit(1);
		/*NOTREACHED*/
	}
	close(t);
#ifdef DEBUG
	BUGOUT("fork successful");
#endif
	result.session[0] = pid;
	return(result);
}

jmp_buf childdiedbuf;

/*
 * Main loop.  Select from pty and network, and
 * hand data to telnet receiver finite state machine.
 * Returns 0 on orderly shutdown, 1 on abnormal shutdown.
 */
gaptelnet()
{
	int on = 1;
	char hostname[32];
	int childdied();
	int ibits = 0, obits = 0;
	register int c;
	struct sphdr *si = (struct sphdr *)buf;
	static struct timeval timeout = {600,0};
	int keepalives = 0;
	int i;

#ifdef DEBUG
	BUGOUT("gaptelnet net=%d,pty=%d",net,pty);
#endif
	if (setjmp(childdiedbuf) != 0)
	  return(0);		/* child died */
	signal(SIGCHLD, childdied);
	signal(SIGTSTP, SIG_IGN);
	ioctl(net, FIONBIO, &on);
	ioctl(pty, FIONBIO, &on);


	/*
	 * Show banner that getty never gave.
	 */
	gethostname(hostname, sizeof (hostname));
	sprintf(nfrontp, BANNER, hostname, "");
	nfrontp += strlen(nfrontp);
	/*
	 * Send status message indicating we're ready to go
	 */
	changeSPPopts(net, GAPCTLnone, 1);
	sendoobdata(GAPCTLmediumUp);
	for (;;) {
#ifdef DEBUG
		BUGOUT("looping in gaptelnet");
#endif
		ibits = obits = 0;
		/*
		 * Never look for input if there's still
		 * stuff in the corresponding output buffer
		 */
		if (nfrontp - nbackp || pcc > 0)
			obits |= (1 << net);
		else
			ibits |= (1 << pty);
		if (pfrontp - pbackp || ncc > 0)
			obits |= (1 << pty);
		else
			ibits |= (1 << net);
		if (ncc < 0 && pcc < 0)
			break;
		timeout.tv_sec = 600;
		timeout.tv_usec = 0;
		select(16, &ibits, &obits, 0, &timeout);
		if (ibits == 0 && obits == 0) {
			/* timeout means no activity for a long time */
#ifdef DEBUG
			BUGOUT("timeout from select");
#endif
			if (keepalives++ < 2) {
			  /* first 2 times through send warning */
				if (nfrontp == nbackp && pcc == 0) {
					/* but only if not blocked on output */
#define WARNING "\r\nYou've been idle much too long.  Respond or log off.\r\n"
					strcpy(nfrontp, WARNING);
					nfrontp += sizeof(WARNING);
				}
				sleep(5);
				continue;
			}
#ifdef DEBUG
			BUGOUT("keepalive expired -- calling cleanup");
#endif
			/* keepalive count has expired */
			cleanup();
			return(1);
		}

		/*
		 * Something to read from the network...
		 */
		if (ibits & (1 << net)) {
			ncc = read(net, buf, sizeof(buf));
#ifdef DEBUG
			BUGOUT("read from net %d",ncc);
#endif
			if (ncc < 0 && errno == EWOULDBLOCK)
				ncc = 0;
			else if (ncc < sizeof(struct sphdr)) {
#ifdef DEBUG
				BUGOUT("short read, %d.  calling cleanup",ncc);
#endif
				cleanup(); /* will probably fail or block */
				return(1);
			}
			else if (si->sp_cc & SP_OB) {
				/* a status or OOB control */
				switch (buf[sizeof(struct sphdr)]) {
				case GAPCTLinterrupt:
					/* shove interrupt char in buffer */
					interrupt();
					break; /* from switch */
				case GAPCTLareYouThere:
					sendoobdata(GAPCTLiAmHere);
					break; /* from switch */
				default:
					/* Ignore other controls instead of:
					 * sendoobdata(
					 *     GAPCTLunexpectedRemoteBehavior);
					 */
					break; /* from switch */
				}
				ncc = 0; /* no chars here */
			}
			else if (si->sp_dt==GAPCTLnone) {
				/* the normal case */
				ncc -= sizeof(struct sphdr);
				netip = buf + sizeof(struct sphdr);
				keepalives = 0;
			}
			else if(si->sp_dt==GAPCTLcleanup) {
#ifdef DEBUG
				BUGOUT("got CLEANUP packet.  Done");
#endif
				cleanup(); /* normal termination */
				return(0);
			}
			else if (si->sp_dt==SPPSST_END) {
				/* got premature termination */
				quitquit(net, pty);
				return(1);
			}
		}

		/*
		 * Something to read from the pty...
		 */
		if (ibits & (1 << pty)) {
			if (frametimeout > 0) sleep(frametimeout);
			pcc = read(pty, ptyibuf, sizeof(ptyibuf));
#ifdef DEBUG
			BUGOUT("read from pty %d",pcc);
#endif
			if (pcc < 0 && errno == EWOULDBLOCK)
				pcc = 0;
			else if (pcc <= 0) {
#ifdef DEBUG
				BUGOUT("short read from pty. Calling cleanup");
#endif
				cleanup();
				return(1); /* ?? abnormal termination */
			}
			ptyip = ptyibuf;
		}

		while (pcc > 0) {
			if ((&netobuf[sizeof(netobuf)] - nfrontp) < 2)
				break;
			*nfrontp++ = *ptyip++ & 0377; pcc--;
		}
		if ((obits & (1 << net)) && (nfrontp - nbackp) > 0)
			netflush();
		while (ncc > 0) {
			if ((&ptyobuf[sizeof(ptyobuf)] - pfrontp) < 2) break;
			*pfrontp++ = *netip++ & 0377;
			ncc--;
		}
		if ((obits & (1 << pty)) && (pfrontp - pbackp) > 0)
			ptyflush();
	}
	/* we should never get to here */
#ifdef DEBUG
	BUGOUT("broke out of for(;;) somehow.  calling cleanup");
#endif
	cleanup();
	return(0);
}

/*
 * Send out of band data to other end of network
 */
sendoobdata(value)
	u_char value;
{
	struct {
		struct sphdr hdr;
		char val;
	} oob;
	oob.hdr = our_sphdr;
	oob.val = value;
#ifdef DEBUG
	BUGOUT("sendoobdata 0%o",value);
#endif
	send(net, &oob, sizeof(oob), MSG_OOB);
}

/*
 * Send interrupt to process on other side of pty.
 * If it is in raw mode, just write NULL;
 * otherwise, write intr char.
 */
interrupt()
{
	struct sgttyb b;
	struct tchars tchars;

	ptyflush();	/* half-hearted */
	ioctl(pty, TIOCGETP, &b);
	if (b.sg_flags & RAW) {
		*pfrontp++ = '\0';
		return;
	}
	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
		'\177' : tchars.t_intrc;
}

ptyflush()
{
	register int n;

	if ((n = pfrontp - pbackp) > 0)
		n = write(pty, pbackp, n);
#ifdef DEBUG
	BUGOUT("ptyflush wrote %d",n);
#endif
	if (n < 0)
		return;
	pbackp += n;
	if (pbackp >= pfrontp)	/* actually, > is an error */
		pbackp = pfrontp = ptyobuf;
}

netflush()
{
	register int n;

	if ((n = nfrontp - nbackp) > 0) {
		our_iovec[1].iov_len = ((n > SPPMAXDATA) ? SPPMAXDATA : n);
		our_iovec[1].iov_base = nbackp;
		n = writev(net, our_iovec, 2) - sizeof(struct sphdr);
	}
#ifdef DEBUG
	BUGOUT("netflush wrote %d",n);
	if (our_iovec[0].iov_base != (char*)&our_sphdr)
		BUGOUT("Oops:  our_iovec clobbered");
	BUGOUT("header: %d %d, %d %d %d %d %d %d",
		our_sphdr.sp_cc, our_sphdr.sp_dt, 
		our_sphdr.sp_sid, our_sphdr.sp_did, our_sphdr.sp_seq, 
		our_sphdr.sp_ack, our_sphdr.sp_alo);
#endif
	if (n < 0) {
		if (errno == EWOULDBLOCK)
			return;
		/* should blow this guy away... */
		return;
	}
	nbackp += n;
	if (nbackp >= nfrontp)	/* actually , > is an error */
		nbackp = nfrontp = netobuf;
}

/*
 * handle receipt of an SPPSST_END packet
 * This is currently an error, since client didn't send "cleanup" first
 */
quitquit()
{
	changeSPPopts(net, SPPSST_ENDREPLY, 1);
	write(net, &our_sphdr, sizeof(our_sphdr));
	sleep(3);

	rmut();
	vhangup();	/* XXX */
	shutdown(net, 1);
	close(net);
}

/*
 * shut down the data connection for one reason or another
 */
cleanup()
{
	int fdmask;
	struct timeval timeout;
	struct sphdr *si = (struct sphdr *)buf;
	int off = 0;

	signal(SIGCHLD, SIG_IGN);
	sendoobdata(GAPCTLcleanup);
	changeSPPopts(net, SPPSST_END, 1);
	if (write(net, &our_sphdr, sizeof(our_sphdr)) < 0) {
		fdmask = 1<<net;
		timeout.tv_sec = 10;
		while (select(net+1,&fdmask,(int*)0, (int*)0, &timeout) > 0 &&
		       read(net,buf,sizeof(buf)) >= sizeof(struct sphdr)) {
#ifdef DEBUG
			BUGOUT("cleanup -- got packet");
#endif
			if ((si->sp_cc & SP_OB)
			    && si->sp_dt == SPPSST_ENDREPLY) {
				changeSPPopts(net, SPPSST_ENDREPLY, 1);
				write(net, &our_sphdr, sizeof(our_sphdr));
#ifdef DEBUG
				BUGOUT("cleanup -- wrote ENDREPLY");
#endif
				sleep(1);
				changeSPPopts(net,0,0);
				ioctl(net, FIONBIO, &off);
				rmut();
				vhangup();	/* XXX */
				return;
			}
			/* loop: ignore everything except ENDREPLY */
			fdmask = 1<<net;
			timeout.tv_sec = 10;
		}
		/* timed out or read failed */
		changeSPPopts(net, SPPSST_ENDREPLY, 1);
		write(net, &our_sphdr, sizeof(our_sphdr));
		sleep(1);
	}
	shutdown(net, 1);
	close(net);
	rmut();
	vhangup();	/* XXX */
}

/*
 * SIGCHLD interrupt handler
 */
childdied()
{
#ifdef DEBUG
	BUGOUT("child died");
#endif
	cleanup();
	longjmp(childdiedbuf, -1);
}

changeSPPopts(s, stream, eom)
	int s;			/* SPP socket */
	u_char stream;		/* datastream type */
	char eom;		/* Boolean EOM */
{
	our_sphdr.sp_dt = stream;
	our_sphdr.sp_cc = (eom ? SP_EM : 0);
}


#include <utmp.h>

struct	utmp wtmp;
char	wtmpf[]	= "/usr/adm/wtmp";
char	utmp[] = "/etc/utmp";
#define SCPYN(a, b)	strncpy(a, b, sizeof (a))
#define SCMPN(a, b)	strncmp(a, b, sizeof (a))

rmut()
{
	register f;
	int found = 0;

	f = open(utmp, 2);
	if (f >= 0) {
		while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) {
			if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0)
				continue;
			lseek(f, -(long)sizeof (wtmp), 1);
			SCPYN(wtmp.ut_name, "");
			SCPYN(wtmp.ut_host, "");
			time(&wtmp.ut_time);
			write(f, (char *)&wtmp, sizeof (wtmp));
			found++;
		}
		close(f);
	}
	if (found) {
		f = open(wtmpf, 1);
		if (f >= 0) {
			SCPYN(wtmp.ut_line, line+5);
			SCPYN(wtmp.ut_name, "");
			SCPYN(wtmp.ut_host, "");
			time(&wtmp.ut_time);
			lseek(f, (long)0, 2);
			write(f, (char *)&wtmp, sizeof (wtmp));
			close(f);
		}
	}
	chmod(line, 0666);
	chown(line, 0, 0);
	line[strlen("/dev/")] = 'p';
	chmod(line, 0666);
	chown(line, 0, 0);
}

/*
 * Convert network-format xns address
 * to ascii
 * --Replace this with a clearinghouse name lookup someday.
 */
char *
wsname(addr)
	struct ns_addr addr;
{
	static char b[50];
	char temp[10];
	int i;

	/* net */
	sprintf(b, "%D.", ntohl(ns_netof(addr)));
	/* skip leading zeros */
	for(i=0; (addr.x_host.c_host[i] == (char) 0); i++) ;
	/* print the rest */
	for(; i < 6; i++) {
		sprintf(temp,"%x", addr.x_host.c_host[i]);
		strcat(b, temp);
		if(i != 5) strcat(b, ":");
	}
	return (b);
}

/*
  * generate an xns address that "DE" can parse.
  * This goes in the environment.  Should be the same as above
  */
char *
xntoa(addr)
	struct ns_addr addr;
{
	static char b[50];
	char temp[10];
	int i;

	/* net */
	sprintf(b, "%X#", ntohl(ns_netof(addr)));
	/* print the rest */
	for(i=0; i < 6; i++) {
		sprintf(temp,"%x", addr.x_host.c_host[i]);
		strcat(b, temp);
		if(i != 5) strcat(b, ".");
	}
	return (b);
}

#ifdef DEBUG
BUGOUT(str,a,b,c,d,e,f,g,h)
	char *str;
{
	FILE *fd;
	fd = fopen("/tmp/GAP2d.log","a");
	fprintf(fd,str,a,b,c,d,e,f,g,h);
	putc('\n',fd);
	fclose(fd);
}
#endif