예제 #1
0
bool ErrorLog::WriteErrorString(char* ErrorString)
{
	if(!ErrorString){
		return false;
	}
	if(mFileBeingAccessed){
		return false;
	}
	if(!OpenErrorLog()){
		return false;
	}

	fputs(ErrorString, mFilePointer);
	fputc('\n', mFilePointer); //NEW LINE CHARACTER IS REQUIRED.
	CloseErrorLog();
	return true;
}
예제 #2
0
void ErrorLog::AddFinalErrorLogString()
{
		time_t OurTime = time(NULL);
	tm* timestruct = localtime(&OurTime); //LOCAL TIME IS REQUIRED.
	char InitialString[500];
	
	if(!OpenErrorLog()){
		return;
	}
	
	sprintf(InitialString, "--------------------------------------------");
	fputs(InitialString, mFilePointer);
	fputc('\n', mFilePointer);
	sprintf(InitialString, "Logging Ended %d/%d/%d - %2.2d:%2.2d", timestruct->tm_mon + 1, timestruct->tm_mday, 1900 + timestruct->tm_year, timestruct->tm_hour, timestruct->tm_min);
	fputs(InitialString, mFilePointer);
	fputc('\n', mFilePointer);
	
	CloseErrorLog();
}
예제 #3
0
void ErrorLog::AddInitialErrorLogString()
{
	time_t OurTime = time(NULL);
	tm* timestruct = localtime(&OurTime); //LOCAL TIME IS REQUIRED.
	char InitialString[500];
	sprintf(InitialString, "Charles Cox's Error Log and Reporting Dynamic (C2ELRD) (c)2001 Charles Cox - Log #%d", mErrorLogID); //LET US NOT FORGET WHO WROTE THIS THING.
	if(!OpenErrorLog()){
		return;
	}
	fputs(InitialString, mFilePointer);
	fputc('\n', mFilePointer);
	sprintf(InitialString, "Logging Started %d/%d/%d - %2.2d:%2.2d", timestruct->tm_mon + 1, timestruct->tm_mday, 1900 + timestruct->tm_year, timestruct->tm_hour, timestruct->tm_min);
	fputs(InitialString, mFilePointer);
	fputc('\n', mFilePointer);
	sprintf(InitialString, "--------------------------------------------");
	fputs(InitialString, mFilePointer);
	fputc('\n', mFilePointer);
	CloseErrorLog();
}
예제 #4
0
bool ErrorLog::GetInternalErrorInf(int ErrorNumber)
{
	//RIGHT. OKAY. THIS FILLS THE TEMPORARY ERROR INFORMATION STRUCTURE WITH STUFF.
	//WE CANNOT INITIALIZE AN INSTANCE OF AN ERRORINFO UNTIL WE HAVE THE STRING AND THE ID.
	long FirstAsterixPoint;
	long OurErrorFrontAsterixPoint;
	long OurErrorBackAsterixPoint;
	long CurrentPos = 0;
	long NumToMalloc;
	char * TempBuf;
	char * PureString;
	int StringPos;
	int PureStringStartPos;
	int PureStringEndPos;
	int PureStringLen;
	int StringLen;
	
	int CurrentError;
	//FIRST, ELIMINATE WRONG CASES.
	if(ErrorNumber < 0 || ErrorNumber >= mNumberOfErrors || !mErrorFileName || mErrorLogID < 0){
		return false;
	}

	//NOW, WE UTILIZE AN ALGORITHM TO SEARCH THROUGH FILE. START BY MOVING TO THE FIRST ASTERIX.
	if(!OpenErrorLog()){
		return false;
	}
	fseek(mFilePointer, 0, SEEK_SET);

	FirstAsterixPoint = SeekToNextAsterix();
	if(FirstAsterixPoint == -1){
		CloseErrorLog();
		return false;
	} //WE CAN NOW USE THIS FIRST ASTERIX POINT AS AN OFFSET.

	OurErrorFrontAsterixPoint = FirstAsterixPoint;

	for(CurrentError = -1; CurrentError < ErrorNumber - 1; CurrentError++){
		SeekToNextAsterix();
		OurErrorFrontAsterixPoint = SeekToNextAsterix();
	}

	//WE NOW SHOULD BE AT THE FIRST ASTERIX OF OUR DESIRED LINE.
	//RUN TO THE NEXT ASTERIX, RETRIEVE THE DIFFERENCE, AND THAT'S OUR MALLOC AMOUNT... ADD 3 FOR NEWLINE, ENDCHAR, AND LF WACKINESS.
	OurErrorBackAsterixPoint = SeekToNextAsterix();
	if(OurErrorFrontAsterixPoint == -1 || OurErrorBackAsterixPoint == -1){
		CloseErrorLog();
		return false;
	}
	NumToMalloc = OurErrorBackAsterixPoint - OurErrorFrontAsterixPoint;
	TempBuf = new char[NumToMalloc + 3];
	//SHOULD BE ABLE TO SET BACK TO THE LAST ASTERIX, THEN DO AN FGETS, STRIP NEW LINE.
	fseek(mFilePointer, OurErrorFrontAsterixPoint, SEEK_SET);
	fgets(TempBuf, NumToMalloc, mFilePointer);
	//THAT HAD OUGHT TO BE ALL WE NEED WITH THE FILE.
	CloseErrorLog();
	StripStringNewLine(TempBuf);
	StringLen = strlen(TempBuf); //WE HAD BETTER BE SURE.


	//TADA. NOW PARSE THE STRING.
	for(StringPos = 0; StringPos < StringLen;  StringPos++){
		if(TempBuf[StringPos] == '-' && TempBuf[StringPos + 1] == ' ' && TempBuf[StringPos - 1] == ' '){ //THIS LOOKS LIKE OUR START POINT.
			PureStringStartPos = StringPos + 2;
			break;
		}
	}
	if(PureStringStartPos == -1){
		delete [] TempBuf;
		return false;
	}
	PureStringEndPos = StringLen - 3; //ONE FOR '\0', ONE FOR *, AND ONE FOR ZERO-BASED COUNTING SYSTEM
	PureStringLen = PureStringEndPos - PureStringStartPos;
	PureString = new char[StringLen + 2];
	for(int i = 0, k = PureStringStartPos; i < StringLen; i++, k++){
		PureString[i] = TempBuf[k];
	}
	PureString[i] = '\0';
	//WE GOT IT.
	delete [] TempBuf;
	ErrorInformation EInfo(ErrorNumber, PureString);
	mTemporaryInfo = EInfo;
	delete [] PureString;
	return true;
}
예제 #5
0
파일: wwwoffled.c 프로젝트: metux/wwwoffle
int main(int argc, char** argv)
{
 int i;
 int err;
 struct stat buf;
 int uid,gid;

 /* Parse the command line options */

 for(i=1;i<argc;i++)
   {
    if(!strcmp(argv[i],"-h") || !strcmp(argv[i],"--help"))
       usage(1);

    if(!strcmp(argv[i],"--version"))
       usage(2);

    if(!strcmp(argv[i],"-d"))
      {
       detached=0;

       if(i<(argc-1) && isdigit(argv[i+1][0]))
         {
          StderrLevel=Fatal+1-atoi(argv[++i]);

          if(StderrLevel<0 || StderrLevel>Fatal)
            {fprintf(stderr,"wwwoffled: The '-d' option requires a number between 0 and %d.\n",Fatal+1); exit(1);}
         }

       continue;
      }

    if(!strcmp(argv[i],"-l"))
      {
       if(++i>=argc)
         {fprintf(stderr,"wwwoffled: The '-l' option requires a filename.\n"); exit(1);}
       if(argv[i][0]!='/')
         {fprintf(stderr,"wwwoffled: The '-l' option requires an absolute pathname.\n"); exit(1);}

       log_file=argv[i];
       continue;
      }

    if(!strcmp(argv[i],"-p"))
      {
       print_pid=1;
       continue;
      }

    if(!strcmp(argv[i],"-f"))
      {
       nofork=1;
       detached=0;
       continue;
      }

    if(!strcmp(argv[i],"-c"))
      {
       if(++i>=argc)
         {fprintf(stderr,"wwwoffled: The '-c' option requires a filename.\n"); exit(1);}

       config_file=argv[i];
       continue;
      }

    fprintf(stderr,"wwwoffled: Unknown option '%s'.\n",argv[i]); exit(1);
   }

 /* Combination options */

 if(log_file)
   {
    if(nofork)
       log_file=NULL;           /* -f overrides -l */
    else
       detached=1;              /* -l overrides -d */

    if(StderrLevel==-1)
       StderrLevel=Inform;      /* -l sets log level if no -d */
   }

 /* Initialise things. */

 for(i=0;i<MAX_FETCH_SERVERS;i++)
    fetch_pids[i]=0;

 for(i=0;i<MAX_SERVERS;i++)
    server_pids[i]=0;

 if(log_file)
    OpenErrorLog(log_file);

 InitErrorHandler("wwwoffled",0,1); /* use stderr and not syslog to start with. */

 /* Read the configuration file. */

 InitConfigurationFile(config_file);

 init_io(STDERR_FILENO);

 if(ReadConfigurationFile(STDERR_FILENO))
    PrintMessage(Fatal,"Error in configuration file '%s'.",ConfigurationFileName());

 finish_io(STDERR_FILENO);

 InitErrorHandler("wwwoffled",ConfigInteger(UseSyslog),1); /* enable syslog if requested. */

 /* Print a startup message. */

#if USE_IPV6
#define IPV6_STRING "with ipv6"
#else
#define IPV6_STRING "without ipv6"
#endif

#define ZLIB_STRING "with zlib"

#define GNUTLS_STRING "with gnutls"

 PrintMessage(Important,"WWWOFFLE Demon Version %s (%s,%s,%s) started.",WWWOFFLE_VERSION,IPV6_STRING,ZLIB_STRING,GNUTLS_STRING);
 PrintMessage(Inform,"WWWOFFLE Read Configuration File '%s'.",ConfigurationFileName());

 /* Change the user and group. */

 gid=ConfigInteger(WWWOFFLE_Gid);
 uid=ConfigInteger(WWWOFFLE_Uid);

 if(uid!=-1)
    seteuid(0);

 if(log_file && (uid!=-1 || gid!=-1))
    chown(log_file,(uid_t)uid,(gid_t)gid);

 if(gid!=-1)
   {
#if HAVE_SETGROUPS
    if(getuid()==0 || geteuid()==0)
       if(setgroups(0,NULL)<0)
          PrintMessage(Fatal,"Cannot clear supplementary group list [%!s].");
#endif

#if HAVE_SETRESGID
    if(setresgid((gid_t)gid,(gid_t)gid,(gid_t)gid)<0)
       PrintMessage(Fatal,"Cannot set real/effective/saved group id to %d [%!s].",gid);
#else
    if(geteuid()==0)
      {
       if(setgid((gid_t)gid)<0)
          PrintMessage(Fatal,"Cannot set group id to %d [%!s].",gid);
      }
    else
      {
#if HAVE_SETREGID
       if(setregid(getegid(),(gid_t)gid)<0)
          PrintMessage(Fatal,"Cannot set effective group id to %d [%!s].",gid);
       if(setregid((gid_t)gid,(gid_t)~1)<0)
          PrintMessage(Fatal,"Cannot set real group id to %d [%!s].",gid);
#else
       PrintMessage(Fatal,"Must be root to totally change group id.");
#endif
      }
#endif
   }

 if(uid!=-1)
   {
#if HAVE_SETRESUID
    if(setresuid((uid_t)uid,(uid_t)uid,(uid_t)uid)<0)
       PrintMessage(Fatal,"Cannot set real/effective/saved user id to %d [%!s].",uid);
#else
    if(geteuid()==0)
      {
       if(setuid((uid_t)uid)<0)
          PrintMessage(Fatal,"Cannot set user id to %d [%!s].",uid);
      }
    else
      {
#if HAVE_SETREUID
       if(setreuid(geteuid(),(uid_t)uid)<0)
          PrintMessage(Fatal,"Cannot set effective user id to %d [%!s].",uid);
       if(setreuid((uid_t)uid,(uid_t)~1)<0)
          PrintMessage(Fatal,"Cannot set real user id to %d [%!s].",uid);
#else
       PrintMessage(Fatal,"Must be root to totally change user id.");
#endif
      }
#endif
   }

 if(uid!=-1 || gid!=-1)
    PrintMessage(Inform,"Running with uid=%d, gid=%d.",geteuid(),getegid());

 if(geteuid()==0 || getegid()==0)
    PrintMessage(Warning,"Running with root user or group privileges is not recommended.");

 /* Create, Change to and open the spool directory. */

 umask(0);

 if(stat(ConfigString(SpoolDir),&buf))
   {
    err=mkdir(ConfigString(SpoolDir),(mode_t)ConfigInteger(DirPerm));
    if(err==-1 && errno!=EEXIST)
       PrintMessage(Fatal,"Cannot create spool directory %s [%!s].",ConfigString(SpoolDir));
    stat(ConfigString(SpoolDir),&buf);
   }

 if(!S_ISDIR(buf.st_mode))
    PrintMessage(Fatal,"The spool directory %s is not a directory.",SpoolDir);

 err=ChangeToSpoolDir(ConfigString(SpoolDir));
 if(err==-1)
    PrintMessage(Fatal,"Cannot change to spool directory %s [%!s].",ConfigString(SpoolDir));

 /* Bind the HTTP proxy socket(s). */

#if USE_IPV6
 if(ConfigString(Bind_IPv6))
   {
    http_fd[1]=OpenServerSocket(ConfigString(Bind_IPv6),ConfigInteger(HTTP_Port));
    if(http_fd[1]==-1)
       PrintMessage(Fatal,"Cannot create HTTP IPv6 server socket.");
   }
#endif

 if(ConfigString(Bind_IPv4))
   {
    http_fd[0]=OpenServerSocket(ConfigString(Bind_IPv4),ConfigInteger(HTTP_Port));
    if(http_fd[0]==-1)
      {
#if USE_IPV6
       if(http_fd[1]!=-1 && /* PS 2003-01-13 redundant? If IPv6 is listening to 0 then it doesn't matter what address IPv4 is listening to... (at least on linux?) */
                            /* ConfigString(Bind_IPv4) && !strcmp(ConfigString(Bind_IPv4),"0.0.0.0") && */
                            ConfigString(Bind_IPv6) && !strcmp(ConfigString(Bind_IPv6),"[0:0:0:0:0:0:0:0]"))
          PrintMessage(Warning,"Cannot create HTTP IPv4 server socket (but the IPv6 one might accept IPv4 connections).");
       else
          PrintMessage(Fatal,"Cannot create HTTP IPv4 server socket.");
#else
       PrintMessage(Fatal,"Cannot create HTTP server socket.");
#endif
      }
   }

 if(http_fd[0]==-1 && http_fd[1]==-1)
   {
#if USE_IPV6
    PrintMessage(Fatal,"The IPv4 and IPv6 HTTP sockets were not bound; are they disabled in the config file?");
#else
    PrintMessage(Fatal,"The HTTP socket was not bound; is it disabled in the config file?");
#endif
   }

 /* Bind the HTTPS socket(s). */

 if(LoadRootCredentials())
   {
    PrintMessage(Warning,"Failed to read (or create if needed) the WWWOFFLE root certificates.");
   }

 if(LoadTrustedCertificates())
   {
    PrintMessage(Warning,"Failed to read in any trusted certificates.");
   }

#if USE_IPV6
 if(ConfigString(Bind_IPv6))
   {
    https_fd[1]=OpenServerSocket(ConfigString(Bind_IPv6),ConfigInteger(HTTPS_Port));
    if(https_fd[1]==-1)
       PrintMessage(Fatal,"Cannot create HTTPS IPv6 server socket.");
   }
#endif

 if(ConfigString(Bind_IPv4))
   {
    https_fd[0]=OpenServerSocket(ConfigString(Bind_IPv4),ConfigInteger(HTTPS_Port));
    if(https_fd[0]==-1)
      {
#if USE_IPV6
       if(https_fd[1]!=-1 && ConfigString(Bind_IPv4) && !strcmp(ConfigString(Bind_IPv4),"0.0.0.0") &&
                             ConfigString(Bind_IPv6) && !strcmp(ConfigString(Bind_IPv6),"[0:0:0:0:0:0:0:0]"))
          PrintMessage(Warning,"Cannot create HTTPS IPv4 server socket (but the IPv6 one might accept IPv4 connections).");
       else
          PrintMessage(Fatal,"Cannot create HTTPS IPv4 server socket.");
#else
       PrintMessage(Fatal,"Cannot create HTTPS server socket.");
#endif
      }
   }

 if(https_fd[0]==-1 && https_fd[1]==-1)
   {
#if USE_IPV6
    PrintMessage(Fatal,"The IPv4 and IPv6 HTTPS sockets were not bound; are they disabled in the config file?");
#else
    PrintMessage(Fatal,"The HTTPS socket was not bound; is it disabled in the config file?");
#endif
   }

 /* Bind the WWWOFFLE control socket(s). */

#if USE_IPV6
 if(ConfigString(Bind_IPv6))
   {
    wwwoffle_fd[1]=OpenServerSocket(ConfigString(Bind_IPv6),ConfigInteger(WWWOFFLE_Port));
    if(wwwoffle_fd[1]==-1)
       PrintMessage(Fatal,"Cannot create WWWOFFLE IPv6 server socket.");
   }
#endif

 if(ConfigString(Bind_IPv4))
   {
    wwwoffle_fd[0]=OpenServerSocket(ConfigString(Bind_IPv4),ConfigInteger(WWWOFFLE_Port));
    if(wwwoffle_fd[0]==-1)
      {
#if USE_IPV6
       if(wwwoffle_fd[1]!=-1 && ConfigString(Bind_IPv4) && !strcmp(ConfigString(Bind_IPv4),"0.0.0.0") &&
                                ConfigString(Bind_IPv6) && !strcmp(ConfigString(Bind_IPv6),"[0:0:0:0:0:0:0:0]")) {
          PrintMessage(Warning,"Cannot create WWWOFFLE IPv4 server socket (but the IPv6 one might accept IPv4 connections).");
          PrintMessage(Warning,"Consider adding \"bind-ipv4 = none\" to wwwoffle.conf if using IPv6.");
       }
       else
          PrintMessage(Fatal,"Cannot create WWWOFFLE IPv4 server socket.");
#else
       PrintMessage(Fatal,"Cannot create WWWOFFLE server socket.");
#endif
      }
   }

 if(wwwoffle_fd[0]==-1 && wwwoffle_fd[1]==-1)
   {
#if USE_IPV6
    PrintMessage(Fatal,"The IPv4 and IPv6 WWWOFFLE sockets were not bound; are they disabled in the config file?");
#else
    PrintMessage(Fatal,"The WWWOFFLE socket was not bound; is it disabled in the config file?");
#endif
   }

 /* Detach from terminal */

 if(detached)
   {
    demoninit();

    PrintMessage(Important,"Detached from terminal and changed pid to %d.",getpid());

    if(log_file)
       InitErrorHandler("wwwoffled",-1,-1); /* pid changes after detaching, keep stderr as was. */
    else
       InitErrorHandler("wwwoffled",-1, 0); /* pid changes after detaching, disable stderr. */

    if(!log_file)
       close(STDERR_FILENO);
   }

 close(STDIN_FILENO);
 close(STDOUT_FILENO);

 install_sighandlers();

 max_servers=ConfigInteger(MaxServers);
 max_fetch_servers=ConfigInteger(MaxFetchServers);

 /* Loop around waiting for connections. */

 PrintMessage(Inform,"WWWOFFLE Ready to accept connections.");

 do
   {
    struct timeval tv;
    fd_set readfd;
    int nfds=0,nfd=0;

    FD_ZERO(&readfd);

#if USE_IPV6
       for(nfd=0;nfd<=1;nfd++)
         {
#endif
          if(http_fd[nfd]>=nfds)
             nfds=http_fd[nfd]+1;
          if(https_fd[nfd]>=nfds)
             nfds=https_fd[nfd]+1;
          if(wwwoffle_fd[nfd]>=nfds)
             nfds=wwwoffle_fd[nfd]+1;

          if(n_servers<max_servers)
            {
             if(http_fd[nfd]!=-1)
                FD_SET(http_fd[nfd],&readfd);
             if(https_fd[nfd]!=-1)
                FD_SET(https_fd[nfd],&readfd);
            }

          if(wwwoffle_fd[nfd]!=-1)
             FD_SET(wwwoffle_fd[nfd],&readfd);
#if USE_IPV6
         }
#endif

    tv.tv_sec=10;
    tv.tv_usec=0;

    if(select(nfds,&readfd,NULL,NULL,&tv)!=-1)
      {
#if USE_IPV6
       for(nfd=0;nfd<=1;nfd++)
         {
#endif
          if(wwwoffle_fd[nfd]!=-1 && FD_ISSET(wwwoffle_fd[nfd],&readfd))
            {
             char *host,*ip;
             int port,client;

             client=AcceptConnect(wwwoffle_fd[nfd]);

             if(client>=0)
               {
                init_io(client);
                configure_io_timeout(client,ConfigInteger(SocketTimeout),ConfigInteger(SocketTimeout));

                if(SocketRemoteName(client,&host,&ip,&port))
                  {
                   finish_io(client);
                   CloseSocket(client);
                  }
                else
                  {
                   char *canonical_ip=CanonicaliseHost(ip);

                   if(IsAllowedConnectHost(host) || IsAllowedConnectHost(canonical_ip))
                     {
                      PrintMessage(Important,"WWWOFFLE Connection from host %s (%s).",host,canonical_ip); /* Used in audit-usage.pl */

                      CommandConnect(client);

                      if(fetch_fd!=client)
                        {
                         finish_io(client);
                         CloseSocket(client);
                        }
                     }
                   else
                     {
                      PrintMessage(Warning,"WWWOFFLE Connection rejected from host %s (%s).",host,canonical_ip); /* Used in audit-usage.pl */
                      finish_io(client);
                      CloseSocket(client);
                     }

                   free(canonical_ip);
                  }
               }
            }

          if(http_fd[nfd]!=-1 && FD_ISSET(http_fd[nfd],&readfd))
            {
             char *host,*ip;
             int port,client;

             client=AcceptConnect(http_fd[nfd]);

             if(client>=0)
               {
                init_io(client);
                configure_io_timeout(client,ConfigInteger(SocketTimeout),ConfigInteger(SocketTimeout));

                if(!SocketRemoteName(client,&host,&ip,&port))
                  {
                   char *canonical_ip=CanonicaliseHost(ip);

                   if(IsAllowedConnectHost(host) || IsAllowedConnectHost(canonical_ip))
                     {
                      PrintMessage(Inform,"HTTP Proxy connection from host %s (%s).",host,canonical_ip); /* Used in audit-usage.pl */
                      ForkServer(client);
                     }
                   else
                      PrintMessage(Warning,"HTTP Proxy connection rejected from host %s (%s).",host,canonical_ip); /* Used in audit-usage.pl */

                   free(canonical_ip);
                  }

                if(nofork)
                   got_sigexit=1;

                if(!nofork)
                  {
                   finish_io(client);
                   CloseSocket(client);
                  }
               }
            }

          if(https_fd[nfd]!=-1 && FD_ISSET(https_fd[nfd],&readfd))
            {
             char *host,*ip;
             int port,client;

             client=AcceptConnect(https_fd[nfd]);

             if(client>=0)
               {
                init_io(client);
                configure_io_timeout(client,ConfigInteger(SocketTimeout),ConfigInteger(SocketTimeout));

                if(!SocketRemoteName(client,&host,&ip,&port))
                  {
                   char *canonical_ip=CanonicaliseHost(ip);

                   if(IsAllowedConnectHost(host) || IsAllowedConnectHost(canonical_ip))
                     {
                      PrintMessage(Inform,"HTTPS Proxy connection from host %s (%s).",host,canonical_ip); /* Used in audit-usage.pl */
                      ForkServer(client);
                     }
                   else
                      PrintMessage(Warning,"HTTPS Proxy connection rejected from host %s (%s).",host,canonical_ip); /* Used in audit-usage.pl */

                   free(canonical_ip);
                  }

                if(nofork)
                   got_sigexit=1;

                if(!nofork)
                  {
                   finish_io(client);
                   CloseSocket(client);
                  }
               }
            }

#if USE_IPV6
         }
#endif
      }

    if(got_sighup)
      {
       got_sighup=0;

       PrintMessage(Important,"SIGHUP signalled.");

       if(log_file)
         {
          PrintMessage(Important,"Closing and opening log file.");

          OpenErrorLog(log_file);
         }

       PrintMessage(Important,"WWWOFFLE Re-reading Configuration File.");

       if(ReadConfigurationFile(-1))
          PrintMessage(Warning,"Error in configuration file; keeping old values.");

       PrintMessage(Important,"WWWOFFLE Finished Re-reading Configuration File.");
      }

    if(got_sigchld)
      {
       int pid, status;
       int isserver=0;

       /* To avoid race conditions, reset the flag before fetching the status */

       got_sigchld=0;

       while((pid=waitpid(-1,&status,WNOHANG))>0)
         {
          int i;
          int exitval=0;

          if(WIFEXITED(status))
             exitval=WEXITSTATUS(status);
          else if(WIFSIGNALED(status))
             exitval=-WTERMSIG(status);

          if(purging)
             if(purge_pid==pid)
               {
                if(exitval>=0)
                   PrintMessage(Inform,"Purge process exited with status %d (pid=%d).",exitval,pid);
                else
                   PrintMessage(Important,"Purge process terminated by signal %d (pid=%d).",-exitval,pid);

                purging=0;
               }

          for(i=0;i<max_servers;i++)
             if(server_pids[i]==pid)
               {
                n_servers--;
                server_pids[i]=0;
                isserver=1;

                if(exitval>=0)
                   PrintMessage(Inform,"Child wwwoffles exited with status %d (pid=%d).",exitval,pid);
                else
                   PrintMessage(Important,"Child wwwoffles terminated by signal %d (pid=%d).",-exitval,pid);

                break;
               }

          /* Check if the child that terminated is one of the fetching wwwoffles */

          for(i=0;i<max_fetch_servers;i++)
             if(fetch_pids[i]==pid)
               {
                n_fetch_servers--;
                fetch_pids[i]=0;
                break;
               }

          if(exitval==3)
             fetching=0;

          if(exitval==4 && online!=0)
             fetching=1;

          if(online==0)
             fetching=0;
         }

       if(isserver)
          PrintMessage(Debug,"Currently running: %d servers total, %d fetchers.",n_servers,n_fetch_servers);
      }

    /* The select timed out or we got a signal. If we are currently fetching,
       start fetch servers to look for jobs in the spool directory. */

    while(fetching && n_fetch_servers<max_fetch_servers && n_servers<max_servers)
       ForkServer(fetch_fd);

    if(fetch_fd!=-1 && !fetching && n_fetch_servers==0)
      {
       write_string(fetch_fd,"WWWOFFLE No more to fetch.\n");

       finish_io(fetch_fd);
       CloseSocket(fetch_fd);
       fetch_fd=-1;

       PrintMessage(Important,"WWWOFFLE Fetch finished.");

       ForkRunModeScript(ConfigString(RunFetch),"fetch","stop",-1);
      }
   }
 while(!got_sigexit);

 /* Close down and exit. */

 /* These four sockets don't need finish_io() calling because they never
    had init_io() called, they are just bound to a port listening. */

 if(http_fd[0]!=-1) CloseSocket(http_fd[0]);
 if(http_fd[1]!=-1) CloseSocket(http_fd[1]);
 if(wwwoffle_fd[0]!=-1) CloseSocket(wwwoffle_fd[0]);
 if(wwwoffle_fd[1]!=-1) CloseSocket(wwwoffle_fd[1]);

 if(!nofork)
   {
    if(n_servers)
       PrintMessage(Important,"Exit signalled - waiting for %d child wwwoffles servers.",n_servers);
    else
       PrintMessage(Important,"Exit signalled.");
   }

 while(n_servers)
   {
    int i;
    int pid,status,exitval=0;

    while((pid=waitpid(-1,&status,0))>0)
      {
       if(WIFEXITED(status))
          exitval=WEXITSTATUS(status);
       else if(WIFSIGNALED(status))
          exitval=-WTERMSIG(status);

       for(i=0;i<max_servers;i++)
          if(server_pids[i]==pid)
            {
             n_servers--;
             server_pids[i]=0;

             if(exitval>=0)
                PrintMessage(Inform,"Child wwwoffles exited with status %d (pid=%d).",exitval,pid);
             else
                PrintMessage(Important,"Child wwwoffles terminated by signal %d (pid=%d).",-exitval,pid);

             break;
            }

       if(purging)
          if(purge_pid==pid)
            {
             if(exitval>=0)
                PrintMessage(Inform,"Purge process exited with status %d (pid=%d).",exitval,pid);
             else
                PrintMessage(Important,"Purge process terminated by signal %d (pid=%d).",-exitval,pid);

             purging=0;
            }
      }
   }

 PrintMessage(Important,"Exiting.");

 FinishConfigurationFile();

 FreeLoadedCredentials();

 return(0);
}