[comp.sources.amiga] v89i103: dnet - networking code

page%rishathra@Sun.COM (Bob Page) (04/29/89)

Submitted-by: dillon@postgres.berkeley.edu (Matt Dillon)
Posting-number: Volume 89, Issue 103
Archive-name: comm/dnet20-us.2

# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
#	dnet/net.c
#	dnet/internal.c
#	dnet/globals.c
#	dnet/files.c
#	dnet/dnet.h
#	dnet/dnet.c
#	dnet/Makefile
#	dnet/MODS
#	dnet.servers
#	doc/history
#	doc/dnet.doc
#	doc/putfiles.doc
#	doc/dsoc.doc
#	doc/dnetlib.doc
#	doc/draw.doc
#	lib/dnetlib.h
#	lib/dnetlib.c
#	lib/Makefile
#	server/sgcopy.c
#	server/sshell.c
#	server/sloadav.c
#	server/scopy.c
#	server/Makefile
#	server/servers.h
# This is archive 2 of a 2-part kit.
# This archive created: Thu Apr 27 15:41:48 1989
if `test ! -d dnet`
then
  mkdir dnet
  echo "mkdir dnet"
fi
echo "extracting dnet/net.c"
sed 's/^X//' << \SHAR_EOF > dnet/net.c
X
X/*
X *  NET.C
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X *
X *  NetWork raw device interface.  Replace with whatever interface you
X *  want.
X */
X
X#include "dnet.h"
X#include <sys/stat.h>
X
XRcvInt()
X{
X    int n = read(0, RcvBuf + RcvData, RCVBUF - RcvData);
X    if (n >= 0)
X	RcvData += n;
X    if (n <= 0)         /* disallow infinite fast-timeout select loops */
X	RExpect = 0;
X    if (DDebug && n < 0) {
X	write(2, "RcvInt ERR\n", 11);
X    }
X}
X
Xstatic struct sgttyb	ttym;
Xstatic struct stat	Stat;
X
XNetOpen()
X{
X    int async = 1;
X
X    fstat(0, &Stat);
X    fchmod(0, 0600);
X    /*
X    signal(SIGIO, RcvInt);
X    */
X    ioctl (0, TIOCGETP, &ttym);
X    ttym.sg_flags |= RAW;
X    ttym.sg_flags &= ~CBREAK;
X    ttym.sg_flags &= ~ECHO;
X    ioctl (0, TIOCSETP, &ttym);
X    /*
X    ioctl (0, FIOASYNC, &async);
X    */
X    ioctl (0, FIONBIO, &async);
X}
X
XNetClose()
X{
X    int async = 0;
X
X    fchmod(0, Stat.st_mode);
X    ioctl (0, FIONBIO, &async);
X    /*
X    ioctl (0, FIOASYNC, &async);
X    */
X    ioctl (0, TIOCGETP, &ttym);
X    ttym.sg_flags &= ~RAW;
X    ttym.sg_flags |= ECHO;
X    ioctl (0, TIOCSETP, &ttym);
X}
X
XNetWrite(buf, bytes, expectreply)
Xubyte *buf;
X{
X    if (expectreply)
X	DidWrite = 1;
X    if (DDebug)
X	fprintf(stderr, "NETWRITE %08lx %ld\n", buf, bytes);
X    gwrite(0, buf, bytes);
X}
X
Xgwrite(fd, buf, bytes)
Xregister char *buf;
Xregister long bytes;
X{
X    register long n;
X    while (bytes) {
X	n = write(fd, buf, bytes);
X	if (n > 0) {
X	    bytes -= n;
X	    buf += n;
X	    continue;
X	}
X	if (errno == EINTR)
X	    continue;
X	if (errno == EWOULDBLOCK) {
X	    fd_set fd_wr;
X	    fd_set fd_ex;
X	    FD_ZERO(&fd_wr);
X	    FD_ZERO(&fd_ex);
X	    FD_SET(fd, &fd_wr);
X	    FD_SET(fd, &fd_ex);
X	    select(fd+1, NULL, &fd_wr, &fd_ex, NULL);
X	    continue;
X	}
X	if (errno == EPIPE)
X	    return;
X	dneterror("gwrite");
X    }
X}
X
SHAR_EOF
echo "extracting dnet/internal.c"
sed 's/^X//' << \SHAR_EOF > dnet/internal.c
X
X/*
X *  INTERNAL.C
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X *
X *	Usually SCMD_OPEN requests attempt to connect() to the UNIX
X *	domain socket of the server.  However, some 'ports' are designated
X *	as internal to DNET.  They reside here.
X *
X *	-IALPHATERM
X */
X
X#include "dnet.h"
X#include "../server/servers.h"
X#include <sys/stat.h>
X#include <sys/wait.h>
X#include <sys/resource.h>
X#include <sys/uio.h>
X#include <strings.h>
X
Xextern char *getenv();
X
Xisinternalport(port)
Xuword port;
X{
X    if (port == PORT_IALPHATERM)
X	return(1);
X    return(0);
X}
X
Xiconnect(ps, port)
Xint *ps;
Xuword port;
X{
X    if (port == PORT_IALPHATERM)
X	return(ialphaterm_connect(ps, port));
X    return(-1);
X}
X
Xialphaterm_connect(pmaster)
Xint *pmaster;
X{
X    struct sgttyb sg;
X    struct tchars tc;
X    struct ltchars ltc;
X#ifdef TIOCGSIZE
X    struct ttysize ts;
X#else
X#ifdef TIOCGWINSZ
X    struct winsize ws;
X#endif
X#endif
X    int lmode;
X    int fdmaster;
X    int fdslave;
X    int pid;
X    char *slavename;
X
X    ioctl(0, TIOCGETP, (char *)&sg);
X    ioctl(0, TIOCGETC, (char *)&tc);
X    ioctl(0, TIOCGLTC, (char *)&ltc);
X    ioctl(0, TIOCLGET, (char *)&lmode);
X#ifdef TIOCGSIZE
X    ioctl(0, TIOCGSIZE, &ts);
X#else
X#ifdef TIOCGWINSZ
X    ioctl(0, TIOCGWINSZ, &ws);
X#endif
X#endif
X
X    sg.sg_flags &= ~(RAW);
X    sg.sg_flags |= ECHO;
X#ifdef TIOCGSIZE
X    ts.ts_lines = 23;
X    ts.ts_cols = 77;
X#else
X#ifdef TIOCGWINSZ
X    ws.ws_row = 23;
X    ws.ws_col = 77;
X#endif
X#endif
X
X    if (DDebug)
X	fprintf(stderr, "PTY openning internal pty\n");
X    if (openpty(&fdmaster, &fdslave, &slavename) >= 0) {
X	if (DDebug)
X	    fprintf(stderr, "PTY open successfull\n");
X	if ((pid = fork()) == NULL) {
X	    int i;
X	    setenv("DNET=", "IALPHATERM");
X	    setuid(getuid());
X	    signal(SIGHUP, SIG_DFL);
X	    signal(SIGINT, SIG_DFL);
X	    signal(SIGQUIT, SIG_DFL);
X	    signal(SIGTERM, SIG_DFL);
X	    signal(SIGCHLD, SIG_DFL);
X	    signal(SIGTSTP, SIG_IGN);
X	    ioctl(open("/dev/tty", 2), TIOCNOTTY, NULL);
X	    close(open(slavename, 0));
X	    dup2(fdslave, 0);
X	    dup2(0, 1);
X	    dup2(0, 2);
X	    for (i = 3; i < 256; ++i)
X		close(i);
X	    ioctl(0, TIOCSETN, &sg);
X	    ioctl(0, TIOCSETC, &tc);
X	    ioctl(0, TIOCSLTC, &ltc);
X	    ioctl(0, TIOCLSET, &lmode);
X#ifdef TIOCSSIZE
X    	    ioctl(0, TIOCSSIZE, &ts);
X#else
X#ifdef TIOCSWINSZ
X    	    ioctl(0, TIOCSWINSZ, &ws);
X#endif
X#endif
X	    {
X		char *shell = getenv("SHELL");
X		char *home = getenv("HOME");
X		if (!shell)
X		    shell = "/bin/sh";
X		if (!home)
X		    home = ".";
X		chdir(home);
X		execl(shell, "-fshell", NULL);
X		perror(shell);
X	    }
X	    _exit(1);
X	}
X	if (pid > 0) {
X	    *pmaster = fdmaster;
X	    close(fdslave);
X	    if (DDebug)
X                fprintf(stderr, "PTY OPEN OK.2\n");
X	    return(1);
X	}
X	close(fdmaster);
X	close(fdslave);
X	if (DDebug)
X            fprintf(stderr, "PTY OPEN FAILURE.1\n");
X    }
X    if (DDebug)
X        fprintf(stderr, "PTY OPEN FAILURE.2\n");
X    return(-1);
X}
X
Xopenpty(pfdm, pfds, pnames)
Xint *pfdm;
Xint *pfds;
Xchar **pnames;
X{
X    static char ptcs[] = { "0123456789abcdef" };
X    static char plate[] = { "/dev/ptyxx" };
X    struct stat stat;
X    int i;
X    int j;
X
X    for (i = 'p';; ++i) {
X	plate[8] = i;
X	plate[9] = ptcs[0];
X	if (lstat(plate, &stat) < 0)
X		break;
X	for (j = 0; ptcs[j]; ++j) {
X	    plate[9] = ptcs[j];
X	    plate[5] = 'p';
X	    if ((*pfdm = open(plate, O_RDWR)) >= 0) {
X		plate[5] = 't';
X		if ((*pfds = open(plate, O_RDWR)) >= 0) {
X		    *pnames = plate;
X		    if (DDebug)
X			fprintf(stderr, "PTY FOUND %s\n", *pnames);
X		    return(1);
X		}
X		close(*pfdm);
X	    }
X	}
X    }
X    return(-1);
X}
X
Xisetrows(fd, rows)
X{
X#ifdef TIOCSSIZE
X    struct ttysize ts;
X    if (ioctl(fd, TIOCGSIZE, &ts) >= 0) {
X	ts.ts_lines = rows;
X	ioctl(fd, TIOCSSIZE, &ts);
X    }
X#else
X#ifdef TIOCSWINSZ
X    struct winsize ws;
X    if (ioctl(fd, TIOCGWINSZ, &ws) >= 0) {
X	ws.ws_row = rows;
X	ioctl(fd, TIOCSWINSZ, &ws);
X    }
X#endif
X#endif
X}
X
Xisetcols(fd, cols)
X{
X#ifdef TIOCSSIZE
X    struct ttysize ts;
X    if (ioctl(fd, TIOCGSIZE, &ts) >= 0) {
X	ts.ts_cols = cols;
X	ioctl(fd, TIOCSSIZE, &ts);
X    }
X#else
X#ifdef TIOCSWINSZ
X    struct winsize ws;
X    if (ioctl(fd, TIOCSWINSZ, &ws) >= 0) {
X	ws.ws_col = cols;
X	ioctl(fd, TIOCSWINSZ, &ws);
X    }
X#endif
X#endif
X}
X
SHAR_EOF
echo "extracting dnet/globals.c"
sed 's/^X//' << \SHAR_EOF > dnet/globals.c
X
X/*
X *  GLOBALS.C
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X */
X
X#include "dnet.h"
X
Xint Enable_Abort;
X
Xlong USecPerByte = 1000;/*  for 9600 baud.. should really set properly */
Xint DNet_fd = -1;	/*  Master listen socket	*/
XPKT Pkts[9];		/*  data buffers for packets	*/
XPKT *Raux = Pkts+8;	/*  next packet in		*/
Xubyte WCBuf[64];
XPKT *RPak[4] = { Pkts+0,Pkts+1,Pkts+2,Pkts+3 };
XPKT *WPak[4] = { Pkts+4,Pkts+5,Pkts+6,Pkts+7 };
XCHAN Chan[MAXCHAN];    /*  Channels			   */
XLIST TxList;	       /*  For pending SCMD_DATA reqs.     */
Xfd_set Fdread;
Xfd_set Fdwrite;
Xfd_set Fdexcept;
Xvoid (*Fdstate[FD_SETSIZE])();
Xubyte Fdperm[FD_SETSIZE];
Xuword FdChan[FD_SETSIZE];
Xubyte RcvBuf[RCVBUF];
Xuword RcvData;
Xuword RExpect;
Xubyte RTimedout;
Xubyte WTimedout;
Xuword WChan;		/*  Read and Write channels	    */
Xuword RChan;
Xuword RPStart;
Xuword WPStart;
Xuword WPUsed;
Xuword RState;
Xuword Rto_act, Wto_act;
Xubyte DDebug;
Xubyte DidWrite;
Xubyte Restart;
Xubyte DeldQuit;
Xulong NumCon;
X
X
SHAR_EOF
echo "extracting dnet/files.c"
sed 's/^X//' << \SHAR_EOF > dnet/files.c
X
X/*
X * FILES.C
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X *
X *	handles actions on a per file descriptor basis, including accepting
X *	new connections, closing old connections, and transfering data
X *	between connections.
X */
X
X#include "dnet.h"
X
Xextern void do_localopen(), do_connect(), do_open1(), do_openwait(), do_open();
X
X/*
X *  new connection over master port... open request.  read two byte port
X *  number, allocate a channel, and send off to the remote
X */
X
Xvoid
Xdo_localopen(n, fd)
X{
X    struct sockaddr sa;
X    int addrlen = sizeof(sa);
X    int s;
X    uword chan;
X
X    if (DDebug)
X	fprintf(stderr, "DO_LOCALOPEN %ld %ld\n", n, fd);
X    while ((s = accept(fd, &sa, &addrlen)) >= 0) {
X	chan = alloc_channel();
X	fcntl(s, F_SETFL, FNDELAY);
X	if (DDebug)
X	    fprintf(stderr, " ACCEPT: %ld on channel %ld ", s, chan);
X	if (chan == 0xFFFF) {
X	    ubyte error = 1;
X	    gwrite(s, &error, 1);
X	    close(s);
X	    if (DDebug)
X	        fprintf(stderr, "(no channels)\n");
X	    continue;
X	} 
X	Fdstate[s] = do_open1;
X	FdChan[s] = chan;
X	FD_SET(s, &Fdread);
X	FD_SET(s, &Fdexcept);
X	Chan[chan].fd = s;
X	Chan[chan].state = CHAN_LOPEN;
X	if (DDebug)
X	    fprintf(stderr, "(State = CHAN_LOPEN)\n");
X    }
X}
X
Xvoid
Xdo_open1(n, fd)
X{
X    uword port;
X    char  trxpri[2];
X    uword chan = FdChan[fd];
X    COPEN co;
X    int n;
X
X    if (DDebug)
X	fprintf(stderr, "DO_OPEN %ld %ld on channel %ld  ", n, fd, chan);
X    for (;;) {
X        n = read(fd, &port, 2);
X	if (n < 0) {
X	    if (errno == EINTR)
X		continue;
X	    if (errno == EWOULDBLOCK)
X		return;
X	}
X	read(fd, trxpri, 2);
X	if (n != 2)
X	    dneterror("do_open1: unable to read 2 bytes");
X	break;
X    }
X    if (DDebug)
X	fprintf(stderr, "Port %ld\n", port);
X    co.chanh = chan >> 8;
X    co.chanl = chan;
X    co.porth = port >> 8;
X    co.portl = port;
X    co.error = 0;
X    co.pri   = trxpri[1];
X    Chan[chan].port = port;
X    Chan[chan].pri = 126;
X    WriteStream(SCMD_OPEN, &co, sizeof(co), chan);
X    Chan[chan].pri = trxpri[0];
X    Fdstate[fd] = do_openwait;
X    if (DDebug)
X    	fprintf(stderr, " Newstate = openwait\n");
X}
X
Xvoid
Xdo_openwait(n, fd)
X{
X    ubyte buf[32];
X    if (DDebug)
X	fprintf(stderr, "************ ERROR DO_OPENWAIT %ld %ld\n", n, fd);
X    n = read(fd, buf, 32);
X    if (DDebug) {
X	fprintf(stderr, "    OPENWAIT, READ %ld bytes\n", n);
X    	if (n < 0)
X	    perror("openwait:read");
X    }
X}
X
Xvoid
Xdo_open(nn, fd)
X{
X    extern void nop();
X    char buf[256];
X    uword chan = FdChan[fd];
X    int n;
X
X    n = read(fd, buf, sizeof(buf));
X    if (DDebug) {
X	fprintf(stderr, "DO_OPEN %ld %ld, RECEIVE DATA on chan %ld (%ld by)\n",
X	    nn, fd, chan, n);
X	fprintf(stderr, " fd, chanfd %ld %ld\n", fd, Chan[chan].fd);
X	if (n < 0)
X	    perror("open:read");
X    }
X    if (n == 0 || nn == 2) {	/* application closed / exception cond */
X	CCLOSE cc;
X
X	if (DDebug)
X	    fprintf(stderr, " DO_OPEN: REMOTE EOF, channel %d\n", chan);
X
X	cc.chanh = chan >> 8;
X	cc.chanl = chan;
X	WriteStream(SCMD_CLOSE, &cc, sizeof(CCLOSE), chan);
X	Chan[chan].state = CHAN_CLOSE;
X	Chan[chan].flags |= CHANF_LCLOSE;
X	if (Chan[chan].flags & CHANF_RCLOSE) {
X	    ;
X	    /* should never happen
X	    int fd = Chan[chan].fd;
X	    Chan[chan].state = CHAN_FREE;
X	    Chan[chan].fd = -1;
X	    Fdstate[fd] = nop;
X	    FD_CLR(fd, &Fdread);
X	    FD_CLR(fd, &Fdexcept);
X	    close(fd);
X	    */
X	} else {
X	    FD_CLR(fd, &Fdread);
X	    FD_CLR(fd, &Fdexcept);
X	}
X    }
X    if (n > 0) {
X	WriteStream(SCMD_DATA, buf, n, chan);
X    }
X}
X
SHAR_EOF
echo "extracting dnet/dnet.h"
sed 's/^X//' << \SHAR_EOF > dnet/dnet.h
X
X/*
X *  DNET.H
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X */
X
X#include <sys/types.h>
X#include <sys/ioctl.h>
X#include <sys/socket.h>
X#include <sys/time.h>
X#include <sys/file.h>
X
X/* V2.01 */
X#include <sys/param.h>
X#include <pwd.h>
X
X#include <signal.h>
X#include <stdio.h>
X#include <errno.h>
X#ifndef FD_SETSIZE
X#define FD_SETSIZE (sizeof(struct fd_set) * 8)
X#endif
X#ifndef NFDBITS
X#define NFDBITS 32
X#define	FD_SET(n, p)	((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
X#define	FD_CLR(n, p)	((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
X#define	FD_ISSET(n, p)	((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
X#define FD_ZERO(p)	bzero((char *)(p), sizeof(*(p)))
X#endif
X#ifndef sigmask
X#define sigmask(m) (1 << ((m)-1))
X#endif
X
X#ifndef LASTTRYDNETSERVERS
X#define LASTTRYDNETSERVERS "/usr/local/lib/dnet/dnet.servers"
X#endif
X
Xstruct Node {
X    struct Node *ln_Succ;
X    struct Node *ln_Pred;
X};
X
Xstruct List {
X    struct Node *lh_Head;
X    struct Node *lh_Tail;
X    struct Node *lh_TailPred;
X};
X
X
Xtypedef unsigned char	ubyte;
Xtypedef unsigned short	uword;
Xtypedef unsigned long	ulong;
X
Xtypedef struct List	    LIST;
Xtypedef struct Node	    NODE;
X
X#include "channel.h"
X
X#define PKT struct _PKT
X#define XIOR struct _XIOR
X
X#define EMPTY	0	/*  empty (sent)                    */
X#define READY	1	/*  data ready (not sent yet)       */
X#define RCVBUF  4096
X
X#define MAXCHAN 128	/*  Max # of channels supported     */
X#define SYNC	0xFF	/*  SYNC character		    */
X#define WTIME	2000000 /*  in micro seconds (expect ack)   */
X#define RTIME	1000000 /*  in micro seconds (read to)      */
X#define MAXPKT	200	/*  maximum packet size  (data area)	          */
X#define MINPKT  32	/*  minimum packet size  (data area) for purposes */
X			/*  of determining the dynamic maximum packet size*/
X			/*  only.  Actual minimum is, of course, 1 byte   */
X
X#define OVERHEAD    7	/*  for packets with data	    */
X
XXIOR {
X    NODE    io_Node;
X    ubyte   *io_Data;
X    ulong   io_Length;
X    ulong   io_Actual;
X    uword   io_Channel;
X    ubyte   io_Command;
X    ubyte   io_Error;
X    char    io_Pri;
X};
X
XPKT {
X    uword   iolength;	/*  send: length of packet, recv: length of data    */
X    ubyte   state;	/*  EMPTY, READY				    */
X
X    ubyte   sync;	/*  THE PACKET	    */
X    ubyte   ctl;
X    ubyte   cchk;
X    ubyte   lenh;
X    ubyte   lenl;
X    ubyte   data[MAXPKT+2];
X};
X
X	/*
X	 *    In receiving a packet the receiver can be in one of these
X	 *    states.
X	 */
X
X#define RS_SYNC 0	    /*	Waiting for sync		*/
X#define RS_CTL	1	    /*	Waiting for command		*/
X#define RS_CCHK 2	    /*	Waiting for check byte		*/
X#define RS_LEN1 3	    /*	Waiting for MSB length byte	*/
X#define RS_LEN2 4	    /*	Waiting for LSB length byte	*/
X#define RS_DATA 5	    /*	Waiting for data & checksum	*/
X
X	/*
X	 *    The low level protocol generates packets.   This is used 
X	 *    for error checking, data sequencing, retries, restart, etc...
X	 *    The packet format is:
X	 *
X	 *	SYNC sss0xccc CHK
X	 *	SYNC sss1xccc CHK nnnnnnnn nnnnnnnn [DATA] CHK2
X	 *			    msb       lsb
X	 *
X	 *	sss = sequence #
X	 *	B4  = packet contains data
X	 *	B3  = reserved (may be used to extend the command set)
X	 *	ccc = PKF_?????
X	 *
X	 *	NOTE that the data length nnn..nn is not checked with either
X	 *	CHK or CHK2 .  The protocol will determine if the length
X	 *	is reasonable (< MAXPKT) and then try to read that many
X	 *	bytes.  The CHK will obviously fail if the length was 
X	 *	incorrect.
X	 */
X
X#define PKF_SEQUENCE	0xE0	/*  Sequence #			*/
X#define PKF_DATA	0x10	/*  1-65535 bytes		*/
X#define PKF_RESERVED	0x08	/*  reserved bit		*/
X#define PKF_MASK	0x07	/*  command mask		*/
X
X#define PKCMD_WRITE	1	/*  A DATA packet		*/
X#define PKCMD_CHECK	2	/*  Request ACK or NAK for win	*/
X#define PKCMD_ACK	3	/*  ACK a window		*/
X#define PKCMD_NAK	4	/*  NAK a window		*/
X#define PKCMD_RESTART 	5	/*  RESTART dnet.  (new)	*/
X#define PKCMD_ACKRSTART	6	/*  ACK a restart  (new)	*/
X#define PKCMD_RESERVE3	7
X
X	/*
X	 *  All channel multiplexing, channel commands, etc... is encoded
X	 *  within a PKCMD_WRITE packet.
X	 *
X	 *  Channel commands are characterized by a one byte control
X	 *  field and up to 7 bytes of data.  The control field format
X	 *  is:		10cccnnn [DATA]		ccc = SCMD_??
X	 *					nnn = # additional data bytes
X	 */
X
X#define SCMD_SWITCH	0x00	/*  switch active channel #	*/
X#define SCMD_OPEN	0x01	/*  open a channel		*/
X#define SCMD_CLOSE	0x02	/*  close a channel		*/
X#define SCMD_ACKCMD	0x03	/*  ack an open/close request	*/
X#define SCMD_EOFCMD	0x04	/*  Reof or Weof		*/
X#define SCMD_QUIT	0x05	/*  QUIT dnet			*/
X#define SCMD_IOCTL	0x06	/*  channel ioctl (new),PTY sup	*/
X#define SCMD_RESERVE1	0x07
X
X	/*
X	 *  Stream data is characterized by the following format:
X	 *
X	 *		   msb      lsb	
X	 *		11nnnnnn nnnnnnnn [128-16383 bytes DATA]
X	 *			 0nnnnnnn [0-127 bytes DATA]
X	 *
X	 *  NOTE:  11000000 0ccccccc nnnnnnnn   reserved for furture ext. of
X	 *					SCMD commands.
X	 */
X
X#define SCMD_DATA	0x08	/*  stream command, DATA (dummy ID)	*/
X
X	/*
X	 *  Each channel can be in one of several states.
X	 */
X
X#define CHAN_FREE	0x01	/*  free channel		*/
X#define CHAN_ROPEN	0x02	/*  remote open, wait port msg	*/
X#define CHAN_LOPEN	0x03	/*  local open, wait reply	*/
X#define CHAN_OPEN	0x04	/*  channel open		*/
X#define CHAN_CLOSE	0x05	/*  one side of channel closed  */
X
X#define CHANF_ROK	0x01	/*  remote hasn't EOF'd us yet  */
X#define CHANF_WOK	0x02	/*  remote will accept data	*/
X#define CHANF_LCLOSE	0x04 	/*  channel closed on our side  */
X#define CHANF_RCLOSE	0x08    /*  channel closed on rem side  */
X
Xextern ubyte *RemHead();
Xextern ubyte *malloc();
Xextern char *getenv();
X
X#ifndef NOEXT
Xextern long USecPerByte;
Xextern int DNet_fd;
Xextern PKT Pkts[9];
Xextern ubyte WCBuf[64];
Xextern PKT *RPak[4];
Xextern PKT *WPak[4];
Xextern CHAN Chan[MAXCHAN];
Xextern LIST TxList;	       /*  For pending DNCMD_WRITE reqs.   */
Xextern fd_set Fdread;
Xextern fd_set Fdwrite;
Xextern fd_set Fdexcept;
Xextern void (*Fdstate[FD_SETSIZE])();
Xextern ubyte Fdperm[FD_SETSIZE];
Xextern uword FdChan[FD_SETSIZE];
Xextern ubyte RcvBuf[RCVBUF];
Xextern ubyte RTimedout, WTimedout;
Xextern uword Rto_act, Wto_act;
Xextern uword RcvData;
Xextern uword RExpect;
Xextern uword RChan;
Xextern uword WChan;
Xextern uword RPStart;
Xextern uword WPStart;
Xextern uword WPUsed;
Xextern uword RState;
Xextern ubyte DDebug;
Xextern ubyte DidWrite;
Xextern ubyte Restart;
Xextern ubyte DeldQuit;
Xextern ulong NumCon; 
X
Xextern int errno;
X
X#endif
X
SHAR_EOF
echo "extracting dnet/dnet.c"
sed 's/^X//' << \SHAR_EOF > dnet/dnet.c
X
X/*
X *  DNET.C
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X *
X *	Handles action on all active file descriptors and dispatches
X *	to the proper function in FILES.C
X *
X */
X
X#include "dnet.h"
X#include <sys/wait.h>
X#include <sys/resource.h>
X
Xhandle_child()
X{
X    union wait stat;
X    struct rusage rus;
X    while (wait3(&stat, WNOHANG, &rus) > 0);
X}
X
Xchar *
Xshowselect(ptr)
Xfd_set *ptr;
X{
X    static char buf[FD_SETSIZE+32];
X    short i;
X
X    for (i = 0; i < FD_SETSIZE; ++i) {
X	buf[i] = (FD_ISSET(i, ptr)) ? '1' : '0';
X    }
X    buf[i] = 0;
X    return(buf);
X}
X
X
Xloganddie()
X{
X    fflush(stderr);
X    fprintf(stderr, "\nHUPSIGNAL\n");
X    perror("HUP, last error:");
X    fprintf(stderr, "Last select return:\n");
X    fprintf(stderr, "  %s\n", showselect(&Fdread));
X    fprintf(stderr, "  %s\n", showselect(&Fdwrite));
X    fprintf(stderr, "  %s\n", showselect(&Fdexcept));
X    fprintf(stderr, "RcvData = %ld\n", RcvData);
X    fprintf(stderr, "RChan/WChan = %ld/%ld\n", RChan, WChan);
X    fprintf(stderr, "RPStart = %ld\n", RPStart);
X    fprintf(stderr, "WPStart = %ld\n", WPStart);
X    fprintf(stderr, "WPUsed = %ld\n", WPUsed);
X    fprintf(stderr, "RState = %ld\n", RState);
X    fflush(stderr);
X    kill(0, SIGILL);
X    exit(1);
X}
X
X#define SASIZE(sa)      (sizeof(sa)-sizeof((sa).sa_data)+strlen((sa).sa_data))
X
Xmain(ac,av)
Xchar *av[];
X{
X    long sink_mask, dnet_mask;
X    long mark = 0;
X    ubyte notdone;
X    char local_dir[MAXPATHLEN];
X    struct passwd pw_info;
X    extern void RcvInt();
X
X    if (getenv("DNETDIR")) {
X	strcpy(local_dir, getenv("DNETDIR"));
X	if (chdir(local_dir)) {
X	    fprintf(stderr, "Unable to chdir to DNETDIR: %s\n", local_dir);
X	    exit(1);
X	}
X	freopen("DNET.LOG", "w", stderr);
X    } else {
X	pw_info = *getpwuid(getuid());
X	strcpy(local_dir, pw_info.pw_dir);
X	strcat(local_dir, "/.dnet");
X	if (chdir(local_dir)) {
X	    mkdir(local_dir, 0700);
X	    if (chdir(local_dir)) {
X		fprintf(stderr, "Unable to create dir %s\n", local_dir);
X		exit(1);
X	    }
X	}
X	freopen("DNET.LOG", "w", stderr);
X    }
X    fprintf(stderr, "DNet startup\n");
X    fprintf(stderr, "Log file placed in %s\n", local_dir);
X    fflush(stderr);
X
X    signal(SIGINT, SIG_IGN);
X    signal(SIGPIPE, SIG_IGN);
X    signal(SIGQUIT, SIG_IGN);
X    signal(SIGCHLD, handle_child);
X    signal(SIGHUP, loganddie);
X
X    bzero(Pkts,sizeof(Pkts));
X    setlistenport("");
X
X    {
X	register short i;
X	for (i = 1; i < ac; ++i) {
X	    register char *ptr = av[i];
X	    if (*ptr != '-') {
X		DDebug = 1;
X		fprintf(stderr, "Debug mode on\n");
X		continue;
X	    }
X	    switch(*ptr) {
X	    case 'B':
X		USecPerByte = atoi(ptr+1) / 10;     /*  bytes per second */
X		USecPerByte = 1000000 / USecPerByte;
X		fprintf(stderr, "Assuming about %ld uS/byte for timeout calc\n",
X		    USecPerByte
X		);
X		break;
X	    default:
X		fprintf(stderr, "Unknown option: %c\n", *ptr);
X		printf("Unknown option: %c\n", *ptr);
X		exit(1);
X	    }
X	}
X    }
X
X    NewList(&TxList);
X
X    Fdperm[0] = 1;
X    Fdstate[0] = RcvInt;
X    FD_SET(0, &Fdread);
X    FD_SET(0, &Fdexcept);
X
X    fprintf(stderr, "DNET RUNNING, Listenfd=%ld\n", DNet_fd);
X    NetOpen();          /* initialize network and interrupt driven read */
X    TimerOpen();        /* initialize timers                            */
X
X    do_netreset();
X    do_restart();
X
X    notdone = 1;
X    while (notdone) {
X	/*
X	 *    MAIN LOOP.  select() on all the file descriptors.  Set the
X	 *    timeout to infinity (NULL) normally.  However, if there is
X	 *    a pending read or write timeout, set the select timeout
X	 *    to 2 seconds in case they timeout before we call select().
X	 *    (i.e. a timing window).  OR, if we are in the middle of a
X	 *    read, don't use descriptor 0 and timeout according to
X	 *    the expected read length, then set the descriptor as ready.
X	 */
X
X	fd_set fd_rd;
X	fd_set fd_wr;
X	fd_set fd_ex;
X	struct timeval tv, *ptv;
X	int err;
X
X	fd_rd = Fdread;
X	fd_wr = Fdwrite;
X	fd_ex = Fdexcept;
X
X	tv.tv_sec = 0;		/* normally wait forever for an event */
X	tv.tv_usec= 0;
X	ptv = NULL;
X	if ((Rto_act || Wto_act)) {     /* unless timeout pending */
X	    ptv = &tv;
X	    tv.tv_sec = 2;
X	}
X
X	/*   ... or expecting data (don't just wait for one byte).
X	 *
X	 *   This is an attempt to reduce the CPU usage for the process.
X	 *   If we are expecting data over the serial line, then don't
X	 *   return from the select() even if data is available, but
X	 *   wait for the timeout period indicated before reading the
X	 *   data.  Don't wait more than 64 byte times or we may loose
X	 *   some data (the silo's are only so big.. like 128 bytes).
X	 *
X	 *   Currently, USecPerByte is not set properly (set for 9600 baud)
X	 */
X
X	if (RExpect) {
X	    ptv = &tv;
X	    tv.tv_usec= USecPerByte * ((RExpect < 64) ? RExpect : 64);
X	    tv.tv_sec = 0;
X	    FD_CLR(0, &fd_rd);
X	}
X	err = select(FD_SETSIZE, &fd_rd, &fd_wr, &fd_ex, ptv);
X	if (RExpect) {
X	    FD_SET(0, &fd_rd);   /* pretend data ready */
X	}
X	if (DDebug)
X	    fprintf(stderr, "SERR %ld %ld %08lx %08lx\n",
X		err, errno, RExpect, ptv
X	    );
X
X	if (RTimedout) {
X	    RTimedout = 0;
X	    do_rto();
X	    mark = 0;
X	}
X	if (WTimedout) {
X	    WTimedout = 0;
X	    do_wto();
X	}
X	if (err < 0) {
X	    if (errno == EBADF) {
X		perror("select");
X		dneterror(NULL);
X	    }
X	} else {
X	    register short i;
X	    register short j;
X	    register long mask;
X
X	    for (i = 0; i < FD_SETSIZE/NFDBITS; ++i) {
X		if (mask = fd_ex.fds_bits[i]) {
X		    for (j = i * NFDBITS; mask; (mask >>= 1),(++j)) {
X			if (mask & 1)
X			    (*Fdstate[j])(2,j);
X		    }
X		}
X		if (mask = fd_wr.fds_bits[i]) {
X		    for (j = i * NFDBITS; mask; (mask >>= 1),(++j)) {
X			if (mask & 1)
X			    (*Fdstate[j])(1,j);
X		    }
X		}
X		if (mask = fd_rd.fds_bits[i]) {
X		    for (j = i * NFDBITS; mask; (mask >>= 1),(++j)) {
X			if (mask & 1)
X			    (*Fdstate[j])(0,j);
X		    }
X		}
X	    }
X	}
X	if (RcvData != mark)
X	    mark = do_rnet();
X	do_wupdate();
X	do_wnet();
X    }
X    dneterror(NULL);
X}
X
Xvoid
Xnop()
X{
X}
X
Xdo_netreset()
X{
X    register short i;
X    register CHAN *ch;
X    for (i = 0; i < FD_SETSIZE; ++i) {
X	if (!Fdperm[i])
X	    Fdstate[i] = nop;
X    }
X    for (i = 0, ch = Chan; i < MAXCHAN; ++i, ++ch) {
X	switch(ch->state) {
X	case CHAN_OPEN:
X	case CHAN_LOPEN:	/*  pending on network	    */
X	case CHAN_CLOSE:
X	    if (ch->fd >= 0) {
X		FD_CLR(ch->fd, &Fdread);
X		FD_CLR(ch->fd, &Fdexcept);
X		Fdstate[ch->fd] = nop;
X		close(ch->fd);
X		ch->fd = -1;
X		ch->state = CHAN_FREE;
X		ch->flags = 0;
X		--NumCon;
X	    }
X	    ClearChan(&TxList, i, 1);
X	    break;
X	}
X    }
X    RPStart = 0;
X    WPStart = 0;
X    WPUsed  = 0;
X    RState  = 0;
X    RChan = 0;
X    WChan = 0;
X}
X
Xdo_restart()
X{
X    static ubyte buf[3] = { SYNC, PKCMD_RESTART, (SYNC<<1)^PKCMD_RESTART };
X    Restart = 1;
X    NetWrite(buf, sizeof(buf), 1);
X    WTimeout(WTIME);
X}
X
Xsetlistenport(remotehost)
Xchar *remotehost;
X{
X    static struct sockaddr sa[2];
X    int s;
X    extern void do_localopen();
X
X    if (DNet_fd >= 0) {
X	unlink(sa[0].sa_data);
X	Fdstate[DNet_fd] = nop;
X	Fdperm[DNet_fd] = 0;
X	FD_CLR(DNet_fd, &Fdread);
X	FD_CLR(DNet_fd, &Fdexcept);
X	close(DNet_fd);
X    }
X    setenv("DNETHOST=", remotehost);
X    sprintf(sa[0].sa_data, "DNET.%s", remotehost);
X    unlink(sa[0].sa_data);
X    sa[0].sa_family = AF_UNIX;
X
X    s = socket(PF_UNIX, SOCK_STREAM, 0);
X    fcntl(s, F_SETOWN, getpid());
X    fcntl(s, F_SETFL,  FNDELAY);
X    if (bind(s, &sa[0], SASIZE(sa[0])) < 0) {
X	perror("bind");
X	exit(1);
X    }
X    if (listen(s, 5) < 0) {
X	unlink(sa[0].sa_data);
X	perror("listen");
X	exit(1);
X    }
X    DNet_fd = s;
X    Fdstate[DNet_fd] = do_localopen;
X    Fdperm[DNet_fd] = 1;
X    FD_SET(DNet_fd, &Fdread);
X    FD_SET(DNet_fd, &Fdexcept);
X}
X
SHAR_EOF
echo "extracting dnet/Makefile"
sed 's/^X//' << \SHAR_EOF > dnet/Makefile
X
X#	THE DNET DRIVER, UNIX END
X# 
X# 	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X#
X#	the BIN should be placed in your path for ease of use.  DNET uses 
X#	UNIX level sockets allowing multiple users on a system to run it
X#	independantly.  You should make a special directory (Example: .DNET)
X#	and then setenv DNETDIR ~/.DNET/  ... all clients and servers will
X#	use the enviroment variable to find/create the UNIX level sockets.
X
XBIN	= ../bin
XOBJS	= dnet.o control.o files.o globals.o net.o subs.o internal.o
X
X
X$(BIN)/dnet:	$(OBJS)
X	cc $(OBJS) -o $(BIN)/dnet
X
Xclean:	
X	rm -f *.o make.out
X
SHAR_EOF
echo "extracting dnet/MODS"
sed 's/^X//' << \SHAR_EOF > dnet/MODS
X
Xnet.c:	notion of reply expected added to NetWrite()
Xdnet.c: select modified.  On error still attempt write ops
X
SHAR_EOF
echo "extracting dnet.servers"
sed 's/^X//' << \SHAR_EOF > dnet.servers
X
X8192	~/src/dnet.unix/bin/scopy	~
X8193	~/src/dnet.unix/bin/sshell	~
X8197	~/src/dnet.unix/bin/sloadav	~
X8201	~/src/dnet.unix/bin/sgcopy	~
X
SHAR_EOF
if `test ! -d doc`
then
  mkdir doc
  echo "mkdir doc"
fi
echo "extracting doc/history"
sed 's/^X//' << \SHAR_EOF > doc/history
X
X    V1.00   sometime in 1987
X	    First release
X
X    V1.20   sometime in 1988
X	    Fixes to the first release
X
X    V2.00   1 March 1989
X
X	    Major fixes, additions, and other items.
X
X
X
SHAR_EOF
echo "extracting doc/dnet.doc"
sed 's/^X//' << \SHAR_EOF > doc/dnet.doc
X
XUNIX/DNET
X
X	DNET [debug]
X
X	Run the DNET protocol on descriptor 0.  If the enviroment variable
X	DNETDIR exists, DNET will create the rendezvous sockets in the
X	specified directory (DNETDIR must have a hanging slash on it),
X	otherwise the current directory is used.
X
X	DNET also opens a log file (DNET.LOG) in the directory.  If the debug
X	option is given, DNET writes gobs of debugging information to the log.
X
X	Descriptor 0 may be a tty or socket.
X
X	To allow DNET to startup servers on demand, the file "dnet.servers"
X	must exist in the rendezvous directory.
X
X					-Matt
X
SHAR_EOF
echo "extracting doc/putfiles.doc"
sed 's/^X//' << \SHAR_EOF > doc/putfiles.doc
XUNIX/PUTFILES
XAMIGA/PUTFILES
X
X	PUTFILES [-dDESTDIR] file/dir file/dir file/dir ..
X
X	File transfer client.  Transfer the specified files and or directories
X	to the destination directory on the remote machine.  For instance:
X
X	unix:  putfiles -dram: charlie.txt /usr/include
X
X	*places the file charlie.txt as ram:charlie.txt, and places the 
X	 directory structure /usr/include as ram:include/(whatever).
X
X	The default destination directory depends on the remote host.  If the
X	destination is an Amiga, the default is DF0: (so you probably always
X	want to specify a -d switch).  On the UNIX end, the default is whatever
X	is set in the file dnet.servers
X
X	Currently, no file compression is done.  I also hope to add 
X	semi-automatic crash recovery (continue download where you left off
X	after a crash), though it should be noted that it is very difficult
X	to crash DNET with random modem noise.
X
SHAR_EOF
echo "extracting doc/dsoc.doc"
sed 's/^X//' << \SHAR_EOF > doc/dsoc.doc
X
X
XUNIX/DSOC
X
X
XDSOC [port#]
X
X	(default: 8196 = AMIGSHELL (CLI window))
X
X	Connect to the remote port but keep the TTY in COOKED mode (normal).
X	The connection will be terminated by killing the process on the UNIX
X	end or typing ^C.  NOTE THAT ^C KILLS DSOC AND IS NOT SENT THROUGH TO
X	THE REMOTE CLI.
X
X	The default port accesses the SCLI server on the Amiga end, which
X	starts up a CLI.  Things work properly only if *MY* PIPE: device is
X	installed on the Amiga.  Things close down properly only if you
X	have a CLI prompt when you exit DSOC.
X
X	The SCLI server currently handles only one connection at a time.
X
SHAR_EOF
echo "extracting doc/dnetlib.doc"
sed 's/^X//' << \SHAR_EOF > doc/dnetlib.doc
X
XUNIX/DNETLIB
X
X	UNIX END DNET LIBRARY INTERFACE
X
X	See the server source for good examples of usage.
X
Xlong	private;
Xuword	port
Xchar 	*buf;
Xint 	fd;
Xint	res1..5, bytes
Xchar	pri;
Xlong	val68, valvax;
X
Xprivate	= DListen(port)				listen on a port (server)
X     fd = DAccept(private)			accept a connection (server)
X	  DNAAccept(private)			don't accept a conn.(server)
X     fd = DOpen(host, port, txpri, rxpri)	open a remote connection (client)
X   res1 = read(fd, buf, bytes)			standard unix read()
X   res2 = gread(fd, buf, bytes)			(see below)
X   res3 = ggread(fd, buf, bytes)		(see below)
X   res4 = write(fd, buf, bytes)			standard unix write()
X   res5 = gwrite(fd, buf, bytes)		(see below)
X	  close(fd);
X
X   val68 = ntohl68(valvax)	convert to and from MC68000 longword format.
X   valvax= htonl68(val68)
X
X
XDListen()	sets up a UNIX domain socket in the current directory unless
X		another is specified by the DNETDIR enviroment variable.
X		Returns a private structure pointer or NULL on error.
X
X		This call will override any existing server for the port in
X		the directory permanently.  It does not cause an existing
X		server to exit, however, and you should be careful to kill
X		old servers before starting new ones.  See the source for a
X		template of correct server code.
X
XDAccept()	Accepts a new connection on the port.  Returns a file 
X		descriptor or a negative value.  This call will block.
X
XDOpen()		Attempt to connect to a port on the remote machine.  Returns
X		a file descriptor or a negative value.  The error is either
X		due to not finding DNET's master port (DNET must be running),
X		or the remote server not running.
X
X		This call looks in the current directory for the master
X		socket unless another is specified by the DNETDIR enviroment 
X		variable.
X
X		Two priorities are specified.  One for sending, and one for
X		receiving data.  A priority is a value -127 to 126 inclusive,
X		with 126 the highest priority.  Normally, priorities range
X		from -80 (file transfers) to +20 (terminal window).
X
XDEof()		Send an EOF without closing the connection.  Currently doesn't
X		work worth shit, so don't use it.
X
Xwrite()		Standard UNIX write call.  But we are dealing with a socket
X		here, so one must be careful of the return value.  (1) WRITE()
X		may not return the # bytes requested to write, but less,
X		(2) WRITE() may return a negative value indicating an error or
X		that it was interrupted or that it would have blocked (if you
X		have got non-blocking IO setup).  (3) WRITE() returns 0 on 
X		socket EOF.
X
Xgwrite()	This call will write all the bytes specified, whether the
X		socket is non-blocking or not.  It handles restarting the
X		WRITE() call on EINTR and properly handles EWOULDBLOCK.
X
X		It returns the number bytes requested or fewer on error.  If
X		fewer bytes are returned you should close the socket and exit.
X
Xread()		Standard UNIX read call.  But we are still dealing with a 
X		socket here and anything might happen.  READ() can return fewer
X		than the number of bytes requested, 0 on EOF, or a negative
X		number indicating various errors.
X
Xgread()		GREAD() handles retrying if the EINTR error occurs.  GREAD()
X		blocks until at least one character is received (or EOF), and
X		does this even if the socket is marked non-blocking.  GREAD()
X		returns 0 on EOF/ERROR.
X
Xggread()	GGREAD() not only retries on EINTR, but will block until ALL
X		the requested bytes are read in, even if the socket is marked
X		non-blocking.  GGREAD() returns -1 on EOF/ERROR.  You can also
X		tell if an EOF/ERROR occured if fewer than the requested 
X		number of bytes are read.
X
SHAR_EOF
echo "extracting doc/draw.doc"
sed 's/^X//' << \SHAR_EOF > doc/draw.doc
X
XUNIX/DRAW
X
X
XDRAW [port#]
X
X	(default: 8195 = AMIGTERM (talk window))
X
X	Connect to the remote port and place the TTY in RAW mode.  The
X	connection can only be terminated by the remote end, or by killing
X	the process on the UNIX end.
X
X	The default port brings up a 'talk' window on the Amiga.  AMIGATERM
X	will recognize a ^C sent from DRAW and close the connection.
X
SHAR_EOF
if `test ! -d lib`
then
  mkdir lib
  echo "mkdir lib"
fi
echo "extracting lib/dnetlib.h"
sed 's/^X//' << \SHAR_EOF > lib/dnetlib.h
X
X#define EFATAL 0
X#define EWARN 1
X#define EDEBUG 2
X
SHAR_EOF
echo "extracting lib/dnetlib.c"
sed 's/^X//' << \SHAR_EOF > lib/dnetlib.c
X
X/*
X *  DNETLIB.C
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X *
X *  Library Interface for DNET.
X */
X
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <fcntl.h>
X#include <signal.h>
X#include <stdio.h>
X#include <errno.h>
X#ifdef O_CREAT
X#include <sys/file.h>
X#endif
X#include "../lib/dnetlib.h"
X
Xextern char *getenv();
X
Xtypedef unsigned short uword;
Xtypedef unsigned long ulong;
Xtypedef unsigned char ubyte;
Xtypedef struct sockaddr SOCKADDR;
X
Xtypedef struct {
X	int s;
X	uword port;
X} CHANN;
X
X#define NAMELEN sizeof(".PORT.XXXXX")
X#define NAMEPAT "%s.PORT.%ld"
X
Xchar *getdirpart();
X
XCHANN *
XDListen(port)
Xuword port;
X{
X    CHANN *chan;
X    int s;
X    SOCKADDR *sa = (SOCKADDR *)malloc(sizeof(SOCKADDR)+256);
X    char *dirstr = getenv("DNETDIR") ? getenv("DNETDIR") : "";
X
X    sprintf(sa->sa_data, NAMEPAT, dirstr, port);
X    sa->sa_family = AF_UNIX;
X    unlink(sa->sa_data);
X
X    s = socket(PF_UNIX, SOCK_STREAM, 0);
X    fcntl(s, F_SETOWN, getpid());
X    if (bind(s, sa, sizeof(*sa)-sizeof(sa->sa_data)+strlen(sa->sa_data)) < 0) {
X	close(s);
X	free(sa);
X	return(NULL);
X    }
X    if (listen(s, 5) < 0) {
X	close(s);
X	unlink(sa->sa_data);
X	free(sa);
X	return(NULL);
X    }
X    chan = (CHANN *)malloc(sizeof(CHANN));
X    chan->s = s;
X    chan->port = port;
X    free(sa);
X    return(chan);
X}
X
X
XDUnListen(chan)
XCHANN *chan;
X{
X    char *dirstr = getenv("DNETDIR") ? getenv("DNETDIR") : "";
X    char buf[32];
X
X    close(chan->s);
X    sprintf(buf, NAMEPAT, dirstr, chan->port);
X    unlink(buf);
X    free(chan);
X}
X
XDAccept(chan)
XCHANN *chan;
X{
X    SOCKADDR sa;
X    int addrlen = sizeof(sa);
X    int fd;
X
X    fd = accept(chan->s, &sa, &addrlen);
X    return(fd);
X}
X
XDOpen(host, port, txpri, rxpri)
Xchar *host;
Xuword port;
Xchar txpri, rxpri;
X{
X    int s;
X    char rc;
X    short xb[3];
X    SOCKADDR *sa = (SOCKADDR *)malloc(sizeof(SOCKADDR)+256);
X    char *dirstr = getenv("DNETDIR") ? getenv("DNETDIR") : "";
X
X    if (rxpri < -127)
X	rxpri = -127;
X    if (rxpri > 126)
X	rxpri = 126;
X    if (txpri < -127)
X	txpri = -127;
X    if (txpri > 126)
X	txpri = 126;
X
X    if (host == NULL)
X	host = (getenv("DNETHOST")) ? getenv("DNETHOST"):"";
X
X    sa->sa_family = AF_INET;
X    sprintf(sa->sa_data, "%s%s%s", dirstr, "DNET.", host);
X
X    s = socket(PF_UNIX, SOCK_STREAM, 0);
X    fcntl(s, F_SETOWN, getpid());
X    if (connect(s, sa, sizeof(*sa)-sizeof(sa->sa_data)+
X    strlen(sa->sa_data))<0) {
X	close(s);
X	free(sa);
X	return(-1);
X    }
X    free(sa);
X    xb[0] = port;
X    ((char *)&xb[1])[0] = txpri;
X    ((char *)&xb[1])[1] = rxpri;
X    write(s, xb, 4);
X    if (read(s, &rc, 1) == 1 && rc == 0)
X	return(s);
X    close(s);
X    return(-1);
X}
X
XDEof(fd)
X{
X    char dummy;
X
X    shutdown(fd, 1);
X    write(fd, &dummy, 0);
X}
X
Xgwrite(fd, buf, bytes)
Xchar *buf;
X{
X    int n;
X    int orig = bytes;
X    extern int errno;
X    while (bytes) {
X	n = write(fd, buf, bytes);
X	if (n > 0) {
X	    bytes -= n;
X	    buf += n;
X	    continue;
X	}
X	if (n < 0) {
X	    if (errno == EINTR)
X		continue;
X	    if (errno == EWOULDBLOCK) {
X		int wm = 1 << fd;
X		int em = 1 << fd;
X		if (select(fd+1, NULL, &wm, &em, NULL) < 0)
X		    continue;
X		if (wm)
X		    continue;
X	    }
X	    return(orig - bytes);
X	}
X    }
X    return(orig);
X}
X
Xgread(fd, buf, bytes)
Xchar *buf;
X{
X    int n;
X    int orig = bytes;
X    extern int errno;
X    while (bytes) {
X	n = read(fd, buf, bytes);
X	if (n > 0) {
X	    bytes -= n;
X	    buf += n;
X	    break;
X	}
X	if (n < 0) {
X	    if (errno == EINTR)
X		continue;
X	    if (errno == EWOULDBLOCK) {
X		int rm = 1 << fd;
X		int em = 1 << fd;
X		if (select(fd+1, &rm, NULL, &em, NULL) < 0)
X		    continue;
X		if (rm)
X		    continue;
X	    }
X	    return(orig - bytes);
X	}
X	if (n == 0)
X	    break;
X    }
X    return(orig - bytes);
X}
X
Xggread(fd, buf, bytes)
Xchar *buf;
X{
X    int n;
X    int ttl = 0;
X    while (bytes) {
X	n = gread(fd, buf, bytes);
X	if (n > 0) {
X	    bytes -= n;
X	    buf += n;
X	    ttl += n;
X	    continue;
X	}
X	return(-1);
X    }
X    return(ttl);
X}
X
X/*
X *	Convert to and from 68000 longword format.  Of course, it really
X *	doesn't matter what format you use, just as long as it is defined.
X */
X
Xntohl68(n)
Xulong n;
X{
X    return(
X	(((ubyte *)&n)[0] << 24)|
X	(((ubyte *)&n)[1] << 16)|
X	(((ubyte *)&n)[2] << 8)|
X	(((ubyte *)&n)[3])
X    );
X}
X
Xhtonl68(n)
Xulong n;
X{
X    ulong v;
X    ((ubyte *)&v)[0] = n >> 24;
X    ((ubyte *)&v)[1] = n >> 16;
X    ((ubyte *)&v)[2] = n >> 8;
X    ((ubyte *)&v)[3] = n;
X    return(v);
X}
X
X
XDoOption(ac, av, ops, args)
Xshort ac;
Xchar *av[];
Xchar *ops;
Xlong args;
X{
X    register short i;
X    short j;
X
X    for (i = j = 1; i < ac; ++i) {
X	register char *ptr = av[i];
X	if (*ptr != '-') {
X	    av[j++] = av[i];
X	    continue;
X	}
X	while (*++ptr) {
X	    register char *op;
X	    long **ap = (long **)&args;
X	    short isshort;
X
X	    for (op = ops; *op && *op != *ptr;) {
X		if (*op == *ptr)
X		    break;
X		if (*++op == '%') {
X		    while (*op && *op != 's' && *op != 'd')
X			++op;
X		    if (*op)
X			++op;
X		}
X		if (*op == ',')     /*  optional ,  */
X		    ++op;
X		++ap;
X	    }
X	    if (*op == 0)
X		return(-1);
X	    if (op[1] != '%') {
X		*(short *)*ap = 1;
X		++ap;
X		continue;
X	    }
X	    op += 2;
X	    isshort = 1;
X	    while (*op && *op != 's' && *op != 'd') {
X		switch(*op) {
X		case 'h':
X		    isshort = 1;
X		    break;
X		case 'l':
X		    isshort = 0;
X		    break;
X		default:
X		    return(-1);
X		}
X		++op;
X	    }
X	    switch(*op) {
X	    case 's':
X		if (ptr[1]) {
X		    *(char **)*ap = ptr + 1;
X		    ptr = "\0";
X		} else {
X		    *(char **)*ap = av[++i];
X		}
X		break;
X	    case 'd':
X		if (isshort)
X		    *(short *)*ap = atoi(++ptr);
X		else
X		    *(long *)*ap = atoi(++ptr);
X		while (*ptr >= '0' && *ptr <= '9')
X		    ++ptr;
X		break;
X	    default:
X		return(-1);
X	    }
X	}
X    }
X    return(j);
X}
X
Xelog(how, ctl, arg)
Xchar *ctl;
Xlong arg;
X{
X    char *dir = getenv("DNETDIR");
X    FILE *fi;
X    char buf[256];
X    long dtime;
X
X    time(&dtime);
X
X    if (!dir)
X	dir = "";
X    sprintf(buf, "%s%s", dir, "DNET.LOG");
X    if (fi = fopen(buf, "a")) {
X	strcpy(buf, ctime(&dtime));
X	buf[strlen(buf)-1] = 0;
X	fprintf(fi, "%s ", buf);
X	fprintf(fi, ctl, arg);
X	putc('\n', fi);
X	fclose(fi);
X    }
X    if (how == EFATAL)
X	exit(1);
X}
X
SHAR_EOF
echo "extracting lib/Makefile"
sed 's/^X//' << \SHAR_EOF > lib/Makefile
X
X
Xdnetlib.o : dnetlib.c
X	cc -c dnetlib.c
X
SHAR_EOF
if `test ! -d server`
then
  mkdir server
  echo "mkdir server"
fi
echo "extracting server/sgcopy.c"
sed 's/^X//' << \SHAR_EOF > server/sgcopy.c
X
X/*
X *  SGCOPY.C	 V1.1
X *
X *  DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved.
X *
X *  GET-COPY SERVER	(NEW COPY SERVER)
X *
X *  The current version only accepts one connection at a time.	This server
X *  will send requested files to the remote machine.
X *
X *  length in 68000 longword format.
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/wait.h>
X#include <sys/time.h>
X#include <sys/dir.h>
X#include <sys/file.h>
X#include <sys/resource.h>
X#include <stdio.h>
X#include <errno.h>
X#include <signal.h>
X
X#include "servers.h"
X
Xtypedef struct {
X    char    Cmd;
X    char    Str[64];
X    long    Val;
X} HDR;
X
Xtypedef unsigned char ubyte;
Xchar *getnamepart();
Xchar *getdirpart();
X
Xchar Buf[4096];
Xint Chan;
X
Xchandler()
X{
X    union wait stat;
X    struct rusage rus;
X    while (wait3(&stat, WNOHANG, &rus) > 0);
X}
X
Xmain(ac,av)
Xchar *av[];
X{
X    long chann = DListen(PORT_GFILECOPY);
X    int fd;
X    int n;
X    char buf[256];
X    extern int errno;
X
X    if (av[1])
X	chdir(av[1]);
X    signal(SIGCHLD, chandler);
X    signal(SIGPIPE, SIG_IGN);
X    for (;;) {
X	fd = DAccept(chann);
X	if (fd < 0) {
X	    if (errno == EINTR)
X		continue;
X	    break;
X	}
X	if (fork() == NULL) {
X	    SGCopy(fd);
X	    _exit(1);
X	}
X	close(fd);
X    }
X    perror("SCOPY");
X}
X
XSGCopy(fd)
Xint fd;
X{
X    short error = 0;
X    static HDR Hdr;
X
X    Chan = fd;
X    error = WriteHeader('H', "Hello, GCopy server V1.30", 0);
X    if (error)
X	return(error);
X    switch(ReadHeader(&Hdr)) {
X    default:
X    case -1:
X	error = 1;
X	return(error);
X    case 'H':
X	break;
X    }
X    while (!error) {
X	switch(ReadHeader(&Hdr)) {
X	case 'G':
X	    {
X		char svdir[1024];
X		getwd(svdir);
X		if (chdir(getdirpart(Hdr.Str)) < 0) {
X		    error = WriteHeader('N', "Unable to cd to dir", 0);
X		} else {
X	    	    error = PutObject(getnamepart(Hdr.Str));
X		}
X		chdir(svdir);
X	    }
X	    break;
X	case 'E':
X	    goto done;
X	case 'P':   /*  put-files, not implemented  */
X	default:
X	    error = 1;
X	    break;
X	}
X    }
Xdone:
X    ;
X}
X
XPutObject(str)
Xchar *str;
X{
X    struct stat stat;
X    short error = 0;
X
X    if (lstat(str, &stat) < 0) {
X	error = WriteHeader('N', "Unable to find object", 0);
X	return(0);
X    }
X    if (stat.st_mode & S_IFDIR) {
X	error = PutDir(str);
X    } else {
X	error = PutFile(str);
X    }
X    return(0);
X}
X
XPutDir(name)
Xchar *name;
X{
X    struct stat stat;
X    char svdir[1024];
X    static HDR Hdr;
X    short error = 0;
X    char *fn = getnamepart(name);
X    DIR *dir;
X    struct direct *de;
X
X    if (lstat(name, &stat) < 0 || !(dir = opendir(name))) {
X	WriteHeader('N', "Possible Disk Error", 0);
X	error = 1;
X	goto done;
X    }
X    if (error = WriteHeader('D', fn, 0)) 
X	goto done;
X    switch(ReadHeader(&Hdr)) {
X    case 'Y':
X	break;
X    case 'S':
X	goto done;
X    case 'N':
X	error = 1;
X	break;
X    default:
X	error = 1;
X	break;
X    }
X    if (error)
X	goto done;
X
X    getwd(svdir);
X    if (chdir(name) < 0) {
X	error = 1;
X	WriteHeader('N', "unable to chdir", 0);
X    }
X    if (error)
X	goto done;
X
X    while (de = readdir(dir)) {
X	if (strcmp(de->d_name, ".") == 0)
X	    continue;
X	if (strcmp(de->d_name, "..") == 0)
X	    continue;
X	if (lstat(de->d_name, &stat) < 0) {
X	    continue;
X	}
X	if (stat.st_mode & S_IFDIR) {
X	    error = PutDir(de->d_name);
X	} else {
X	    error = PutFile(de->d_name);
X	}
X	if (error)
X	    break;
X    }
X    WriteHeader('E', NULL, 0);
X    chdir(svdir);
Xdone:
X    return(error);
X}
X
XPutFile(name)
Xchar *name;
X{
X    int fd = -1;
X    static HDR Hdr;
X    long len;
X    short error = 0;
X    char *fn = getnamepart(name);
X
X    fd = open(fn, O_RDONLY, 0);
X    if (fd < 0) {       /*  don't do anything if unable to open it */
X	WriteHeader('N', "file not readable", 0);
X	goto done;
X    }
X    len = lseek(fd, 0L, 2);
X    if (error = WriteHeader('F', fn, len))
X	goto done;
X    switch(ReadHeader(&Hdr)) {
X    case 'Y':
X	lseek(fd, Hdr.Val, 0);  /*  start pos.  */
X	len -= Hdr.Val;
X	if (len < 0)
X	    len = 0;
X	break;
X    case 'S':
X	goto done;
X    case 'N':
X	error = 1;
X	break;
X    default:
X	error = 1;
X	break;
X    }
X    if (error)
X	goto done;
X    while (len) {
X	register long n = (len > sizeof(Buf)) ? sizeof(Buf) : len;
X
X	if (read(fd, Buf, n) != n) {    /*  read failed! */
X	    error = 10;
X	    goto done;
X	}
X	if (gwrite(Chan, Buf, n) != n) {
X	    error = 10;
X	    goto done;
X	}
X	len -= n;
X    }
Xdone:
X    if (fd >= 0)
X	close(fd);
X    return(error);
X}
X
X
XWriteHeader(c, str, len)
Xchar c;
Xchar *str;
Xlong len;
X{
X    ubyte sl;
X
X    if (str == NULL)
X	str = "";
X    sl = strlen(str);
X
X    if (gwrite(Chan, &c, 1) < 0)
X	return(1);
X    if (gwrite(Chan, &sl,1) < 0)
X	return(1);
X    if (gwrite(Chan, str, sl) != sl)
X	return(1);
X    len = htonl68(len);
X    if (gwrite(Chan, &len, 4) != 4)
X	return(1);
X    return(0);
X}
X
XReadHeader(hdr)
XHDR *hdr;
X{
X    ubyte sl;
X    ubyte cmd;
X
X    hdr->Cmd = -1;
X    if (ggread(Chan, &cmd, 1) != 1)
X	return(-1);
X    if (ggread(Chan, &sl, 1) != 1)
X	return(-1);
X    if (sl >= sizeof(hdr->Str)) {
X	return(-1);
X    }
X    if (ggread(Chan, hdr->Str, sl) != sl)
X	return(-1);
X    hdr->Str[sl] = 0;
X    if (ggread(Chan, &hdr->Val, 4) != 4)
X	return(-1);
X    hdr->Val = ntohl68(hdr->Val);
X    hdr->Cmd = cmd;
X    return(hdr->Cmd);
X}
X
Xchar *
Xgetnamepart(str)
Xchar *str;
X{
X    register char *ptr = str + strlen(str);
X
X    while (ptr >= str) {
X	if (*ptr == '/')
X	    break;
X	--ptr;
X    }
X    return(ptr+1);
X}
X
Xchar *
Xgetdirpart(str)
Xchar *str;
X{
X    static char buf[1024];
X
X    strcpy(buf, str);
X    getnamepart(buf)[0] = 0;
X    if (buf[0] == 0)
X	return(".");
X    return(buf);
X}
SHAR_EOF
echo "extracting server/sshell.c"
sed 's/^X//' << \SHAR_EOF > server/sshell.c
X
X/*
X *  S_SHELL.C		OBSOLETE OBSOLETE OBSOLETE
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X *
X *   Connect a csh to a pseudo terminal pair... PORT_ALPHATERM
X *   NOTE!!  PORT_IALPHATERM (a pseudo-terminal csh) is also available
X *   through the FTERM client program on the Amiga side, and much faster
X *   since the server for PORT_IALPHATERM is DNET itself (one less process
X *   to go through).
X *
X *	-doesn't handle SIGWINCH
X *	-doesn't handle flow control ... don't cat any long files!
X */
X
X#include <stdio.h>
X#include <sys/wait.h>
X#include <sys/time.h>
X#include <sys/resource.h>
X#include <errno.h>
X#include <signal.h>
X
X#include "servers.h"
X
Xchandler()
X{
X    union wait stat;
X    struct rusage rus;
X    while (wait3(&stat, WNOHANG, &rus) > 0);
X}
X
Xmain(ac,av)
Xchar *av[];
X{
X    long chann = DListen(PORT_ALPHATERM);
X    int fd;
X    int n;
X    char buf[256];
X    extern int errno;
X
X    if (av[1])
X	chdir(av[1]);
X    signal(SIGCHLD, chandler);
X    signal(SIGPIPE, SIG_IGN);
X    for (;;) {
X	fd = DAccept(chann);
X	if (fd < 0) {
X	    if (errno == EINTR)
X		continue;
X	    break;
X	}
X	if (fork() == NULL) {
X	    dup2(fd, 0);
X	    dup2(fd, 1);
X	    dup2(fd, 2);
X	    close(fd);
X	    execl("/usr/ucb/rlogin", "rlogin", "localhost", NULL);
X	    exit(1);
X	}
X	close(fd);
X    }
X    perror("SSHELL");
X}
X
SHAR_EOF
echo "extracting server/sloadav.c"
sed 's/^X//' << \SHAR_EOF > server/sloadav.c
X
X/*
X *	SLOADAV.C
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X *
X *	Reports the load average every 5 minutes or until the connection
X *	is closed.
X */
X
X#include <stdio.h>
X#include <sys/wait.h>
X#include <sys/time.h>
X#include <sys/resource.h>
X#include <errno.h>
X#include <signal.h>
X
X#include "servers.h"
X
Xchandler()
X{
X    union wait stat;
X    struct rusage rus;
X    while (wait3(&stat, WNOHANG, &rus) > 0);
X}
X
Xmain(ac,av)
Xchar *av[];
X{
X    long chann = DListen(PORT_LOADAV);
X    int fd;
X    int n;
X    char buf[256];
X    extern int errno;
X
X    if (av[1])
X	chdir(av[1]);
X    signal(SIGCHLD, chandler);
X    signal(SIGPIPE, SIG_IGN);
X    for (;;) {
X	fd = DAccept(chann);
X	if (fd < 0) {
X	    if (errno == EINTR)
X		continue;
X	    break;
X	}
X	if (fork() == NULL) {
X	    do_loadav(fd);
X	    close(fd);
X	    _exit(1);
X	}
X	close(fd);
X    }
X    perror("SLOADAV");
X}
X
Xdo_loadav(fd)
X{
X    char dummy;
X    char buf[256];
X    FILE *fi;
X
X    while (ggread(fd, &dummy, 1) == 1) {
X	fi = popen("uptime", "r");
X	if (fi == NULL)
X	    break;
X	if (fgets(buf, 256, fi)) {
X	    dummy = strlen(buf);
X	    buf[dummy-1] = 0;
X	    gwrite(fd, &dummy, 1);
X	    gwrite(fd, buf, dummy);
X	}
X	if (ferror(fi))
X	    break;
X	pclose(fi);
X    }
X}
X
SHAR_EOF
echo "extracting server/scopy.c"
sed 's/^X//' << \SHAR_EOF > server/scopy.c
X
X/*
X *	SCOPY.C
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X *
X *	Remote file copy server (putfiles is the client program)
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/wait.h>
X#include <sys/time.h>
X#include <sys/file.h>
X#include <sys/resource.h>
X#include <stdio.h>
X#include <errno.h>
X#include <signal.h>
X
X#include "servers.h"
X#include "../lib/dnetlib.h"
X
Xchar Buf[4096];
X
Xchandler()
X{
X    union wait stat;
X    struct rusage rus;
X    while (wait3(&stat, WNOHANG, &rus) > 0);
X}
X
Xmain(ac,av)
Xchar *av[];
X{
X    long chann = DListen(PORT_FILECOPY);
X    int fd;
X    int n;
X    char buf[256];
X    extern int errno;
X
X    elog(EDEBUG, "SCOPY START", 0);
X    if (av[1])
X        chdir(av[1]);
X    signal(SIGCHLD, chandler);
X    signal(SIGPIPE, SIG_IGN);
X    for (;;) {
X	fd = DAccept(chann);
X	if (fd < 0) {
X	    if (errno == EINTR)
X		continue;
X	    break;
X	}
X        elog(EDEBUG, "SCOPY CONNECT", 0);
X	if (fork() == NULL) {
X	    putdir(fd, "."); 
X	    _exit(1);
X	}
X	close(fd);
X    }
X    perror("SCOPY");
X}
X
Xputdir(chan, dirname)
Xchar *dirname;
X{
X    struct stat stat;
X    char olddir[256];
X    char co, nl, name[128];
X    long len;
X    int ret = -1;
X
X    getwd(olddir);
X    if (lstat(dirname, &stat) >= 0 && !(stat.st_mode & S_IFDIR)) {
X	char rc = 'N';
X	gwrite(chan, &rc, 1);
X	elog(EWARN, "SCOPY: Unable to cd to dir '%s'", dirname);
X	return(1);
X    }
X    if (chdir(dirname) < 0) {
X	if (mkdir(dirname, 0777) < 0 || chdir(dirname) < 0) {
X	    char rc = 'N';
X	    elog(EWARN, "SCOPY: Unable to create directory '%s'", dirname);
X	    gwrite(chan, &rc, 1);
X	    return(1);
X	}
X    }
X    co = 'Y';
X    gwrite(chan, &co, 1);
X    while (ggread(chan, &co, 1) == 1) {
X	if (ggread(chan, &nl, 1) != 1 || ggread(chan, name, nl) != nl)
X	    break;
X	if (ggread(chan, &len, 4) != 4)
X	    break;
X	len = ntohl68(len);
X	switch(co) {
X	case 'C':
X	    co = 'Y';
X    	    if (chdir(name) < 0) {
X		if (mkdir(name, 0777) < 0 || chdir(name) < 0)  {
X		    co = 'N';
X	            elog(EWARN, "SCOPY: Unable to create directory '%s'", 
X			dirname);
X		}
X	    }
X	    gwrite(chan, &co, 1);
X	    break;
X	case 'W':
X	    if (putfile(chan, name, len) < 0) {
X		ret = -1;
X		elog(EWARN, "SCOPY: Failure on file %.*s", len, name);
X		goto fail;
X	    }
X	    break;
X	case 'X':
X	    if (putdir(chan, name) < 0) {
X		ret = -1;
X		goto fail;
X	    }
X	    break;
X	case 'Y':
X	    ret = 1;
X	    co = 'Y';
X	    gwrite(chan, &co, 1);
X	    goto fail;
X	default:
X	    co = 'N';
X	    gwrite(chan, &co, 1);
X	    break;
X	}
X    }
Xfail:
X    chdir(olddir);
X    return(ret);
X}
X
Xputfile(chan, name, len)
Xchar *name;
X{
X    long fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0666);
X    long n, r;
X    char rc;
X
X    if (fd < 0) {
X	rc = 'N';
X	gwrite(chan, &rc, 1);
X	return(0);
X    }
X    rc = 'Y';
X    gwrite(chan, &rc, 1);
X    while (len) {
X	r = (len > sizeof(Buf)) ? sizeof(Buf) : len;
X	n = ggread(chan, Buf, r);
X	if (n != r)
X	    break;
X        if (write(fd, Buf, n) != n)
X	    break;
X	len -= n;
X    }
X    close(fd);
X    if (len) {
X	unlink(name);
X	return(-1);
X    }
X    rc = 'Y';
X    gwrite(chan, &rc, 1);
X    return(0);
X}
X
SHAR_EOF
echo "extracting server/Makefile"
sed 's/^X//' << \SHAR_EOF > server/Makefile
X
X#	DNET SERVERS
X#
X#
X#	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X
XNETLIB = ../lib/dnetlib.o
XBIN = ../bin
X
X
Xall:	$(NETLIB) $(BIN)/scopy $(BIN)/sshell \
X	$(BIN)/sloadav $(BIN)/sgcopy
X
X$(BIN)/scopy:		scopy.o
X	cc scopy.o $(NETLIB) -o $(BIN)/scopy
X
X$(BIN)/sgcopy:		sgcopy.o
X	cc sgcopy.o $(NETLIB) -o $(BIN)/sgcopy
X
X$(BIN)/sshell:    	sshell.o
X	cc sshell.o $(NETLIB) -o $(BIN)/sshell
X
X$(BIN)/sloadav:    	sloadav.o
X	cc sloadav.o $(NETLIB) -o $(BIN)/sloadav
X
Xclean:
X	rm -f *.o make.out
X
SHAR_EOF
echo "extracting server/servers.h"
sed 's/^X//' << \SHAR_EOF > server/servers.h
X
X/*
X *  SERVERS.H
X *
X *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
X */
X
X#define PORT_FILECOPY	8192
X#define PORT_ALPHATERM	8193
X#define PORT_ECHO	8194
X#define PORT_IALPHATERM 8195
X#define PORT_AMIGATERM	8195
X#define PORT_AMIGASHELL 8196
X#define PORT_LOADAV	8197
X#define PORT_PRINTER	8198
X#define PORT_PASSWD	8199
X#define PORT_BBS	8200
X#define PORT_GFILECOPY	8201
X
X
SHAR_EOF
echo "End of archive 2 (of 2)"
# if you want to concatenate archives, remove anything after this line
exit