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; }
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(); }
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(); }
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; }
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); }