[alt.sources] SLIP/getty/printers on Terminal Servers

rossc@extro.ucc.su.oz.au (Ross Cartlidge) (12/09/89)

Original-posting-by: rossc@extro.ucc.su.oz.au (Ross Cartlidge)
Reposted-by: emv@math.lsa.umich.edu (Edward Vielmetti)
Posting-id: 891208.1915
Posting-number: Volume TEST, Number TEST
Archive-name: tcpcon -- connect an arbitrary process to a device

[This is an experimental alt.sources re-posting from the
newsgroup(s) comp.protocols.tcp-ip.
No attempt has been made to edit, clean, modify, or otherwise
change the contents of the original posting, or to contact the
author.  Please consider cross-posting all sources postings to
alt.sources as a matter of course.]

[Comments on this service to emv@math.lsa.umich.edu (Edward Vielmetti)]


For all those people which replied to my posting
here is the source:-
________________________________________________________________________
Ross Rodney Cartlidge			    |   rossc@extro.ucc.su.oz.au
University Computing Service, H08	    |   Phone:     +61 2 6923497
University of Sydney, NSW 2006, Australia   |   FAX:       +61 2 6606557

------------------------------- Cut Here -------------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	Makefile
#	README
#	nd.1
#	nd.c
#	tcpcon.1
#	tcpcon.c
#	tcpserv.1
#	tcpserv.c
# This archive created: Fri Dec  8 16:58:58 1989
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
#	Set the follwing for your site
#
#	INSTALLDIR	Place to install binaries
#	XCFLAGS		Extra libraries needed for bsd stuff
#	XLIBS		Extra libraries needed for bsd stuff
#	
#	Values for MIPS RISC/OS 3/4
INSTALLDIR=/usr/local
XCFLAGS=-I/usr/include/bsd
XLIBS=-lbsd
#
#	Values for BSD 4.3
#INSTALLDIR=/usr/local
#XCFLAGS=
#XLIBS=
#

PROGS=nd tcpserv tcpcon

all: $(PROGS)

tcpcon:	tcpcon.c
	$(CC) $(XCFLAGS) $(CFLAGS) -o $@ tcpcon.c $(LIBS) $(XLIBS)

nd:	nd.c
	$(CC) $(XCFLAGS) $(CFLAGS) -o $@ nd.c $(LIBS) $(XLIBS)

tcpserv:	tcpserv.c
	$(CC) $(XCFLAGS) $(CFLAGS) -o $@ tcpserv.c $(LIBS) $(XLIBS)

install: all
	/etc/install -o  -f $(INSTALLDIR) nd
	/etc/install -o  -f $(INSTALLDIR) tcpcon
	/etc/install -o  -f $(INSTALLDIR) tcpserv

clean:

clobber: clean
	rm -f $(PROGS)
SHAR_EOF
fi
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
cat << \SHAR_EOF > 'README'
This distribution is a set of programs to connect
an arbitrary process to a device.
It works by attaching the process to the master side
of a pty device. This makes the slave side appear to be
a connection to this process.

The process can do anything, however, most commonly
it is a process to establish a network connection
such as telnet or tcpcon , a command supplied which
connects to an arbitrary tcp address and port.

It is usually used to assign a device to a port
on a terminal server so that the port can be used
just like a real port on a machine. This enables the
port to connect a printer, run a getty, slip, uucp, ACSnet, etc.

FILES:-
Makefile
README
nd.1
nd.c
tcpcon.1
tcpcon.c
tcpserv.1
tcpserv.c

INSTALLATION:
.	Edit Makefile to reflect your machine
.	type "make install"
.	Install the manual entries.
.	Set up an "nd" device (See manual entries and also notes below)

A TEST:
Try these commands to test it
	nd -l /dev/ptypf tcpcon localhost smtp &
	cat < /dev/ttypf &
	stty -echo < /dev/ttypf # < should be > for BSD systems
	cat > /dev/ttypf
You should now be talking to the SMTP server of your machine.
try the command help<CR>
Use <CNTL>D to kill cat and then
kill the async cat.

Notes for Bridge/3COM terminal servers.

.	Enable the generic port 2000 service for your terminal
	servers.

.	Assign an IP address to the server port to which
	you wish to connect.

.	Make the port a host device.

.	Set the Physical parameters of the port  ( Use CTS/RTS flow control
	and 8bit no parity if possible)

To set up a connection to a port with address "ipaddr"
set up the following line in /etc/inittab after creating the directory
"/etc/tcpcon.d"

vn:respawn:/usr/local/nd -r -d /etc/tcpcon.d -t ptypf /usr/local/tcpcon -l /dev/ptypf ipaddr 2000

If you don't have inittab then the nd command should be in a startup rc file.

The pty's should be allocated from your last pty downward to stop nd
clashing with automatically allocated ptys.


Notes for Bridge/3COM terminal servers.

.	Create a server process for each port you wish to
	put a virtual device on, calling each process port<n>
	and assigning it to TCP port 2000+n.

.	Attach the appropriate server to the port.

.	Set the Physical parameters of the port  ( Use CTS/RTS flow control
	and 8bit no parity if possible)

.	Enable the server.

To set up device on port 6 of a cmc server with IP name cmcip, say
set up the following line in /etc/inittab after creating the directory
"/etc/tcpcon.d"

vn:respawn:/usr/local/nd -r -d /etc/tcpcon.d -t ptypf /usr/local/tcpcon -l /dev/ptypf cmcip 2006

If you don't have inittab then the nd command should be in a startup rc file.

The pty's should be allocated from your last pty downward to stop nd
clashing with automatically allocated ptys.

Caveats
	I only just added the UDP support so it might be buggy

	Some systems (RISC/OS 4.0.1 eg) don't make a read return
	on a pty when the corresponding tty is closed. This makes the
	process (eg the tcp connection) continue, when the tty is closed,
	this is annoying if you wish to cause a modem drop at the other end.
	To get round this you have to send a SIGINT to the nd process. See nd(1)
	for more details.
SHAR_EOF
fi
if test -f 'nd.1'
then
	echo shar: "will not over-write existing file 'nd.1'"
else
cat << \SHAR_EOF > 'nd.1'
.TH ND 1 "April 1989"
.SH NAME
nd \- connect a process to a pty
.SH SYNOPSIS
.B nd
[
.B \-r
]
[
.B \-l
pty
] 
[
.B \-f
failtime
] 
[
.B \-c
cleartime
] 
[
.B \-d
piddir
] 
[
.B \-t
pidtag
] 
.I command
.SH DESCRIPTION
.B Nd
runs the process specified in
.I command
with standard input, output, error
and controlling terminal assigned
to the device specified by the
.IR pty
specified in the 
.B \-l
option.
If no
.B \-l
option is supplied then
.I command
must
open the appropriate pty.
The command is passed to a forked
.I sh
as
.BI "sh \-c 'exec " "command" "'" "'"
so any legal
.I sh
syntax can appear in the
.I command
field.
.PP
Unless
.B \-r
is specified,
.B nd
dies
.I cleartime
seconds after the successful completion of
.IR command ,
allowing the other side of the connection to clear
the connection. By default
.I cleartime
is set to 2 seconds.
In the event of
.I command
exiting with a non-zero exit status
.B nd
will sleep for
.I failtime
before exiting.
If
.B \-r
is specified
.B nd
will re-exec
.I command
after waiting for
.I cleartime
or
.I failtime
as appropriate.
The
.I failtime
is to stop
.B nd
from exec-ing
the command too often.
By default
.I failtime
is set to 15 seconds,
preventing
.B nd
from respawning more than 10 times
in 2 minutes and being disabled by
.BR init (1M),
if it is being spawned from an entry in
.B inittab (4)
.PP
.BR Init (1M)
will respawn
.B nd
automatically
if a line similar to:-
.IP
.BI p1:234:respawn: " nd command"
.LP
is added to
.B /etc/inittab.
.PP
If you don't wish to run it from inittab or don't have inittab,
.B nd
should be started with the
.B \-r
option  if you wish the connection to be permanent.
.SH OPTIONS
.TP 1i
.BI \-l " pty"
Attach
.I command
to
.IR pty .
.TP 1i
.BI \-d " piddir"
Log the
.B pid
of
.I command
in
.IR piddir/tag .
Where
.I tag
is the tag specified by
.BR \-t
or if not specified
the basename of 
.I dev
or if no
.B \-l
specified then
tag defaults to
.IR pid. "<pid of command>."
Therefore if you wish to drop the connection,
then kill the process with the
.B pid
stored in this file.
.TP 1i
.BI \-t " tag"
set the name of the file which stores the
pid of command to
.IR tag .
.I failtime
seconds.
.TP 1i
.BI \-f " failtime"
set the fail sleep time to
.I failtime
seconds.
.TP 1i
.BI \-c " cleartime"
set the clear sleep time to
.I cleartime
seconds.
.SH EXAMPLES
.IP
.B
nd -r /dev/ptyqf telnet nos
.LP
will make the device
.I /dev/ttyqf
a direct connection
to the
.B telnet
session to the host
.IR nos .
The connection will be respawned
everytime the
.I telnet
dies.
.IP
.B
kill `cat /etc/tcpcon.d/ptyqf`
.LP
will drop the connection established in
the previous example.
.IP
.B
nd tcpcon \-l /dev/ptyqe terminal_server 2000
.LP
will make the device
.I /dev/ttyqe
a direct connection
to the
the
.B TCP
port
2000
on the host
.IR terminal_server .
.IP
.B
nd \-l /dev/ptyqd tcpserv 2000
.LP
will make the device
.I /dev/ttyqd
a direct connection
to any client which connects to
.B TCP
port
2000.
.SH SIGNALS
.TP 1i
SIGHUP
Sends a SIGTERM at the process group associated
with
.IR command
and restarts
.IR command .
.TP 1i
SIGTERM
Sends a SIGTERM at the process group associated
with
.I command
and exits.
.SH "SEE ALSO"
.BR tcpserv (1),
.BR tcpcon (1),
.BR telnet (1),
.BR pty (7),
.BR inittab (4).
.SH FILES
.PD 0
.TP 2i
/etc/services
List of ports and services
.TP 2i
/etc/hosts
List of hosts
.TP 2i
/etc/inittab
Script for init processes
.PD
SHAR_EOF
fi
if test -f 'nd.c'
then
	echo shar: "will not over-write existing file 'nd.c'"
else
cat << \SHAR_EOF > 'nd.c'
/*
	Written by Ross Cartlidge (rossc@extro.ucc.su.oz)
	University Computer Service
	March 1989

	tcpcon - Program to connect a tty to a tcp socket
	Developed on a MIPS M/2000 running SysVr3
	Ported to BSD/SUN-OS
*/
#include	<fcntl.h>
#include	<sys/ioctl.h>
#include	<sys/signal.h>
#include	<sys/types.h>
#include	<syslog.h>
#include	<errno.h>
#include	<stdio.h>
#include	<string.h>

#if defined(SYSTYPE_BSD43)
#define sigset signal
#define sighold(s) sigblock(sigmask(s))
#define sigrelse(s)	sigsetmask(sigsetmask(-1) & ~sigmask(s))
#endif

int		pid;
int		to;
int		term;
char		*piddir;

main(argc, argv)
int	argc;
char	*argv[];
{
	int		i;
	char		sh_c[2048];
	int		ret;
	char		*dev		= (char *)0;
	int		failtime	= 15; /* > 2m/10 for init */
	int		cleantime	= 2; /* tcp connection to cleanup*/
	int		respawn		= 0; /* respawn process*/
	char		*tag		= (char *)0;
	int		c;
	int		errflg		= 0;
	int		retval;
	void		terminate();
	void		timeout();
	extern int	errno;
	extern char	*sys_errlist[];
	extern char	*optarg;
	extern int	optind;

	openlog(argv[0], 0,  LOG_LOCAL0);
	while ((c = getopt(argc, argv, "rl:f:c:d:t:")) != -1)
		switch (c)
		{
		case 'f':
			failtime = atoi(optarg);
			break;
		case 'c':
			cleantime = atoi(optarg);
			break;
		case 'l':
			dev = optarg;
			break;
		case 'd':
			piddir = optarg;
			break;
		case 't':
			tag = optarg;
			break;
		case 'r':
			respawn++;
			break;
		case '?':
			errflg++;
		}
	if (errflg || optind >= argc)
	{
		syslog(LOG_ERR, "Usage: %s [ -d <pty> ] <command>", argv[0]);
		exit(2);
	}
	strcpy(sh_c, "exec");
	for (i = optind; i < argc; i++)
	{
		strcat(sh_c, " ");
		strcat(sh_c, argv[i]);
	}
	do
	{
		sighold(SIGTERM);
		sighold(SIGHUP);
		sigset(SIGTERM, terminate);
		sigset(SIGHUP, terminate);
		if (pid = fork())
		{
			char	pidf[64];
			FILE	*pidfs;

			if (piddir)
			{
				if (!tag)
				{
					if (dev)
					{
						if (tag = strrchr(dev , '/'))
							tag++;
						else
							tag = dev;
						sprintf(pidf, "%s/%s", piddir, tag);
					}
					else
						sprintf(pidf, "%s/pid.%d", piddir, pid);
				}
				else
					sprintf(pidf, "%s/%s", piddir, tag);
				if ((pidfs = fopen(pidf, "w")) == NULL)
					syslog(LOG_ERR, "open(%s) - %s", pidf, sys_errlist[errno]);
				else
				{
					fprintf(pidfs, "%d\n", pid);
					fclose(pidfs);
				}
			}
			if (failtime > 0)
			{
				sigset(SIGALRM, timeout);
				alarm(failtime);
			}
			else
				to = 1;
			sigrelse(SIGTERM);
			sigrelse(SIGHUP);
			while (wait(&ret) != -1 || errno == EINTR)
				;
			if ((ret & 0xff) == 0)
			{
				if ((ret >> 8 & 0xff) != 0)
				{
					unlink(pidf);
					syslog(LOG_ERR, "Failed(%d) %s", ret >> 8, sh_c);
					sighold(SIGALRM);
					while (!to)
						sigpause(SIGALRM);
					sigrelse(SIGALRM);
					retval = 1;
					continue;
				}
				else
					syslog(LOG_DEBUG, "Completed %s", sh_c);
			}
			else
				syslog(LOG_DEBUG, "Killed(%d) %s", ret, sh_c);
			unlink(pidf);
			sleep(cleantime);
			retval = 0;
			continue;
		}
#if defined(SYSTYPE_BSD43)
		ioctl(0, TIOCNOTTY, 0);
		setpgrp(0, getpid());
#endif
#if defined(SYSTYPE_SYSV)
		setpgrp();
#endif
		close(0);
		close(1);
		close(2);
		sigset(SIGTERM, SIG_DFL);
		sigset(SIGHUP, SIG_DFL);
		sigrelse(SIGTERM);
		sigrelse(SIGHUP);
		if (dev)
		{
			if (open(dev, O_RDWR) == -1)
			{
				syslog(LOG_ERR, "open(%s) - %s", dev, sys_errlist[errno]);
				exit(2);
			}
			dup(0);
			dup(0);
		}
		else
		{
			open("/dev/null", O_RDONLY);
			open("/dev/null", O_WRONLY);
			dup(1);
		}
		syslog(LOG_DEBUG, "Started %s", sh_c);
		execl("/bin/sh", "sh", "-c", sh_c, (char *)0);
		syslog(LOG_ERR, "Exec failed %s", sh_c);
	}
	while (!term);
}

void
terminate(s)
int	s;
{
	if (s == SIGTERM)
		term = 1;
	kill(-pid, SIGTERM);
}

void
timeout(s)
int	s;
{
	to = 1;
}
SHAR_EOF
fi
if test -f 'tcpcon.1'
then
	echo shar: "will not over-write existing file 'tcpcon.1'"
else
cat << \SHAR_EOF > 'tcpcon.1'
.TH TCPCON 1 "April 1989"
.SH NAME
tcpcon \- Connect to a TCP socket
.SH SYNOPSIS
.B tcpcon
[
.B \-a
] 
[
.B \-l
pty
] 
[
.B \-k
tty
] 
[
.B \-t
mintime
] 
[
.B \-r
minreads
] 
[
.B \-u
] 
.I host
.I port
.SH DESCRIPTION
.B Tcpcon
sets up a
.B TCP
connection to
.I host
on port number
.IR port ,
forwarding standard input down the connection
and writing data from the connection
to standard output.
No data is sent down the connection until
either
.I minreads
have been received
or
.I mintime
has expired.
This is because many terminal servers
accept connections to send information
such as
.B "Host Busy"
and then close the connection.
Data sent down these transient connections
would be lost.
.PP
It is is usually used with
.BR nd (1)
to attach a
.B TCP
connection to a device.
.SH OPTIONS
.TP 1i
.BI \-a
Set socket connection in
.B KEEPALIVE
mode so that the connection will die
if the other side goes down.
.TP 1i
.BI \-l " pty"
Attach the TCP connection
to
.I pty
rather than standard input and output.
Only open
.I pty
after
.I mintime
or
.I minreads
has occurred.
This will ensure that the slave will
block until a reliable
connection is created.
.TP 1i
.BI \-k " tty"
Open
.I tty
after setting up the TCP connection
so that the connection will be kept
open when other processes close the
.IR tty .
If
this option is specified
.I tty
should be the slave partner to
the device specified in the
.B -l
option.
.TP 1i
.BI \-t " mintime"
Set the minimum time 
a connection must be up before
sending characters down the connection to
.I mintime
seconds.
.TP 1i
.BI \-r " minreads"
Set the minimum number of reads received 
before
sending characters down the connection to
.I mintime
seconds.
.TP 1i
.BI \-u
connect to a UDP socket instead of a TCP socket.
.SH EXAMPLES
.IP
.B
tcpcon -l /dev/ptyqe localhost smtp
.LP
connect to the
.B smtp
server on the local machine
and attach this connection to
.IR /dev/ttyqe .
.IP
.B
kill `cat /etc/tcpcon.d/ptyqe`
.LP
will drop the connection established in
the previous example.
.SH "SEE ALSO"
.BR nd (1),
.BR setsockopt (2),
.BR tcpserv (1),
.SH FILES
.PD 0
.TP 2i
/etc/hosts
List of hosts
.TP 2i
/etc/services
List of ports and services
.PD
SHAR_EOF
fi
if test -f 'tcpcon.c'
then
	echo shar: "will not over-write existing file 'tcpcon.c'"
else
cat << \SHAR_EOF > 'tcpcon.c'
/*
	Written by Ross Cartlidge (rossc@extro.ucc.su.oz)
	University Computer Service
	March 1989

	tcpcon - Program to connect a tty to a tcp socket
	Developed on a MIPS M/2000 running SysVr3
	Ported to BSD/SUN-OS
*/
#include	<fcntl.h>
#include	<sys/ioctl.h>
#include	<sys/signal.h>
#include	<sys/types.h>
#include	<syslog.h>
#include	<errno.h>
#include	<stdio.h>
#include	<string.h>

#include <sys/time.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <setjmp.h>

#define max(a,b) 	(a<b ? b : a)
#define min(a,b) 	(a>b ? b : a)

#if defined(SYSTYPE_BSD43)
#define sigset		signal
#define sighold(s)	sigblock(sigmask(s))
#define sigrelse(s)	sigsetmask(sigsetmask(-1) & ~sigmask(s))
extern int		errno;
#endif

#if !defined(FD_SET)
#define	fd_set	int
#define	FD_SET(n,p)	(*(p) |= 1 << (n))
#define	FD_CLR(n,p)	(*(p) &= ~(1 << (n)))
#define	FD_ISSET(n,p)	(*(p) & 1 << (n))
#define	FD_ZERO(p)	(*(p) = 0)
#endif

#define	BUFSIZE	1024

struct buf
{
	char	buf[BUFSIZE];
	int	cnt;
};

jmp_buf	env;
int	keepalive	= 0;
int	pid;

main(argc, argv)
int	argc;
char	*argv[];
{
	struct hostent		*host;
	struct servent		*serv;
	struct sockaddr_in	sin;
	int			s;
	int			i;
	int			r;
	char			perror_fmt[128];
	char			usage[128];
	int			p;
	fd_set			rfds;
	fd_set			efds;
	fd_set			wfds;
	int			mintime		= 2;
	int			minreads	= 2;
	char			*dev		= (char *)0;
	char			*kdev		= (char *)0;
	int			errflg		= 0;
	void			terminate();
	int			c;
	int			status;
	struct buf		*bufs;
	int			timeout();
	int			con_type	= SOCK_STREAM;
	extern char		*optarg;
	extern int		optind;

	sprintf(perror_fmt, "PERROR_FMT=%s: %%t %%s%%m - (%%e)", argv[0]);
	sprintf
	(
		usage,
		"USAGE: %s [-a] [-t mintime ] [-r minreads] [-l pty] [-k tty] <IP Address> <TCP Port>\n", argv[0]
	);
#if defined(SYSTYPE_SYSV)
	putenv(perror_fmt);
#endif
	while ((c = getopt(argc, argv, "at:l:r:k:u")) != -1)
		switch (c)
		{
		case 'u':
			con_type =  SOCK_DGRAM;
			break;
		case 'a':
			keepalive = 1;
			break;
		case 'k':
			kdev = optarg;
			break;
		case 't':
			mintime = atoi(optarg);
			break;
		case 'r':
			minreads = atoi(optarg);
			break;
		case 'l':
			dev = optarg;
			break;
		case '?':
			errflg++;
		}
	if (errflg || optind + 1 >= argc)
	{
		fputs(usage, stderr);
		exit(2);
	}
	if ((sin.sin_addr.s_addr = inet_addr(argv[optind])) != -1)
		sin.sin_family = AF_INET;
	else
	{
		if (host = gethostbyname(argv[optind]))
		{
			sin.sin_family = host->h_addrtype;
			memcpy(&sin.sin_addr.s_addr, host->h_addr, host->h_length);
		}
		else
		{
			fprintf
			(
				stderr,
				"%s: %s: unknown host\n",
				argv[0],
				argv[optind]
			);
			exit(2);
		}
	}
	if (serv = getservbyname(argv[optind + 1], "tcp"))
		sin.sin_port = serv->s_port;
	else
		if
		(
			(
				sin.sin_port
				=
				htons
				(
					(short)strtol(argv[optind + 1],
					(char **)0, 0)
				)
			)
			<=
			0
		)
		{
			fprintf
			(
				stderr,
				"%s: %s: unknown service\n",
				argv[0],
				argv[optind + 1]
			);
			exit(2);
		}
	if ((s = connectsocket(&sin, con_type)) < 0)
		exit(1);
	if ((bufs = (struct buf *)calloc(max(minreads, 1) ,sizeof (struct buf))) < 0)
	{
		perror("calloc bufs");
		exit(1);
	}
	sigset(SIGALRM, timeout);
	sighold(SIGALRM);
	alarm(mintime);
	if (setjmp(env) == 0)
		for (i = 0; i < minreads; i++)
		{
			sigrelse(SIGALRM);
			r = read(s, bufs[i].buf, BUFSIZE);
			sighold(SIGALRM);
			if (r <= 0)
				exit(1);
			else
				bufs[i].cnt = r;
		}
	alarm(0);
	sigset(SIGALRM, SIG_IGN);
	sigrelse(SIGALRM);
	if (dev)
	{
		close(0);
		close(1);
		sighold(SIGTERM);
		if (pid = fork())
		{
			sigset(SIGTERM, terminate);
			sigrelse(SIGTERM);
			if (kdev != (char *)0 && open(kdev, O_RDWR) == -1)
				kill(pid, SIGTERM);
#if defined(SYSTYPE_BSD43)
			ioctl(0, TIOCNOTTY, 0);
			setpgrp(0, getpid());
#endif
#if defined(SYSTYPE_SYSV)
			setpgrp();
#endif
			while (wait(&status) != -1 || errno == EINTR)
				;
			if ((status & 0xff) == 0 && (status >> 8 & 0xff))
				exit(status >> 8 & 0xff);
			else
				exit(0);
		}
#if defined(SYSTYPE_BSD43)
		ioctl(0, TIOCNOTTY, 0);
		setpgrp(0, getpid());
#endif
#if defined(SYSTYPE_SYSV)
		setpgrp();
#endif
		sigrelse(SIGTERM);
		if (open(dev, O_RDWR) == -1)
		{
			perror(dev);
			exit(1);
		}
		dup(0);
	}
	for (i = 0; i < minreads && bufs[i].cnt > 0; i++)
		if (write(1, bufs[i].buf, bufs[i].cnt) != bufs[i].cnt)
			exit(0);
	for (;;)
	{
		char	*buf	= bufs[0].buf;

		FD_ZERO(&rfds);
		FD_ZERO(&efds);
		FD_SET(0, &rfds);
		FD_SET(s, &rfds);
		FD_SET(0, &efds);
		FD_SET(s, &efds);
		select(s + 1, &rfds, (fd_set *)0, &efds, (struct timeval *)0);
		if (FD_ISSET(s, &rfds) || FD_ISSET(s, &efds))
		{
			if ((r = read(s, buf, BUFSIZE)) <= 0)
			{
				perror("read");
				exit(0);
			}
			if (write(1, buf, r) != r)
			{
				perror("write");
				exit(0);
			}
		}
		if ((FD_ISSET(0, &rfds) || FD_ISSET(0, &efds)))
		{
			if ((r = read(0, buf, BUFSIZE)) <= 0)
			{
				perror("read");
				exit(0);
			}
			if (write(s, buf, r) != r)
			{
				perror("write");
				exit(0);
			}
		}
	}
}

connectsocket(sinp, t)
struct sockaddr_in	*sinp;
int			t;
{
	int	s;
	int	l;
	int	sockopt;

	if ((s = socket(AF_INET, t, 0)) <  0)
	{
		perror("socket");
		return -1;
	}
	l = sizeof *sinp;
	if (connect(s, sinp, l) < 0)
	{
		perror("connect");
		return -1;
	}
	if
	(
		t == SOCK_STREAM
		&&
		keepalive == 1
		&&
		setsockopt
		(
			s,
			SOL_SOCKET,
			SO_KEEPALIVE,
			(sockopt = 1, (char *)&sockopt),
			sizeof sockopt
		)
		<
		0
	)
	{
		perror("setsockopt");
		return -1;
	}
	return s;
}

timeout(s)
int	s;
{
	longjmp(env, 1);
}

void
terminate(s)
int	s;
{
	kill(pid, s);
}
SHAR_EOF
fi
if test -f 'tcpserv.1'
then
	echo shar: "will not over-write existing file 'tcpserv.1'"
else
cat << \SHAR_EOF > 'tcpserv.1'
.TH TCPSERV 1 "April 1989"
.SH NAME
tcpserv \- accept a TCP connection
.SH SYNOPSIS
.B tcpserv
[
.B \-u
] 
.I port
.SH DESCRIPTION
.B Tcpserv
accepts a
.B TCP
connection on
on port number
.IR port ,
forwarding standard input down the connection
and writing data from the connection
to standard output.
.PP
It is usually used with
.BR nd (1)
to attach a
.B TCP
connection to a device.
.SH OPTIONS
.BI \-u
service a UDP socket instead of a TCP socket.
.SH EXAMPLES
.IP
.B
tcpserv smtp
.LP
accepts connections to the
.B smtp
port on the local machine.
.SH "SEE ALSO"
.BR nd (1),
.BR tcpcon (1).
.SH FILES
.PD 0
.TP 2i
/etc/services
List of ports and services
.PD
.SH BUGS
SHAR_EOF
fi
if test -f 'tcpserv.c'
then
	echo shar: "will not over-write existing file 'tcpserv.c'"
else
cat << \SHAR_EOF > 'tcpserv.c'
/*
	Written by Ross Cartlidge (rossc@extro.ucc.su.oz)
	University Computer Service
	March 1989

*/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>

#if !defined(FD_SET)
#define	fd_set	int
#define	FD_SET(n,p)	(*(p) |= 1 << (n))
#define	FD_CLR(n,p)	(*(p) &= ~(1 << (n)))
#define	FD_ISSET(n,p)	(*(p) & 1 << (n))
#define	FD_ZERO(p)	(*(p) = 0)
#endif


#define	BUFSIZE	1024
char			buf[BUFSIZE];

main(argc, argv)
int	argc;
char	*argv[];
{
	struct sockaddr_in	sin;
	struct sockaddr_in	from;
	int			fromlen;
	int			s;
	int			c;
	int			errflg		= 0;
	int			r;
	char			perror_fmt[128];
	char			usage[128];
	int			nfds;
	fd_set			rfds;
	fd_set			efds;
	int			sockopt;
	int			con_type	= SOCK_STREAM;
	extern char		*optarg;
	extern int		optind;

	sprintf(perror_fmt, "PERROR_FMT=%s: %%t %%s%%m - (%%e)", argv[0]);
	sprintf(usage, "USAGE: %s <TCP Port>\n", argv[0]);
#if defined(SYSTYPE_SYSV)
	putenv(perror_fmt);
#endif
	while ((c = getopt(argc, argv, "u")) != -1)
		switch (c)
		{
		case 'u':
			con_type =  SOCK_DGRAM;
			break;
		case '?':
			errflg++;
			break;
		}
	if (errflg || optind  >= argc)
	{
		fputs(usage, stderr);
		exit(2);
	}
	if ((s = socket(AF_INET, con_type, 0)) < 0)
	{
		perror("socket");
		exit(1);
	}
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = INADDR_ANY;
	if ((sin.sin_port = htons((short)strtol(argv[optind], (char **)0, 0))) <= 0)
	{
		fputs(usage, stderr);
		exit(2);
	}
	if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0)
	{
		perror("bind");
		exit(1);
	}
	if (con_type == SOCK_STREAM)
	{
		listen(s, 5);
		if ((s = accept(s, (struct sockaddr *)0, (int *)0)) < 0)
		{
			perror("accept");
			exit(1);
		}
		if
		(
			setsockopt
			(
				s,
				SOL_SOCKET,
				SO_KEEPALIVE,
				(sockopt = 1, (char *)&sockopt),
				sizeof sockopt
			)
			<
			0
		)
		{
			perror("setsockopt");
			exit(1);
		}
	}
	else
	{
		fromlen = sizeof from;
		if ((r = recvfrom(s, buf, sizeof buf, 0, &from , &fromlen)) == -1)
		{
			perror("recvfom");
			exit(1);
		}
		if (connect(s, &from, sizeof from) == -1)
		{
			perror("connect");
			exit(1);
		}
		if (write(1, buf, r) != r)
		{
			perror("write");
			exit(0);
		}
	}
	for (;;)
	{
		FD_ZERO(&rfds);
		FD_ZERO(&efds);
		FD_SET(0, &rfds);
		FD_SET(s, &rfds);
		FD_SET(0, &efds);
		FD_SET(s, &efds);
		select(s + 1, &rfds, (fd_set *)0, &efds, (struct timeval *)0);
		if (FD_ISSET(0, &rfds) || FD_ISSET(0, &efds))
		{
			if ((r = read(0, buf, BUFSIZE)) <= 0)
			{
				perror("read");
				exit(0);
			}
			if (write(s, buf, r) != r)
			{
				perror("write");
				exit(0);
			}
		}
		if (FD_ISSET(s, &rfds) || FD_ISSET(s, &efds))
		{
			if ((r = read(s, buf, BUFSIZE)) <= 0)
			{
				perror("read");
				exit(0);
			}
			if (write(1, buf, r) != r)
			{
				perror("write");
				exit(0);
			}
		}
	}
}
SHAR_EOF
fi
exit 0
#	End of shell archive
-- 
________________________________________________________________________
Ross Rodney Cartlidge			    |   rossc@extro.ucc.su.oz.au
University Computing Service, H08	    |   Phone:     +61 2 6923497
University of Sydney, NSW 2006, Australia   |   FAX:       +61 2 6606557