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

#include "dnet.h"

/*
 *   WRITESTREAM()
 *
 *	Queues new SCMD_?? level commands to be sent
 */

WriteStream(sdcmd, buf, len, chan)
ubyte *buf;
uword chan;
{
    register XIOR *ior = (XIOR *)malloc(sizeof(XIOR));

    if (DDebug)
	fprintf(stderr, "**SEND MPX CMD %ld (%ld bytes on channel %ld)\n", 
	    sdcmd, len, chan
	);

    ior->io_Data = (ubyte *)malloc(len);
    ior->io_Length = len;
    ior->io_Actual = 0;
    ior->io_Command = sdcmd;
    ior->io_Error = 0;
    ior->io_Channel = chan;
    ior->io_Pri = (chan > MAXCHAN) ? 126 : Chan[chan].pri;
    bcopy(buf, ior->io_Data, len);
    Enqueue(&TxList, ior);
    do_wupdate();
}

/*
 *  ALLOC_CHANNEL()
 *
 *	Allocate a free channel.  Used in SCMD_OPEN and SCMD_ACKCMD
 */

alloc_channel()
{
    static ulong ran = 13;
    register uword i;

    ran = ((ran * 13) + 1) ^ (ran >> 9) + time(0);
    for (i = ran % MAXCHAN; i < MAXCHAN; ++i) {
	if (Chan[i].state == 0)
	    return(i);
    }
    for (i = ran % MAXCHAN; i < MAXCHAN; --i) {
	if (Chan[i].state == 0)
	    return(i);
    }
    return(-1);
}

/*
 *    Remove all nodes with the given channel ID.
 */

ClearChan(list, chan, all)
LIST *list;
uword chan;
{
    register XIOR *io, *in;

    for (io = (XIOR *)list->lh_Head; io != (XIOR *)&list->lh_Tail; io = in) {
	in = (XIOR *)io->io_Node.ln_Succ;
	if (io->io_Channel == chan) {
	    if (all || io->io_Command == SCMD_DATA) {
	        io->io_Node.ln_Succ->ln_Pred = io->io_Node.ln_Pred;
	        io->io_Node.ln_Pred->ln_Succ = io->io_Node.ln_Succ;
	        free(io->io_Data);
	        free(io);
	    }
	}
    }
}

/*
 *  Queue a packet into a prioritized list.  FIFO is retained for packets
 *  of the same priority.  This implements one level of channel priorities,
 *  before the packets actually get queued to the network.  Since up to
 *  4 packets might be queued (200 * 4 = 800 bytes of data or 4 seconds @ 
 *  2400 baud), a second level of prioritization will also reduce the
 *  physical packet size when two channels at relatively large differing
 *  priorities are in use.
 *
 *	These and other list routines compatible with Amiga list routines.
 */

Enqueue(list, ior)
LIST *list;
XIOR *ior;
{
    register XIOR *io;
    char pri = ior->io_Pri;

    io = (XIOR *)list->lh_Head;
    while (io != (XIOR *)&list->lh_Tail) {
	if (pri > io->io_Pri)
	    break;
	io = (XIOR *)io->io_Node.ln_Succ;
    }
    ior->io_Node.ln_Succ = (NODE *)io;
    ior->io_Node.ln_Pred = io->io_Node.ln_Pred;
    ior->io_Node.ln_Succ->ln_Pred = (NODE *)ior;
    ior->io_Node.ln_Pred->ln_Succ = (NODE *)ior;
}

AddTail(list, node)
LIST *list;
NODE *node;
{
    node->ln_Succ = (NODE *)&list->lh_Tail;
    node->ln_Pred = list->lh_TailPred;
    node->ln_Succ->ln_Pred = node;
    node->ln_Pred->ln_Succ = node;
}

AddHead(list, node)
LIST *list;
NODE *node;
{
    node->ln_Succ = list->lh_Head;
    node->ln_Pred = (NODE *)list;
    node->ln_Succ->ln_Pred = node;
    node->ln_Pred->ln_Succ = node;
}

ubyte *
RemHead(list)
LIST *list;
{
    NODE *node;

    node = list->lh_Head;
    if (node->ln_Succ == NULL)
	return(NULL);
    node->ln_Succ->ln_Pred = node->ln_Pred;
    node->ln_Pred->ln_Succ = node->ln_Succ;
    return((ubyte *)node);
}

NewList(list)
LIST *list;
{
    list->lh_Head = (NODE *)&list->lh_Tail;
    list->lh_Tail = NULL;
    list->lh_TailPred = (NODE *)&list->lh_Head;
}

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

/*
 *  CHKBUF
 *
 *	Checksum a buffer.  Uses a simple, but supposedly very good 
 *	scheme.
 */

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

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

/*
 *   Write timeout signal handler.
 */

sigwto()
{
    WTimedout = 1;
    Wto_act = 0;
}

TimerOpen()
{
    static struct sigvec SA = { sigwto, 0, 0 };
    sigvec(SIGALRM, &SA, NULL);
}

TimerClose()
{
    signal(SIGALRM, SIG_IGN);
}

WTimeout(us)
{
    static struct itimerval itv;
    struct itimerval ov;
    long mask;

    itv.it_value.tv_sec = us / 1000000;
    itv.it_value.tv_usec= (us % 1000000);

    mask = sigblock(sigmask(SIGALRM));
    setitimer(ITIMER_REAL, &itv, &ov);
    Wto_act = 1;
    WTimedout = 0;
    sigsetmask(mask);
    if (DDebug)
	fprintf(stderr, "WTimeout set\n");
}

dneterror(str)
char *str;
{
    register short i;

    NetClose();
    TimerClose();
    exit(1);
}

/*
 *    setenv(name, str).  name must be of the form "NAME="
 */

setenv(name, str)
char *name;
char *str;
{
    extern char **environ;
    static char **elist;
    static int elen;
    char *ptr;
    int i, len;

    len = strlen(name);
    if (elist == NULL) {
	for (i = 0; environ[i]; ++i);
	elist = (char **)malloc((i+3)*sizeof(char *));
	elen = i + 3;
	bcopy(environ, elist, i*sizeof(char *));
	environ = elist;
    }
    for (i = 0; elist[i]; ++i) {
	if (strncmp(elist[i], name, len) == 0)
	    break;
    }
    if (i == elen) {
	elen += 4;
	elist = environ = (char **)realloc(elist, elen*sizeof(char *));
    }
    ptr = (char *)malloc(len + strlen(str) + 1);
    sprintf(ptr, "%s%s", name, str);
    if (elist[i]) {
	elist[i] = ptr;
    } else {
	elist[i] = ptr;
	elist[i+1] = NULL;
	elen = i + 1;
    }
}

void
startserver(port)
uword port;
{
    char *dir = (getenv("DNETDIR"))?getenv("DNETDIR"):"./";
    char buf[256];
    char path[128];
    char cdir[128];
    long portno;
    FILE *fi;

    sprintf(buf, "%sdnet.servers", dir);
    if (!port || (fi = fopen(buf, "r")) == NULL)
	return;
    while (fgets(buf, 256, fi)) {
	if (sscanf(buf, "%ld %s %s", &portno, path, cdir) == 3) {
	    if (portno == port) {
		if (!fork()) {
	    	    int i;
		    fclose(fi);
		    /*
		    if (chdir(cdir) < 0) {
			fprintf(stderr, "Unable to set server dir %s\n", cdir);
			_exit(1);
		    }
		    */
	    	    setuid(getuid());
	    	    signal(SIGHUP, SIG_DFL);
	    	    signal(SIGINT, SIG_DFL);
	    	    signal(SIGQUIT, SIG_DFL);
	    	    signal(SIGTERM, SIG_DFL);
	    	    signal(SIGCHLD, SIG_DFL);
	    	    signal(SIGTSTP, SIG_IGN);
	    	    ioctl(open("/dev/tty", 2), TIOCNOTTY, NULL);
		    i = open("/dev/null", O_RDWR, 0);
		    dup2(i, 0);
		    dup2(i, 1);
	    	    for (i = 3; i < 256; ++i)
		        close(i);
		    sprintf(buf, "server.%ld.%ld", port, getuid());
		    execl(path, buf, cdir, NULL);
		    fprintf(stderr, "Unable to exec server: %s\n", path);
		    _exit(1);
		}
		sleep(4);    /* is a hack */
		break;
	    }
	}
    }
    fclose(fi);
}

