This is httpd.c in view mode; [Download] [Up]
/************************************************************************ * NCSA HTTPd Server * Software Development Group * National Center for Supercomputing Applications * University of Illinois at Urbana-Champaign * 605 E. Springfield, Champaign, IL 61820 * httpd@ncsa.uiuc.edu * * Copyright (C) 1995, Board of Trustees of the University of Illinois * ************************************************************************ * * httpd.c,v 1.113 1995/11/10 02:01:50 blong Exp * ************************************************************************ * * httpd.c: simple http daemon for answering WWW file requests * * * 03-21-93 Rob McCool wrote original code (up to NCSA HTTPd 1.3) * * 03-06-95 blong * changed server number for child-alone processes to 0 and changed name * of processes * * 03-10-95 blong * Added numerous speed hacks proposed by Robert S. Thau (rst@ai.mit.edu) * including set group before fork, and call gettime before to fork * to set up libraries. * * 04-28-95 guillory * Changed search pattern on child processes to better distribute load * * 04-30-95 blong * added patch by Kevin Steves (stevesk@mayfield.hp.com) to fix * rfc931 logging. We were passing sa_client, but this information * wasn't known yet at the time of the pass to the child. Now uses * getpeername in child_main to find this information. * * 08-16-95 blong * added patch by Vince Tkac (tkac@oclc.org) to allow restart when * a relative path is used on the command line with -f * * 10-26-95 blong * added a RESOURCE_LIMIT compiletime option which limits the number * of possible servers running to MaxServers * * 10-26-95 blong * added patch by Stuart Lynne (sl@wimsey.com) to turn the proc title * into a tachometer */ #include "config.h" #include "portability.h" #include <stdio.h> #ifndef NO_STDLIB_H # include <stdlib.h> #endif /* NO_STDLIB_H */ #ifndef NO_MALLOC_H # ifdef NEED_SYS_MALLOC_H # include <sys/malloc.h> # else # include <malloc.h> # endif /* NEED_SYS_MALLOC_H */ #endif /* NO_MALLOC_H */ #include <string.h> #include <errno.h> #include <setjmp.h> #include <fcntl.h> #include <pwd.h> #include <time.h> #include <signal.h> #include <sys/time.h> #include <sys/types.h> #include <sys/param.h> #include <sys/signal.h> #include <sys/socket.h> #ifndef NO_SYS_WAIT_H # include <sys/wait.h> #endif /* NO_SYS_WAIT_H */ #include <netinet/in_systm.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> #ifdef NEED_SELECT_H # include <sys/select.h> #endif /* NEED_SELECT_H */ #ifndef NO_SYS_RESOURCE_H #include <sys/resource.h> #endif /* NO_SYS_RESOURCE_H */ #include "constants.h" #include "fdwrap.h" #include "httpd.h" #include "http_request.h" #include "http_config.h" #include "host_config.h" #include "http_log.h" #include "http_auth.h" #include "http_access.h" #include "http_dir.h" #include "http_ipc.h" #include "http_mime.h" #include "util.h" JMP_BUF jmpbuffer; JMP_BUF restart_buffer; int servernum=0; int mainSocket; pid_t pgrp; int Child=0; int Alone=0; int csd = -1; /* To keep from being clobbered with setjmp */ static per_request *reqInfo = NULL; KeepAliveData keep_alive; /* global keep alive info */ #ifndef NO_PASS char donemsg[]="DONE"; ChildInfo *Children; int num_children = 0; #endif /* NO_PASS */ #if defined(KRB4) || defined(KRB5) #define HAVE_KERBEROS #endif /* defined(KRB4) || defined(KRB5) */ void htexit(per_request *reqInfo, int status, int die_type) { #ifdef NO_SIGLONGJMP if(standalone || keep_alive.bKeepAlive) longjmp(jmpbuffer,die_type); #else if(standalone || keep_alive.bKeepAlive) siglongjmp(jmpbuffer,die_type); #endif /* NO_SIGLONGJMP */ else { fflush(reqInfo->out); exit(status); } } /* * detach: This function disassociates httpd from its inherited * process group and forms its own process group. Failure * to do this would make httpd susceptible to signals sent * to the entire process group. Also disassociates from * control terminal. */ void detach() { int x; chdir("/"); /* to ensure we're not a process group leader, fork */ if((x = fork()) > 0) exit(0); else if(x == -1) { fprintf(stderr,"httpd: unable to fork new process\n"); perror("fork"); exit(1); } #ifndef NO_SETSID if((pgrp=setsid()) == -1) { fprintf(stderr,"httpd: setsid failed\n"); perror("setsid"); exit(1); } #else if((pgrp=setpgrp(getpid(),0)) == -1) { fprintf(stderr,"httpd: setpgrp failed\n"); perror("setpgrp"); exit(1); } #endif /* NO_SETSID */ } static int Restart = FALSE; static int Exit = FALSE; void sig_term() { log_error("HTTPd: caught SIGTERM, shutting down", gConfiguration->error_log); #ifndef NO_KILLPG killpg(pgrp,SIGKILL); #else kill(-pgrp,SIGKILL); #endif /* NO_KILLPG */ shutdown(mainSocket,2); close(mainSocket); Exit = TRUE; } #ifdef BSD void ign() { #ifndef NeXT int status; #else union wait status; #endif /* NeXT */ pid_t pid; while( (pid = wait3(&status, WNOHANG, NULL)) > 0); } #endif /* BSD */ void bus_error() { log_error("HTTPd: caught SIGBUS, dumping core",gConfiguration->error_log); close_all_logs(); chdir(core_dir); abort(); exit(1); } void seg_fault() { log_error("HTTPd: caught SIGSEGV, dumping core",gConfiguration->error_log); close_all_logs(); chdir(core_dir); abort(); exit(1); } void dump_debug() { per_request *tmp; log_error("HTTPd: caught USR1, dumping debugging information", gConfiguration->error_log); tmp = gCurrentRequest; while (tmp != NULL) { fprintf(gConfiguration->error_log," Status: %d \t\t Bytes: %ld\n",tmp->status, tmp->bytes_sent); fprintf(gConfiguration->error_log," URL: %s \t Filename: %s Args: %s\n", tmp->url, tmp->filename, tmp->args); fprintf(gConfiguration->error_log," RequestLine: %s\n",the_request); fprintf(gConfiguration->error_log," Request: %s %s?%s %s\n", methods[tmp->method],tmp->url,tmp->args, protocals[tmp->http_version]); fprintf(gConfiguration->error_log," Host: %s \t IP: %s\n", tmp->remote_host, tmp->remote_ip); fprintf(gConfiguration->error_log," Content-type: %s \t Content-Length: %d\n", content_type, content_length); fprintf(gConfiguration->error_log," Refererer: %s\n", tmp->referer); fprintf(gConfiguration->error_log," User Agent: %s\n", tmp->agent); if (tmp->hostInfo->server_hostname) fprintf(gConfiguration->error_log," ServerName: %s\n", tmp->hostInfo->server_hostname); tmp = tmp->next; } log_error("HTTPd: finished dumping debugging information", gConfiguration->error_log); } #ifdef PURIFY void dump2() { log_error("HTTPd: caught USR2, dumping debugging information", gConfiguration->error_log); purify_new_leaks(); } #endif /* PURIFY */ void restart() { if (Child) { if (!Alone) { #ifdef NO_SIGLONGJMP longjmp(jmpbuffer,1); #else siglongjmp(jmpbuffer,1); #endif /* NO_SIGLONGJMP */ } else exit (-1); } else { Restart = TRUE; #ifdef NO_SIGLONGJMP longjmp(restart_buffer,1); #else siglongjmp(restart_buffer,1); #endif /* NO_SIGLONGJMP */ } } void set_signals() { signal(SIGSEGV,(void (*)())seg_fault); signal(SIGBUS,(void (*)())bus_error); signal(SIGTERM,(void (*)())sig_term); signal(SIGHUP,(void (*)())restart); signal(SIGUSR1,(void (*)())dump_debug); #ifdef PURIFY signal(SIGUSR2,(void (*)())dump2); #endif /* PURIFY */ #ifdef BSD signal(SIGCHLD,(void (*)())ign); #else signal(SIGCHLD,SIG_IGN); #endif /* BSD */ } /* Reset group privileges, after rereading the config files * (our uid may have changed, and if so, we want the new perms). * * Don't reset the uid yet --- we do that only in the child process, * so as not to lose any root privs. But we can set the group stuff * now, once, and avoid the overhead of doing all this on each * connection. * * rst 1/23/95 */ void set_group_privs() { struct passwd *pwent; int tmp_stand; per_request fakeit; fakeit.out = stdout; if (!getuid()) { /* Change standalone so that on error, we die, instead of siglongjmp */ tmp_stand = standalone; standalone = 0; if ((pwent = getpwuid(user_id)) == NULL) die(&fakeit, SC_CONF_ERROR, "couldn't determine user name from uid"); /* Reset `groups' attributes. */ if (initgroups(pwent->pw_name, group_id) == -1) die(&fakeit, SC_CONF_ERROR,"unable to setgroups"); if (setgid(group_id) == -1) die(&fakeit, SC_CONF_ERROR,"unable to change gid"); standalone = tmp_stand; } } /* More idiot speed-hacking --- the first time conversion makes the C * library open the files containing the locale definition and time * zone. If this hasn't happened in the parent process, it happens in * the children, once per connection --- and it does add up. * * rst 1/23/95 */ void speed_hack_libs() { time_t dummy_time_t = time(NULL); struct tm *dummy_time = localtime (&dummy_time_t); struct tm *other_dummy_time = gmtime (&dummy_time_t); char buf[MAX_STRING_LEN]; strftime (buf, MAX_STRING_LEN, "%d/%b/%Y:%H:%M:%S", dummy_time); } /* * This function blocks on the socket when in keepalive mode. It * is called for all requests after the first one. It returns when * another request is ready, or the timeout period is up. */ int WaitForRequest (int csd, KeepAliveData *kad) { fd_set listen_set; struct timeval ka_timeout; int val; ka_timeout.tv_sec = kad->nTimeOut; ka_timeout.tv_usec = 0; while (1) { FD_ZERO(&listen_set); FD_SET(csd,&listen_set); if ((val = select(csd + 1,&listen_set,NULL,NULL,&ka_timeout)) == -1) continue; /* For now, assume signal occurred */ else if (!val) { kad->bKeepAlive = 0; return 0; } else return 1; } } void CompleteRequest (per_request *reqInfo, int pipe) { /* Changed from shutdown(csd,2) to allow kernel to finish sending data required on OSF/1 2.0 (Achille Hui (eillihca@drizzle.stanford.edu)) */ shutdown(csd,0); close(csd); #ifndef NO_PASS if (pipe >= 0) { write(pipe,donemsg,sizeof(donemsg)); CloseAll(); free_request(reqInfo,0); } else #endif /* NO_PASS */ #ifdef PROFILE exit (2); #else exit (0); #endif /* PROFILE */ } void child_alone(int csd, struct sockaddr_in *sa_server, CLIENT_SOCK_ADDR *sa_client) { close(mainSocket); #ifdef PROFILE moncontrol(1); #endif /* PROFILE */ Child = Alone = 1; standalone = 0; keep_alive.nCurrRequests = 0; /* Only try to switch if we're running as root */ if(!getuid()) { if (setuid(user_id) == -1) { per_request fakeit; fakeit.out = stdout; die(&fakeit, SC_CONF_ERROR,"unable to change uid"); } } /* this should check error status, but it's not crucial */ close(0); close(1); dup2(csd,0); dup2(csd,1); remote_logname = (!do_rfc931 ? NULL : rfc931((struct sockaddr_in *)sa_client, sa_server)); #ifdef NO_SIGLONGJMP if (setjmp(jmpbuffer) != 0) { #else if (sigsetjmp(jmpbuffer,1) != 0) { #endif /* NO_SIGLONGJMP */ /* WaitForRequests returns 0 if timeout */ /* CompleteRequest doesn't return */ fflush(gCurrentRequest->out); if ((keep_alive.nMaxRequests && (++keep_alive.nCurrRequests >= keep_alive.nMaxRequests)) || !WaitForRequest(csd, &keep_alive)) { CompleteRequest(gCurrentRequest,-1); reqInfo = NULL; } } while (1) { reqInfo = initialize_request(reqInfo); reqInfo->connection_socket = 0; reqInfo->out = stdout; get_request(reqInfo); fflush(reqInfo->out); if (!keep_alive.bKeepAlive) { CompleteRequest(reqInfo,-1); reqInfo = NULL; } else if ((keep_alive.nMaxRequests && (++keep_alive.nCurrRequests >= keep_alive.nMaxRequests)) || !WaitForRequest(csd, &keep_alive)) { CompleteRequest(reqInfo,-1); reqInfo = NULL; } } } #ifndef NO_PASS /* to keep from being clobbered by setjmp */ static int x; static int val; /* indicates if keep_alive should remain active */ static int nFirst = 0; #ifdef FD_LINUX static int switch_uid = 0; #endif /* FD_LINUX */ void child_main(int parent_pipe, SERVER_SOCK_ADDR *sa_server) { close(mainSocket); #ifdef PROFILE moncontrol(1); #endif /* PROFILE */ #ifdef QUANTIFY quantify_clear_data(); #endif /* QUANTIFY */ /* Only try to switch if we're running as root */ if(!getuid()) { /* set setstandalone to 0 so we die on error, and not sigjmp */ standalone = 0; #ifdef FD_LINUX /* * This is very tricky, because we want to switch real * and effective UID while retaining a saved uid. */ /* First, make us set-uid. */ if (setreuid(user_id, -1) == -1) { per_request fakeit; fakeit.out = stdout; die(&fakeit, SC_CONF_ERROR,"unable to change ruid"); } /* Saved uid is now 0. Reset effective uid. */ if (seteuid(user_id) == -1) { per_request fakeit; fakeit.out = stdout; die(&fakeit, SC_CONF_ERROR,"unable to change euid"); } switch_uid = 1; #else if (setuid(user_id) == -1) { per_request fakeit; fakeit.out = stdout; die(&fakeit, SC_CONF_ERROR,"unable to change uid"); } #endif /* FD_LINUX */ standalone = 1; } for(x=0;x<num_children;x++) { if (parent_pipe != Children[x].parentfd) close(Children[x].parentfd); if (parent_pipe != Children[x].childfd) close(Children[x].childfd); } free(Children); #ifdef NO_SIGLONGJMP if ((val = setjmp(jmpbuffer)) != 0) { #else if ((val = sigsetjmp(jmpbuffer,1)) != 0) { #endif /* NO_SIGLONGJMP */ reqInfo = gCurrentRequest; fflush(reqInfo->out); if (val == DIE_KEEPALIVE) { /* returns 0 if timeout during multiple request session */ if ((keep_alive.nMaxRequests && (++keep_alive.nCurrRequests >= keep_alive.nMaxRequests)) || !WaitForRequest(csd, &keep_alive)) { keep_alive.bKeepAlive = 0; CompleteRequest(reqInfo,parent_pipe); reqInfo = NULL; } } else { /* in case it was in effect. probably a better place to reset */ keep_alive.bKeepAlive = 0; CompleteRequest(reqInfo,parent_pipe); reqInfo = NULL; } } while (1) { alarm (0); if (!nFirst || !keep_alive.bKeepAlive) { GetDescriptor (parent_pipe); remote_logname = GetRemoteLogName(sa_server); nFirst = 1; keep_alive.nCurrRequests = 0; } reqInfo = initialize_request(reqInfo); reqInfo->connection_socket = 0; reqInfo->out = stdout; get_request(reqInfo); fflush(reqInfo->out); if (!keep_alive.bKeepAlive) { CompleteRequest(reqInfo,parent_pipe); reqInfo = NULL; nFirst = 0; } else if ((keep_alive.nMaxRequests && (++keep_alive.nCurrRequests >= keep_alive.nMaxRequests)) || !WaitForRequest(csd, &keep_alive)) { keep_alive.bKeepAlive = 0; nFirst = 0; CompleteRequest(reqInfo,parent_pipe); reqInfo = NULL; } } } void GetDescriptor (int parent_pipe) { dup2(parent_pipe,0); dup2(parent_pipe,1); #ifdef SETPROCTITLE setproctitle("idle"); #endif /* SETPROCTITLE */ #ifdef FD_LINUX /* Switch to root privilige temporarily */ if (switch_uid && seteuid(0) < 0) { log_error("child error: seteuid(0)",gConfiguration->error_log); close(0); close(1); close(csd); close(parent_pipe); exit(1); } #endif /* FD_LINUX */ csd = recv_fd(parent_pipe); #ifdef FD_LINUX /* Give up priviliges. */ if (switch_uid && seteuid(user_id) < 0) { log_error("unable to change uid", gConfiguration->error_log); csd = -1; } #endif /* FD_LINUX */ if (csd < 0) { log_error("child error: recv_fd()",gConfiguration->error_log); close(0); close(1); close(mainSocket); close(parent_pipe); exit(1); } close(0); close(1); dup2(csd,0); dup2(csd,1); } char* GetRemoteLogName (SERVER_SOCK_ADDR *sa_server) { if (do_rfc931) { CLIENT_SOCK_ADDR sa_client; int addrlen; addrlen = sizeof sa_client; getpeername(csd,(struct sockaddr *) &sa_client, &addrlen); return rfc931((struct sockaddr_in *) &sa_client, sa_server); } else return NULL; } int make_child(int argc, char **argv, int childnum, SERVER_SOCK_ADDR *sa_server) { int fd[2]; int pid; char namestr[30]; pid = 1; servernum = childnum; #ifndef NEED_SPIPE if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1) { #else if (s_pipe(fd) == -1) { #endif /* NEED_SPIPE */ log_error("Unable to open socket for new process", gConfiguration->error_log); return -1; } else { if ((pid = fork()) == -1) { log_error("Unable to fork new process",gConfiguration->error_log); close(fd[1]); close(fd[0]); } else { Children[childnum].childfd = fd[1]; Children[childnum].parentfd = fd[0]; Children[childnum].pid = pid; Children[childnum].busy = 0; } if (!pid) { close(Children[childnum].childfd); #ifdef BSD signal(SIGCHLD,(void (*)())ign); #else signal(SIGCHLD,SIG_IGN); #endif /* BSD */ sprintf(namestr,"child %d",childnum); #ifdef SETPROCTITLE /* inststr(argv,argc,namestr); */ setproctitle(namestr); #endif /* SETPROCTITLE */ Child = 1; child_main(Children[childnum].parentfd, sa_server); } else { close(Children[childnum].parentfd); } } return childnum; } #endif /* NO_PASS */ void initialize_socket(SERVER_SOCK_ADDR *sa_server, CLIENT_SOCK_ADDR *sa_client) { int one=1; int keepalive_value = 1; if ((mainSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == -1) { fprintf(stderr,"httpd: could not get socket\n"); perror("socket"); exit(1); } if((setsockopt(mainSocket,SOL_SOCKET,SO_REUSEADDR,(char *)&one, sizeof(one))) == -1) { fprintf(stderr,"httpd: could not set socket option SO_REUSEADDR\n"); perror("setsockopt"); exit(1); } /* Added at the suggestion of SGI, seems some PC clients don't close the connection, which causes IRIX and Solaris to keep sending them packets for a really long time . . . Sent in From: Brian Behlendorf <brian@wired.com> */ if((setsockopt(mainSocket,SOL_SOCKET,SO_KEEPALIVE,(void *)&keepalive_value, sizeof(keepalive_value))) == -1) { fprintf(stderr,"httpd: could not set socket option SO_KEEPALIVE\n"); perror("setsockopt"); exit(1); } bzero((char *) sa_server, sizeof(*sa_server)); sa_server->sin_family=AF_INET; sa_server->sin_addr= gConfiguration->address_info; /* sa_server.sin_addr.s_addr=htonl(INADDR_ANY); */ sa_server->sin_port=htons(port); if(bind(mainSocket,(struct sockaddr *) sa_server,sizeof(*sa_server)) == -1) { if (gConfiguration->address_info.s_addr != htonl(INADDR_ANY)) fprintf(stderr,"HTTPd: cound not bind to address %s port %d\n", inet_ntoa(gConfiguration->address_info),port); else fprintf(stderr,"httpd: could not bind to port %d\n",port); perror("bind"); exit(1); } listen(mainSocket,35); } void standalone_main(int argc, char **argv) { static char msg[10]; static fd_set listen_set; static int csd, clen; static int x, num_sigs, nread; static int max, free_child; static int last_child = 0; static int no_child_busy = 0; static int search_cnt; static SERVER_SOCK_ADDR sa_server; static CLIENT_SOCK_ADDR sa_client; static int one = 1; #ifdef TACHOMETER static int Requests[MAX_TACHOMETER]; static time_t Request_time = 0L; static int i; static char Request_title[64]; #endif /* Some systems need to call TZSET before detaching from the shell process to ensure proper time zone settings */ tzset(); #if !defined(PROFILE) && !defined(QUANTIFY) detach(); #endif /* PROFILE */ sprintf(error_msg,"HTTPd: Starting as %s",argv[0]); x = 1; while (x < argc) sprintf(error_msg,"%s %s",error_msg, argv[x++]); log_error(error_msg, gConfiguration->error_log); #ifdef SETPROCTITLE setproctitle("Accepting Connections"); # ifdef TACHOMETER for (i=0;i<MAX_TACHOMETER;i++) Requests[i] = 0; Request_time = time(NULL); Request_time -= Request_time%60; # endif /* TACHOMETER */ #endif /* SETPROCTITLE */ while(!Exit) { initialize_socket(&sa_server,&sa_client); set_signals(); speed_hack_libs(); log_pid(); #ifndef NO_PASS num_children = 0; Children = (ChildInfo *) malloc(sizeof(ChildInfo)*(max_servers+1)); while (num_children < start_servers) { make_child(argc, argv, num_children++, &sa_server); } #endif /* NO_PASS */ #ifdef NO_SIGLONGJMP setjmp(restart_buffer); #else sigsetjmp(restart_buffer,1); #endif /* NO_SIGLONGJMP */ while(!(Restart || Exit)) { FD_ZERO(&listen_set); #ifndef RESOURCE_LIMIT FD_SET(mainSocket,&listen_set); max = mainSocket; #else if (no_child_busy < max_servers) { FD_SET(mainSocket,&listen_set); max = mainSocket; } else max = 0; #endif /* RESOURCE_LIMIT */ #ifndef NO_PASS for(x=0 ; x < num_children ; x++) { FD_SET(Children[x].childfd, &listen_set); if (Children[x].childfd > max) max = Children[x].childfd; } #endif /* NO_PASS */ if ((num_sigs = select(max+1,&listen_set,NULL,NULL,NULL))== -1) { if (errno != EINTR) { fprintf(stderr,"Select error %d\n",errno); perror("select"); } } else { #ifndef NO_PASS for(x = 0; x < num_children ; x++) { if (FD_ISSET(Children[x].childfd, &listen_set)) { if ((nread = read(Children[x].childfd, msg, 10)) < 0) { log_error("child error: read msg failure", gConfiguration->error_log); } else if (nread == 0) { log_error("child error: child connection closed", gConfiguration->error_log); close(Children[x].childfd); kill(Children[x].pid,SIGKILL); make_child(argc, argv, x, &sa_server); } else { if (!strcmp(msg,donemsg)) { Children[x].busy = 0; no_child_busy--; } /* if strcmp */ } /* if nread else */ } /* if FD_ISSET */ } /* for x to num_children */ #endif /* NO_PASS */ if (FD_ISSET(mainSocket, &listen_set)) { clen=sizeof(sa_client); if((csd=accept(mainSocket,(struct sockaddr *)&sa_client,&clen)) == -1) { if (errno == EINTR) { #ifdef BSD ign(); #endif /* BSD */ } else { sprintf(error_msg, "HTTPd: socket error: accept failed %d", errno); log_error(error_msg, gConfiguration->error_log); } } else { /* connection accepted */ /* Fix for Solaris as suggested by Sun. Not sure if it does anything * on other platforms, so its not currently #ifdef'd */ #ifdef TCP_NODELAY setsockopt(csd, IPPROTO_TCP, TCP_NODELAY, (void *) &one, sizeof(one)); #endif /* TCP_NODELAY */ #ifndef NO_PASS if (num_children) { /*free_child = 0;*/ search_cnt = 0; free_child = last_child; if (num_children) { while (Children[free_child].busy) { free_child = (free_child + 1) % num_children; search_cnt++; /* free_child++ */ if (free_child == last_child) break; } } if (search_cnt >= num_children) { if ( num_children < max_servers) { if (make_child(argc, argv, num_children, &sa_server) >= 0) { free_child=num_children; Children[free_child].busy = 1; no_child_busy++; if (pass_fd(Children[free_child].childfd,csd) < 0) log_error("child error: pass failed", gConfiguration->error_log); last_child = 0; num_children++; } else { log_error("Main error: make_child failed", gConfiguration->error_log); } /* if (make_child) else ... */ } else { /* Already have as many children as compiled for.*/ if (!fork()) { /* inststr(argv, argc, "httpd-alone"); */ child_alone(csd,&sa_server,&sa_client); } /* fork */ } /* if (num_children < max_servers) ... else ... */ } else { Children[free_child].busy = 1; no_child_busy++; last_child = (free_child + 1) % num_children; if (pass_fd(Children[free_child].childfd,csd) < 0) log_error("child error: pass failed", gConfiguration->error_log); } /* if (search_cnt >= num_children) ... else ... */ close(csd); } else #endif /* NO_PASS */ { if (!fork()) { /* inststr(argv, argc, "httpd-alone"); */ child_alone(csd,&sa_server,&sa_client); } /* fork */ close(csd); } /* if (num_children) ... else ... */ #ifdef TACHOMETER Requests[0]++; if (time(NULL) - Request_time > 60) { int avg1 = Requests[0]; int avg5 = 0; int avg30 = 0; Request_time += 60; for (i=MAX_TACHOMETER-1; i>0; i--) { if ( i < 5 ) avg5 += Requests[i]; avg30 += Requests[i]; Requests[i] = Requests[i-1]; } avg5 += Requests[0]; avg30 += Requests[0]; Requests[0] = 0; sprintf(Request_title, "Accepting Connections: %d/%d/%d", avg1, avg5/5, avg30/30); setproctitle(Request_title); } #endif } /* If good accept */ } /* if mainSocket ready for read */ } /* if select */ } /* while (!(Restart || Exit)) */ if (Restart) { FILE *error_log; /* open up the old log file to dump errors from reading of logfiles, then close. Need to do this before free_host_conf which will destroy the old name. */ shutdown(mainSocket,2); close(mainSocket); error_log = fopen(gConfiguration->error_fname,"a"); log_error("HTTPd: caught SIGHUP, restarting",error_log); kill_mime(); kill_security(); kill_indexing(); #ifndef NO_PASS for(x=0;x<num_children;x++) { close(Children[x].parentfd); close(Children[x].childfd); kill(Children[x].pid,SIGKILL); } free(Children); #endif /* NO_PASS */ free_host_conf(); read_config(error_log); set_group_privs(); log_error("HTTPd: successful restart",error_log); get_local_host(); fclose(error_log); Restart = FALSE; } /* if (Restart) */ } /* while (!Exit) */ } /* standalone_main */ void usage(char *bin) { fprintf(stderr,"NCSA HTTPd %s\n",SERVER_VERSION); fprintf(stderr,"Documentation online at http://hoohoo.ncsa.uiuc.edu/\n\n"); fprintf(stderr,"Compiled in Options:\n"); #ifdef SETPROCTITLE fprintf(stderr,"\tSETPROCTITLE\n"); #ifdef TACHOMETER fprintf(stderr,"\tTACHOMETER\n"); #endif /* TACHOMETER */ #endif /* SETPROCTITLE */ #ifdef MINIMAL_DNS fprintf(stderr,"\tMINIMAL_DNS\n"); #elif defined(MAXIMUM_DNS) fprintf(stderr,"\tMAXIMUM_DNS\n"); #endif /* MINIMAL_DNS */ #ifdef XBITHACK fprintf(stderr,"\tXBITHACK\n"); #endif /* XBITHACK */ #ifdef SECURE_LOGS fprintf(stderr,"\tSECURE_LOGS\n"); #endif /* SECURE_LOGS */ #ifdef NO_PASS fprintf(stderr,"\tNO_PASS\n"); #endif /* NO_PASS */ #ifdef DBM_SUPPORT fprintf(stderr,"\tDBM_SUPPORT\n"); #endif /* DBM_SUPPORT */ #ifdef DIGEST_AUTH fprintf(stderr,"\tDIGEST_AUTH\n"); #endif /* DIGEST_AUTH */ #ifdef KRB4 fprintf(stderr,"\tKRB4\n"); #endif /* KRB4 */ #ifdef KRB5 fprintf(stderr,"\tKRB5\n"); #endif /* KRB5 */ fprintf(stderr,"\tHTTPD_ROOT = %s\n",HTTPD_ROOT); fprintf(stderr,"\tDOCUMENT_ROOT = %s\n", DOCUMENT_LOCATION); fprintf(stderr,"\n"); #ifdef HAVE_KERBEROS fprintf(stderr,"Usage: %s [-d directory] [-f file] [-k file] [-K file] [-v]\n",bin); #else fprintf(stderr,"Usage: %s [-d directory] [-f file] [-v]\n",bin); #endif /* HAVE_KERBEROS */ fprintf(stderr,"-d directory\t : specify an alternate initial ServerRoot\n"); fprintf(stderr,"-f file\t\t : specify an alternate ServerConfigFile\n"); fprintf(stderr,"-v\t\t : version information (this screen)\n"); #ifdef KRB4 fprintf(stderr,"-k file\t\t : specify an alternate Kerberos V4 svrtab file\n"); #endif /* KRB4 */ #ifdef KRB5 fprintf(stderr,"-K file\t\t : specify an alternate Kerberos V5 svrtab file\n"); #endif /* KRB5 */ exit(1); } extern char *optarg; extern int optind; int main (int argc, char **argv, char **envp) { int c; #ifdef RLIMIT_NOFILE /* If defined (not user defined, but in sys/resource.h), this will attempt * to set the max file descriptors per process as high as allowed under * the OS. This is due to the large number of file descriptors HTTPd * can use in several configurations. */ struct rlimit rlp; getrlimit(RLIMIT_NOFILE, &rlp); rlp.rlim_cur = rlp.rlim_max; setrlimit(RLIMIT_NOFILE, &rlp); #endif /* RLIMIT_NOFILE */ #ifdef PROFILE /* When we're running profiled, we do not want to collect * statistics on server startup --- each child process inherits * those counts, which means that startup overhead is massively * overrepresented in the final output. This turns off collection * of profiling statistics; we turn it back on immediately after * the fork() in standalone_main(). (This means it *never* gets * turned on for people running under inetd, but they can't be * much concerned with performance anyway). */ moncontrol(0); #endif /* PROFILE */ strcpy(server_root,HTTPD_ROOT); make_full_path(server_root,SERVER_CONFIG_FILE,server_confname); #ifdef HAVE_KERBEROS while((c = getopt(argc,argv,"d:f:vk:K:")) != -1) { #else while((c = getopt(argc,argv,"d:f:v")) != -1) { #endif /* HAVE_KERBEROS */ switch(c) { case 'd': strcpy(server_root,optarg); make_full_path(server_root,SERVER_CONFIG_FILE,server_confname); break; case 'f': if (optarg[0] == '/') { strcpy(server_confname,optarg); } else { char *cwd = getcwd(NULL,255); make_full_path(cwd, optarg, server_confname); if (cwd) free(cwd); } break; case 'v': usage(argv[0]); exit(1); #ifdef HAVE_KERBEROS case 'k': #ifdef KRB4 strcpy(k4_srvtab, optarg); break; #else fprintf(stderr,"Kerberos V4 not supported (srvtab arg ignored)\n"); break; #endif /* KRB4 */ case 'K': #ifdef KRB5 strcpy(k5_srvtab, optarg); break; #else fprintf(stderr,"Kerberos V5 not supported (srvtab arg ignored)\n"); break; #endif /* KRB5 */ #endif /* HAVE_KERBEROS */ case '?': usage(argv[0]); } } InitFdTable(); read_config(stderr); #ifdef SETPROCTITLE initproctitle(process_name, argc, argv, envp); #endif /* SETPROCTITLE */ set_group_privs(); get_local_host(); #ifdef __QNX__ dup2(0,1); dup2(0,2); #endif /* __QNX */ if(standalone) standalone_main(argc,argv); else { per_request *reqInfo; reqInfo = initialize_request(NULL); user_id = getuid(); group_id = getgid(); reqInfo->connection_socket = 0; reqInfo->out = stdout; port = get_portnum(reqInfo,fileno(reqInfo->out)); if(do_rfc931) remote_logname = get_remote_logname(reqInfo->out); get_request(reqInfo); fflush(reqInfo->out); } close_all_logs(); fclose(stdin); fclose(stdout); return 0; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.