/* ScianNetDaemon.c John R. Murray 1-19-92
 */

/* BSD_SIGS or SYSV_SIGS */

#ifdef PAUSE
#ifdef BSD_SIGS
#define _BSD_SIGNALS
#endif
#include <signal.h>
#include <stropts.h>
#endif /* def PAUSE */

#include "machine.h"

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>

#if MACHINE == IRIS4D
#include <sys/prctl.h>
#endif

#if MACHINE == CONVEX
#include <time.h>
#endif

#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <string.h>
#include <sys/times.h>
#include <sys/param.h>

#if MACHINE == CRAYYMP
#include <time.h>
#endif

#include "ScianNetDaemon.h"

#define PERROR

/* to avoid having to include lots of Scian include files */
#define ICONUNKNOWN     61

/* if you don't know how to pronounce "Daemon", listen to the beginning of
 * the first song on the first side of _Locust Abortion Technician_ by
 * the Butthole Surfers. Really loud. Really, really loud.
 */

#define true 1
#define false 0

/*Values for socketState*/
#define NOTLISTENING	0	/*Not even listening for a connection*/
#define LISTENING	1	/*Socket is listening for a connection*/
#define VALIDATING	2	/*Socket is connected*/
#define CONNECTED	3	/*Have gotten a good initial inquiry*/
#define TRANSFERRING	4	/*transferring connection to a scian process*/

#define NULLFILED	-1

#define MYBUFSIZ 1024		/* size of local stream buffers */

#define IsMainConn(c) ((c) == mainConn)
/* #define IsMainConn(c) ((c)->sockNum == baseSocketNumber)
*/

int lasterror;				/* tracks last system error */

#define ATTENTION_SPAN 30		/* start napping if  < idle time */
#define DEFAULT_TIME_OUT (60 * 60)	/* default timeout one hour */

typedef double Time;
/* timeOut = 0 means never time out */
Time timeOut = DEFAULT_TIME_OUT;	/* no-activity timeout in seconds. */
struct tms timesBuffer;			/* dummy for times() calls */

typedef struct ConnStruct {
    int sockNum;		/* socket number this one is connected to */
    int template;		/* socket template file descriptor */
    int sock;			/* socket file descriptor */
    int state;			/* state of socket */
    int flags;			/* informational bits */
    char buf[MYBUFSIZ];		/* stream input buffer */
    int bufi;			/* index into stream buffer */
    struct ConnStruct *next;
} *ConnPtr;

typedef struct SLStruct {
    /* int sockNum; */
    ConnPtr conn;
    struct SLStruct *next;
} *SockListPtr;

typedef struct ORStruct {
    unsigned long objectNum;
    struct ORStruct *next;
    int iconNum;
    char objectName[1];
} *ObjRecPtr;

typedef struct PStruct {
    int procNum;
    int sockNum;		/* process is listening on sockNum */
    Time lastAccess;
    ObjRecPtr objectList;
    SockListPtr keepAdvised;
    struct PStruct *next;
    char procName[1];
} ProcessStruct, *ProcessPtr;

ProcessPtr processes;

char tempStr[MYBUFSIZ];		/* temp Str. */

#define ND_FLAGS_KEEPADVISED 0x01
#define ND_FLAGS_STREAMERROR 0x80

int socketBlockLength;	/* how many socket numbers to try */
int baseSocketNumber;	/* port number that connections come in on */

int lastPort;		/* FindSocket keeps track of last socket number tried */

ConnPtr mainConn = (ConnPtr) 0;	/* primary listener */
ConnPtr connList = (ConnPtr) 0;	/* list of secondary connections */
int proto;			/* protocol num. returned by getprotobyname()*/

#ifdef PAUSE
/* SIGIO signal handler */

int handlerCount = 0;
int oldHandlerCount = 0;

sigIOHandler()
{
    ++handlerCount;
#ifdef SYSV_SIGS
    signal(SIGIO, sigIOHandler);
#endif
}
#endif

#ifdef ALARM
int alarmClock = 0;
sigAlrmHandler()
{
    ++alarmClock;
#ifdef SYSV_SIGS
    signal(SIGALRM, sigAlrmHandler);
#endif
}
#endif

int writen(fd, cptr, nbytes)
int fd;
char *cptr;
int nbytes;
{
    int nleft, nwritten;

    nleft = nbytes;
    while (nleft > 0)
    {
	nwritten = write(fd, cptr, nleft);
	if (nwritten < 0)
	{
	    perror("Uh-oh! writen");
	    return nwritten;
	}
	nleft -= nwritten;
	cptr += nwritten;
    }
}

/*NewConn: creates a new connection record from the stream associated with it*/
ConnPtr NewConn()
{
    ConnPtr retVal;

    retVal = malloc(sizeof(struct ConnStruct));
    retVal -> bufi = 0;
    retVal -> state = NOTLISTENING;
    retVal -> flags = 0;
    retVal -> sockNum = 0;
    retVal -> next = (struct ConnStruct *) 0;
    return retVal;
}

int FindSocket(c)
/* FindSocket looks for an open port. It returns 0 if successful, -1
 * if failed. If the port number in the connection record (c->sockNum) is
 * non-zero, FindSocket tries to open that port, and fails if it's not
 * available. If c->sockNum is zero, FindSocket will search the possible
 * port numbers until it successfully binds one.
 */
ConnPtr c;
{
    struct sockaddr_in templateAddr;	/*Socket template for listening*/
    int any_port;
    int first_tried;
    int status;

    any_port = true;

    /* sanity check */
    if (c->state != NOTLISTENING)
    {
	fprintf(stderr, "FindSocket sanity check! Bad state %d\n", c->state);
	return -1;
    }

    /* initialize */
    if (c->sockNum != 0)
    {
	templateAddr . sin_port = htons((u_short) c->sockNum);
	first_tried = c->sockNum;
	any_port = false;
    }
    else
    {
	if (++lastPort >= baseSocketNumber + socketBlockLength)
	    lastPort = baseSocketNumber + 1;
	templateAddr . sin_port = htons((u_short) lastPort);
	first_tried = lastPort;
	c->sockNum = lastPort;
    }
    templateAddr . sin_family = PF_INET;
    templateAddr . sin_addr.s_addr = INADDR_ANY;

    c->template = socket(PF_INET, SOCK_STREAM, proto);
    if (c->template < 0)
    {
	{
#ifdef PERROR
	    perror("socket() in FindSocket");
#endif
	    if (any_port) c->sockNum = 0;
	    return -1;
	}
    }

#ifdef PAUSE
    /* ioctl(c->template, I_SETSIG, S_INPUT); */
    fcntl(c->template, F_SETOWN, getpid());
    fcntl(c->template, F_SETFL, fcntl(c->template, F_GETFL) | FASYNC);
    if (!(fcntl(c->template, F_GETFL) & FASYNC))
	fprintf(stderr, "template flag not set, Bub!\n");
#endif

    status = -1;
    while (status < 0)
    {
#ifdef DEBUG
	fprintf(stderr, "trying to bind socket %d\n", ntohs(templateAddr.sin_port));
#endif
	status = bind(c->template, &templateAddr, sizeof(templateAddr));
	if (status >= 0)
	{
	    /*Name was bound OK*/

	    /*Set socket to be non blocking*/
#if CRAYYMP
	    fcntl(c->template, F_SETFL, 
		fcntl(c->template, F_GETFL) | O_NDELAY);
	    fcntl(c->template, F_SETFL, 
		fcntl(c->template, F_GETFL) | O_NONBLOCK);
#else
	    fcntl(c->template, F_SETFL, 
		fcntl(c->template, F_GETFL) | FNDELAY);
#endif

	    /*Listen for a maximum of 5 connections*/
	    if (listen(c->template, 5) < 0)
	    {
#ifdef PERROR
		perror("listen() in FindSocket");
#endif
		if (any_port) c->sockNum = 0;
		close (c->template); c->template = -1;
		return -1;
	    }
	}
	else
	{
	    if (any_port && (errno == EADDRINUSE))
	    {
		if (++lastPort >= baseSocketNumber+socketBlockLength)
		    lastPort = baseSocketNumber + 1;
		templateAddr . sin_port = htons((u_short) lastPort);
		c->sockNum = lastPort;
		if (lastPort == first_tried)
		{
		    /*went all the way through the block without finding one*/
		    fprintf(stderr, "No sockets open!\n");
		    if (any_port) c->sockNum = 0;
		    close (c->template); c->template = -1;
		    return -1;
		}
	    }
	    else
	    {
#ifdef PERROR
		perror("bind() call failed in FindSocket");
#endif
		if (any_port) c->sockNum = 0;
		close (c->template); c->template = -1;
		return -1;
	    }
	}
    }
    return 0;
}

void CloseSocket(c)
ConnPtr c;
{
    if (c->sock >= 0)
    {
	close(c->sock); c->sock = -1;
    }
    if (c->template >= 0)
    {
	close(c->template); c->template = -1;
    }
    c->bufi = 0;
    c->state = NOTLISTENING;
}

void CloseAll()
{
    ConnPtr runner;
    CloseSocket(mainConn);

    runner = connList;
    while(runner)
    {
	CloseSocket(runner);
	runner = runner->next;
    }
}

void AddConnectionRecord(c)
ConnPtr c;
{
    c -> next = connList;
    connList = c;
}

/* using the socket number, find the connection record for that socket */
ConnPtr FindConnectionRecord(num)
int num;
{
    ConnPtr runner;

    runner = connList;

    while(runner)
    {
	if (runner -> sockNum == num)
	{
	    return runner;
	}
	runner = runner -> next;
    }
    return (ConnPtr) 0;
}

void DeleteConnectionRecord(c)
ConnPtr c;
{
    ConnPtr *runner;

    runner = &connList;
    while (*runner)
    {
	if ((*runner) == c)
	{
	    while (DoDeferredMessage()) /* What a hack! clear deferred msgs */
		;
	    (*runner) = (*runner) -> next;
	    free (c);
	    return;
	}
	runner = &((*runner) -> next);
    }
}

int strcmp2(s1, s2)
char s1[], s2[];
/*Compares s1 and s2 without regard to case*/
{
    int k;
    for (k = 0; s1[k]; ++k)
    {
	if (toupper(s1[k]) < toupper(s2[k])) return -k - 1;
	if (toupper(s1[k]) > toupper(s2[k])) return k + 1;
    }
    return s2[k] ? -k - 1 : 0;
}

int strncmp2(s1, s2, n)
char s1[], s2[];
int n;
/*Compares s1 and s2 up to character <n> without regard to case*/
{
    int k;
    for (k = 0; k<n && s1[k]; ++k)
    {
	if (toupper(s1[k]) < toupper(s2[k])) return -k - 1;
	if (toupper(s1[k]) > toupper(s2[k])) return k + 1;
    }
    return k<n && s2[k] ? -k - 1 : 0;
}

char *nbfgets(c)
ConnPtr c;
{
    char *retVal;

    retVal = (char *) 0;

    errno = 0;

    while (!retVal && (1 == read(c -> sock, &(c->buf[c->bufi]),1)))
    {
#ifdef DEBUG
	if (isprint(c->buf[c->bufi]))
	    fprintf(stderr, "...(%c)\n", c->buf[c->bufi]);
	else
	    fprintf(stderr, "---(\\%o)\n", c->buf[c->bufi]);
#endif
	if ((c->buf[c->bufi] == '\n' && (c->bufi==0 || c->buf[c->bufi-1]!='\\'))
	    || c->buf[c->bufi] == '\0')
	{
	    c->buf[++(c->bufi)] = '\0';
	    c->bufi = 0;
	    retVal = c->buf;
	}
	else
	{
	    c->bufi++;
	}

	if (errno)
	{
	    if (errno != lasterror)
	    {
		fprintf(stderr, "error #%d in socket descriptor\n", errno, c->sock);
#ifdef PERROR
		perror("nbfgets");
#endif
		lasterror = errno;
	    }
	    if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EINTR))
	    {
		fprintf(stderr, "Yanking someone's connection\n");
		if (errno == EBADF)
		{
		    close(c -> sock);
		}
		else
		{
		    CloseSocket(c);
		}
		DeleteConnectionRecord(c);
	    }
	    errno = 0;
	}
    }
    return retVal;
}

char *CopyScianString(l, s)
char *l, *s;
{
    char *lPtr, *sPtr;
    int notdone;

    lPtr = l;
    sPtr = s;

    /* skip whitespace */
    while (isspace(*sPtr))
    {
	++sPtr;
    }

    notdone = true;
    while(notdone && sPtr - s < MYBUFSIZ)
    {
	switch (*sPtr)
	{
	    case ' ': case '\n': case '\t': case '\0': case '\r':
		notdone = false;
		*lPtr = '\0';
		break;
	    case '\\':
		*lPtr++ = *sPtr;
		++sPtr;
		*lPtr++ = *sPtr;
		break;
	    default:
		*lPtr++ = *sPtr;
		break;
	}
	if (notdone)
	    ++sPtr;
    }
    if (lPtr == l)
    {
	/* didn't write anything! */
	return (char *) 0;
    }
    return sPtr;
}

/* write a string to a socket. Force a \n onto the end */
void PutSock(conn, s)
ConnPtr conn;
char *s;
{
    int tmp, sofar;

    if (conn->sock < 0)
    {
	fprintf(stderr, "tried to write to bad file descriptor!\n");
	return;
    }

    if (conn -> state != CONNECTED)
    {
	fprintf(stderr, "write with socket state = %d\n", conn -> state);
	return;
    }

    errno = 0;

    writen(conn -> sock, s, strlen(s));

    if (errno)
    {
#ifdef PERROR
	perror("PutSock");
#endif
	conn -> flags = conn -> flags | ND_FLAGS_STREAMERROR;
	errno = 0;
    }
}

typedef struct Message {
    /* int sockNum; */
    ConnPtr conn;
    struct Message *next;
    char s;
} *MessagePtr;

MessagePtr messageList = (MessagePtr) 0;

void DeferMessage(c, s)
/* int cNum; */
ConnPtr c;
char *s;
{
    MessagePtr tmp, *runner;

    /* allocate space for the structure (-1 byte) plus the string (+1 byte) */
    tmp = malloc(sizeof(struct Message) + strlen(s));
    tmp->next = (struct Message *) 0;
    tmp->conn = c;
    strcpy(&(tmp->s), s);

    /*stick message on the end of the list */
    runner = &messageList;
    while(*runner)
	runner = &((*runner)->next);

    *runner = tmp;
}

int DoDeferredMessage()
{
    MessagePtr msg;

    if (!messageList)
	return false;

    msg = messageList;
    messageList = messageList -> next;

    PutSock(msg -> conn, &(msg -> s));

    return true;
}

Time Clock()
{
#if MACHINE == IRIS4D
    return ((double) times(&timesBuffer)) / HZ;
#else
#if MACHINE == CRAYYMP
    return ((double) times(&timesBuffer)) / CLK_TCK;
#else
    return ((double) times(&timesBuffer)) / CLK_TCK;
#endif
#endif
}

ProcessPtr FindProcess(num)
int num;
{
    ProcessPtr tmp;

    tmp = processes;

    while (tmp)
    {
	if (tmp->procNum == num)
	{
	    return tmp;
	}
	tmp = tmp->next;
    }
    return (ProcessPtr) 0;
}

int DeleteProcess(num)
int num;
{
    ProcessPtr *runner, tmp;
    ObjRecPtr objRunner;
    SockListPtr sockRunner;

    runner = &processes;

    while (*runner)
    {
	if ((*runner)->procNum == num)
	{
	    tmp = *runner;
	    *runner = (*runner) -> next;
	    objRunner = tmp -> objectList;
	    while (objRunner)
	    {
		free (objRunner);
		objRunner = objRunner -> next;
	    }
	    sockRunner = tmp -> keepAdvised;
	    while (sockRunner)
	    {
		free (sockRunner);
		sockRunner = sockRunner -> next;
	    }
	    free (tmp);
	    return 0;
	}
	runner = &((*runner)->next);
    }
#ifdef DEBUG
    fprintf(stderr, "DeleteProcess: process number %d does not exist\n", num);
#endif
    return -1;
}

int AddProcess(num, sockNum, name)
int num;
int sockNum;
char *name;
{
    ProcessPtr procPtr;

    if (FindProcess(num))
    {
	/* it's already there */
#ifdef DEBUG
	fprintf(stderr, "Tried to duplicate process number\n");
#endif
	return -1;
    }
    /* it wasn't already there, add new process. */
    procPtr = (ProcessPtr) malloc(sizeof(ProcessStruct)+(name?strlen(name):0));
    procPtr -> procNum = num;
    procPtr -> sockNum = sockNum;
    procPtr -> lastAccess = Clock();
    procPtr -> next = processes;
    procPtr -> objectList = (ObjRecPtr) 0;
    procPtr -> keepAdvised = (SockListPtr) 0;
    if (name)
	strcpy(procPtr -> procName, name);
    else
	procPtr -> procName[0] = '\0';
    processes = procPtr;
#ifdef DEBUG
    fprintf(stderr, "added process #%d\n", num);
#endif
    return 0;
}

ObjRecPtr FindObject(procNum, objNum)
int procNum;
unsigned long objNum;
{
    ProcessPtr procPtr;
    ObjRecPtr runner;

    if (!(procPtr = FindProcess(procNum)))
    {
	return (ObjRecPtr) 0;
    }

    runner = procPtr -> objectList;
    while (runner)
    {
	if (runner -> objectNum == objNum)
	{
	    return runner;
	}
	runner = runner -> next;
    }
    return (ObjRecPtr) 0;
}

int DeleteObject(procNum, objNum)
int procNum;
unsigned long objNum;
{
    ProcessPtr procPtr;
    ObjRecPtr *runner;

    if (!(procPtr = FindProcess(procNum)))
    {
	return -1;
    }

    runner = &(procPtr -> objectList);
    while (*runner)
    {
	if ((*runner) -> objectNum == objNum)
	{
	    ObjRecPtr tmp;

	    tmp = *runner;
	    *runner = (*runner) -> next;
	    free(tmp);
	    return 0;
	}
	runner = &((*runner) -> next);
    }

    return -1;
}

int AddObject(procNum, objNum, iconNum, objName)
int procNum;
unsigned long objNum;
int iconNum;
char *objName;
{
    ProcessPtr procPtr;
    ObjRecPtr tmp;

    if (!(procPtr = FindProcess(procNum)))
    {
	return -1;
    }

    if (FindObject(procNum, objNum))
    {
	/* object already existed? */
	return -1;
    }

    tmp = malloc(sizeof(struct ORStruct) + (objName ? strlen(objName) : 0));
    tmp -> objectNum = objNum;
    tmp -> iconNum = iconNum;
    strcpy(tmp -> objectName, objName);
    tmp -> next = procPtr -> objectList;
    procPtr -> objectList = tmp;

    return 0;
}

#if 0
static u_short SocketPort(c)
/*Returns the port for the socket*/
ConnPtr c;
{
    return htons((u_short) c -> sockNum);
}
#endif

void InterpretSocketCommand(c, s)
ConnPtr c;
char *s;
{
    int procNum;
    unsigned long objNum;
    int objIconNum;
    ProcessPtr procPtr;
    ObjRecPtr objPtr;
    SockListPtr sockPtr;
    char procName[MYBUFSIZ], objName[MYBUFSIZ];
    char *cPtr;
    int chri;

    if (4 != ND_CMD_STR_LENGTH)
    {
	fprintf(stderr, "ScianNetDaemon: Internal Error!\n");
    }

    if (strlen(s) < ND_CMD_STR_LENGTH)
    {
#ifdef DEBUG
	fprintf(stderr, "ScianNetDaemon: socket command format error\n");
#endif
	sprintf(tempStr, "%s\n", ND_ERROR);
	PutSock(c, tempStr);
	return;
    }
    
    if (0 == strncmp2(s, ND_REG_PROCESS, ND_CMD_STR_LENGTH))
    {
	ConnPtr runner;
	int sockNum;

	/* should be a process number following */
	if (2 != sscanf (s + 4, "%d%d%n", &procNum, &sockNum, &chri))
	{
#ifdef DEBUG
	    fprintf(stderr, "Error: no process number\n%s\n", s);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}

	if (!CopyScianString(procName, s + 4 + chri))
	{
	    sprintf(procName, "proc%d", procNum);
	}
	else if (procName[strlen(procName) - 1] == '\\')
	{
	    /* more string on the next line */
	}

	if(AddProcess(procNum, sockNum, procName))
	{
#ifdef DEBUG
		fprintf(stderr, "process #%d already existed\n", procNum);
#endif
		sprintf(tempStr, "%s\n", ND_ERROR);
		PutSock(c, tempStr);
		return;
	}

	runner = connList;
	sprintf(tempStr, "%s %d %s\n", ND_INFO_LIVE_PROCESS,
	    sockNum, procName);
	while (runner)
	{
	    if (runner -> flags & ND_FLAGS_KEEPADVISED)
	    {
		DeferMessage(runner, tempStr);
	    }
	    runner = runner -> next;
	}

    }
    else if (0 == strncmp2(s, ND_UNREG_PROCESS, ND_CMD_STR_LENGTH))
    {
	/* should be a process number following */
	if (1 != sscanf (s + 4, "%d", &procNum))
	{
#ifdef DEBUG
	    fprintf(stderr, "Error: no process number\n%s\n", s);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}

	if (procPtr = FindProcess(procNum))
	{
	    sprintf(tempStr, "%s %d %s\n", ND_INFO_DEAD_PROCESS,
		procPtr -> sockNum, procPtr -> procName);
	}

	if (DeleteProcess(procNum) != -1)
	{
	    ConnPtr runner;

	    runner = connList;
	    while (runner)
	    {
		if (runner -> flags & ND_FLAGS_KEEPADVISED)
		{
		    DeferMessage(runner, tempStr);
		}
		runner = runner -> next;
	    }
	}
    }
    else if (0 == strncmp2(s, ND_INQ_PROCESS, ND_CMD_STR_LENGTH))
    {
	/* should be etc., etc., etc.. */
	if (1 != sscanf (s + 4, "%d", &procNum))
	{
#ifdef DEBUG
	    fprintf(stderr, "Error: no process number\n%s\n", s);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}

	if (!(procPtr = FindProcess(procNum)))
	{
#ifdef DEBUG
	    fprintf(stderr, "Error: Process #%d not found\n", procNum);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}

	sprintf(tempStr, "%s %d %s\n", ND_INFO_LIVE_PROCESS, procPtr -> sockNum,
	    procPtr -> procName);
	DeferMessage(c, tempStr);

    }
    else if (0 == strncmp2(s, ND_LIST_PROCESSES, ND_CMD_STR_LENGTH))
    {
	ProcessPtr runner;

	runner = processes;
	while (runner)
	{
	    sprintf(tempStr, "%s %d %s\n", ND_INFO_LIVE_PROCESS,
		runner -> sockNum, runner -> procName);
	    DeferMessage(c, tempStr);
	    runner = runner -> next;
	}
    }
    else if (0 == strncmp2(s, ND_UPDATE_PROCESSES, ND_CMD_STR_LENGTH))
    {
	ProcessPtr runner;

	runner = processes;
	while (runner)
	{
	    sprintf(tempStr, "%s %d %s\n", ND_INFO_LIVE_PROCESS,
		runner -> sockNum, runner -> procName);
	    DeferMessage(c, tempStr);
	    runner = runner -> next;
	}
	c -> flags = c -> flags | ND_FLAGS_KEEPADVISED;
    }
    else if (0 == strncmp2(s, ND_NOT_UPDATE_PROCESSES, ND_CMD_STR_LENGTH))
    {
	c -> flags = c -> flags & ~ND_FLAGS_KEEPADVISED;
    }
    else if (0 == strncmp2(s, ND_REG_OBJECT, ND_CMD_STR_LENGTH))
    {
	SockListPtr runner;

	/* should be a process number and object number following */
	if (2 != sscanf (s + 4, "%d%d%n", &procNum, &objNum, &chri))
	{
#ifdef DEBUG
	    fprintf(stderr, "Error: process and object numbers expected\n%s\n", s);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}
	if (!(cPtr = CopyScianString(objName, s + 4 + chri)))
	{
	    sprintf(objName, "obj%d", objNum);
	}

	if (cPtr)
	{
	    if (1 != sscanf (cPtr, "%d", &objIconNum))
	    {
		objIconNum = ICONUNKNOWN;
	    }
	}
	else
	{
	    objIconNum = ICONUNKNOWN;
	}

	if (!(procPtr = FindProcess(procNum)))
	{
#ifdef DEBUG
	    fprintf (stderr, "no process #%d\n", procNum);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}

	if(-1 == AddObject(procNum, objNum, objIconNum, objName))
	{
#ifdef DEBUG
		fprintf(stderr, "Couldn't add object %ld to process %d\n", objNum, procNum);
#endif
		sprintf(tempStr, "%s\n", ND_ERROR);
		PutSock(c, tempStr);
		return;
	}

	runner = procPtr -> keepAdvised;
	sprintf(tempStr, "%s %d %d %s %d\n", ND_INFO_LIVE_OBJECT,
	    procNum, objNum, objName, objIconNum);
	while (runner)
	{
	    DeferMessage(runner -> conn, tempStr);
	    runner = runner -> next;
	}
    }
    else if (0 == strncmp2(s, ND_UNREG_OBJECT, ND_CMD_STR_LENGTH))
    {
	/* should be a process number and object number following */
	if (2 != sscanf (s + 4, "%d%d", &procNum, &objNum))
	{
#ifdef DEBUG
	    fprintf(stderr, "Error: process and object numbers expected\n%s\n", s);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}

	if (!(procPtr = FindProcess(procNum)))
	{
#ifdef DEBUG
	    fprintf (stderr, "no process #%d\n", procNum);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}

	if(-1 == DeleteObject(procNum, objNum))
	{
#ifdef DEBUG
		fprintf(stderr, "Error deleting object %ld from process %d\n", objNum, procNum);
#endif
		sprintf(tempStr, "%s\n", ND_ERROR);
		PutSock(c, tempStr);
		return;
	}

	{
	    SockListPtr runner;

	    runner = procPtr -> keepAdvised;
	    sprintf(tempStr, "%s %d %d\n", ND_INFO_DEAD_OBJECT, procNum, objNum);
	    while (runner)
	    {
		DeferMessage(runner -> conn, tempStr);
		runner = runner -> next;
	    }
	}
    }
    else if (0 == strncmp2(s, ND_INQ_OBJECT, ND_CMD_STR_LENGTH))
    {
	/* should be a process number and object number following */
	if (2 != sscanf (s + 4, "%d%d", &procNum, &objNum))
	{
#ifdef DEBUG
	    fprintf(stderr, "Error: process and object numbers expected\n%s\n", s);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}

	if (!(procPtr = FindProcess(procNum)))
	{
#ifdef DEBUG
	    fprintf (stderr, "no process #%d\n", procNum);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}

	if(!(objPtr = FindObject(procNum, objNum)))
	{
#ifdef DEBUG
		fprintf(stderr, "Couldn't find object %ld in process %d\n",
		    objNum, procNum);
#endif
		sprintf(tempStr, "%s\n", ND_ERROR);
		PutSock(c, tempStr);
		return;
	}
	else
	{
	    sprintf(tempStr, "%s %d %d %s %d\n", ND_INFO_LIVE_OBJECT,
		procNum, objNum, objPtr -> objectName, objPtr -> iconNum);
	    DeferMessage(c, tempStr);
	}
    }
    else if (0 == strncmp2(s, ND_LIST_OBJECTS, ND_CMD_STR_LENGTH))
    {
	/* should be a process number number following */
	if (1 != sscanf (s + 4, "%d", &procNum))
	{
#ifdef DEBUG
	    fprintf(stderr, "Error: process number expected\n%s\n", s);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}

	if (!(procPtr = FindProcess(procNum)))
	{
#ifdef DEBUG
	    fprintf (stderr, "no process #%d\n", procNum);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}

	/* advise of objects that are already listed */
	objPtr = procPtr -> objectList;
	while (objPtr)
	{
	    sprintf(tempStr, "%s %d %d %s %d\n", ND_INFO_LIVE_OBJECT, procNum,
		objPtr -> objectNum, objPtr -> objectName, objPtr -> iconNum);
	    DeferMessage(c, tempStr);
	    objPtr = objPtr -> next;
	}
    }
    else if (0 == strncmp2(s, ND_UPDATE_OBJECTS, ND_CMD_STR_LENGTH))
    {
	/* should be a process number number following */
	if (1 != sscanf (s + 4, "%d", &procNum))
	{
#ifdef DEBUG
	    fprintf(stderr, "Error: process number expected\n%s\n", s);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}

	if (!(procPtr = FindProcess(procNum)))
	{
#ifdef DEBUG
	    fprintf (stderr, "no process #%d\n", procNum);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}

	sockPtr = malloc(sizeof(struct SLStruct));
	sockPtr -> conn = c;
	sockPtr -> next = procPtr -> keepAdvised;
	procPtr -> keepAdvised = sockPtr;

	/* advise of objects that are already listed */
	objPtr = procPtr -> objectList;
	while (objPtr)
	{
	    sprintf(tempStr, "%s %d %d %s %d\n", ND_INFO_LIVE_OBJECT, procNum,
		objPtr -> objectNum, objPtr -> objectName, objPtr -> iconNum);
	    DeferMessage(c, tempStr);
	    objPtr = objPtr -> next;
	}
    }
    else if (0 == strncmp2(s, ND_NOT_UPDATE_OBJECTS, ND_CMD_STR_LENGTH))
    {
	SockListPtr *runner;
	/* should be a process number number following */
	if (1 != sscanf (s + 4, "%d", &procNum))
	{
#ifdef DEBUG
	    fprintf(stderr, "Error: process number expected\n%s\n", s);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}

	if (!(procPtr = FindProcess(procNum)))
	{
#ifdef DEBUG
	    fprintf (stderr, "no process #%d\n", procNum);
#endif
	    sprintf(tempStr, "%s\n", ND_ERROR);
	    PutSock(c, tempStr);
	    return;
	}

	runner = &(procPtr -> keepAdvised);
	while (*runner)
	{
	    if ((*runner) -> conn == c)
	    {
		SockListPtr tmp;
		tmp = *runner;
		*runner = (*runner) -> next;
		free (tmp);
		break;
	    }
	    runner = &((*runner) -> next);
	}
    }
    else if (0 == strncmp2(s, ND_CLOSE, ND_CMD_STR_LENGTH))
    {
        if (IsMainConn(c))
        {
            close(c->sock); c->sock = -1;
            c->state = LISTENING;
        }
        else
        {
            CloseSocket(c);
            DeleteConnectionRecord(c);
        }
    }
    else if (0 == strncmp2(s, ND_EXIT, ND_CMD_STR_LENGTH))
    {
#ifdef DEBUG
	fprintf(stderr, "got exit directive, byebye\n");
#endif
	CloseAll();
	exit (0);
    }
    else
    {
#ifdef DEBUG
	fprintf(stderr, "Error: unparseable\n%s\n", s);
#endif
	sprintf(tempStr, "%s\n", ND_ERROR);
	PutSock(c, tempStr);
	return;
    }
}

#define COMMAND_SIZ 256


int IdleSocket(c)
ConnPtr c;
/* My version of idling the sockets */
{
    struct sockaddr_in inSocketAddr;
    int inSocket;
    int tmp;
    ConnPtr tmpConn;

/* temporary? */
    char *tmpPtr;

    if (!c)
    {
	fprintf(stderr, "Null Connection pointer!\n");
	return false;
    }

    switch (c->state)
    {
	case NOTLISTENING:
	    if (FindSocket(c) < 0)
	    {
#ifdef DEBUG
		fprintf(stderr, "FindSocket() failed!\n");
#endif
		if (IsMainConn(c)) sleep(1);
		return false;
	    }

	    fprintf(stderr, "connected\n");
	    c->state = LISTENING;
	    break;
	case LISTENING:
	    /* waiting for a phone call */
	    c->sock = accept(c->template, (struct sockaddr *) 0, (int *) 0);
	    if (c->sock != -1)
	    {
#ifdef PAUSE
		/* ioctl(c->sock, I_SETSIG, S_INPUT); */
		fcntl(c->sock, F_SETOWN, getpid());
		fcntl(c->sock, F_SETFL,
			fcntl(c->sock, F_GETFL) | FASYNC);
#endif
		/* set up main connection */
/*
		if (IsMainConn(c))
		{
		    c->state = VALIDATING;
		}
		else
		{
		    c->state = CONNECTED;
		    sprintf(tempStr, "%s %s\n", ND_INFO_VERSION,
			ND_NET_DAEMON_VERSION);
		    DeferMessage(c, tempStr);
		    
		}
*/
		c -> state = VALIDATING;
		/* version message moved down to case VALIDATING */
	    }
	    else
	    {
		return false;
	    }
	    break;
	case VALIDATING:
	    /* one ringy dingy... */

	    if (tmpPtr = nbfgets(c))
	    {
#ifdef DEBUG
		fprintf(stderr, "tmpPtr = %s\n", tmpPtr);
#endif
		if (0 != strncmp2(tmpPtr, ND_PASSWORD, ND_CMD_STR_LENGTH))
		{
		    return false;
		}
		fprintf(stderr, "accepted and validated connection\n");
		tmpConn = NewConn();
		AddConnectionRecord(tmpConn);
		tmpConn -> state = CONNECTED;
		tmpConn -> sock = c -> sock;

		sprintf(tempStr, "%s %s\n", ND_INFO_VERSION,
		    ND_NET_DAEMON_VERSION);
		DeferMessage(tmpConn, tempStr);

		c -> sock = -1;
		c->state = LISTENING;
		return true;
	    }
	    else
	    {
		return false;
	    }
	    break;
	case CONNECTED:
	    if (tmpPtr = nbfgets(c))
	    {
#ifdef DEBUG
		fprintf(stderr, "tmpPtr = %x, *tmpPtr: %s\n", tmpPtr, tmpPtr);
#endif
	    InterpretSocketCommand(c, tmpPtr);
	    }
	    else
	    {
		return false;
	    }
	    break;
	case TRANSFERRING:
	    /* forwarding your call to the appropriate party */
	    break;
    }
    return true;
}

/* ConnPtr connTracker = (ConnPtr) 0; */

Time lastActionTime;

void IdleAllSockets()
{
    ConnPtr connTracker;
    int didSomething = 0;

    didSomething += IdleSocket(mainConn);
/*
    if (!connTracker)
    {
	connTracker = connList;
    }
*/
    connTracker = connList;
    /* if */ while (connTracker)
    {
	didSomething += IdleSocket(connTracker);
	connTracker = connTracker -> next;
    }
    if (!didSomething)
    {
	didSomething += DoDeferredMessage();
    }

    if (didSomething)
    {
	lastActionTime = Clock();
    }

    /* go out to lunch if no activity in the last ATTENTION_SPAN secs*/
    if (Clock() > lastActionTime + ATTENTION_SPAN && !didSomething)
    {
#ifdef PAUSE
	sigblock(sigmask(SIGIO));
	sigpause(sigmask(SIGIO));
	fprintf(stderr, "handler was called %d times\n", handlerCount - oldHandlerCount);
	oldHandlerCount = handlerCount;
	sigsetmask(0);
#else
	sleep(1);
#endif
    }

    if (timeOut > 0.0 && Clock() > lastActionTime + timeOut && !didSomething)
    {
	/* we've been idle for too long, time to go home */
	fprintf(stderr, "No activity for %d seconds, NetDaemon exiting\n",
		(int) (Clock() - lastActionTime));
	exit(0);
    }
}

void CommandLineError(name)
char *name;
{
    fprintf(stderr, "error on command line\n");
    fprintf(stderr, "usage: %s [-t <timeout>] [-s <socketnumber>] [-l <blocklength>]\n", name);
    exit (-1);
}

ParseArgs(argc, argv)
int argc;
char *argv[1];
{
    int i;
    char *cPtr;

    socketBlockLength = ND_SOCKET_BLOCK_LENGTH; /*may be overridden */
    baseSocketNumber = ND_BASE_SOCKET_NUMBER; /*may be overridden on cmd line*/

    for (i = 1; i < argc; ++i)
    {
	cPtr = argv[i];
	if (*cPtr == '-')
	{
	    switch(*++cPtr)
	    {
		case 't':
		    if (1 != sscanf(++cPtr, "%lf", &timeOut))
		    {
			if ((i + 1 >= argc) || (1 != sscanf(argv[i+1],
						"%lf", &timeOut)))
			{
			    fprintf(stderr, "integer expected after -t\n");
			    CommandLineError(argv[0]);
			}
			else
			{
			    ++i;
			}
		    }
		    break;
		case 's':
		    if (1 != sscanf(++cPtr, "%d", &baseSocketNumber))
		    {
			if ((i + 1 >= argc) || (1 != sscanf(argv[i+1],
						"%d", &baseSocketNumber)))
			{
			    fprintf(stderr, "integer expected after -s\n");
			    CommandLineError(argv[0]);
			}
			else
			{
			    ++i;
			}
		    }
		    break;
		case 'l':
		    if (1 != sscanf(++cPtr, "%d", &socketBlockLength))
		    {
			if ((i + 1 >= argc) || (1 != sscanf(argv[i+1],
						"%d", &socketBlockLength)))
			{
			    fprintf(stderr, "integer expected after -l\n");
			    CommandLineError(argv[0]);
			}
			else
			{
			    ++i;
			}
		    }
		    break;
		default:
		    fprintf(stderr, "unexpected argument\n");
		    CommandLineError(argv[0]);
		    break;
	    }
	}
	else
	{
	    fprintf(stderr, "error in argument\n");
	    CommandLineError(argv[0]);
	}
    }
}

void SanityCheck()
{
    ConnPtr runner1, runner2;

    runner1 = connList;
    while (runner1)
    {
	runner2 = runner1 -> next;
	while (runner2)
	{
	    if (runner1 -> sock == runner2 -> sock)
	    {
		fprintf(stderr, "shared file descriptor! %d.\n", runner1 -> sock);
	    }
	}
    }
}

#define SANITYCHECKINTERVAL 5

main(argc, argv)
int argc;
char *argv[1];
{
    Time lastSanityCheck;

#ifdef PAUSE
#ifdef BSD_SIGS
    struct sigvec sv,osv;
    
    sv.sv_handler = sigIOHandler;     /* pointer to routine. */
    sv.sv_mask = 0;              /* no additional signals masked */
    sv.sv_flags = SV_INTERRUPT;  /* no restarted system calls */
    sigvec(SIGIO,&sv,&osv);
#else
#ifdef SYSV_SIGS
    signal(SIGIO, sigIOHandler);
#else
    Yer up shits creek, bud!;
#endif /* def SYSV_SIGS */
#endif /* def BSD_SIGS */
#endif /* def PAUSE */

#ifdef ALARM
#ifdef SYSV_SIGS
    signal(SIGALRM, sigAlrmHandler);
#else
#ifdef BSD_SIGS
    sv.sv_handler = sigAlrmHandler;     /* pointer to routine. */
    sv.sv_mask = 0;              /* no additional signals masked */
    sv.sv_flags = SV_INTERRUPT;  /* no restarted system calls */
    sigvec(SIGALRM,&sv,&osv);

#else
    Need BSD_SIGS or SYSV_SIGS defined!
#endif /* def BSD_SIGS */
#endif /* def SYSV_SIGS */
#endif /* def ALARM */

    ParseArgs(argc, argv);

    lastPort = baseSocketNumber;

    lastActionTime = Clock();

    mainConn = NewConn();
    mainConn -> sockNum = baseSocketNumber;

    proto = getprotobyname("tcp") -> p_proto;


    for (;;)
    {
	IdleAllSockets();
#ifdef ALARM
	if (alarmClock)
	{
	    CloseAll();
	    exit(0);
	}
#endif /* def ALARM */
	if (lastSanityCheck + SANITYCHECKINTERVAL > Clock())
	{
	    SanityCheck();
	}
    }
}
