home *** CD-ROM | disk | FTP | other *** search
/ The HTML Web Publisher's Construction Kit / HTMLWPCK.ISO / unix / servers / httpd_14 / source.z / source / httpd_1.4.1 / src / httpd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-18  |  16.2 KB  |  670 lines

  1. /*
  2.  * httpd.c: simple http daemon for answering WWW file requests
  3.  *
  4.  * All code contained herein is covered by the Copyright as distributed
  5.  * in the README file in the main directory of the distribution of 
  6.  * NCSA HTTPD.
  7.  *
  8.  * 
  9.  * 03-21-93  Rob McCool wrote original code (up to NCSA HTTPd 1.3)
  10.  * 
  11.  * 03-06-95  blong
  12.  *  changed server number for child-alone processes to 0 and changed name
  13.  *   of processes
  14.  *
  15.  * 03-10-95  blong
  16.  *     Added numerous speed hacks proposed by Robert S. Thau (rst@ai.mit.edu) 
  17.  *    including set group before fork, and call gettime before to fork
  18.  *     to set up libraries.
  19.  *
  20.  * 04-28-95  guillory
  21.  *    Changed search pattern on child processes to better distribute load
  22.  *
  23.  * 04-30-95  blong
  24.  *    added patch by Kevin Steves (stevesk@mayfield.hp.com) to fix
  25.  *    rfc931 logging.  We were passing sa_client, but this information
  26.  *    wasn't known yet at the time of the pass to the child.  Now uses
  27.  *    getpeername in child_main to find this information.
  28.  */
  29.  
  30.  
  31. #include "httpd.h"
  32. #include <sys/types.h>
  33. #include <sys/param.h>
  34. #include "new.h"
  35.  
  36.  
  37. JMP_BUF jmpbuffer;
  38. JMP_BUF restart_buffer;
  39. int servernum=0;
  40. int sd;
  41. pid_t pgrp;
  42. int Child=0;
  43. int Alone=0;
  44. int csd = -1;
  45.  
  46. #ifndef NO_PASS
  47. char donemsg[]="DONE";
  48. ChildInfo *Children;
  49. int num_children = 0;
  50. #endif
  51.  
  52. void usage(char *bin) {
  53.     fprintf(stderr,"Usage: %s [-d directory] [-f file] [-v]\n",bin);
  54.     fprintf(stderr,"-d directory : specify an alternate initial ServerRoot\n");
  55.     fprintf(stderr,"-f file : specify an alternate ServerConfigFile\n");
  56.     exit(1);
  57. }
  58.  
  59. void htexit(int status, FILE *out) {
  60.     fflush(out);
  61. #if defined(NeXT) || defined(__mc68000__)
  62.     if(standalone) longjmp(jmpbuffer,1);
  63. #else
  64.     if(standalone) siglongjmp(jmpbuffer,1);
  65. #endif
  66.     else exit(status);
  67. }
  68.  
  69.  
  70. void detach() {
  71.     int x;
  72.  
  73.     chdir("/");
  74.     if((x = fork()) > 0)
  75.         exit(0);
  76.     else if(x == -1) {
  77.         fprintf(stderr,"httpd: unable to fork new process\n");
  78.         perror("fork");
  79.         exit(1);
  80.     }
  81. #ifndef NO_SETSID
  82.     if((pgrp=setsid()) == -1) {
  83.         fprintf(stderr,"httpd: setsid failed\n");
  84.         perror("setsid");
  85.         exit(1);
  86.     }
  87. #else
  88.     if((pgrp=setpgrp(getpid(),0)) == -1) {
  89.         fprintf(stderr,"httpd: setpgrp failed\n");
  90.         perror("setpgrp");
  91.         exit(1);
  92.     }
  93. #endif    
  94. }
  95.  
  96. void sig_term() {
  97.     log_error("httpd: caught SIGTERM, shutting down");
  98. #ifndef NO_KILLPG
  99.     killpg(pgrp,SIGKILL);
  100. #else
  101.     kill(-pgrp,SIGKILL);
  102. #endif
  103.     shutdown(sd,2);
  104.     close(sd);
  105. }
  106.  
  107. #ifdef BSD
  108. void ign() {
  109. #ifndef NeXT
  110.     int status;
  111. #else
  112.     union wait status;
  113. #endif
  114.     pid_t pid;
  115.  
  116.     while( (pid = wait3(&status, WNOHANG, NULL)) > 0);
  117. }
  118. #endif
  119.  
  120. void bus_error() {
  121.     log_error("httpd: caught SIGBUS, dumping core");
  122.     close_logs();
  123.     chdir(server_root);
  124.     abort();         
  125.     exit(1);
  126. }
  127.  
  128. void seg_fault() {
  129.     log_error("httpd: caught SIGSEGV, dumping core");
  130.     close_logs();
  131.     chdir(server_root);
  132.     abort();
  133.     exit(1);
  134. }
  135.  
  136. /* Reset group privileges, after rereading the config files
  137.  * (our uid may have changed, and if so, we want the new perms).
  138.  *
  139.  * Don't reset the uid yet --- we do that only in the child process,
  140.  * so as not to lose any root privs.  But we can set the group stuff
  141.  * now, once, and avoid the overhead of doing all this on each
  142.  * connection.
  143.  *
  144.  * rst 1/23/95
  145.  */
  146.   
  147. void set_group_privs()
  148. {
  149.   struct passwd *pwent;
  150.   
  151.   if(!getuid()) {
  152.     /* Change standalone so that on error, we die, instead of siglongjmp */
  153.     standalone = 0;
  154.  
  155.     if ((pwent = getpwuid(user_id)) == NULL)
  156.       die(CONF_ERROR,"couldn't determine user name from uid",
  157.           stdout);
  158.     
  159.     /* Reset `groups' attributes. */
  160.     
  161.     if (initgroups(pwent->pw_name, group_id) == -1)
  162.       die(CONF_ERROR,"unable to setgroups",stdout);
  163.  
  164.     if (setgid(group_id) == -1)
  165.       die(CONF_ERROR,"unable to change gid",stdout);
  166.  
  167.     standalone = 1;
  168.   }
  169. }
  170.  
  171. /* More idiot speed-hacking --- the first time conversion makes the C
  172.  * library open the files containing the locale definition and time
  173.  * zone.  If this hasn't happened in the parent process, it happens in
  174.  * the children, once per connection --- and it does add up.
  175.  *
  176.  * rst 1/23/95
  177.  */
  178.   
  179. void speed_hack_libs() {
  180.    time_t dummy_time_t = time(NULL);
  181.    struct tm *dummy_time = localtime (&dummy_time_t);
  182.    struct tm *other_dummy_time = gmtime (&dummy_time_t);
  183.    char buf[MAX_STRING_LEN];
  184.    
  185.    strftime (buf, MAX_STRING_LEN, "%d/%b/%Y:%H:%M:%S", dummy_time);
  186. }
  187.  
  188.  
  189.  
  190. void restart() {
  191. #ifndef NO_PASS
  192.     int x;
  193. #endif
  194.  
  195.     if (Child) {
  196. #if defined(NeXT) || defined(__mc68000__)
  197.       longjmp(jmpbuffer,1);
  198. #else
  199.       siglongjmp(jmpbuffer,1);
  200. #endif
  201.     } else {
  202.       log_error_noclose("httpd: caught SIGHUP, restarting");
  203.       kill_mime();
  204.       kill_security();
  205.       kill_indexing();
  206.  
  207. #ifndef NO_PASS
  208.       for(x=0;x<num_children;x++) {
  209.     close(Children[x].parentfd);
  210.     close(Children[x].childfd);
  211.         kill(Children[x].pid,SIGKILL);
  212.       }
  213.       free(Children);
  214. #endif 
  215.       reset_error();
  216.  
  217.       if(server_hostname) {
  218.           free(server_hostname);
  219.           server_hostname = NULL;
  220.       }
  221.       read_config(error_log);
  222.       close_logs();
  223.       open_logs();
  224.       set_group_privs();
  225.       log_error_noclose("httpd: successful restart");
  226.       get_local_host();
  227.       set_signals();
  228. #if defined(NeXT) || defined(__mc68000__)      
  229.       longjmp(restart_buffer,1);
  230. #else
  231.       siglongjmp(restart_buffer,1);
  232. #endif
  233.     }
  234. }
  235.  
  236. void set_signals() {
  237.     signal(SIGSEGV,(void (*)())seg_fault);
  238.     signal(SIGBUS,(void (*)())bus_error);
  239.     signal(SIGTERM,(void (*)())sig_term);
  240.     signal(SIGHUP,(void (*)())restart);
  241.  
  242. #ifdef BSD
  243.     signal(SIGCHLD,(void (*)())ign);
  244. #else
  245.     signal(SIGCHLD,SIG_IGN);
  246. #endif
  247. }
  248.  
  249. void child_alone(int csd, 
  250. #if defined(NeXT) || defined(LINUX) || defined(SOLARIS2) || defined (__bsdi__)
  251.     struct sockaddr_in *sa_server,
  252.     struct sockaddr *sa_client)
  253. #else
  254.     struct sockaddr_in *sa_server,
  255.     struct sockaddr_in *sa_client)
  256. #endif
  257. {
  258.  
  259. /*    struct passwd* pwent; */
  260.  
  261.     Child = 1;
  262.     Alone = 1;
  263.     standalone = 0;
  264.     servernum = 0;
  265. /* Only try to switch if we're running as root */
  266.     if(!getuid()) {
  267.        /* Now, make absolutely certain we don't have any privileges
  268.         * except those mentioned in the configuration file. */
  269.  
  270.        /* Reset `groups' attribute. */
  271.         
  272.        /* Note the order, first setgid() and then setuid(), it
  273.         * wouldn't work the other way around. */
  274.  
  275.     /* all of that removed, since group already set correctly */
  276.  
  277.        if (setuid(user_id) == -1)
  278.         die(CONF_ERROR,"unable to change uid",stdout);
  279.     }
  280.     ErrorStat = 0;
  281.     status = 200;
  282.  
  283.     initialize_request();
  284.  
  285.     /* this should check error status, but it's not crucial */
  286.     close(0);
  287.     close(1);    
  288.     dup2(csd,0);
  289.     dup2(csd,1);
  290.     
  291.     remote_logname = (!do_rfc931 ? NULL :
  292.               rfc931((struct sockaddr_in *)sa_client,
  293.                  sa_server));
  294.     
  295.     process_request(0,stdout);
  296.     fflush(stdout);
  297.     shutdown(csd,2);
  298.     close(csd);
  299.     exit(0);
  300. }
  301.  
  302. #ifndef NO_PASS
  303. void child_main(int parent_pipe, struct sockaddr_in *sa_server) {
  304.     int x;
  305.     
  306. /*    struct passwd* pwent; */
  307.     
  308.  
  309.     /* Only try to switch if we're running as root */
  310.     if(!getuid()) {
  311.     /* set setstandalone to 0 so we die on error, and not sigjmp */
  312.     standalone = 0;
  313.        /* Now, make absolutely certain we don't have any privileges
  314.         * except those mentioned in the configuration file. */
  315.         
  316.        if (setuid(user_id) == -1)
  317.         die(CONF_ERROR,"unable to change uid",stdout);
  318.       standalone = 1;
  319.     } 
  320.  
  321.  
  322.     for(x=0;x<num_children;x++) {
  323.       if (parent_pipe != Children[x].parentfd) close(Children[x].parentfd);
  324.       if (parent_pipe != Children[x].childfd) close(Children[x].childfd);
  325.     }
  326.   
  327.     free(Children);
  328.  
  329. #if defined(NeXT) || defined(__mc68000__)
  330.     if (setjmp(jmpbuffer) != 0) {
  331. #else
  332.     if (sigsetjmp(jmpbuffer,1) != 0) {
  333. #endif
  334.     fflush(stdout);
  335.     shutdown(csd,2);
  336.     close(csd);
  337.     write(parent_pipe,donemsg,sizeof(donemsg));
  338.     }
  339.     while (1) {
  340.     alarm(0);
  341.     initialize_request();
  342.     dup2(parent_pipe,0);
  343.     dup2(parent_pipe,1);
  344.     if ((csd = recv_fd(parent_pipe)) < 0) {
  345.       log_error("child error: recv_fd()");
  346.       close(0);
  347.          close(1);
  348.       close(sd);
  349.       close(parent_pipe);
  350.       exit(1);
  351.     }
  352.  
  353.        close(0);
  354.     close(1);    
  355.     dup2(csd,0);
  356.     dup2(csd,1);
  357.     
  358.         if (do_rfc931) {
  359. #if defined(NeXT) || defined(LINUX) || defined(SOLARIS2) || defined(__bsdi__)
  360.             struct sockaddr sa_client;
  361. #else
  362.             struct sockaddr_in sa_client;
  363. #endif
  364.             int addrlen;
  365.  
  366.             addrlen = sizeof sa_client;
  367.             getpeername(csd, &sa_client, &addrlen);
  368.             remote_logname = rfc931((struct sockaddr_in *) &sa_client,
  369.                                     sa_server);
  370.         } else
  371.             remote_logname = NULL;
  372.     
  373.     process_request(0,stdout);
  374.     fflush(stdout);
  375.     shutdown(csd,2);
  376.     close(csd);
  377.     write(parent_pipe,donemsg,sizeof(donemsg));
  378.     }    
  379. }
  380.  
  381. int make_child(int argc, char **argv, int childnum, 
  382.         struct sockaddr_in *sa_server) {
  383.  
  384.     int fd[2];
  385.     int pid;
  386.     char namestr[30];
  387.  
  388.     pid = 1;
  389.     servernum = childnum;
  390. #ifndef NEED_SPIPE
  391.     if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1) { 
  392. #else
  393.     if (s_pipe(fd)  == -1) {
  394. #endif
  395.     log_error("Unable to open socket for new process");
  396.     return -1;
  397.      } else { 
  398.       if ((pid = fork()) == -1) {
  399.     log_error("Unable to fork new process");
  400.     close(fd[1]);
  401.     close(fd[0]);
  402.       } else {
  403.     Children[childnum].childfd = fd[1];
  404.     Children[childnum].parentfd = fd[0];
  405.     Children[childnum].pid = pid;
  406.     Children[childnum].busy = 0;
  407.       }
  408.     
  409.       if (!pid) {
  410.     close(Children[childnum].childfd);
  411. #ifdef BSD
  412.         signal(SIGCHLD,(void (*)())ign);
  413. #else
  414.     signal(SIGCHLD,SIG_IGN);
  415. #endif
  416.     sprintf(namestr,"httpd-child%d",childnum);
  417.     inststr(argv,argc,namestr);
  418.     Child = 1;
  419.     child_main(Children[childnum].parentfd, sa_server);
  420.       } else {
  421.     close(Children[childnum].parentfd);
  422.       } /* if child else */
  423.    } /* if error on open else */
  424.    return childnum;
  425. }
  426. #endif /* NO_PASS */
  427.  
  428. void standalone_main(int argc, char **argv) {
  429.     char msg[10];
  430.     fd_set listen_set;
  431.     int csd, clen,pid;
  432.     int x,error,num_sigs,nread;
  433.     int max, free_child;
  434.     static int last_child = 0;
  435.     int    search_cnt;
  436.     int keepalive_value = 1;  
  437.     int one = 1;
  438.     struct sockaddr_in sa_server;
  439. #if defined(NeXT) || defined(LINUX) || defined(SOLARIS2) || defined(__bsdi__)
  440.     struct sockaddr sa_client;
  441. #else
  442.     struct sockaddr_in sa_client;
  443. #endif
  444.  
  445.  
  446.    /* Some systems need to call TZSET before detaching from the shell
  447.       process to ensure proper time zone settings */
  448. #ifdef CALL_TZSET
  449.     tzset();
  450. #endif 
  451.  
  452.     detach();  
  453.     inststr(argv, argc, "httpd-root");
  454.  
  455.     if ((sd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == -1) {
  456.         fprintf(stderr,"httpd: could not get socket\n");
  457.         perror("socket");
  458.         exit(1);
  459.     }
  460.  
  461.     if((setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one))) == -1) {
  462.         fprintf(stderr,"httpd: could not set socket option SO_REUSEADDR\n");
  463.         perror("setsockopt");
  464.         exit(1);
  465.     }
  466.  
  467. /* Added at the suggestion of SGI, seems some PC clients don't close the 
  468.    connection, which causes IRIX and Solaris to keep sending them packets
  469.    for a really long time . . . 
  470.    Sent in From: Brian Behlendorf <brian@wired.com> */
  471.  
  472.     if((setsockopt(sd,SOL_SOCKET,SO_KEEPALIVE,(void *)&keepalive_value,
  473.         sizeof(keepalive_value))) == -1) {
  474.         fprintf(stderr,"httpd: could not set socket option SO_KEEPALIVE\n"); 
  475.         perror("setsockopt"); 
  476.         exit(1); 
  477.     }
  478.  
  479.     bzero((char *) &sa_server, sizeof(sa_server));
  480.     sa_server.sin_family=AF_INET;
  481.     sa_server.sin_addr.s_addr=htonl(INADDR_ANY);
  482.     sa_server.sin_port=htons(port);
  483.     if(bind(sd,(struct sockaddr *) &sa_server,sizeof(sa_server)) == -1) {
  484.         fprintf(stderr,"httpd: could not bind to port %d\n",port);
  485.         perror("bind");
  486.         exit(1);
  487.     }
  488.     listen(sd,35);
  489.  
  490.     log_pid();
  491.  
  492. #if defined(NeXT) || defined(__mc68000__)
  493.     setjmp(restart_buffer);
  494. #else
  495.     sigsetjmp(restart_buffer,1);
  496. #endif
  497.  
  498.     set_signals();
  499.     speed_hack_libs();
  500.  
  501.     pid = 1;
  502.     error = 0;
  503.  
  504. #ifndef NO_PASS
  505.     num_children = 0;
  506.     Children = (ChildInfo *) malloc(sizeof(ChildInfo)*(max_servers+1));
  507.     while (num_children < start_servers) {
  508.     make_child(argc, argv, num_children++, &sa_server);
  509.     }
  510. #endif
  511.  
  512.     while(1) {
  513.     FD_ZERO(&listen_set);
  514.  
  515.     FD_SET(sd,&listen_set);
  516.     max = sd;
  517.  
  518. #ifndef NO_PASS
  519.     for(x=0 ; x < num_children ; x++) {
  520.         FD_SET(Children[x].childfd, &listen_set);
  521.         if (Children[x].childfd > max) max = Children[x].childfd;
  522.     }
  523. #endif /* NO_PASS */
  524.  
  525.     if ((num_sigs = select(max+1,&listen_set,NULL,NULL,NULL))== -1) {
  526.       if (errno != EINTR) {
  527.          fprintf(stderr,"Select error %d\n",errno);
  528.         perror("select");
  529.       }
  530.     } else {
  531. #ifndef NO_PASS
  532.         for(x = 0; x < num_children ; x++) {
  533.         if (FD_ISSET(Children[x].childfd, &listen_set)) {
  534.             if ((nread = read(Children[x].childfd, msg, 10)) < 0) {
  535.             log_error("child error: read msg failure");
  536.             } else if (nread == 0) {
  537.             log_error("child error: child connection closed");
  538.             close(Children[x].childfd);
  539.             kill(Children[x].pid,SIGKILL);
  540.             make_child(argc, argv, x, &sa_server);
  541.             } else {
  542. #ifdef DEBUG_BCL
  543.             fprintf(stderr,"-%d ",x); 
  544. #endif
  545.             if (!strcmp(msg,donemsg)) {
  546.                 Children[x].busy = 0;
  547.             } /* if strcmp */
  548.             } /* if nread else */
  549.         } /* if FD_ISSET */
  550.         } /* for x to num_children */
  551. #endif /* NO_PASS */
  552.         if (FD_ISSET(sd, &listen_set)) {
  553.         clen=sizeof(sa_client);
  554.         if((csd=accept(sd,&sa_client,&clen)) == -1) {
  555.             if (errno == EINTR) {
  556. #ifdef BSD              
  557.             ign();
  558. #endif
  559.             } else {
  560.             log_error("socket error: accept failed");
  561.             }
  562.         } else { /* connection accepted */
  563. #ifndef NO_PASS
  564.           if (num_children) {
  565.             /*free_child = 0;*/
  566.             search_cnt = 0;
  567.             free_child = last_child;
  568.             while (Children[free_child].busy) {
  569.             free_child = (free_child + 1) % num_children; 
  570.             search_cnt++;
  571.                 /* free_child++ */
  572.             if (free_child == last_child) 
  573.                 break;
  574.             }
  575.             if (search_cnt >= num_children) {
  576.             if ( num_children < max_servers) {
  577.                 if (make_child(argc, argv, num_children, &sa_server) >= 0) {
  578.                   free_child=num_children;
  579.                   Children[free_child].busy = 1;
  580.                   if (pass_fd(Children[free_child].childfd,csd) < 0)
  581.                 log_error("child error: pass failed");
  582.                   last_child = 0;
  583.                   num_children++;
  584.                 } else {
  585.                   log_error("Main error: make_child failed");
  586.                     } /* if (make_child) else ... */
  587.             } else { 
  588. /* Already have as many children as compiled for.*/
  589.                 if (!fork()) {
  590.                 inststr(argv, argc, "httpd-alone");
  591.                 child_alone(csd,&sa_server,&sa_client);
  592.                 } /* fork */
  593.             } /* if (num_children < max_servers) ... else ... */
  594.             } else {
  595.             Children[free_child].busy = 1;
  596.             last_child = (free_child + 1) % num_children;
  597.             if (pass_fd(Children[free_child].childfd,csd) < 0)
  598.                 log_error("child error: pass failed");
  599.             } /* if (search_cnt >= num_children) ... else ... */
  600.            close(csd);
  601.                   } else 
  602. #endif /* NO_PASS */
  603.           {
  604.              if (!fork()) {
  605.             inststr(argv, argc, "httpd-alone");
  606.             child_alone(csd,&sa_server,&sa_client);
  607.               } /* fork */
  608.               close(csd);
  609.           } /* if (num_children) ... else ... */
  610.         } /* If good accept */
  611.         } /* if sd ready for read */
  612.     } /* if select */
  613.     } /* while 1 */
  614. } /* standalone_main */
  615.  
  616. extern char *optarg;
  617. extern int optind;
  618.  
  619. main(int argc, char *argv[])
  620. {
  621.     int c;
  622.  
  623.     strcpy(server_root,HTTPD_ROOT);
  624.     make_full_path(server_root,SERVER_CONFIG_FILE,server_confname);
  625.  
  626.     while((c = getopt(argc,argv,"d:f:v")) != -1) {
  627.         switch(c) {
  628.           case 'd':
  629.             strcpy(server_root,optarg);
  630.       make_full_path(server_root,SERVER_CONFIG_FILE,server_confname);
  631.             break;
  632.           case 'f':
  633.             strcpy(server_confname,optarg);
  634.             break;
  635.           case 'v':
  636.             printf("NCSA httpd version %s.\n",SERVER_VERSION);
  637.             exit(1);
  638.           case '?':
  639.             usage(argv[0]);
  640.         }
  641.     }
  642.     read_config(stderr);
  643.     open_logs();
  644.     set_group_privs();
  645.     get_local_host();
  646.  
  647. #ifdef __QNX__
  648.     dup2(0,1);
  649.     dup2(0,2);
  650. #endif
  651.  
  652.     if(standalone)
  653.         standalone_main(argc,argv);
  654.     else {
  655.     initialize_request();
  656.         user_id = getuid();
  657.         group_id = getgid();
  658.  
  659.         port = get_portnum(fileno(stdout),stdout);
  660.         if(do_rfc931)
  661.             remote_logname = get_remote_logname(stdout);
  662.         process_request(0,stdout);
  663.     fflush(stdout);
  664.     }
  665.     close_logs();
  666.     fclose(stdin);
  667.     fclose(stdout);
  668.     exit(0);
  669. }
  670.