dgaudet     97/04/24 16:35:29

  Modified:    src       CHANGES alloc.c alloc.h http_main.c
                        http_protocol.c  mod_browser.c mod_include.c
                        mod_rewrite.c mod_status.c  scoreboard.h
  Log:
  Graceful restarts, reliable signals, and memory fixes.
  
  Reviewed by:  Paul S., Roy, (and others reviewed earlier incarnations)
  
  Revision  Changes    Path
  1.248     +12 -0     apache/src/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /export/home/cvs/apache/src/CHANGES,v
  retrieving revision 1.247
  retrieving revision 1.248
  diff -C3 -r1.247 -r1.248
  *** CHANGES   1997/04/24 23:28:44     1.247
  --- CHANGES   1997/04/24 23:35:18     1.248
  ***************
  *** 1,4 ****
  --- 1,16 ----
    Changes with Apache 1.2
  +   
  +   *) Fix graceful restart on architectures not using scoreboard files
  +      (it is still broken on scoreboard-file architectures).
  +      Eliminate many signal-related race conditions in both forms of
  +      restart, and in SIGTERM.  See htdocs/manual/stopping.html for
  +      details on stopping and restarting the parent.
  +      [Dean Gaudet]
  + 
  +   *) Fix memory leaks in mod_rewrite, mod_browser, mod_include.  Tune
  +      memory allocator to avoid a behaviour that required extra blocks to
  +      be allocated.
  +      [Dean Gaudet]
    
      *) Allow suexec to access files relative to current directory but not
         above.  (Excluding leading / or any .. directory.)  [Ken Coar]
  
  
  
  1.27      +12 -4     apache/src/alloc.c
  
  Index: alloc.c
  ===================================================================
  RCS file: /export/home/cvs/apache/src/alloc.c,v
  retrieving revision 1.26
  retrieving revision 1.27
  diff -C3 -r1.26 -r1.27
  *** alloc.c   1997/04/07 10:58:38     1.26
  --- alloc.c   1997/04/24 23:35:18     1.27
  ***************
  *** 185,194 ****
       * on the free list...
       */
    
  -   min_size += BLOCK_MINFREE;
  - 
      while (blok != NULL) {
  !     if (min_size <= blok->h.endp - blok->h.first_avail) {
          *lastptr = blok->h.next;
          blok->h.next = NULL;
          return blok;
  --- 185,192 ----
       * on the free list...
       */
    
      while (blok != NULL) {
  !     if (min_size + BLOCK_MINFREE <= blok->h.endp - blok->h.first_avail) {
          *lastptr = blok->h.next;
          blok->h.next = NULL;
          return blok;
  ***************
  *** 201,207 ****
    
      /* Nope. */
    
  !   return malloc_block (min_size);
    }
    
    
  --- 199,206 ----
    
      /* Nope. */
    
  !   min_size += BLOCK_MINFREE;
  !   return malloc_block((min_size > BLOCK_MINALLOC) ? min_size : 
BLOCK_MINALLOC);
    }
    
    
  ***************
  *** 896,901 ****
  --- 895,909 ----
        register_cleanup (p, (void *)preg, regex_cleanup, regex_cleanup);
    
        return preg;
  + }
  + 
  + 
  + void pregfree(pool *p, regex_t *reg)
  + {
  +     block_alarms();
  +     regfree (reg);
  +     kill_cleanup (p, (void *)reg, regex_cleanup);
  +     unblock_alarms();
    }
    
    /*****************************************************************
  
  
  
  1.19      +6 -3      apache/src/alloc.h
  
  Index: alloc.h
  ===================================================================
  RCS file: /export/home/cvs/apache/src/alloc.h,v
  retrieving revision 1.18
  retrieving revision 1.19
  diff -C3 -r1.18 -r1.19
  *** alloc.h   1997/04/07 10:58:38     1.18
  --- alloc.h   1997/04/24 23:35:19     1.19
  ***************
  *** 210,216 ****
    void note_cleanups_for_fd (pool *, int);
    void kill_cleanups_for_fd (pool *p, int fd);
    
  ! regex_t *pregcomp(pool *p, const char *pattern, int cflags);
    
    /* routines to note closes... file descriptors are constrained enough
     * on some systems that we want to support this.
  --- 210,217 ----
    void note_cleanups_for_fd (pool *, int);
    void kill_cleanups_for_fd (pool *p, int fd);
    
  ! regex_t *pregcomp (pool *p, const char *pattern, int cflags);
  ! void pregfree (pool *p, regex_t *reg);
    
    /* routines to note closes... file descriptors are constrained enough
     * on some systems that we want to support this.
  ***************
  *** 236,244 ****
                     FILE **pipe_err);
    #define spawn_child(p,f,v,k,in,out) spawn_child_err(p,f,v,k,in,out,NULL)
    
  ! /* magic numbers --- only one so far, min free bytes in a new pool block */
    
  ! #define BLOCK_MINFREE 8192     
    
    /* Finally, some accounting */
    
  --- 237,247 ----
                     FILE **pipe_err);
    #define spawn_child(p,f,v,k,in,out) spawn_child_err(p,f,v,k,in,out,NULL)
    
  ! /* magic numbers --- min free bytes to consider a free pool block useable,
  !  * and the min amount to allocate if we have to go to malloc() */
    
  ! #define BLOCK_MINFREE 4096
  ! #define BLOCK_MINALLOC 8192
    
    /* Finally, some accounting */
    
  
  
  
  1.139     +409 -318  apache/src/http_main.c
  
  Index: http_main.c
  ===================================================================
  RCS file: /export/home/cvs/apache/src/http_main.c,v
  retrieving revision 1.138
  retrieving revision 1.139
  diff -C3 -r1.138 -r1.139
  *** http_main.c       1997/04/21 20:29:07     1.138
  --- http_main.c       1997/04/24 23:35:20     1.139
  ***************
  *** 73,78 ****
  --- 73,82 ----
     *      Extensive rework for Apache.
     */
    
  + /* XXX: systems without HAVE_SHMGET or HAVE_MMAP do not reliably update
  +  * the scoreboard because a received signal might interrupt the scoreboard
  +  * calls.
  +  */
    
    #define CORE_PRIVATE
    
  ***************
  *** 157,163 ****
    
    server_rec *server_conf;
    JMP_BUF jmpbuffer;
  - JMP_BUF restart_buffer;
    int sd;
    static fd_set listenfds;
    static int listenmaxfd;
  --- 161,166 ----
  ***************
  *** 176,181 ****
  --- 179,200 ----
    
    int one_process = 0;
    
  + /* small utility macros to make things easier to read */
  + 
  + #ifdef NO_KILLPG
  + #define ap_killpg(x, y)             (kill (-(x), (y)))
  + #else
  + #define ap_killpg(x, y)             (killpg ((x), (y)))
  + #endif
  + 
  + #if defined(USE_LONGJMP)
  + #define ap_longjmp(x, y)    (longjmp ((x), (y)))
  + #define ap_setjmp(x)                (setjmp (x))
  + #else
  + #define ap_longjmp(x, y)    (siglongjmp ((x), (y)))
  + #define ap_setjmp(x)                (sigsetjmp ((x), 1))
  + #endif
  + 
    #if defined(USE_FCNTL_SERIALIZED_ACCEPT)
    static struct flock lock_it = { F_WRLCK, 0, 0, 0 };
    static struct flock unlock_it = { F_UNLCK, 0, 0, 0 };
  ***************
  *** 359,369 ****
        }
        
        if (!current_conn) {
  ! #if defined(USE_LONGJMP)
  !     longjmp(jmpbuffer,1);
  ! #else
  !     siglongjmp(jmpbuffer,1);
  ! #endif
        }
        
        if (timeout_req != NULL) dirconf = timeout_req->per_dir_config;
  --- 378,384 ----
        }
        
        if (!current_conn) {
  !     ap_longjmp (jmpbuffer, 1);
        }
        
        if (timeout_req != NULL) dirconf = timeout_req->per_dir_config;
  ***************
  *** 401,411 ****
        bclose(timeout_req->connection->client);
        
        if (!standalone) exit(0);
  ! #if defined(USE_LONGJMP)
  !     longjmp(jmpbuffer,1);
  ! #else
  !     siglongjmp(jmpbuffer,1);
  ! #endif
        }
        else {   /* abort the connection */
            bsetflag(current_conn->client, B_EOUT, 1);
  --- 416,423 ----
        bclose(timeout_req->connection->client);
        
        if (!standalone) exit(0);
  ! 
  !     ap_longjmp (jmpbuffer, 1);
        }
        else {   /* abort the connection */
            bsetflag(current_conn->client, B_EOUT, 1);
  ***************
  *** 538,548 ****
        }
        
        if (!current_conn) {
  ! #if defined(USE_LONGJMP)
  !     longjmp(jmpbuffer,1);
  ! #else
  !     siglongjmp(jmpbuffer,1);
  ! #endif
        }
        bsetflag(current_conn->client, B_EOUT, 1);
        current_conn->aborted = 1;
  --- 550,556 ----
        }
        
        if (!current_conn) {
  !     ap_longjmp (jmpbuffer, 1);
        }
        bsetflag(current_conn->client, B_EOUT, 1);
        current_conn->aborted = 1;
  ***************
  *** 1172,1266 ****
    in wait_or_timeout(). But this routine is still useful for systems with no
    waitpid().
    */
  ! int reap_children()
  !     {
  !     int status,n;
  !     int ret=0;
    
  !     for(n=0 ; n < HARD_SERVER_LIMIT ; ++n)
  !     if(scoreboard_image->servers[n].status != SERVER_DEAD
  !        && waitpid(scoreboard_image->servers[n].pid,&status,WNOHANG) == -1
  !        && errno == ECHILD)
  !         {
  !         sync_scoreboard_image();
  !         update_child_status(n,SERVER_DEAD,NULL);
  !         ret=1;
  !         }
  !     return ret;
        }
    #endif
    
    /* Finally, this routine is used by the caretaker process to wait for
     * a while...
     */
    
  ! #if 1
  ! 
  ! static int wait_or_timeout(int *status)
  !     {
    #ifndef NEED_WAITPID
        int ret;
    
  !     ret=waitpid(-1,status,WNOHANG);
  !     if(ret <= 0)
  !     {
  !     sleep(1);
        return -1;
  !     }
        return ret;
    #else
  !     if(!reap_children())
        sleep(1);
        return -1;
    #endif
  !     }
    
  - #else
    
  ! static JMP_BUF wait_timeout_buf;
    
  ! static void longjmp_out_of_alarm (int sig) {
  ! #if defined(USE_LONGJMP)
  !     longjmp (wait_timeout_buf, 1);
  ! #else
  !     siglongjmp (wait_timeout_buf, 1);
  ! #endif
    }
    
  ! int wait_or_timeout (int *status)
    {
  !     int wait_or_timeout_retval = -1;
  ! #ifdef BROKEN_WAIT
  !     static int ntimes;
  ! #endif
    
  ! #if defined(USE_LONGJMP)
  !     if (setjmp(wait_timeout_buf) != 0) {
  ! #else 
  !     if (sigsetjmp(wait_timeout_buf, 1) != 0) {
  ! #endif
  !     errno = ETIMEDOUT;
  !     return wait_or_timeout_retval;
        }
  ! #ifdef BROKEN_WAIT
  !     if(++ntimes == 60)
  !     {
  !     reap_children();
  !     ntimes=0;
  !     }
  ! #endif
  !     signal (SIGALRM, longjmp_out_of_alarm);
  !     alarm(1);
  ! #if defined(NEXT)
  !     wait_or_timeout_retval = wait((union wait *)status);
    #else
  !     wait_or_timeout_retval = wait(status);
    #endif
  -     alarm(0);
  -     return wait_or_timeout_retval;
    }
    
  - #endif
    
    /*****************************************************************
     * Here follows a long bunch of generic server bookkeeping stuff...
  --- 1180,1344 ----
    in wait_or_timeout(). But this routine is still useful for systems with no
    waitpid().
    */
  ! int reap_children ()
  ! {
  !     int status, n;
  !     int ret = 0;
    
  !     for (n = 0; n < HARD_SERVER_LIMIT; ++n) {
  !     if (scoreboard_image->servers[n].status != SERVER_DEAD
  !             && waitpid (scoreboard_image->servers[n].pid, &status, WNOHANG)
  !                 == -1
  !             && errno == ECHILD) {
  !         sync_scoreboard_image ();
  !         update_child_status (n, SERVER_DEAD, NULL);
  !         ret = 1;
  !     }
        }
  +     return ret;
  + }
    #endif
    
    /* Finally, this routine is used by the caretaker process to wait for
     * a while...
     */
    
  ! static int wait_or_timeout ()
  ! {
    #ifndef NEED_WAITPID
        int ret;
    
  !     ret = waitpid (-1, NULL, WNOHANG);
  !     if (ret == -1 && errno == EINTR) {
        return -1;
  !     }
  !     if (ret <= 0) {
  !     sleep (1);
  !     return -1;
  !     }
        return ret;
    #else
  !     if (!reap_children ()) {
        sleep(1);
  +     }
        return -1;
    #endif
  ! }
    
    
  ! void sig_term() {
  !     log_error("httpd: caught SIGTERM, shutting down", server_conf);
  !     cleanup_scoreboard();
  !     ap_killpg (pgrp, SIGKILL);
  !     close(sd);
  !     exit(1);
  ! }
    
  ! void bus_error(void) {
  !     char emsg[256];
  ! 
  !     ap_snprintf
  !     (
  !         emsg,
  !         sizeof(emsg) - 1,
  !         "httpd: caught SIGBUS, attempting to dump core in %s",
  !         server_root
  !     );
  !     log_error(emsg, server_conf);
  !     chdir(server_root);
  !     abort();         
  !     exit(1);
  ! }
  ! 
  ! void seg_fault() {
  !     char emsg[256];
  ! 
  !     ap_snprintf
  !     (
  !         emsg,
  !         sizeof(emsg) - 1,
  !         "httpd: caught SIGSEGV, attempting to dump core in %s",
  !         server_root
  !     );
  !     log_error(emsg, server_conf);
  !     chdir(server_root);
  !     abort();
  !     exit(1);
    }
    
  ! void just_die()                     /* SIGHUP to child process??? */
    {
  !     exit (0);
  ! }
    
  ! static int deferred_die;
  ! 
  ! static void deferred_die_handler ()
  ! {
  !     deferred_die = 1;
  ! }
  ! 
  ! /* volatile just in case */
  ! static volatile int restart_pending;
  ! static volatile int is_graceful;
  ! static volatile int generation;
  ! 
  ! static void restart (int sig)
  ! {
  !     is_graceful = (sig == SIGUSR1);
  !     restart_pending = 1;
  ! }
  ! 
  ! 
  ! void set_signals()
  ! {
  ! #ifndef NO_USE_SIGACTION
  !     struct sigaction sa;
  ! 
  !     sigemptyset(&sa.sa_mask);
  !     sa.sa_flags = 0;
  ! 
  !     if (!one_process) {
  !     sa.sa_handler = (void (*)())seg_fault;
  !     if (sigaction (SIGSEGV, &sa, NULL) < 0)
  !         log_unixerr ("sigaction(SIGSEGV)", NULL, NULL, server_conf);
  !     sa.sa_handler = (void (*)())bus_error;
  !     if (sigaction (SIGBUS, &sa, NULL) < 0)
  !         log_unixerr ("sigaction(SIGBUS)", NULL, NULL, server_conf);
        }
  !     sa.sa_handler = (void (*)())sig_term;
  !     if (sigaction (SIGTERM, &sa, NULL) < 0)
  !     log_unixerr ("sigaction(SIGTERM)", NULL, NULL, server_conf);
  ! 
  !     /* wait_or_timeout uses sleep() which could deliver a SIGALRM just as 
we're
  !      * trying to process the restart requests.  That's not good.  restart
  !      * cleans out the SIGALRM handler, but this totally avoids the race
  !      * condition between when the restart request is made and when the 
handler
  !      * is invoked.
  !      *
  !      * We also don't want to ignore HUPs and USR1 while we're busy 
processing
  !      * one.
  !      */
  !     sigaddset (&sa.sa_mask, SIGALRM);
  !     sigaddset (&sa.sa_mask, SIGHUP);
  !     sigaddset (&sa.sa_mask, SIGUSR1);
  !     sa.sa_handler = (void (*)())restart;
  !     if (sigaction (SIGHUP, &sa, NULL) < 0)
  !     log_unixerr ("sigaction(SIGHUP)", NULL, NULL, server_conf);
  !     if (sigaction (SIGUSR1, &sa, NULL) < 0)
  !     log_unixerr ("sigaction(SIGUSR1)", NULL, NULL, server_conf);
    #else
  !     if(!one_process) {
  !     signal (SIGSEGV, (void (*)())seg_fault);
  !             signal (SIGBUS, (void (*)())bus_error);
  !     }
  ! 
  !     signal (SIGTERM, (void (*)())sig_term);
  !     signal (SIGHUP, (void (*)())restart);
  !     signal (SIGUSR1, (void (*)())restart);
    #endif
    }
    
    
    /*****************************************************************
     * Here follows a long bunch of generic server bookkeeping stuff...
  ***************
  *** 1310,1364 ****
    #endif
    }
    
  - void sig_term() {
  -     log_error("httpd: caught SIGTERM, shutting down", server_conf);
  -     cleanup_scoreboard();
  - #ifndef NO_KILLPG
  -     killpg(pgrp,SIGKILL);
  - #else
  -     kill(-pgrp,SIGKILL);
  - #endif
  -     close(sd);
  -     exit(1);
  - }
  - 
  - void bus_error(void) {
  -     char emsg[256];
  - 
  -     ap_snprintf
  -     (
  -         emsg,
  -         sizeof(emsg) - 1,
  -         "httpd: caught SIGBUS, attempting to dump core in %s",
  -         server_root
  -     );
  -     log_error(emsg, server_conf);
  -     chdir(server_root);
  -     abort();         
  -     exit(1);
  - }
  - 
  - void seg_fault() {
  -     char emsg[256];
  - 
  -     ap_snprintf
  -     (
  -         emsg,
  -         sizeof(emsg) - 1,
  -         "httpd: caught SIGSEGV, attempting to dump core in %s",
  -         server_root
  -     );
  -     log_error(emsg, server_conf);
  -     chdir(server_root);
  -     abort();
  -     exit(1);
  - }
  - 
  - void just_die()                     /* SIGHUP to child process??? */
  - {
  -     exit (0);
  - }
  - 
    /* Reset group privileges, after rereading the config files
     * (our uid may have changed, and if so, we want the new perms).
     *
  --- 1388,1393 ----
  ***************
  *** 1429,1499 ****
        return (suexec_enabled);
    }
    
  - static int is_graceful;
  - static int generation;
  - 
  - void restart() {
  -     signal (SIGALRM, SIG_IGN);
  -     alarm (0);
  -     is_graceful=0;
  - #if defined(USE_LONGJMP)
  -     longjmp(restart_buffer,1);
  - #else
  -     siglongjmp(restart_buffer,1);
  - #endif
  - }
  - 
  - void graceful_restart()
  -     {
  -     scoreboard_image->global.exit_generation=generation;
  -     is_graceful=1;
  -     update_scoreboard_global();
  - #if defined(USE_LONGJMP)
  -     longjmp(restart_buffer,1);
  - #else
  -     siglongjmp(restart_buffer,1);
  - #endif
  -     }
  - 
  - void set_signals()
  - {
  - #ifndef NO_USE_SIGACTION
  -     struct sigaction sa;
  - 
  -     sigemptyset(&sa.sa_mask);
  -     sa.sa_flags = 0;
  - 
  -     if (!one_process) {
  -     sa.sa_handler = (void (*)())seg_fault;
  -     if (sigaction(SIGSEGV, &sa, NULL) < 0)
  -         log_unixerr("sigaction(SIGSEGV)", NULL, NULL, server_conf);
  -     sa.sa_handler = (void (*)())bus_error;
  -     if (sigaction(SIGBUS, &sa, NULL) < 0)
  -         log_unixerr("sigaction(SIGBUS)", NULL, NULL, server_conf);
  -     }
  -     /* USE WITH EXTREME CAUTION. Graceful restarts are known to break */
  -     /*  problems will be dealt with in a future release */
  -     sa.sa_handler=(void (*)())sig_term;
  -     if(sigaction(SIGTERM,&sa,NULL) < 0)
  -     log_unixerr("sigaction(SIGTERM)", NULL, NULL, server_conf);
  -     sa.sa_handler=(void (*)())restart;
  -     if(sigaction(SIGHUP,&sa,NULL) < 0)
  -     log_unixerr("sigaction(SIGHUP)", NULL, NULL, server_conf);
  -     sa.sa_handler=(void (*)())graceful_restart;
  -     if(sigaction(SIGUSR1,&sa,NULL) < 0)
  -     log_unixerr("sigaction(SIGUSR1)", NULL, NULL, server_conf);
  - #else
  -     if(!one_process) {
  -     signal(SIGSEGV,(void (*)())seg_fault);
  -             signal(SIGBUS,(void (*)())bus_error);
  -     }
  - 
  -     signal(SIGTERM,(void (*)())sig_term);
  -     signal(SIGHUP,(void (*)())restart);
  -     signal(SIGUSR1,(void (*)())graceful_restart);
  - #endif
  - }
  - 
    /*****************************************************************
     * Connection structures and accounting...
     * Should these be global?  Only to this file, at least...
  --- 1458,1463 ----
  ***************
  *** 1727,1737 ****
         * Setup the jump buffers so that we can return here after
         * a signal or a timeout (yeah, I know, same thing).
         */
  ! #if defined(USE_LONGJMP)
  !     setjmp(jmpbuffer);
  ! #else
  !     sigsetjmp(jmpbuffer,1);
  ! #endif
    #ifndef __EMX__
        signal(SIGURG, timeout);
    #endif    
  --- 1691,1697 ----
         * Setup the jump buffers so that we can return here after
         * a signal or a timeout (yeah, I know, same thing).
         */
  !     ap_setjmp (jmpbuffer);
    #ifndef __EMX__
        signal(SIGURG, timeout);
    #endif    
  ***************
  *** 1741,1746 ****
  --- 1701,1712 ----
        BUFF *conn_io;
        request_rec *r;
          
  +     /* Prepare to receive a SIGUSR1 due to graceful restart so that
  +      * we can exit cleanly.  Since we're between connections right
  +      * now it's the right time to exit, but we might be blocked in a
  +      * system call when the graceful restart request is made. */
  +     signal (SIGUSR1, (void (*)())just_die);
  + 
            /*
             * (Re)initialize this child to a pre-connection state.
             */
  ***************
  *** 1804,1813 ****
                        continue;
                }
    
  !             do {
                    clen = sizeof(sa_client);
                    csd  = accept(sd, &sa_client, &clen);
  !             } while (csd < 0 && errno == EINTR);
    
                if (csd >= 0)
                    break;      /* We have a socket ready for reading */
  --- 1770,1789 ----
                        continue;
                }
    
  !         /* if we accept() something we don't want to die, so we have to
  !          * defer the exit
  !          */
  !         deferred_die = 0;
  !         signal (SIGUSR1, (void (*)())deferred_die_handler);
  !             for (;;) {
                    clen = sizeof(sa_client);
                    csd  = accept(sd, &sa_client, &clen);
  !             if (csd >= 0 || errno != EINTR) break;
  !             if (deferred_die) {
  !                 /* we didn't get a socket, and we were told to die */
  !                 exit (0);
  !             }
  !         }
    
                if (csd >= 0)
                    break;      /* We have a socket ready for reading */
  ***************
  *** 1822,1831 ****
  --- 1798,1819 ----
    #endif
                    log_unixerr("accept", "(client socket)", NULL, server_conf);
                }
  + 
  +         /* go around again, safe to die */
  +         signal (SIGUSR1, (void (*)())just_die);
  +         if (deferred_die) {
  +             /* ok maybe not, see ya later */
  +             exit (0);
  +         }
            }
    
            accept_mutex_off(); /* unlock after "accept" */
    
  +     /* We've got a socket, let's at least process one request off the
  +      * socket before we accept a graceful restart request.
  +      */
  +     signal (SIGUSR1, SIG_IGN);
  + 
        note_cleanups_for_fd(ptrans,csd);
    
            /*
  ***************
  *** 1867,1872 ****
  --- 1855,1866 ----
    
            for (;;) {
                r = read_request(current_conn);
  + 
  +         /* ok we've read the request... it's a little too late
  +          * to do a graceful restart, so ignore them for now.
  +          */
  +         signal (SIGUSR1, SIG_IGN);
  + 
                (void)update_child_status(child_num, SERVER_BUSY_WRITE, r);
    
                if (r) process_request(r); /* else premature EOF --- ignore */
  ***************
  *** 1885,1890 ****
  --- 1879,1899 ----
                    bclose(conn_io);
                    exit(0);
                }
  + 
  +         /* In case we get a graceful restart while we're blocked
  +          * waiting for the request.
  +          *
  +          * XXX: This isn't perfect, we might actually read the
  +          * request and then just die without saying anything to
  +          * the client.  This can be fixed by using deferred_die
  +          * but you have to teach buff.c about it so that it can handle
  +          * the EINTR properly.
  +          *
  +          * In practice though browsers (have to) expect keepalive
  +          * connections to close before receiving a response because
  +          * of network latencies and server timeouts.
  +          */
  +         signal (SIGUSR1, (void (*)())just_die);
            }
    
            /*
  ***************
  *** 1921,1929 ****
  --- 1930,1946 ----
        child_main (child_num);
        }
    
  +     Explain1 ("Starting new child in slot %d", child_num);
  +     (void)update_child_status (child_num, SERVER_STARTING, (request_rec 
*)NULL);
  + 
        if ((pid = fork()) == -1) {
        log_unixerr("fork", NULL, "Unable to fork new process", server_conf);
    
  +     /* fork didn't succeed. Fix the scoreboard or else
  +      * it will say SERVER_STARTING forever and ever
  +      */
  +     (void)update_child_status (child_num, SERVER_DEAD, (request_rec*)NULL);
  + 
        /* In case system resources are maxxed out, we don't want
               Apache running away with the CPU trying to fork over and
               over and over again. */
  ***************
  *** 1933,1942 ****
  --- 1950,1974 ----
        } 
        
        if (!pid) {
  +     /* Disable the restart signal handlers and enable the just_die stuff.
  +      * Note that since restart() just notes that a restart has been
  +      * requested there's no race condition here.
  +      */
        signal (SIGHUP, (void (*)())just_die);
  +     signal (SIGUSR1, (void (*)())just_die);
        signal (SIGTERM, (void (*)())just_die);
        child_main (child_num);
        }
  + 
  +     /* If the parent proceeds with a restart before the child has written
  +      * their pid into the scoreboard we'll end up "forgetting" about the
  +      * child.  So we write the child pid into the scoreboard now.  (This
  +      * is safe, because the child is going to be writing the same value
  +      * to the same word.)
  +      * XXX: this needs to be sync'd to disk in the non shared memory stuff
  +      */
  +     scoreboard_image->servers[child_num].pid = pid;
  + 
        return 0;
    }
    
  ***************
  *** 2076,2225 ****
     * Executive routines.
     */
    
  - static int num_children = 0;
  - 
    void standalone_main(int argc, char **argv)
    {
        struct sockaddr_in sa_server;
        int saved_sd;
    
        standalone = 1;
        sd = listenmaxfd = -1;
  -     
  -     if (!one_process) detach(); 
  -     
  - #if defined(USE_LONGJMP)
  -     setjmp(restart_buffer);
  - #else
  -     sigsetjmp(restart_buffer,1);
  - #endif
    
        ++generation;
    
  !     signal (SIGHUP, SIG_IGN);       /* Until we're done (re)reading config 
*/
  !     
  !     if(!one_process && !is_graceful)
  !     {
  ! #ifndef NO_KILLPG
  !       if (killpg(pgrp,SIGHUP) < 0)    /* Kill 'em off */
  ! #else
  !       if (kill(-pgrp,SIGHUP) < 0)
  ! #endif
  !         log_unixerr ("killpg SIGHUP", NULL, NULL, server_conf);
  !     }
  !     
  !     if(is_graceful)
  !     {
  !     /* USE WITH EXTREME CAUTION. Graceful restarts are known to break */
  !     /*  problems will be dealt with in a future release */
  !     log_error("SIGUSR1 received.  Doing graceful restart",server_conf);
  !     kill_cleanups_for_fd(pconf,sd);
  !     }
  !     else if (sd != -1 || listenmaxfd != -1) {
  !     reclaim_child_processes(); /* Not when just starting up */
  !     log_error ("SIGHUP received.  Attempting to restart", server_conf);
  !     }
  !     
  !     copy_listeners(pconf);
  !     saved_sd=sd;
  !     restart_time = time(NULL);
  !     clear_pool (pconf);
  !     ptrans = make_sub_pool (pconf);
  !     
  !     server_conf = read_config(pconf, ptrans, server_confname); 
  !     open_logs(server_conf, pconf);
  !     set_group_privs();
  !     accept_mutex_init(pconf);
  !     reinit_scoreboard(pconf);
  !     
  !     default_server_hostnames (server_conf);
  ! 
  !     if (listeners == NULL) {
  !         if(!is_graceful) {
  !         memset((char *) &sa_server, 0, sizeof(sa_server));
  !         sa_server.sin_family=AF_INET;
  !         sa_server.sin_addr=bind_address;
  !         sa_server.sin_port=htons(server_conf->port);
    
  !         sd = make_sock(pconf, &sa_server);
        }
        else {
  !         sd = saved_sd;
  !         note_cleanups_for_fd(pconf, sd);
        }
  -     }
  -     else {
  -     listen_rec *lr;
  -     int fd;
    
  !     listenmaxfd = -1;
  !     FD_ZERO(&listenfds);
  !     for (lr=listeners; lr != NULL; lr=lr->next)
  !     {
  !         fd=find_listener(lr);
  !         if(fd < 0)
  !             fd = make_sock(pconf, &lr->local_addr);
  !         FD_SET(fd, &listenfds);
  !         if (fd > listenmaxfd) listenmaxfd = fd;
  !         lr->fd=fd;
        }
  -     close_unused_listeners();
  -     sd = -1;
  -     }
    
  !     set_signals();
  !     log_pid(pconf, pid_fname);
    
  !     num_children = 0;
  !     
  !     if (daemons_max_free < daemons_min_free + 1) /* Don't thrash... */
  !     daemons_max_free = daemons_min_free + 1;
    
  !     while (num_children < daemons_to_start && num_children < daemons_limit) 
{
  !     make_child(server_conf, num_children++);
  !     }
    
  !     log_error ("Server configured -- resuming normal operations", 
server_conf);
  !     
  !     while (1) {
  !     int status, child_slot;
  !     int pid = wait_or_timeout(&status);
  !     
  !     if (pid >= 0) {
  !         /* Child died... note that it's gone in the scoreboard. */
            sync_scoreboard_image();
  !         child_slot = find_child_by_pid (pid);
  !         Explain2("Reaping child %d slot %d",pid,child_slot);
  !         if (child_slot >= 0)
  !             (void)update_child_status (child_slot, SERVER_DEAD,
  !              (request_rec*)NULL);
  !         }
  ! 
  !     sync_scoreboard_image();
  !     if ((count_idle_servers() < daemons_min_free)
  !      && (child_slot = find_free_child_num()) >= 0
  !      && child_slot < daemons_limit) {
  !         Explain1("Starting new child in slot %d",child_slot);
  !         (void)update_child_status(child_slot,SERVER_STARTING,
  !          (request_rec*)NULL);
  !         if (make_child(server_conf, child_slot) < 0) {
  !             /* fork didn't succeed. Fix the scoreboard or else
  !                it will say SERVER_STARTING forever and ever */
  !             (void)update_child_status(child_slot,SERVER_DEAD,
  !                  (request_rec*)NULL);
            }
  ! 
        }
    
  !     /*
  !     if(scoreboard_image->global.please_exit && !count_live_servers())
  ! #if defined(USE_LONGJMP)
  !         longjmp(restart_buffer,1);
  ! #else
  !         siglongjmp(restart_buffer,1);
  ! #endif
  !     */
  !     }
    
    } /* standalone_main */
    
  --- 2108,2316 ----
     * Executive routines.
     */
    
    void standalone_main(int argc, char **argv)
    {
        struct sockaddr_in sa_server;
        int saved_sd;
  +     int remaining_children_to_start;
    
        standalone = 1;
        sd = listenmaxfd = -1;
    
  +     is_graceful = 0;
        ++generation;
    
  !     if (!one_process) detach (); 
    
  !     do {
  !     copy_listeners(pconf);
  !     saved_sd = sd;
  !     if (!is_graceful) {
  !         restart_time = time(NULL);
  !     }
  !     clear_pool (pconf);
  !     ptrans = make_sub_pool (pconf);
  ! 
  !     server_conf = read_config (pconf, ptrans, server_confname); 
  !     open_logs (server_conf, pconf);
  !     set_group_privs ();
  !     accept_mutex_init (pconf);
  !     if (!is_graceful) {
  !         reinit_scoreboard(pconf);
  !     }
  ! 
  !     default_server_hostnames (server_conf);
  ! 
  !     if (listeners == NULL) {
  !         if (!is_graceful) {
  !             memset ((char *)&sa_server, 0, sizeof (sa_server));
  !             sa_server.sin_family = AF_INET;
  !             sa_server.sin_addr = bind_address;
  !             sa_server.sin_port = htons (server_conf->port);
  !             sd = make_sock (pconf, &sa_server);
  !         }
  !         else {
  !             sd = saved_sd;
  !             note_cleanups_for_fd(pconf, sd);
  !         }
        }
        else {
  !         listen_rec *lr;
  !         int fd;
  ! 
  !         listenmaxfd = -1;
  !         FD_ZERO (&listenfds);
  !         for (lr = listeners; lr != NULL; lr = lr->next)
  !         {
  !             fd = find_listener (lr);
  !             if (fd < 0) {
  !                 fd = make_sock (pconf, &lr->local_addr);
  !             }
  !             FD_SET (fd, &listenfds);
  !             if (fd > listenmaxfd) listenmaxfd = fd;
  !             lr->fd = fd;
  !         }
  !         close_unused_listeners ();
  !         sd = -1;
        }
    
  !     set_signals ();
  !     log_pid (pconf, pid_fname);
  ! 
  !     if (daemons_max_free < daemons_min_free + 1) /* Don't thrash... */
  !         daemons_max_free = daemons_min_free + 1;
  ! 
  !     /* If we're doing a graceful_restart then we're going to see a lot
  !      * of children exiting immediately when we get into the main loop
  !      * below (because we just sent them SIGUSR1).  This happens pretty
  !      * rapidly... and for each one that exits we'll start a new one until
  !      * we reach at least daemons_min_free.  But we may be permitted to
  !      * start more than that, so we'll just keep track of how many we're
  !      * supposed to start up without the 1 second penalty between each fork.
  !      */
  !     remaining_children_to_start = daemons_to_start;
  !     if( remaining_children_to_start > daemons_limit ) {
  !         remaining_children_to_start = daemons_limit;
  !     }
  !     if (!is_graceful) {
  !         while (remaining_children_to_start) {
  !             --remaining_children_to_start;
  !             make_child (server_conf, remaining_children_to_start);
  !         }
        }
    
  !     log_error ("Server configured -- resuming normal operations",
  !         server_conf);
  !     restart_pending = 0;
  ! 
  !     while (!restart_pending) {
  !         int child_slot;
  !         int pid = wait_or_timeout ();
  ! 
  !         /* XXX: if it takes longer than 1 second for all our children
  !          * to start up and get into IDLE state then we may spawn an
  !          * extra child
  !          */
  !         if (pid >= 0) {
  !             /* Child died... note that it's gone in the scoreboard. */
  !             sync_scoreboard_image ();
  !             child_slot = find_child_by_pid (pid);
  !             Explain2 ("Reaping child %d slot %d", pid, child_slot);
  !             if (child_slot >= 0) {
  !                 (void)update_child_status (child_slot, SERVER_DEAD,
  !                     (request_rec *)NULL);
  !             } else if (is_graceful) {
  !                 /* Great, we've probably just lost a slot in the
  !                  * scoreboard.  Somehow we don't know about this
  !                  * child.
  !                  */
  !                 log_printf (server_conf,
  !                     "long lost child came home! (pid %d)", pid );
  !             }
  !         } else if (remaining_children_to_start) {
  !             /* we hit a 1 second timeout in which none of the previous
  !              * generation of children needed to be reaped... so assume
  !              * they're all done, and pick up the slack if any is left.
  !              */
  !             while (remaining_children_to_start > 0) {
  !                 child_slot = find_free_child_num ();
  !                 if (child_slot < 0 || child_slot >= daemons_limit) {
  !                     remaining_children_to_start = 0;
  !                     break;
  !                 }
  !                 if (make_child (server_conf, child_slot) < 0) {
  !                     remaining_children_to_start = 0;
  !                     break;
  !                 }
  !                 --remaining_children_to_start;
  !             }
  !             /* In any event we really shouldn't do the code below because
  !              * few of the servers we just started are in the IDLE state
  !              * yet, so we'd mistakenly create an extra server.
  !              */
  !             continue;
  !         }
    
  !         sync_scoreboard_image ();
  !         if ((remaining_children_to_start
  !                 || (count_idle_servers () < daemons_min_free))
  !             && (child_slot = find_free_child_num ()) >= 0
  !             && child_slot < daemons_limit) {
  !             make_child (server_conf, child_slot);
  !         }
  !         if (remaining_children_to_start) {
  !             --remaining_children_to_start;
  !         }
  !     }
    
  !     /* we've been told to restart */
    
  !     if (one_process) {
  !         /* not worth thinking about */
  !         exit (0);
  !     }
  ! 
  !     if (is_graceful) {
  !         int i;
  ! 
  !         /* USE WITH CAUTION:  Graceful restarts are not known to work
  !         * in various configurations on the architectures we support. */
  !         scoreboard_image->global.exit_generation = generation;
  !         update_scoreboard_global ();
  ! 
  !         log_error ("SIGUSR1 received.  Doing graceful restart",server_conf);
  !         kill_cleanups_for_fd (pconf, sd);
  !         /* kill off the idle ones */
  !         if (ap_killpg(pgrp, SIGUSR1) < 0) {
  !             log_unixerr ("killpg SIGUSR1", NULL, NULL, server_conf);
  !         }
  !         /* This is mostly for debugging... so that we know what is still
  !          * gracefully dealing with existing request.
  !          * XXX: clean this up a bit?
  !          */
            sync_scoreboard_image();
  !         for (i = 0; i < daemons_limit; ++i ) {
  !             if (scoreboard_image->servers[i].status != SERVER_DEAD) {
  !                 scoreboard_image->servers[i].status = SERVER_GRACEFUL;
  !             }
            }
  ! #if !defined(HAVE_MMAP) && !defined(HAVE_SHMGET)
  !         lseek (scoreboard_fd, 0L, 0);
  !         force_write (scoreboard_fd, (char*)scoreboard_image,
  !                     sizeof(*scoreboard_image));
  ! #endif
        }
  +     else {
  +         /* Kill 'em off */
  +         if (ap_killpg (pgrp, SIGHUP) < 0) {
  +             log_unixerr ("killpg SIGHUP", NULL, NULL, server_conf);
  +         }
  +         reclaim_child_processes(); /* Not when just starting up */
  +         log_error ("SIGHUP received.  Attempting to restart", server_conf);
  +     }
  +     ++generation;
    
  !     } while (restart_pending);
    
    } /* standalone_main */
    
  ***************
  *** 2288,2294 ****
    #endif
    
        setup_prelinked_modules();
  !     
        suexec_enabled = init_suexec();
        server_conf = read_config (pconf, ptrans, server_confname);
        
  --- 2379,2385 ----
    #endif
    
        setup_prelinked_modules();
  ! 
        suexec_enabled = init_suexec();
        server_conf = read_config (pconf, ptrans, server_confname);
        
  
  
  
  1.116     +2 -0      apache/src/http_protocol.c
  
  Index: http_protocol.c
  ===================================================================
  RCS file: /export/home/cvs/apache/src/http_protocol.c,v
  retrieving revision 1.115
  retrieving revision 1.116
  diff -C3 -r1.115 -r1.116
  *** http_protocol.c   1997/04/21 20:29:08     1.115
  --- http_protocol.c   1997/04/24 23:35:20     1.116
  ***************
  *** 626,631 ****
  --- 626,633 ----
                return 0;
        }
        }
  +     /* we've probably got something to do, ignore graceful restart requests 
*/
  +     signal (SIGUSR1, SIG_IGN);
        bsetflag( conn->client, B_SAFEREAD, 0 );
        if (len == (HUGE_STRING_LEN - 1)) {
            log_printf(r->server, "request failed for %s, reason: header too 
long",
  
  
  
  1.9       +3 -2      apache/src/mod_browser.c
  
  Index: mod_browser.c
  ===================================================================
  RCS file: /export/home/cvs/apache/src/mod_browser.c,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -C3 -r1.8 -r1.9
  *** mod_browser.c     1997/03/07 14:15:38     1.8
  --- mod_browser.c     1997/04/24 23:35:21     1.9
  ***************
  *** 117,125 ****
    
        new = push_array(sconf->browsers);
        new->name = name;
  !     new->preg = pcalloc(cmd->pool, sizeof(regex_t));
  !     if (regcomp(new->preg, name, REG_EXTENDED|REG_NOSUB|cflags))
        return "Browser regex could not be compiled.";
        new->features = make_table(cmd->pool, 5);
    
        var = getword(cmd->pool, &feature, '=');
  --- 117,126 ----
    
        new = push_array(sconf->browsers);
        new->name = name;
  !     new->preg = pregcomp (cmd->pool, name, REG_EXTENDED|REG_NOSUB|cflags);
  !     if (new->preg == NULL) {
        return "Browser regex could not be compiled.";
  +     }
        new->features = make_table(cmd->pool, 5);
    
        var = getword(cmd->pool, &feature, '=');
  
  
  
  1.30      +6 -9      apache/src/mod_include.c
  
  Index: mod_include.c
  ===================================================================
  RCS file: /export/home/cvs/apache/src/mod_include.c,v
  retrieving revision 1.29
  retrieving revision 1.30
  diff -C3 -r1.29 -r1.30
  *** mod_include.c     1997/04/22 02:35:10     1.29
  --- mod_include.c     1997/04/24 23:35:21     1.30
  ***************
  *** 861,879 ****
    
    int re_check(request_rec *r, char *string, char *rexp) 
    {
  !     regex_t compiled;
  !     char err_string[MAX_STRING_LEN];
        int regex_error;
    
  !     regex_error = regcomp(&compiled, rexp, REG_EXTENDED|REG_NOSUB);
  !     if (regex_error) {
  !         regerror(regex_error, &compiled, err_string, 
(size_t)MAX_STRING_LEN);
  !         log_printf(r->server,
  !             "unable to compile pattern %s [%s]", rexp, err_string);
            return -1;
        }
  !     regex_error = regexec(&compiled, string, 0, (regmatch_t *)NULL, 0);
  !     regfree(&compiled);
        return(!regex_error);
    }
    
  --- 861,876 ----
    
    int re_check(request_rec *r, char *string, char *rexp) 
    {
  !     regex_t *compiled;
        int regex_error;
    
  !     compiled = pregcomp (r->pool, rexp, REG_EXTENDED|REG_NOSUB);
  !     if (compiled == NULL) {
  !         log_printf(r->server, "unable to compile pattern %s", rexp);
            return -1;
        }
  !     regex_error = regexec(compiled, string, 0, (regmatch_t *)NULL, 0);
  !     pregfree (r->pool, compiled);
        return(!regex_error);
    }
    
  
  
  
  1.28      +1 -1      apache/src/mod_rewrite.c
  
  Index: mod_rewrite.c
  ===================================================================
  RCS file: /export/home/cvs/apache/src/mod_rewrite.c,v
  retrieving revision 1.27
  retrieving revision 1.28
  diff -C3 -r1.27 -r1.28
  *** mod_rewrite.c     1997/04/17 02:52:51     1.27
  --- mod_rewrite.c     1997/04/24 23:35:22     1.28
  ***************
  *** 2759,2765 ****
        cache *c;
    
        c = (cache *)palloc(p, sizeof(cache));
  !     c->pool = make_sub_pool(NULL);
        c->lists = make_array(c->pool, 2, sizeof(cachelist));
        return c;
    }
  --- 2759,2765 ----
        cache *c;
    
        c = (cache *)palloc(p, sizeof(cache));
  !     c->pool = make_sub_pool(p);
        c->lists = make_array(c->pool, 2, sizeof(cachelist));
        return c;
    }
  
  
  
  1.46      +16 -1     apache/src/mod_status.c
  
  Index: mod_status.c
  ===================================================================
  RCS file: /export/home/cvs/apache/src/mod_status.c,v
  retrieving revision 1.45
  retrieving revision 1.46
  diff -C3 -r1.45 -r1.46
  *** mod_status.c      1997/04/06 07:43:42     1.45
  --- mod_status.c      1997/04/24 23:35:23     1.46
  ***************
  *** 227,232 ****
  --- 227,233 ----
        status[SERVER_BUSY_KEEPALIVE]='K';
        status[SERVER_BUSY_LOG]='L';
        status[SERVER_BUSY_DNS]='D';
  +     status[SERVER_GRACEFUL]='G';
    
        if (r->method_number != M_GET) return NOT_IMPLEMENTED;
        r->content_type = "text/html";
  ***************
  *** 279,285 ****
            ready++;
            else if (res == SERVER_BUSY_READ || res==SERVER_BUSY_WRITE || 
                 res == SERVER_STARTING || res==SERVER_BUSY_KEEPALIVE ||
  !              res == SERVER_BUSY_LOG || res==SERVER_BUSY_DNS)
            busy++;
    #if defined(STATUS)
            lres = score_record.access_count;
  --- 280,287 ----
            ready++;
            else if (res == SERVER_BUSY_READ || res==SERVER_BUSY_WRITE || 
                 res == SERVER_STARTING || res==SERVER_BUSY_KEEPALIVE ||
  !              res == SERVER_BUSY_LOG || res==SERVER_BUSY_DNS ||
  !              res == SERVER_GRACEFUL)
            busy++;
    #if defined(STATUS)
            lres = score_record.access_count;
  ***************
  *** 407,412 ****
  --- 409,415 ----
        rputs("\"<B><code>K</code></B>\" Keepalive (read), \n",r);
        rputs("\"<B><code>D</code></B>\" DNS Lookup,<BR>\n",r);
        rputs("\"<B><code>L</code></B>\" Logging, \n",r);
  +     rputs("\"<B><code>G</code></B>\" Gracefully finishing, \n",r);
        rputs("\"<B><code>.</code></B>\" Open slot with no current 
process<P>\n",r);
        }
    
  ***************
  *** 468,473 ****
  --- 471,482 ----
                        case SERVER_DEAD:
                            rputs("Dead",r);
                            break;
  +                     case SERVER_GRACEFUL:
  +                         rputs("Graceful",r);
  +                         break;
  +                     default:
  +                         rputs("?STATE?",r);
  +                         break;
                    }
    #ifdef __EMX__
                        /* Allow for OS/2 not having CPU stats */
  ***************
  *** 521,526 ****
  --- 530,541 ----
                        case SERVER_DEAD:
                            rputs("<td>.",r);
                            break;
  +                     case SERVER_GRACEFUL:
  +                         rputs("<td>G",r);
  +                         break;
  +                     default:
  +                         rputs("<td>?",r);
  +                         break;
                    }
    #ifdef __EMX__
                    /* Allow for OS/2 not having CPU stats */
  
  
  
  1.21      +1 -0      apache/src/scoreboard.h
  
  Index: scoreboard.h
  ===================================================================
  RCS file: /export/home/cvs/apache/src/scoreboard.h,v
  retrieving revision 1.20
  retrieving revision 1.21
  diff -C3 -r1.20 -r1.21
  *** scoreboard.h      1997/01/01 18:10:44     1.20
  --- scoreboard.h      1997/04/24 23:35:23     1.21
  ***************
  *** 71,76 ****
  --- 71,77 ----
    #define SERVER_BUSY_KEEPALIVE 5 /* Waiting for more requests via keepalive 
*/
    #define SERVER_BUSY_LOG 6       /* Logging the request */
    #define SERVER_BUSY_DNS 7       /* Looking up a hostname */
  + #define SERVER_GRACEFUL 8   /* server is gracefully finishing request */
    
    typedef struct {
        pid_t pid;
  
  
  

Reply via email to