char *HTTP_Open(URL *Url) { char *msg=NULL; char *proxy=NULL; char *server_host=NULL; int server_port=-1; /* Sort out the host. */ if(!IsLocalNetHost(Url->host)) proxy=ConfigStringURL(Proxies,Url); if(proxy) { if(proxyUrl) FreeURL(proxyUrl); proxyUrl=NULL; proxyUrl=CreateURL("http",proxy,"/",NULL,NULL,NULL); server_host=proxyUrl->host; server_port=proxyUrl->port; } else { server_host=Url->host; server_port=Url->port; } if(server_port==-1) server_port=DefaultPort(Url); /* Open the connection. */ server=OpenClientSocket(server_host,server_port); if(server==-1) msg=GetPrintMessage(Warning,"Cannot open the HTTP connection to %s port %d; [%!s].",server_host,server_port); else { init_io(server); configure_io_timeout(server,ConfigInteger(SocketTimeout),ConfigInteger(SocketTimeout)); } return(msg); }
void InitConfigurationFile(char *name) { static char startup[5][MAX_INT_STR+1],options[2][1+MAX_INT_STR+1]; if(name) CurrentConfig.name=name; if(*CurrentConfig.name!='/') { static char cwd[PATH_MAX+1]; if(getcwd(cwd,PATH_MAX)) { strcat(cwd,"/"); strcat(cwd,CurrentConfig.name); CurrentConfig.name=cwd; } } /* Default values that cannot be set at compile time. */ sprintf(startup[0],"%d",DEF_HTTP_PORT); startup_itemdefs[ 2].def_val=startup[0]; sprintf(startup[1],"%d",DEF_HTTPS_PORT); startup_itemdefs[ 3].def_val=startup[1]; sprintf(startup[2],"%d",DEF_WWWOFFLE_PORT); startup_itemdefs[ 4].def_val=startup[2]; sprintf(startup[3],"%d",DEF_MAX_SERVERS); startup_itemdefs[10].def_val=startup[3]; sprintf(startup[4],"%d",DEF_MAX_FETCH_SERVERS); startup_itemdefs[11].def_val=startup[4]; sprintf(options[0],"0%o",DEF_DIR_PERM); options_itemdefs[5].def_val=options[0]; sprintf(options[1],"0%o",DEF_FILE_PERM); options_itemdefs[6].def_val=options[1]; ftpoptions_itemdefs[1].def_val=DefaultFTPPassWord(); /* Convert the default values. */ DefaultConfigFile(); SyslogLevel=ConfigInteger(LogLevel); SetDNSTimeout(ConfigInteger(DNSTimeout)); SetConnectTimeout(ConfigInteger(ConnectTimeout)); }
int ReadConfigurationFile(int fd) { static int first_time=1; char *errmsg; if(first_time) { errmsg=ReadConfigFile(1); if(errmsg) { if(fd!=-1) write_string(fd,errmsg); free(errmsg); return(1); } SetLocalPort(ConfigInteger(HTTP_Port)); first_time=0; } errmsg=ReadConfigFile(0); if(errmsg) { if(fd!=-1) write_string(fd,errmsg); free(errmsg); return(1); } SyslogLevel=ConfigInteger(LogLevel); SetDNSTimeout(ConfigInteger(DNSTimeout)); SetConnectTimeout(ConfigInteger(ConnectTimeout)); return(0); }
char *SSL_Open(URL *Url) { char *msg=NULL; char *proxy=NULL,*sproxy=NULL; int socksremotedns=0; socksdata_t *socksdata=NULL,socksbuf; /* Sort out the host. */ if(!IsLocalNetHost(Url->host)) { proxy=ConfigStringURL(SSLProxy,Url); sproxy=ConfigStringURL(SocksProxy,Url); socksremotedns=ConfigBooleanURL(SocksRemoteDNS,Url); } if(proxyUrl) { FreeURL(proxyUrl); proxyUrl=NULL; } if(proxy) Url=proxyUrl=CreateURL("http",proxy,"/",NULL,NULL,NULL); /* Open the connection. */ server=-1; if(Url->portnum) { if((sproxy && !(socksdata= MakeSocksData(sproxy,socksremotedns,&socksbuf))) || (server=OpenUrlSocket(Url,socksdata))==-1) { msg=GetPrintMessage(Warning,"Cannot open the https (SSL) connection to %s port %d; [%!s].",Url->host,Url->portnum); } else { init_io(server); configure_io_timeout_rw(server,ConfigInteger(SocketTimeout)); } } else msg=GetPrintMessage(Warning,"No port given for the https (SSL) connection to %s.",Url->host); return(msg); }
void SSL_Transfer(int client) { int nfd=client>server?client+1:server+1; fd_set readfd; struct timeval tv; int n; ssize_t nc,ns; char buffer[IO_BUFFER_SIZE]; do { nc=ns=0; FD_ZERO(&readfd); FD_SET(server,&readfd); FD_SET(client,&readfd); tv.tv_sec=ConfigInteger(SocketTimeout); tv.tv_usec=0; n=select(nfd,&readfd,NULL,NULL,&tv); if(n<0 && errno==EINTR) continue; else if(n<=0) return; if(FD_ISSET(client,&readfd)) { nc=read_data(client,buffer,IO_BUFFER_SIZE); if(nc>0) nc=write_data(server,buffer,nc); } if(FD_ISSET(server,&readfd)) { ns=read_data(server,buffer,IO_BUFFER_SIZE); if(ns>0) ns=write_data(client,buffer,ns); } } while(nc>0 || ns>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); }