/*--------------------------------------------------------------------------

		              THE  SICS SERVER 
		         
		         

	Mark Koennecke,    October 1996
	Revised for use with tasker: Mark Koennecke, September 1997
      
        Added code to redirect stdout/sterr to file.

        Mark Koennecke, May 2000
       
	Copyright: see copyright.h
----------------------------------------------------------------------------*/
#define NEEDDINTINIT
#include "fortify.h"
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include "conman.h"
#include "SCinter.h"
#include "network.h"
#include "ifile.h"
#include "status.h"
#include "devexec.h"
#include "ofac.h"
#include "passwd.h"
#include "lld.h"
#include "macro.h"  
#include "interface.h"
#include "perfmon.h"
#include "nread.h"
#include "event.h"
#include "telnet.h"
#include "nserver.h"

  int ServerSetupInterrupt(int iPort, pNetRead pNet, pTaskMan pTasker);
  /*
     configures a port for listening for interrupts
  */

  extern void StopExit(void); /* in SICSmain.c */
/* ========================= Less dreadful file statics =================== */

#define DEFAULTINIFILE "servo.tcl"
#define DEFAULTSTATUSFILE "sicsstat.tcl"
  
  static int iFortifyScope;
  	  	
#include "obdes.h"
#include "interface.h"  	  		   
#include "sicsvar.h"
#include "emon.h"
/*----------------------------------------------------------------------*/
  pEnvMon GetEnvMon(SicsInterp *pSics)
  {
     CommandList *pCom;
     
     assert(pSics);
     
     pCom = FindCommand(pSics,"emon");
     assert(pCom);
     assert(pCom->pData);
     return (pEnvMon)pCom->pData;
  }
/*-------------------------------------------------------------------------*/
  int InitServer(char *file, pServer *pServ)
  {
     char *pText = NULL;
     int iPort, iRet;
     FILE *fp;
     pSicsVariable pVar;
     char pBueffel[512];
     SConnection *pCon = NULL;
     pServer self = NULL;
     char *pPtr;
     int iCommandTimeOut, iPasswordTimeOut, i;
     pNetRead pReader = NULL;
     pPerfMon pMon = NULL;
     CommandList *pCom;
     pid_t myPid;     

     /* allocate a new server structure */
     self  = (pServer)malloc(sizeof(SicsServer));
     if(!self)
     {
       puts("DEADLY ERROR: Cannot allocate server data structure!");
       return 0;
     }
     memset(self,0,sizeof(SicsServer));
     *pServ = self;


     /* configure fortify */
     iFortifyScope = Fortify_EnterScope();
     Fortify_CheckAllMemory();
      
     /* interpreter */     
     self->pSics = InitInterp();
     assert(self->pSics);

    /* initialise tasker */
    assert(TaskerInit(&self->pTasker));
     
    /* initialise the server from script */
    if(file == NULL)
    {
       iRet = InitObjectCommands(self,DEFAULTINIFILE);
    }
    else
    {
       iRet = InitObjectCommands(self,file);
    }
    if(!iRet)
    {
      if(file)
      {
         printf("Error on initialization file --> %s <-- \n",file);
      }
      else
      {
         printf("Error on initialization file --> %s <-- \n",DEFAULTINIFILE);
      }
      return 0;
    }
    
     /* 
        check for option RedirectFile and redirect stout/sterr to it
        if present.
     */
     pPtr = NULL;
     pPtr = IFindOption(pSICSOptions,"RedirectFile");
     if(pPtr != NULL)
     {
       myPid = getpid();
       sprintf(pBueffel,"%s%5.5d.log",pPtr,(int)myPid);
       fp = freopen(pBueffel,"w",stdout);
       if(!fp)
       {
          printf("Failed to redirect stdout/stderr to %s\n",pBueffel);
       }
       fp = freopen(pBueffel,"w",stderr);
       if(!fp)
       {
          printf("Failed to redirect stdout/stderr to %s\n",pBueffel);
       }
     }     

    
    /* initialise net reader */
    pPtr = NULL;
    pPtr = IFindOption(pSICSOptions,"ReadTimeOut");
    if(pPtr != NULL)
    {
        i = atoi(pPtr);
        iCommandTimeOut = i;
    }  
    pPtr = NULL;
    pPtr = IFindOption(pSICSOptions,"ReadUserPasswdTimeout");
    if(pPtr != NULL)
    {
        i = atoi(pPtr);
        iPasswordTimeOut = i;
    }  
    assert((pReader = CreateNetReader(self,iPasswordTimeOut,iCommandTimeOut)) != NULL);
    TaskRegister(self->pTasker,
                 NetReaderTask,
                 NetReaderSignal, 
                 DeleteNetReader,
                 pReader,1);    
    self->pReader = pReader;
     
     /* the socket */
     pText = IFindOption(pSICSOptions,"ServerPort");
     if(!pText)
     {
       printf("Cannot find ServerPort number in options file %s\n",
              "This value is required!");
       IFDeleteOptions(pSICSOptions);
       DeleteInterp(self->pSics);
       return 0;
     }
     iRet = sscanf(pText,"%d",&iPort);
     if( (iRet != 1) || (iPort < 1024) )
     {
       printf("Invalid port number specified in Server initialisation file\n");
       IFDeleteOptions(pSICSOptions);
       DeleteInterp(self->pSics);
       return 0;
     }
     self->pServerPort = NETOpenPort(iPort);
     if(!self->pServerPort)
     {
       printf("Cannot open Server Socket\n");
       IFDeleteOptions(pSICSOptions);
       DeleteInterp(self->pSics);
       return 0;
     }
     NetReadRegister(pReader, self->pServerPort, naccept, NULL);
     
     /* the device executor */
     pCom = FindCommand(self->pSics,"stopexe");
     assert(pCom);
     assert(pCom->pData);
     self->pExecutor = (pExeList)pCom->pData;

     
     /* initialize Interrupt Port */
     pText = IFindOption(pSICSOptions,"InterruptPort");
     if(!pText)
     {
       printf("Cannot find InterruptPort number in options file %s\n",
              "This value is required!");
       IFDeleteOptions(pSICSOptions);
       DeleteInterp(self->pSics);
       return 0;
     }
     iRet = sscanf(pText,"%d",&iPort);
     if( (iRet != 1) || (iPort < 1024) )
     {
       printf("Invalid port number specified in Server initialisation file\n");
       IFDeleteOptions(pSICSOptions);
       DeleteInterp(self->pSics);
       return 0;
     }
     iRet = ServerSetupInterrupt(iPort,pReader,self->pTasker);
     if(!iRet)
     {
        StopServer(self);
        return 0;
     }
     /* install a secret fully priviledged entry point for ME */
     AddUser("Achterbahn","Kiel",usInternal);

     /* initialize the last saved status of the system */
     strcpy(pBueffel,"Restore ");
     pText = IFindOption(pSICSOptions,"statusfile");
     if(pText)
     {
       strcat(pBueffel,pText);
     }
     else
     {
       strcat(pBueffel,DEFAULTSTATUSFILE);
       IFAddOption(pSICSOptions,"statusfile",
                   DEFAULTSTATUSFILE);
     }
     pCon = SCCreateDummyConnection(self->pSics);
     if(pCon)
     {
         InterpExecute(self->pSics,pCon,pBueffel);
         SCDeleteConnection(pCon);
     }
     else
     {
      printf("ERROR: Cannot allocate dummy connection, status NOT saved");
     }
   
     /* install performance monitor */
     pMon = CreatePerfMon(20);
     AddCommand(self->pSics,"Performance",PerfMonWrapper,DeletePerfMon,pMon);
     TaskRegister(self->pTasker,
                  PerfMonTask,
                  PerfMonSignal,
                  NULL,
                  pMon,1);

     /* install environment monitor */
     self->pMonitor = GetEnvMon(self->pSics);
     TaskRegister(self->pTasker,
                  EnvMonTask,
                  EnvMonSignal,
                  NULL,
                  self->pMonitor,1);
    
     /* install telnet port */
     InstallTelnet();
     
     /* exit handlers need to be installed here */
     atexit(StopExit);
     Fortify_CheckAllMemory();
     return 1;
     
  }
/*---------------------------------------------------------------------------*/
  void StopServer(pServer self)
  {
    SConnection *pCurrent, *pTemp;
    char pBueffel[512];
    char *pText = NULL;
    SConnection *pCon = NULL;

    /* clear all pending bullshit */
    ClearExecutor(self->pExecutor);

    /* shut telnet down */
    KillTelnet();

    /* shut tasker down */
    TaskerDelete(&self->pTasker);
    
    /* save status */
    strcpy(pBueffel,"Backup ");
    pText = IFindOption(pSICSOptions,"statusfile");
    if(pText)
    {
       strcat(pBueffel,pText);
    }
    else
    {
       strcat(pBueffel,DEFAULTSTATUSFILE);
    }
    pCon = SCCreateDummyConnection(self->pSics);
    if(pCon)
    {
         InterpExecute(self->pSics,pCon,pBueffel);
         SCDeleteConnection(pCon);
    }
    else
    {
      printf("ERROR: Cannot allocate dummy connection, status NOT saved");
    }

    /* close redirection file if present */
    pText = NULL;
    pText = IFindOption(pSICSOptions,"RedirectFile");
    if(pText)
    {
        fclose(stderr);
    }
    
    /* clean out */
    if(pSICSOptions)
    	IFDeleteOptions(pSICSOptions);
    if(self->pSics)
        DeleteInterp(self->pSics);
   
    /* close the server port */
    if(self->pServerPort)
    {
       NETClosePort(self->pServerPort);
       free(self->pServerPort);
    }

    
    /* remove the in memory password database */
    KillPasswd();   

    
    /* close Interrupt system */
    ServerStopInterrupt();

    /* Remove Status Callback */
    KillStatus(NULL);
    
    /* close the List system */
    LLDsystemClose();

    /* make fortify print his findings */
     Fortify_DumpAllMemory(iFortifyScope);
     Fortify_LeaveScope();
     
    free(self); 
  }
/*------------------------------------------------------------------------*/
  void RunServer(pServer self)
  {
     TaskSchedule(self->pTasker);
  }
/*------------------------------------------------------------------------*/
   typedef struct {
                   time_t tFinish;
                   int iEnd;
                  } WaitStruct, *pWaitStruct;     
/*-------------------------------------------------------------------------*/
  static int WaitTask(void *pData)
  {
     time_t tNow;
     pWaitStruct self = NULL;
     
     self = (pWaitStruct)pData;
     if(self->iEnd)
     {
       return 0;
     }
     
     tNow = time(NULL);
     if(tNow >= self->tFinish)
     {
       return 0; /* done */
     }
     else
     {
       return 1;
     }
  }
/*-----------------------------------------------------------------------*/
  static void WaitSignal(void *pUser, int iSignal, void *pEventData)
  {
     pWaitStruct self = NULL;
     int *iInt;
     
     self = (pWaitStruct)pUser;
     assert(self);
     iInt = (int *)pEventData;
     
     if(iSignal == SICSINT)
     {
       iInt = (int *)pEventData;
       if(*iInt > eContinue)
       {
         self->iEnd = 1;
       }
     }
  }
    
/*--------------------------------------------------------------------------
	UserWait: the user command for waiting, expects one arg:
	time to wait in seconds
*/
  int UserWait(SConnection *pCon, SicsInterp *pSics, void *pData,
               int argc, char *argv[])
  {
     int i;
     char pBueffel[80];
     float fVal;
     Status eOld;
     time_t tNow;
     WaitStruct sWait;
     pTaskMan pTask;
     long lID;
     
     assert(pCon);
     assert(pSics);
     assert(pData);
     
     pTask = GetTasker();
     
     if(argc < 2)
     {
         sprintf(pBueffel,"Insufficient number of args to %s",argv[0]);
         SCWrite(pCon,pBueffel,eError);
         return 0;
     }
     
     /* try convert to a number */
     i = sscanf(argv[1],"%f",&fVal);
     if(i < 1)
     {
         sprintf(pBueffel,"Expected numeric argument to  %s, got %s",
         argv[0], argv[1]);
         SCWrite(pCon,pBueffel,eInError);
         return 0;
     }
     
     eOld = GetStatus();
     SetStatus(eUserWait);
     tNow = time(NULL);
     sWait.tFinish = tNow + (time_t)fVal;
     sWait.iEnd = 0;
     lID = TaskRegister(pTask,WaitTask,WaitSignal,NULL,&sWait,1);
     TaskWait(pTask,lID);
     SetStatus(eOld);
     if(SCGetInterrupt(pCon) != eContinue)
     {
        return 0;
     }
     else
     {    
        return 1;
     }   
  }  
/*------------------------------------------------------------------------*/
  int SicsWait(long lTime)
  {
    WaitStruct sWait;
    pTaskMan pTasker = NULL;
    time_t tNow;
    long lID;
    
    pTasker = GetTasker();
    tNow = time(NULL);
    sWait.tFinish = tNow + lTime;
    sWait.iEnd = 0;
    lID = TaskRegister(pTasker,WaitTask,WaitSignal,NULL,&sWait,1);
    TaskWait(pTasker,lID);
    return 1;
  }

/*-------------------------------------------------------------------------*/
  void ServerWriteGlobal(char *pMessage,int iOut)
  {
     pTaskMan pTasker = NULL;
     
     pTasker = GetTasker();
     
     TaskSignal(pTasker,SICSBROADCAST,pMessage);     
  }

