Пример #1
0
void global_startup::perform_startup() {
  if (startup_performed) return;
  startup_performed = true;
  // non graphlab stuff
  Eigen::initParallel();
  install_sighandlers();
  MEMORY_RELEASE_THREAD = new memory_release_thread();
  MEMORY_RELEASE_THREAD->start();
  // graphlab stuff
  graphlab::reap_unused_temp_files();
}
Пример #2
0
/*
 * General server example: accept a client connection and do something.
 * This program just outputs a short HTML page, but can be easily adapted
 * to do other things.
 *
 * This server creates a constant number of processes ("virtual processors"
 * or VPs) and replaces them when they die. Each virtual processor manages
 * its own independent set of state threads (STs), the number of which varies
 * with load against the server. Each state thread listens to exactly one
 * listening socket. The initial process becomes the watchdog, waiting for
 * children (VPs) to die or for a signal requesting termination or restart.
 * Upon receiving a restart signal (SIGHUP), all VPs close and then reopen
 * log files and reload configuration. All currently active connections remain
 * active. It is assumed that new configuration affects only request
 * processing and not the general server parameters such as number of VPs,
 * thread limits, bind addresses, etc. Those are specified as command line
 * arguments, so the server has to be stopped and then started again in order
 * to change them.
 *
 * Each state thread loops processing connections from a single listening
 * socket. Only one ST runs on a VP at a time, and VPs do not share memory,
 * so no mutual exclusion locking is necessary on any data, and the entire
 * server is free to use all the static variables and non-reentrant library
 * functions it wants, greatly simplifying programming and debugging and
 * increasing performance (for example, it is safe to ++ and -- all global
 * counters or call inet_ntoa(3) without any mutexes). The current thread on
 * each VP maintains equilibrium on that VP, starting a new thread or
 * terminating itself if the number of spare threads exceeds the lower or
 * upper limit.
 *
 * All I/O operations on sockets must use the State Thread library's I/O
 * functions because only those functions prevent blocking of the entire VP
 * process and perform state thread scheduling.
 */
int main(int argc, char *argv[])
{
  /* Parse command-line options */
  parse_arguments(argc, argv);

  /* Allocate array of server pids */
  if ((vp_pids = calloc(vp_count, sizeof(pid_t))) == NULL)
    err_sys_quit(errfd, "ERROR: calloc failed");

  /* Start the daemon */
  if (!interactive_mode)
    start_daemon();

  /* Initialize the ST library */
  if (st_init() < 0)
    err_sys_quit(errfd, "ERROR: initialization failed: st_init");

  /* Set thread throttling parameters */
  set_thread_throttling();

  /* Create listening sockets */
  create_listeners();

  /* Change the user */
  if (username)
    change_user();

  /* Open log files */
  open_log_files();

  /* Start server processes (VPs) */
  start_processes();

  /* Turn time caching on */
  st_timecache_set(1);

  /* Install signal handlers */
  install_sighandlers();

  /* Load configuration from config files */
  load_configs();

  /* Start all threads */
  start_threads();

  /* Become a signal processing thread */
  process_signals(NULL);

  /* NOTREACHED */
  return 1;
}
Пример #3
0
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);
}