/***************************************************/
/*                                                 */
/* unixscol.c                                      */
/*                                                 */
/***************************************************/



//
// Modifications History
//
//$ ER(27/07/2001) : add SCsetuser function
//
//$ LB (21/05/2002) : AnyInstance function : manage automatic launch of the supervisor, and CLUBHOUSE_SERVER bahaviour...
//
//$ LB (30/05/2002) : AnyInstance function : if CLUBHOUSE_SERVER, don't launch machine if supervisor has not already been launched
//
//$ LB (30/05/2002) : AnyInstance function : if CLUBHOUSE_SERVER, don't launch the supervisor
//

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>


#include "../kernel/include/vscol.h"
#include "hardload.h"
#include "../kernel/mmemory.h"
#include "mbytec.h"
#include "../kernel/listlab.h"
#include "../kernel/loadpak.h"
#include "../kernel/scol.h"
#include "../kernel/scolsys.h"
#include "../kernel/fifo.h"
#include "../kernel/include/socket.h"
#include "../kernel/mainscol.h"
#include "../kernel/scolsign.h"
//$LB (31/08/2001) : add include scolplugin.h
//TODO TO DO #include "../kernel/scolplugin.h"


//#define DEBUGSELECT //decommenter pour tracer le select

FILE *flog;
int flaglogwinsup=0;
int flaglogwin=1;
int flaglog;
int fstart;
extern char forcedIP[64];

#define MAXSOCK (1024*8)
int scfd[MAXSOCK];
int scfd2[MAXSOCK];
int sctyp[MAXSOCK];
int sctyp2[MAXSOCK];
fd_set rmask[1];
fd_set wmask[1];
fd_set emask[1];
fd_set rwe[3];

char execname[1024];
char execpath[1024];
//$ FA(24/04/2001): Set runtime-checks compiler option
extern int optionRuntimeChecks;
//


/************************************/
/* esdtrfzer   */
/***************/
int SCOLHttpEvent (mmachine m,int hSock,int NetEvent,int NetError);
int SCOLInetEvent(mmachine m,int sck,int event,int error);
int SCOLTelnetEvent(mmachine mscol,int hSock,int NetEvent,int NetError);
int myhtoi(char *q);
int SPregistPart(char *name,char *param);
int mt_timeout();
int mt_event();

int CloseMachine(mmachine m)
{
  int c;

  while(MMgetglobal(m,OFFSCCHAN)!=NIL)
  {
	  c=MMfetch(m,MMgetglobal(m,OFFSCCHAN)>>1,0);
	  SCdestroychannel(m, c, 0);
  }
  return 0;
}

int EndScolMachine(int box)
{
  if (fstart==1)
  {
    fstart =-1;
    CloseMachine(&mscol);
    SCKend();
    MMechostr(MSKRUNTIME,"SCKend\n");
    flaglog=0;
    if (flog) fclose(flog);
  }
  exit(0);
  return 0;
}


int mytickcount()
{
  struct timeval tm;
  int t;
  
  gettimeofday(&tm,NULL);
  t=(tm.tv_sec*1000)+(tm.tv_usec/1000);
  return t;
}
//#define DEBUGTIME

void MMechostr(int i,char *format, ...)
{
  va_list arglist;
  char buf[16384];

  if ((flaglog)&&((i==0)||(i==MSKRUNTIME)||(i&MaskEcho)))
    {
      buf[0]=0;
#ifdef DEBUGTIME
      sprintf(buf,"{%d}",mytickcount());
#endif
      va_start(arglist,format);
//$ FA(02/07/2001): Truncate output if too long
      vsnprintf(buf+strlen(buf),16384,format,arglist);
//
      va_end(arglist);

      /*      Techostr(&tcons,buf,strlen(buf));
       */

printf("%s",buf);

      if (flog)
	{
	  fprintf(flog,"%s",buf);
	  fflush(flog);
	}
    }
}


int nbsock=0;
int nbsockselect=0;
int sock_new()
{
	int i;
	for(i=0;i<nbsock;i++) if (scfd[i]==-1) return i;
	if (nbsock>=MAXSOCK) return -1;
	nbsock++;
	return nbsock-1;
}

int SCKaddsock(int s,int typ)
{
  int i;
  
  i=sock_new();
  if (i==-1) return i;
  scfd[i]=s;
  FD_SET(s,rmask);
  if (typ!=SC_TYPUDP) FD_SET(s,wmask);
  FD_SET(s,emask);
  sctyp[i]=typ;
  return 0;
}

int SCKdelsock(int s)
{
  int i;

  if (s<0) return 0;
  for(i=0;i<nbsock;i++)
    if (scfd[i]==s) scfd[i]=-1;
  while((nbsock>0)&&(scfd[nbsock-1]==-1)) nbsock--;
  FD_CLR(s,rmask);
  FD_CLR(s,wmask);
  FD_CLR(s,emask);
  FD_CLR(s,rwe);
  FD_CLR(s,rwe+1);
  FD_CLR(s,rwe+2);
  return 0;
}

int SCKsetwrite(int s)
{
  FD_SET(s,wmask);
  return 0;
}

int SCKclrwrite(int s)
{
  FD_CLR(s,wmask);
  return 0;
}


int ktimeselect=0;
int displaytime()
{
  time_t timep;
  struct tm *res;
  if (ktimeselect) ktimeselect--;
  else
    {
      time(&timep);
      res=localtime(&timep);
      if (res) MMechostr(
#ifdef DEBUGSELECT
		MSKFOO
#else
		MSKDEBUG
#endif
		,">time=%02d:%02d:%02d\n",
		res->tm_hour,res->tm_min,res->tm_sec);
      ktimeselect=100;
    }
  return 0;
}

int gettypsock(int s)
{
  int i;
  for(i=0;i<nbsock;i++)
    {
      if (scfd[i]==s) return sctyp[i];
    }
  return -1;
}

int dumpselect()
{
  struct sockaddr saddr;
  int s,k,t,l;

  MMechostr(1,"dump select\n");
  for(s=0;s<nbsockselect;s++)
    {
      k=0;
      if (FD_ISSET(s,rmask)) k+=1;
      if (FD_ISSET(s,wmask)) k+=2;
      if (FD_ISSET(s,emask)) k+=4;
      if (k)
	{
	  t=gettypsock(s);
	  MMechostr(1,"#%d ",s);
	  if (k&1) MMechostr(1,"R");
	  if (k&2) MMechostr(1,"W");
	  if (k&4) MMechostr(1,"E");
	  if (t==SC_TYPHTTP) MMechostr(1," HTTP");
	  if (t==SC_TYPHTTPSRV) MMechostr(1," HTTPSRV");
	  if (t==SC_TYPTELNET) MMechostr(1," TELNET");
	  if (t==SC_TYPINET) MMechostr(1," INET");
	  if (t==SC_TYPUDP) MMechostr(1," UDP");
	  if (t==SC_TYPCHN) MMechostr(1," CHN");
	  if (t==SC_TYPSRV) MMechostr(1," SRV");
	  if (t==-1) MMechostr(1," ERROR");

	  l=sizeof(saddr);
	  k=getsockname(s,&saddr,&l);
	  if (k==0) MMechostr(1," ok\n");
	  else MMechostr(1," error %d errno=%d\n",k,errno);
	}
    }
  return 0;
}

int SCKselect()
{
  int i,k,s,d;
  struct timeval tm;

  rwe[0]=rmask[0];
  rwe[1]=wmask[0];
  rwe[2]=emask[0];

  for(i=0;i<nbsock;i++)
    {
      scfd2[i]=scfd[i];
      sctyp2[i]=sctyp[i];
    }
  displaytime();
  //  dumpselect(); // dump de toutes les sockets utilisees pour le select
  d=mt_timeout();
  MMechostr(
#ifdef DEBUGSELECT
	    MSKFOO
#else
	    MSKDEBUG
#endif
	    ,">>debut select (%d/%d) %d ms\n",nbsock,FD_SETSIZE,d);

#ifdef DEBUGSELECT
  dumpselect();
#endif
  if (d<0)
    k=select(nbsockselect,rwe,rwe+1,rwe+2,NULL);
  else
    {
      tm.tv_sec=d/1000;
      tm.tv_usec=(d-(1000*tm.tv_sec))*1000;
      k=select(nbsockselect,rwe,rwe+1,rwe+2,&tm);
    }

  MMechostr(
#ifdef DEBUGSELECT
	    MSKFOO
#else
	    MSKDEBUG
#endif
	    ,"fin select %d errno=%d\n",k,errno);
  if (k<0)
    { 
      if (errno==EINTR)
	{ 
	  MMechostr(MSKFOO,"select interrompu (errno=EINTR)\n"); 
	  return 0; 
	} 
      dumpselect();
      return -1; 
    } 
  
  for(i=0;i<nbsock;i++)
    if ((s=scfd2[i])!=-1)
      {
	if ((sctyp2[i]==SC_TYPHTTP)||(sctyp2[i]==SC_TYPHTTPSRV))
	  {
	    k=0;
	    if (FD_ISSET(s,rwe+2)) k=SCOLHttpEvent(&mscol,s,0,-1);
	    if ((k==0)&&(FD_ISSET(s,rwe+1)))
	      k=SCOLHttpEvent(&mscol,s,FD_WRITE,0);
	    if ((k==0)&&(FD_ISSET(s,rwe)))
	      {
		if (sctyp2[i]==SC_TYPHTTPSRV)
		  k=SCOLHttpEvent(&mscol,s,FD_ACCEPT,0);
		else if (sctyp2[i]==SC_TYPHTTP)
		  k=SCOLHttpEvent(&mscol,s,FD_READ,0);
	      }
 	  }
	else if (sctyp2[i]==SC_TYPTELNET)
	  {
	    k=0;
	    if (FD_ISSET(s,rwe+2)) k=SCOLTelnetEvent(&mscol,s,0,-1);
	    if ((k==0)&&(FD_ISSET(s,rwe)))
	      {
		k=SCOLTelnetEvent(&mscol,s,FD_READ,0);
	      }
	    if ((k==0)&&(FD_ISSET(s,rwe+1)))
	      k=SCOLTelnetEvent(&mscol,s,FD_WRITE,0);
	  }
	else if (sctyp2[i]==SC_TYPINET)
	  {
	    k=0;
	    if (FD_ISSET(s,rwe+2)) k=SCOLInetEvent(&mscol,s,0,-1);
	    if ((k==0)&&(FD_ISSET(s,rwe)))
	      {
		k=SCOLInetEvent(&mscol,s,FD_READ,0);
	      }
	    if ((k==0)&&(FD_ISSET(s,rwe+1)))
	      k=SCOLInetEvent(&mscol,s,FD_WRITE,0);
	  }
	else
	  {
	    k=0;
	    if (FD_ISSET(s,rwe+2)) k=SCOLNetEvent(&mscol,s,0,-1);
	    if ((k==0)&&(FD_ISSET(s,rwe)))
	      {
		if (sctyp2[i]==SC_TYPSRV)
     		  k=SCOLNetEvent(&mscol,s,FD_ACCEPT,0);
		else
		  {
		    k=SCOLNetEvent(&mscol,s,FD_READ,0);
		    if ((k)&&(s==socklife)) EndScolMachine(0);
		  }
	      }
	    if ((k==0)&&(FD_ISSET(s,rwe+1))&&(sctyp2[i]!=SC_TYPUDP))
	      k=SCOLNetEvent(&mscol,s,FD_WRITE,0);
	  }
      }
	
  if (tstscoldead(0)) return 1;
  if (d>=0)
    {
      mt_event();
      if (tstscoldead(1)) return 1;
    }
  return 0;
}

void setlimitmax()
{
  struct rlimit res;

  getrlimit(RLIMIT_NOFILE,&res);
  MMechostr(1,"nb sockets max : %d (kernel=%d)\n",res.rlim_cur,res.rlim_max);

  nbsockselect=res.rlim_max;
  if (nbsockselect>FD_SETSIZE) nbsockselect=FD_SETSIZE;
  /*
  if (res.rlim_cur!=res.rlim_max)
    {
      res.rlim_cur=res.rlim_max;
      setrlimit(RLIMIT_NOFILE,&res);
      getrlimit(RLIMIT_NOFILE,&res);
      MMechostr(1,"nb sockets max : %d (kernel=%d)\n",
		res.rlim_cur,res.rlim_max);
    }
  */
}

int USSelectInit()
{
  int i;

  for(i=0;i<MAXSOCK;i++) scfd[i]=-1;  /* initialisation table sockets */

  FD_ZERO(rmask);
  FD_ZERO(wmask);
  FD_ZERO(emask);
  signal(SIGPIPE,SIG_IGN);
  signal(SIGCHLD,SIG_IGN);
  setlimitmax();
  return 0;
}


int StartUSM(char *buf,char *rights,char *size)
{
  MMechostr(MSKTRACE,"launching %s %s %s %s\n",execname,buf,rights,size);
  
  if (fork()==0)
    {
      execlp(execname,execname,buf,rights,size,NULL);
      exit(0);
    }
  return -1;
}



int StartHTTP(char *buf,int asc)
{
  MMechostr(MSKTRACE,"should start URL %s\n",buf);
  return 0;
}
int StartExe(char *buf,char *param)
{
  MMechostr(MSKTRACE,"should start EXE %s\n",buf);
  return 0;
}
int RefreshExe()
{
  return 0;
}
int AddExe(char *cat, char *path)
{
  return 0;
}
int DelExe(char *cat, char *name)
{
  return 0;
}

int ScolShowConsole()
{
  /*  XMapWindow(Sx.display,XtWindow(tcons.hwnd));
   */
  return 0;
}

int ScolHideConsole()
{
  /*  XUnmapWindow(Sx.display,XtWindow(tcons.hwnd));
   */
  return 0;
}

int TrySUP()
{
  struct sockaddr_in ina;
  int s;
  int k;

 
  s=socket(PF_INET,SOCK_STREAM,0);
  if (s == -1) return -1;
  ina.sin_family = PF_INET;
  ina.sin_port   = htons(STDPORT);
  ina.sin_addr.s_addr = SCKbindadress();
  k=bind(s,(struct sockaddr*)&ina,sizeof(ina));
  close(s);
  return k;
}


int FindPaths(int argc,char **argv)
{
  int  i;
  char buf[1024];
  char* p;
  char path[1024];

  scriptname[0]=0;
  if (argc>1) strcpy(scriptname,argv[1]);
  if (argc>2) SCdefineRights(argv[2]);
  if (argc>3) ScolMemoryLength=atoi(argv[3]);

/* Modif P. Favre le 14/04/99
 * L'exécutable scol est forcément dans la variable d'env 'SCOLPATH'.
 */
  p=(char*)getenv("SCOLPATH");
  if (p==NULL) {
    printf("Missing environment variable 'SCOLPATH'\n");
    return -1;
  }
  else strcpy(execname,p);

  ScolGetPath(execpath,execname);

  scriptcontent[0]='$';
  if ((scriptname[0]=='$') || (scriptname[0]=='@'))
    {
      strcpy(buf,scriptname+1);
      i=0;
      while((buf[i])&&(buf[i]!='$')&&(buf[i]!='@')) i++;
      if (buf[i]==0) return -1;
      buf[i]=0;
      strcpy(scriptname,buf);
      strcpy(scriptcontent,&buf[i+1]);
    }
  else if (scriptname[0])
    {
      getcwd(path,1024);
      strcpy(buf,scriptname);
      sprintf(scriptname,"%s/%s",path,buf);
    }
  chdir(execpath);
  return 0;
}



int LoadUsmIni(char *cmdline)
{
  FILE *f;
  char src[4096];
  char *line;
  char buf[512];
  char* argv[128];
  char path[1204];
  int n;
  time_t timep;
  struct tm *res;
  
  HTTPproxy[0]=0;
  forcedIP[0]=0;
  f=fopen("usm.ini","rb");
  if (f==NULL)
    {
      return 0;
    }
  src[fread(src,1,2047,f)]=0;
  fclose(f);
  line=src;
  while((line=stdGetLine(line,buf,512)))
    {
      if ((n=strCutting(buf,argv)))
	{
	  if ((n>1)&&(!strcasecmp(argv[0],"port")))
	    {
	      STDPORT=atoi(argv[1]);
	    }
	  else if ((n>1)&&(!strcasecmp(argv[0],"echo")))
	    {
	      MaskEcho=myhtoi(argv[1]);
	    }
	  else if ((n>1)&&(!strcasecmp(argv[0],"memory"))&&(!ScolMemoryLength))
	    {
	      ScolMemoryLength=atoi(argv[1]);
	      if ((ScolMemoryLength<0)||(ScolMemoryLength>128)) ScolMemoryLength=0;
	      else ScolMemoryLength*=256*1024;
	    }
	  else if ((n>1)&&(!strcasecmp(argv[0],"forcedIP"))&&(strlen(argv[1])<64))
	    {
	      strcpy(forcedIP,argv[1]);
	    }
	  else if ((n>1)&&(!strcasecmp(argv[0],"HTTPproxy"))&&(strlen(argv[1])<64))
	    {
	      strcpy(HTTPproxy,argv[1]);
	    }
	  else if ((n>1)&&(!strcasecmp(argv[0],"log")))
	    {
	      if ((flog)&&(!strcasecmp(argv[1],"no")))
		{
		  fclose(flog);
		  flog=NULL;
		}
	      else if ((flog==NULL)&&(!strcasecmp(argv[1],"yes")))
		{
		  if (scriptname[0]==0) strcpy(path,"log/supvisor");
		  else sprintf(path,"log/%s",ScolGetName(scriptname));
		  time(&timep);
		  res=localtime(&timep);
		  if (res) sprintf(path+strlen(path),"-%d-%02d-%02d_%02d-%02d-%02d.log",
			  res->tm_year+1900,res->tm_mon+1,res->tm_mday,
			  res->tm_hour,res->tm_min,res->tm_sec);
		  flog=fopen(path,"w");
		}
	    }
	  else if ((n>1)&&(!strcasecmp(argv[0],"supervisor"))&&(!strcasecmp(argv[1],"debug")))
	    {
	      flaglogwinsup=1;
	    }
	  else if ((n>1)&&(!strcasecmp(argv[0],"plugin")))
	    {
              if (n<4) argv[3]=NULL;
              if (n<3) argv[2]=NULL;
              SCregistDLL(argv[1],argv[2],argv[3]);
            }
	  else if ((n>1)&&(!strcasecmp(argv[0],"disk")))
	    {
	      if (n>2) SPregistPart(argv[1],argv[2]);
	      else SPregistPart(argv[1],"");
	    }
//$ FA(24/04/2001): Set runtime-checks compiler option
            else if (n > 1 && !strcasecmp(argv[0], "runtime-checks")) {
              if (!strcasecmp(argv[1], "on"))
                optionRuntimeChecks = 1;
              else if (!strcasecmp(argv[1], "off"))
                optionRuntimeChecks = 0;
            }
//

	}
    }
  return 0;
}






int AnyInstance(int argc,char **argv)
{
  int i;

  if (FindPaths(argc,argv)) return 0;
  flog=NULL;
  flaglogwin=1;
  flaglogwinsup=0;
  LoadUsmIni("");


  LocalMessage=NULL;
  flaglog=1;
  MMechostr(1,"Log File of Scol Virtual Machine\n%s\n",VERSION_NAME);
  USSelectInit();

  if (SCKinit(NULL))
    {
      MMechostr(MSKRUNTIME,"erreur initialisation\n");
      return 0;
    }
  i=SCKgethostIP();
  MMechostr(MSKFOO,"Local IP = %s\n",inet_ntoa(*(struct in_addr*)&i));
  
  socklife=-1;


  //$LB (21/05/2002) : automatic launch of the supervisor
  if (scriptname[0])
  {
		if (TrySUP()==0)
        {

//$ LB (30/05/2002) : if CLUBHOUSE_SERVER, don't launch machine if supervisor has not already been launched
#ifdef CLUBHOUSE_SERVER

			MMechostr(0, "\n\n!! SCOL WARNING : with this release, a supervisor has to be launched first !! Terminate Scol....\n\n");
			EndScolMachine(0);
#else

			//$LB (21/05/2002) : if scol is launched with a script and if port STDPORT is free, automatically launch a supervisor...
			if (fork()==0)
			{
				execlp(execname,execname,NULL);
			}
			else
			{
				//$LB TODO TO DO : use semaphore instead of Sleep !!
				sleep(5);
				if (TrySUP()==0) sleep(5);
			}
#endif
        }
	}

//$ LB (30/05/2002) : if CLUBHOUSE_SERVER, don't launch the supervisor
#ifdef CLUBHOUSE_SERVER
	else
	{
			MMechostr(0, "\n\n!! SCOL WARNING : this release can't launch a supervisor, !! Terminate Scol....\n\n");
			EndScolMachine(0);
	}
#endif








  SCOLInit();






  fstart=1;
  if (mainscol()) return 0;
  
  return 1;
}




int SCOLmain(int argc,char **argv)
{

  if (!AnyInstance(argc,argv)) return -1;

  /*  XtMainLoop();
   */
  while(SCKselect()==0);
  MMechostr(MSKRUNTIME,"end\n");
  return 0;
}


/* SCsetuser : fun [S] I
   Set the calling proccess id to the one associated with the given parameter.
   Used by the control center on Linux platform because the ports
   have to be opened from a process with super-user rigths
   that must change to normal user rights */

int SCsetuser(mmachine m)
{
  struct passwd *PasswdPtr;
  SEWORD mstr;
  int status;
  
  mstr = MMpull (m)>>1;
  if (mstr == NIL) return MMpush( m, NIL );

  PasswdPtr=getpwnam( MMstartstr(m, mstr) );
  status=setuid( PasswdPtr->pw_uid );
  MMpush( m, SEI2W (status) );
  return MERROK;
}


//$LB (31/08/2001) : add MMputs function
void MMputs(int n, const char* text)
//$ FA(03/08/2001): Allow user to echo an arbitrary (non-formatted) long string
{
/* TODO TO DO
    if (flaglog && (!n || n == MSKRUNTIME || n&MaskEcho)) {
#if !defined(SERVER)
        if (theConsole) {
            uint32 color = 0x000000; // black
            switch (n) {
                case MSKFOO:
                    color = 0xaa0000;
                    break;
                case MSKRUNTIME:
                    color = 0x0000aa;
                    break;
                case MSKWARNING:
                    color = 0x000099;
                    break;
                case MSKTRACE:
                    color = 0x00aa00;
                    break;
                case MSKDEBUG:
                    color = 0x000000;
                    break;
                    
            }
            theConsole->setColor(color);
            theConsole->write(text); //$ FA(03/08/2001)
        }
#endif
        if (flog) {
            fprintf(flog, "%s", text);
            fflush(flog);
        }
    }
*/
MMechostr(n,"%s",text);
}
