
/*
 *  SUBS.C
 *
 *  DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved.
 *
 */

#include "dnet.h"
#include <stdio.h>
#ifdef LATTICE
#include <dos.h>
#endif

#ifndef LATTICE
char *FakeArgs;
long  FakeLen;
long  FakeGo;
long  FakeCLI;

extern char *malloc();
#endif

void dneterror();

typedef struct CommandLineInterface CLI;

/*
 *  Add the high-level command to the queue of commands to be sent to
 *  the remote DNET.
 */

void
WriteStream(sdcmd, buf, len, chan)
int sdcmd;
void *buf;
int len;
uword chan;
{
    IOSTD *ior = AllocMem(sizeof(IOSTD), MEMF_PUBLIC);

    if (DDebug)
	printf("WriteStream: cmd %ld  buflen %ld  chan %ld\n", sdcmd, len, chan);
    ior->io_Message.mn_Node.ln_Name = (char *)PKT_REQ;
    ior->io_Message.mn_ReplyPort = IOSink;
    ior->io_Data = (APTR)AllocMem(len, MEMF_PUBLIC);
    ior->io_Length = len;
    ior->io_Actual = 0;
    ior->io_Command = sdcmd;
    ior->io_Error = 0;
    ior->io_Message.mn_Node.ln_Pri = (chan > MAXCHAN) ? 126 : Chan[chan].pri;
    BMov(buf, ior->io_Data, len);
    Enqueue(&TxList, (NODE *)ior);
    do_wupdate();
}

/*
 *  Send a packet to a port.  Used to send data/eof/close to channel
 *  ports, and to relay open requests to server ports.
 */

void
WritePort(port, scmd, buf, len, rettype, unit)
PORT *port;
int scmd;
void *buf;
int len;
int rettype;
int unit;
{
    IOSTD *ior = AllocMem(sizeof(IOSTD), MEMF_PUBLIC);

    if (DDebug)
	printf("WritePort: cmd %ld  buflen %ld  rt %ld unit %ld\n", scmd, len, rettype, unit);
    ior->io_Message.mn_Node.ln_Name = (char *)rettype;
    ior->io_Message.mn_ReplyPort = (rettype) ? IOSink : DNetPort;
    ior->io_Unit = (struct Unit *)unit;
    ior->io_Command = scmd;
    ior->io_Error = 0;
    ior->io_Data  = NULL;
    ior->io_Length = 0;
    ior->io_Actual = 0;
    if (buf) {
	ior->io_Data = (APTR)AllocMem(len, MEMF_PUBLIC);
	ior->io_Length = len;
	BMov(buf, ior->io_Data, len);
    }
    PutMsg(port, (MSG *)ior);
    if (rettype == PKT_REQ)
	++NumCon;
}

/*
 *  ALLOC_CHANNEL()
 *
 *	Allocate a channel.  Starting at a random point, find the first
 *	free channel.  Return -1 if no free channels found.
 */

alloc_channel()
{
    static ulong ran = 13;
    long stamp[3];
    uword i;

    DateStamp(stamp);
    ran = ((ran * 13) + 1) ^ (ran >> 9) + stamp[0] + stamp[1] + stamp[2];
    for (i = ran % MAXCHAN; i < MAXCHAN; ++i) {
	if (Chan[i].state == 0) {
	    return((int)i);
	}
    }
    for (i = ran % MAXCHAN; i != 0xFFFF; --i) {  /*  Fixed 28 September 1988 */
	if (Chan[i].state == 0) {
	    return((int)i);
	}
    }
    return(-1);
}

/*
 *  Get next node in a linked list.  If a pointer to a list base
 *  is passed, gets the first node in the linked list.	Returns
 *  NULL if no further nodes in the list.
 */

GetNext(node)
NODE *node;
{
    NODE *next = node->ln_Succ;
    if (*(long *)next)
	return((long)next);
    return(NULL);
}

/*
 *  CHKBUF
 *
 *  Generate a two-byte checksum for data.  Uses a very simple, but
 *  effective algorithm.
 */

chkbuf(buf, bytes)
ubyte *buf;
uword bytes;
{
    uword i;
    ubyte c1,c2;

    for (i = c1 = c2 = 0; i < bytes; ++i) {
	c1 += buf[i];
	c2 += c1;
    }
    c1 = -(c1 + c2);
    return((c1<<8)|c2);
}

void
TimerOpen(req, sink)
IOT *req;
PORT *sink;
{
    if (OpenDevice("timer.device", UNIT_VBLANK, (IOR *)req, 0))
	dneterror("timer.device");
    req->tr_node.io_Message.mn_ReplyPort = sink;
    req->tr_node.io_Command = TR_ADDREQUEST;
}

void
TimerClose(req)
IOT *req;
{
    CloseDevice((IOR *)req);
    req->tr_node.io_Device = NULL;
}


/*
 *  RUNSERVER
 *
 *	Search the file [S:]DNET.SERVERS for the specified server and
 *	attempt to LoadSeg()/CreateProc() it.  The port number must be
 *	non-zero (allows commening out lines in DNET.SERVERS), and
 *	regardless of whether the server is able to DListen(), it must
 *	perform a small handshake sequence with DNET.
 */

#ifdef LATTICE

static struct ProcID *SvBase;

void
InitServers()
{
    SvBase = NULL;
}

void
RunServer(portnum)
uword portnum;
{
    FILE *fi;
    char buf[128];
    char s1[64], s2[64];
    long v1;

    if (!portnum)
	return;
    if ((fi = fopen("dnet.servers", "r")) == NULL)
	fi = fopen("s:dnet.servers", "r");
    if (fi) {
	while (fgets(buf, 128, fi)) {
	    if (sscanf(buf, "%ld %s %s", &v1, s1, s2) == 3 && v1 == portnum)
		goto success;
	}
	fclose(fi);
    }
    return;
success:
    fclose(fi);

    {
	PROC	*myproc;
	MSG	msg;
	struct ProcID *pid = malloc(sizeof(struct ProcID));
	int error;

	BZero(pid, sizeof(*pid));
	error = forkl(s1, "__dnet", "__dnet", NULL, NULL, pid);

	if (error < 0) {
	    printf("Server not found: %s\n", s1);
	    free(pid);
	} else {
	    myproc = (PROC *)FindTask(NULL);

	    msg.mn_ReplyPort = &myproc->pr_MsgPort;
	    msg.mn_Node.ln_Name = s2;
	    printf("Starting Server: %s proc %08lx  ", s1, pid->process);
	    fflush(stdout);
	    PutMsg(&pid->process->pr_MsgPort, &msg);       /*  startup handshake */
	    WaitPort(&myproc->pr_MsgPort);
	    (void)GetMsg(&myproc->pr_MsgPort);
	    puts("started");
	    fflush(stdout);
	    pid->nextID = SvBase;
	    SvBase = pid;
	}
    }
}

void
DeleteServers()
{
    struct ProcID *pid, *pn;

    for (pid = SvBase; pid; pid = pn) {
	pn = pid->nextID;
	for (;;) {
	    Signal((TASK *)pid->process, SIGBREAKF_CTRL_C);
	    printf("Waiting for pid %08lx to die\n", pid->process);
	    if (CheckPort(pid->parent))
		break;
	    Delay(10);
	}
	wait(pid);
	free(pid);
    }
}

#else

static MLIST SvList;

void
InitServers()
{
    NewList(&SvList);
}

void
RunServer(portnum)
uword portnum;
{
    FILE *fi;
    char buf[128];
    char s1[64], s2[64];
    long v1;
    long seg;

    if (!portnum)
	return;
    if ((fi = fopen("dnet.servers", "r")) == NULL)
	fi = fopen("s:dnet.servers", "r");
    if (fi) {
	while (fgets(buf, 128, fi)) {
	    if (sscanf(buf, "%ld %s %s", &v1, s1, s2) == 3 && v1 == portnum)
		goto success;
	}
	fclose(fi);
    }
    return;
success:
    fclose(fi);

    if (seg = (long)LoadSeg(s1)) {
	PNODE	*pnode;
	PROC	*myproc;
	PROC	*proc;
	char	*name;
	BSTR	oldclicmd;
	CLI	*cli;
	char	*fakestr;

	MSG	msg;
	void	fakeseg();

	printf("Addr: %08lx\n", seg);

	myproc = (PROC *)FindTask(NULL);
	pnode = AllocMem(sizeof(PNODE), MEMF_PUBLIC);
	sprintf(pnode->name, "DNET.SERVER.%ld", portnum);
	pnode->seg = seg;

	FakeArgs= "__dnet";
	FakeLen = 6;
	FakeGo	= ((long)pnode->seg << 2) + 4;
	FakeCLI = (long)myproc->pr_CLI;

	printf("Addr: %08lx %08lx\n", seg, FakeGo);

	fakestr = malloc(32);
	strcpy(fakestr, "\006__dnet");
	cli = BTOC(FakeCLI);
	oldclicmd = cli->cli_CommandName;
	cli->cli_CommandName = CTOB(fakestr);

	if (proc = CreateProc(pnode->name, 0, (long)fakeseg >> 2, 4096)) {
	    proc = (PROC *)((char *)proc - OFFSET(proc,pr_MsgPort));
	    msg.mn_ReplyPort = &myproc->pr_MsgPort;
	    msg.mn_Node.ln_Name = s2;
	    printf("Starting Server: %s %s\n", s1, pnode->name);
	    PutMsg(&proc->pr_MsgPort, &msg);        /*  startup handshake */
	    WaitPort(&myproc->pr_MsgPort);
	    (void)GetMsg(&myproc->pr_MsgPort);
	    AddTail(&SvList, pnode);
	} else {
	    printf("Unable to start server: %s %s\n", s1, pnode->name);
	    FreeMem(pnode, sizeof(PNODE));
	}
	cli->cli_CommandName = oldclicmd;
	free(fakestr);
    } else {
	printf("Server not found: %s\n", s1);
    }
}

void
DeleteServers()
{
    PNODE *pnode;
    PROC *proc;
    while (pnode = (PNODE *)RemHead(&SvList)) {
	Forbid();
	while (proc = (PROC *)FindTask(pnode->name)) {
	    Signal(proc, SIGBREAKF_CTRL_C);
	    Permit();
	    printf("Waiting for server %s to exit\n", pnode->name);
	    Forbid();
	}
	Permit();
	UnLoadSeg(pnode->seg);
	FreeMem(pnode, sizeof(PNODE));
    }
}

#asm

	INCLUDE "exec/types.i"
	INCLUDE "exec/alerts.i"
	INCLUDE "exec/nodes.i"
	INCLUDE "exec/lists.i"
	INCLUDE "exec/ports.i"
	INCLUDE "exec/libraries.i"
	INCLUDE "exec/tasks.i"

	INCLUDE "exec/memory.i"
	INCLUDE "exec/execbase.i"
	INCLUDE "libraries/dos.i"
	INCLUDE "libraries/dosextens.i"


	public	_geta4
	public	_LVOFindTask

	nop
	nop
	nop
	nop
_fakeseg:
	nop
	nop
	nop
	nop

	bsr	_geta4
	move.l	4,A6
	sub.l	A1,A1
	jsr	_LVOFindTask(A6)
	move.l	D0,A0
	move.l	_FakeCLI,pr_CLI(A0)     ; dummy CLI so Lattice startup doesn't
					; croak thinking its a workbench process
	move.l	_FakeArgs,A0
	move.l	_FakeLen,D0
	move.l	_FakeGo,A1
	jmp	(A1)

#endasm

#endif

/*
 *  This cleans up as best as possible, but if there are still ACTIVE
 *  servers or any CLIENTS, this call will not completely get rid of
 *  them...
 *
 *  Note that this call will KILL any servers automatically started up
 *  by DNET and then UnLoadSeg() them.  If the server has already exited,
 *  it simply unloads the server's segment.
 */

void
dneterror(str)
char *str;
{
    short i;
    if (str)
	puts(str);
    if (DNetPort) {
	IOSTD *ior;
	Forbid();
	while ((ior = (IOSTD *)RemHead(&TxList)) || (ior = (IOSTD *)GetMsg(DNetPort))) {
	    ior->io_Error = -1;
	    ReplyMsg((MSG *)ior);
	}
	DeletePort(DNetPort);
	Permit();
	DNetPort = NULL;
    }
    if (Wto.tr_node.io_Device) {
	if (Wto_act) {
	    AbortIO((IOR *)&Wto);
	    WaitIO((IOR *)&Wto);
	    Wto_act = 0;
	}
	TimerClose(&Wto);
    }
    if (Rto.tr_node.io_Device) {
	if (Rto_act) {
	    AbortIO((IOR *)&Rto);
	    WaitIO((IOR *)&Rto);
	    Rto_act = 0;
	}
	TimerClose(&Rto);
    }
    if (Cto.tr_node.io_Device) {
	if (Cto_act) {
	    AbortIO((IOR *)&Cto);
	    WaitIO((IOR *)&Cto);
	    Cto_act = 0;
	}
	TimerClose(&Cto);
    }

    NetClose();
    if (IOSink)
	DeletePort(IOSink);
    IOSink = NULL;
    for (i = 0; i < MAXCHAN; ++i) {
	if (Chan[i].state != CHAN_FREE && Chan[i].ior) {
	    Chan[i].ior->io_Error = -1;
	    Chan[i].state = CHAN_FREE;
	    ReplyMsg((MSG *)Chan[i].ior);
	}
    }
    DeleteServers();

    /*
    if (IPCPort)
	CloseIPC(IPCPort);
    if (DResBase)
	CloseLibrary(DResBase);
    */
    exit(1);
}

void
fixsignal(port)
PORT *port;
{
    if ((long)port->mp_MsgList.lh_Head != (long)&port->mp_MsgList.lh_Tail)
	Signal(port->mp_SigTask, 1 << port->mp_SigBit);
}

void
SetTimeouts(baud)
{
    WTimeoutVal = ((MAXPKT * 2) * 1000 / (baud / 10 + 1));
    RTimeoutVal = ((MAXPKT + 50) * 1000 / (baud / 10 + 1));
    WTimeoutVal *= 1000;
    RTimeoutVal *= 1000;
}

