Exemple #1
0
// OK, we've now got all the headers read in, so we're ready to start
// on the body. But we need to see what info we can glean from the headers
// first...
void Response::BeginBody()
{

	m_Chunked = false;
	m_Length = -1;	// unknown
	m_WillClose = false;

	// using chunked encoding?
	const char* trenc = getheader( "transfer-encoding" );
	if( trenc && 0==_stricmp( trenc, "chunked") )
	{
		m_Chunked = true;
		m_ChunkLeft = -1;	// unknown
	}

	m_WillClose = CheckClose();

	// length supplied?
	const char* contentlen = getheader( "content-length" );
	if( contentlen && !m_Chunked )
	{
		m_Length = atoi( contentlen );
	}

	// check for various cases where we expect zero-length body
	if( m_Status == NO_CONTENT ||
		m_Status == NOT_MODIFIED ||
		( m_Status >= 100 && m_Status < 200 ) ||		// 1xx codes have no body
		m_Method == "HEAD" )
	{
		m_Length = 0;
	}


	// if we're not using chunked mode, and no length has been specified,
	// assume connection will close at end.
	if( !m_WillClose && !m_Chunked && m_Length == -1 )
		m_WillClose = true;



	// Invoke the user callback, if any
	if( m_Connection.m_ResponseBeginCB )
		(m_Connection.m_ResponseBeginCB)( this, m_Connection.m_UserData );

/*
	printf("---------BeginBody()--------\n");
	printf("Length: %d\n", m_Length );
	printf("WillClose: %d\n", (int)m_WillClose );
	printf("Chunked: %d\n", (int)m_Chunked );
	printf("ChunkLeft: %d\n", (int)m_ChunkLeft );
	printf("----------------------------\n");
*/
	// now start reading body data!
	if( m_Chunked )
		m_State = CHUNKLEN;
	else
		m_State = BODY;
}
Exemple #2
0
void Connection::ThreadStarter()
{
    log_trace();
    while (threadRunning_ && !CheckClose())
    {
        Work(100);
    }
    log_debug("Connection thread exiting, fd=" << socket_.GetFileDescriptor());
    // force the close to close since the thread may not be deleted right away
    socket_.Close();
    threadRunning_ = false;
}
Exemple #3
0
void Connection::Work(int ms)
{
    int timeLeft;
    struct timeval startTime;
    struct timeval currentTime;
    gettimeofday( &startTime, 0 );

    do
    {
        //log_debug("Work: timeLeft=" << timeLeft.count());
        // Construct the sets of descriptors we are interested in
        fd_set inFd, outFd;
        FD_ZERO(&inFd);
        FD_ZERO(&outFd);

        // add connection socket
        int maxFd = socket_.GetFileDescriptor();
        if (WaitForReadability())
            FD_SET(maxFd, &inFd);
        if (WaitForWritability())
            FD_SET(maxFd, &outFd);

        // Check for events
        int nEvents;
        if (ms < 0)
            nEvents = select(maxFd + 1, &inFd, &outFd, NULL, NULL);
        else
        {
            gettimeofday( &currentTime, 0 );
            timeLeft = ms - MilliTimeDiff(currentTime,startTime);

            struct timeval tv;
            tv.tv_sec = timeLeft / 1000;
            tv.tv_usec = (timeLeft - tv.tv_sec*1000) * 1000;
            nEvents = select(maxFd + 1, &inFd, &outFd, NULL, &tv);
        }

        if (nEvents < 0)
            break;

        // Process server events
        int fd = GetFileDescriptor();
        if ((FD_ISSET(fd, &inFd )) || (FD_ISSET(fd, &outFd)))
            Process();

        gettimeofday( &currentTime, 0 );
        timeLeft = ms - MilliTimeDiff(currentTime,startTime);

    } while (!CheckClose() && ((ms < 0) || (timeLeft > 0)));
}
Exemple #4
0
void cUIManager::Update() {
	mElapsed = mWindow->Elapsed();

	bool wasDraggingControl = IsControlDragging();

	mControl->Update();

	cUIControl * pOver = mControl->OverFind( mKM->GetMousePosf() );

	if ( pOver != mOverControl ) {
		if ( NULL != mOverControl ) {
			SendMsg( mOverControl, cUIMessage::MsgMouseExit );
			mOverControl->OnMouseExit( mKM->GetMousePos(), 0 );
		}

		mOverControl = pOver;

		if ( NULL != mOverControl ) {
			SendMsg( mOverControl, cUIMessage::MsgMouseEnter );
			mOverControl->OnMouseEnter( mKM->GetMousePos(), 0 );
		}
	} else {
		if ( NULL != mOverControl )
			mOverControl->OnMouseMove( mKM->GetMousePos(), mKM->PressTrigger() );
	}

	if ( mKM->PressTrigger() ) {
		/*if ( !wasDraggingControl && mOverControl != mFocusControl )
			FocusControl( mOverControl );*/

		if ( NULL != mOverControl ) {
			mOverControl->OnMouseDown( mKM->GetMousePos(), mKM->PressTrigger() );
			SendMsg( mOverControl, cUIMessage::MsgMouseDown, mKM->PressTrigger() );
		}

		if ( !mFirstPress ) {
			mDownControl = mOverControl;
			mMouseDownPos = mKM->GetMousePos();

			mFirstPress = true;
		}
	}

	if ( mKM->ReleaseTrigger() ) {
		if ( NULL != mFocusControl ) {
			if ( !wasDraggingControl ) {
				if ( mOverControl != mFocusControl )
					FocusControl( mOverControl );

				mFocusControl->OnMouseUp( mKM->GetMousePos(), mKM->ReleaseTrigger() );
				SendMsg( mFocusControl, cUIMessage::MsgMouseUp, mKM->ReleaseTrigger() );

				if ( mKM->ClickTrigger() ) { // mDownControl == mOverControl &&
					SendMsg( mFocusControl, cUIMessage::MsgClick, mKM->ClickTrigger() );
					mFocusControl->OnMouseClick( mKM->GetMousePos(), mKM->ClickTrigger() );

					if ( mKM->DoubleClickTrigger() ) {
						SendMsg( mFocusControl, cUIMessage::MsgDoubleClick, mKM->DoubleClickTrigger() );
						mFocusControl->OnMouseDoubleClick( mKM->GetMousePos(), mKM->DoubleClickTrigger() );
					}
				}
			}
		}

		mFirstPress = false;
	}

	CheckClose();
}
Exemple #5
0
//---------------------------------------------------------------------------------------------------------------------------
//StartServer()
//start  listen
//---------------------------------------------------------------------------------------------------------------------------
int  CIocpServer::StartServer(const char *local_ip,const char *local_port)
{
	if(!m_Inited)
	{
		fprintf(stderr, "Run Init() first !\n");
		return -1;
	}

	SOCKET_OBJ      *sockobj=NULL;
	HANDLE           hrc;
	int              endpointcount=0,
					 rc,
					 i;
	HANDLE			 event_accept; 

	struct addrinfo *res=NULL,
		*ptr=NULL;


#ifdef LOG_LEVEL2
	printf("LOG_LEVEL2");
#else 
//
#ifdef LOG_LEVEL1
	printf("LOG_LEVEL1");
#else
	printf("LOG_NONE");
#endif
//
#endif

	printf("\n\n连接线程:%d: 监听线程:%d; 平均连接数:%d ; ",
		m_CLThreads,m_Threads,AVERAGE_CONNECTIONS);
	printf("IOCP版本: %s;\n",IOCP_VERSION);

#ifdef ENABLE_KEEPALIVE
	printf("心跳检测:开启; ");
#else
	printf("心跳检测:关闭; ");
#endif
	printf("心跳超时:%d; 关闭延时:%d\n",KEEPALIVE_TIME,CLOSE_DELAY);


	res = ResolveAddress(local_ip, local_port, AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (res == NULL)
	{
		fprintf(stderr, "ResolveAddress failed to return any addresses!\n");
		return -1;
	}

	ptr = res;
	if (ptr)
	{
		sockobj = GetSocketObj(INVALID_SOCKET);
		// create the socket
		sockobj->s = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
		if (sockobj->s == INVALID_SOCKET)
		{
			fprintf(stderr,"socket failed: %d\n", WSAGetLastError());
			return -1;
		}

		// Associate the socket and its SOCKET_OBJ to the completion port
		hrc = CreateIoCompletionPort((HANDLE)sockobj->s, CompletionPort, (ULONG_PTR)sockobj, 0);
		if (hrc == NULL)
		{
			fprintf(stderr, "CreateIoCompletionPort failed: %d\n", GetLastError());
			return -1;
		}

		// bind the socket to a local address and port
		rc = bind(sockobj->s, ptr->ai_addr, (int)ptr->ai_addrlen);
		if (rc == SOCKET_ERROR)
		{
			fprintf(stderr, "bind failed: %d\n", WSAGetLastError());
			return -1;
		}

		BUFFER_OBJ *acceptobj=NULL;
		GUID        guidAcceptEx = WSAID_ACCEPTEX;
		GUID	    guidGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS;
		DWORD       bytes;

		// Need to load the Winsock extension functions from each provider
		//    -- e.g. AF_INET and AF_INET6. 
		rc = WSAIoctl(
			sockobj->s,
			SIO_GET_EXTENSION_FUNCTION_POINTER,
			&guidAcceptEx,
			sizeof(guidAcceptEx),
			&lpfnAcceptEx,
			sizeof(lpfnAcceptEx),
			&bytes,
			NULL,
			NULL
			);
		if (rc == SOCKET_ERROR)
		{
			fprintf(stderr, "WSAIoctl: SIO_GET_EXTENSION_FUNCTION_POINTER failed: %d\n",
				WSAGetLastError());
			return -1;
		}

		rc = WSAIoctl(
			sockobj->s,
			SIO_GET_EXTENSION_FUNCTION_POINTER,
			&guidGetAcceptExSockaddrs,
			sizeof(guidGetAcceptExSockaddrs),
			&lpfnGetAcceptExSockaddrs,
			sizeof(lpfnGetAcceptExSockaddrs),
			&bytes,
			NULL,
			NULL
			);
		if (rc == SOCKET_ERROR)
		{
			fprintf(stderr, "WSAIoctl: SIO_GET_EXTENSION_FUNCTION_POINTER faled: %d\n",
				WSAGetLastError());
			return -1;
		}

		// For TCP sockets, we need to "listen" on them
		rc = _(sockobj->s, 32);
		if (rc == SOCKET_ERROR)
		{
			fprintf(stderr, "listen failed: %d\n", WSAGetLastError());
			return -1;
		}
		
		event_accept =  CreateEvent(NULL, TRUE, FALSE, NULL);
		if(event_accept == NULL)
		{
			fprintf(stderr,"event_accept CreateEvent failed: %d\n",GetLastError());
			return -1;
		}

		rc = WSAEventSelect(sockobj->s,event_accept,FD_ACCEPT);
		if (rc == SOCKET_ERROR)
		{
			fprintf(stderr, "WSAEventSelect failed: %d\n", WSAGetLastError());
			return -1;
		}

        
		// Post the AcceptEx(s)
		for(i=0; i < DEFAULT_ACCEPTEX_COUNT ;i++)
		{
			acceptobj = GetBufferObj();
			sockobj->recvobj = acceptobj;
			if(PostAccept(sockobj, acceptobj) != 0)
			{
				FreeBufferObj(acceptobj);
				fprintf(stderr, "PostAccept failed: %d\n", WSAGetLastError());
				return -1;
			}
		}


		endpointcount++;
		ptr = ptr->ai_next;
	}
	// free the addrinfo structure for the 'bind' address
	freeaddrinfo(res);

	DWORD sleeptime = CLOSE_DELAY / 2;				 //每次sleep的时间

#ifdef LOG_STATUS
	long		sock=0;
	long		buf=0;
#endif

	if(sleeptime < 50) //限制在50ms以外
		sleeptime = 50;

	DWORD time_ontimer=GetTickCount();
	DWORD time_close = time_ontimer;
	DWORD time_alive = time_ontimer;
	DWORD newtime;
	BUFFER_OBJ *acceptobj;

	printf("\n>>> 服务器地址: %s: 端口号: %s <<<\n\n",
		local_ip, local_port);

	while (1)
	{
		rc = WaitForSingleObject(event_accept,sleeptime);
		if(rc == WAIT_FAILED)
		{
			fprintf(stderr,"WaitForSingleObject Failed:%d\n",GetLastError());
			break;
		} 
		else if(rc != WAIT_TIMEOUT) //GOT FD_ACCEPT
		{
		//	acceptobj = GetBufferObj();
		//	PostAccept(sockobj,acceptobj);
			if(m_accept_count < DEFAULT_ACCEPTEX_COUNT /2)
			{
				for(int i=0;i<DEFAULT_ACCEPTEX_COUNT/2;i++)
				{
					acceptobj = GetBufferObj();
					PostAccept(sockobj,acceptobj);
				}
			}
			ResetEvent(event_accept);
		}

		newtime = GetTickCount();

		if(newtime - time_ontimer > 3000) // 3秒
		{ 
			OnTimer();  //call virtual timer();
			time_ontimer = newtime;			
#ifdef LOG_STATUS		//显示服务器状态
			if(sock != gTotalSock || buf != gTotalBuf)
			{
				Log_Server.Write("服务器状态 : sock : %d ; buf : %d ; IoCount : %d ;",gTotalSock,gTotalBuf,gIoCount);

				sock = gTotalSock;
				buf = gTotalBuf;
			}
#endif

		}

		//Check Close
		if(newtime - time_close > sleeptime)
		{
			time_close = newtime;
			CheckClose();
		}

		if(newtime - time_alive > KEEPALIVE_TIME / 3)
		{
			CheckAlive();
			time_alive = newtime;
		}

	}

	WSACleanup();

	return 0;
}
Exemple #6
0
int	main	(int argc, char *argv[])
{
  int Thread;
  int c;
  char Input[MAXCMD];
  char *Arg;
  struct sigaction SigAct;
  int Fed;
  int Len;
  int rc;
  int IsUrgent;
  int KeepRunning=1;
  int RunAsDaemon=0;
  int ResetQueue=0;
  int KillScheduler=0;
  int Test=0; /* 1=test and continue, 2=test and quit */
  pid_t Pid;

  /* check args */
  while((c = getopt(argc,argv,"dkHiIL:lvqRtTh")) != -1)
    {
    switch(c)
      {
      case 'd':
	RunAsDaemon=1;
	break;
      case 'H':
	IgnoreHost=1;
	break;
      case 'i':
	DB = DBopen();
	if (!DB)
	  {
	  fprintf(stderr, "FATAL: Unable to connect to database\n");
	  exit(-1);
	  }
	/* Nothing to initialize */
	DBclose(DB);
	return(0);
      case 'I':
	UseStdin=1;
	break;
      case 'k': /* kill the scheduler */
         KillScheduler = 1;
      case 'l': /* tell the scheduler to redo logs */
	break;
      case 'L':
	LogFile=optarg;
	break;
      case 'q':
	ShowState=0;
	break;
      case 'R':
	ResetQueue=1;
	break;
      case 't':
	Test=2;
	break;
      case 'T':
	Test=1;
	break;
      case 'v':
	Verbose++;
	break;
      case 'h':
      default:
	Usage(argv[0]);
	DBclose(DB);
	exit(-1);
      }
    }

  if ((optind != argc-1) && (optind != argc))
	{
	Usage(argv[0]);
	DBclose(DB);
	exit(-1);
	}

  /* set to PROJECTUSER and PROJECTGROUP */
  SetPuserPgrp(ProcessName);

  if (KillScheduler)
  {
	  DB = DBopen();
    if (!DB)
    {
	    fprintf(stderr, "FATAL: Unable to connect to database\n");
      exit(-1);
    }

    /* kill scheduler */
    LogPrint("Scheduler kill requested.  Killing scheduler.\n");
    StopScheduler(1);
    StopWatchdog();
    exit(0);
  }

  /* Become a daemon?  */
  if (RunAsDaemon)
  {
    /* do not close stdout/stderr when using a LogFile */
    rc = daemon(0,(LogFile!=NULL));
    fclose(stdin);
  }

  /* must be after uid is set since this might create the log file */
  LogPrint("Scheduler started.  %s\n", BuildVersion);

  /* Lock the scheduler, so no other scheduler can run */
  rc = LockName(ProcessName);
  if (rc > 0)  /* already locked */
  {
    Pid = rc;
  }
  else if (rc == 0)  /* new lock */
  {
    Pid = LockGetPID(ProcessName);
    if (!Pid)
    { 
      LogPrint("*** %s lock error ***\n", ProcessName);
      exit(-1);
    }
  }
  else
  {
    LogPrint("*** %s lock failed.  Scheduler exitting. ***\n", ProcessName);
    exit(-1);
  }

  if (Verbose) 
    LogPrint("*** %s successfully locked, pid %d  ***\n", ProcessName, Pid);


  /**** From here on, I am the only scheduler running ****/


  /**************************************/
  /* catch signals */
  /**************************************/
  memset(&SigAct,0,sizeof(SigAct));
  SigAct.sa_sigaction = HandleSig;
  sigemptyset(&(SigAct.sa_mask));
  SigAct.sa_flags = SA_SIGINFO | SA_RESTART;
  sigaction(SIGCHLD,&SigAct,NULL);

  /* handle signals to the parent */
  SigAct.sa_flags = SA_SIGINFO | SA_RESTART;
  SigAct.sa_sigaction = ParentSig;
  if (sigaction(SIGSEGV,&SigAct,NULL) != 0) perror("SIGSEGV");
  if (sigaction(SIGQUIT,&SigAct,NULL) != 0) perror("SIGQUIT");
  if (sigaction(SIGTERM,&SigAct,NULL) != 0) perror("SIGTERM");
  if (sigaction(SIGINT,&SigAct,NULL) != 0) perror("SIGINT");
  if (sigaction(SIGUSR1,&SigAct,NULL) != 0) perror("SIGUSR1");
  if (sigaction(SIGUSR2,&SigAct,NULL) != 0) perror("SIGUSR2");
  if (sigaction(SIGALRM,&SigAct,NULL) != 0) perror("SIGALRM");
  /* ignore dead pipes when using -lpq -- see http://archives.postgresql.org/pgsql-bugs/2003-03/msg00118.php */
  signal(SIGPIPE,SIG_IGN);
  signal(SIGALRM,SIG_IGN); /* ignore self-wakeups */

  /* Prepare logging */
  LogPrint("*** Scheduler started, PID %d  ***\n", Pid);

  /* Log to file? (Not if I'm killing schedulers) */
  if ((dup2(fileno(stdout),fileno(stderr))) < 0)
      {
      LogPrint("FATAL: Unable to write to redirect stderr to log.  Exiting. \n");
      DBclose(DB);
      exit(-1);
      }

  /* init queue */
  DB = DBopen();
  if (!DB)
    {
    LogPrint("FATAL: Unable to connect to database.  Exiting. \n");
    exit(-1);
    }

  DBSetHostname();

  /* Prepare for logging errors to DB */
  DBErrorInit();

  /* If we're resetting the queue */
  if (ResetQueue)
	{
    /* If someone has a start without an end, then it is a hung process */
    DBLockAccess(DB,"UPDATE jobqueue SET jq_starttime=null WHERE jq_endtime is NULL;");
    LogPrint("Job queue reset.\n");
	}

  /* init storage */
  DBQinit();
  if (optind == argc) InitEngines(DEFAULTSETUP);
  else InitEngines(argv[optind]);

  /* Check for good agents */
  if (SelfTest())
    {
    LogPrint("FATAL: Self Test failed.  Inconsistent agent(s) detected.  Exiting. \n");
    DBclose(DB);
    exit(-1);
    }

  /* Check for competing schedulers */
  if (DBCheckSchedulerUnique())
  {
    /* Yes, a scheduler is running.  So log and exit.  */
    LogPrint("*** Scheduler not starting since another currently running.  ***\n");
    exit(0);
  }
  else
    DBCheckStatus();  /* clean scheduler_status and jobqueue tables */

  /* See if we're testing */
  if (Test)
  {
    rc = TestEngines();
    /* rc = number of engine failures */
    if (rc == 0) 
      LogPrint("STATUS: All scheduler agents are operational.\n");
    else 
      LogPrint("STATUS: %d agents failed to initialize.\n",rc);

    if ((Test > 1) || rc)
	  {
	    LogPrint("*** %d agent failures.  Scheduler exiting. \n", rc);
      /* clean up scheduler */
      StopScheduler(0);
      exit(0);
  	}
  }


  /**************************************/
  /* while there are commands to run... */
  /**************************************/
  Thread=0;
  Fed=0;
  while(KeepRunning)
    {
    SaveStatus();

    /* check for data to process */
    if (UseStdin)
	{
	rc = SelectAnyData(1,fileno(stdin));
	if (feof(stdin)) SLOWDEATH=1;
	}
    else rc = SelectAnyData(0,0);

    if (rc & 0x01) /* got stdin input to feed to a child */
	{
	IsUrgent=0;
	Len=ReadCmd(stdin,Input,MAXCMD);
	if (Len < 0) break; /* skip blank lines and EOF */
	if (Len == 0) continue; /* skip blank lines and EOF */
	if (Input[0]=='!')
	  {
	  IsUrgent=1;
	  Input[0]=' ';
	  }

	/* Got a command! */
	if (Verbose) LogPrint("Parent got command: %s\n",Input);
	Arg = strchr(Input,'|');
	if (!Arg)
		{
		LogPrint("ERROR: Unknown command (len=%d) '%s'\n",Len,Input);
		continue; /* skip unknown lines */
		}
	Arg[0]='\0'; Arg++;	/* skip space */
	while((Arg[0] != '\0') && isspace(Arg[0]))	Arg++;

	/* feed command to child */
	/* handle special commands (scheduler is the child) */
	Fed=SchedulerCommand(Input,Arg);

	/* if command needs child, find a child to feed it to */
	while(!Fed)
	  {
	  Thread = GetChild(Input,IsUrgent);
	  if (Thread < 0)
	    {
	    if (SelectAnyData(0,0) & 0x02) /* wait for a child to become ready */
		{
		Thread = GetChild(Input,IsUrgent);
		}
	    }
	  if (Thread >= 0)
		{
		if (CM[Thread].Status != ST_RUNNING)
		  {
		  ChangeStatus(Thread,ST_RUNNING);
		  }
		if (Verbose) LogPrint("(a) Feeding child[%d]: '%s'\n",Thread,Arg);
		memset(CM[Thread].Parm,'\0',MAXCMD);
		strcpy(CM[Thread].Parm,Arg);
		Input[Len++]='\n'; /* add a \n to end of Arg */
		write(CM[Thread].ChildStdin,Arg,strlen(Arg));
		Fed=1;
		}
	  /* Thread == -1 is a timeout -- retry the request */
	  else if (Thread <= -2)
		{
		LogPrint("ERROR: No living engines for '%s'\n",Input);
		Fed=1;	/* skip this bad command */
		}
	  } /* while not Fed */
	} /* if processing stdin input */
    else
	{
	/* this will pause if it detects a fast loop */
	Fed = DBProcessQueue(DB);
	}
//    if (Verbose) printf("Time: %d  Fed=%d\n",(int)time(NULL),Fed);
    if (Fed==0)
      {
      /* What happens if there was no job to process? */
      StaleChild();
      if (SLOWDEATH)
	{
	Thread=0;
	while((Thread < MaxThread) && (CM[Thread].Status != ST_RUNNING))
		Thread++;
	/* nothing running? Quit! */
	if (Thread >= MaxThread) KeepRunning=0;
	/* else, keep running */
	} /* if SLOWDEATH */
      }
    } /* while reading a command */

  /* if it gets here, then either all children are dead or there is
     no more input */

  /* wait for all children to finish */
  while(RunCount > 0)
    {
    SelectAnyData(0,0);
    }

  /* tell children "no more food" by closing stdin */
  if (Verbose) LogPrint("Telling all children: No more items to process.\n");
  for(Thread=0; Thread < MaxThread; Thread++)
    {
    if (CM[Thread].Status > ST_FREE) CheckClose(CM[Thread].ChildStdin);
    }

  /* At this point, there should be no children */
  SLOWDEATH=1;
  SelectAnyData(0,0);

  /* clean up: kill children (should be redundant) */
  signal(SIGCHLD,SIG_IGN); /* ignore screams of death */
  for(Thread=0; (Thread < MaxThread); Thread++)
    {
    if (CM[Thread].Status > ST_FREE)
      {
      /* kill the children! kill! kill! */
      if (CM[Thread].Status == ST_RUNNING)
	{
	if (CM[Thread].IsDB) DBremoveChild(Thread,1,"Scheduler ended");
	}
      CM[Thread].IsDB=0;
      CheckClose(CM[Thread].ChildStdin);
      CheckClose(CM[Thread].ChildStdinRev);
      CheckClose(CM[Thread].ChildStdout);
      CheckClose(CM[Thread].ChildStdoutRev);
      CM[Thread].ChildStdin = 0;
      CM[Thread].ChildStdinRev = 0;
      CM[Thread].ChildStdout = 0;
      CM[Thread].ChildStdoutRev = 0;
      ChangeStatus(Thread,ST_FREE);
      if (CM[Thread].ChildPid) kill(CM[Thread].ChildPid,SIGKILL);
      }
    }

  /* scheduler cleanup */
  if (DB) DBErrorClose();  /* currently a noop */
  DBQclose();

  /* cleanup scheduler */
  StopScheduler(0);

  /* print statistics */
  DebugThreads(1);
  LogPrint("*** Scheduler completed.  Exiting.  ***\n");
  return(0);
} /* main() */