Mailing List Archive

cvs commit: apache/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
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;