You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Ben Laurie <be...@gonzo.ben.algroup.co.uk> on 1996/06/25 21:15:45 UTC
Oh sod it
Regardless of the Listen thing, here's the patches for a proper graceful
restart. If someone could test Listen, it would be a Good Thing.
Now, the downside: it can take a large number of connections before the "old"
servers get used again, so they can serve up old configurations an
unexpectedly long time after the kill. The only neat way I can see around this
involves sending the children a signal which is blocked everywhere except in
the select in the main loop. Do all platforms support blocking? Can I be
bothered to write the code right now? No.
Cheers,
Ben.
Index: http_main.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_main.c,v
retrieving revision 1.40
diff -c -r1.40 http_main.c
*** http_main.c 1996/06/22 13:58:34 1.40
--- http_main.c 1996/06/25 19:51:29
***************
*** 88,93 ****
--- 88,94 ----
#include "http_core.h" /* for get_remote_host */
#include "scoreboard.h"
#include <setjmp.h>
+ #include <assert.h>
#ifdef HAVE_SHMGET
#include <sys/types.h>
#include <sys/ipc.h>
***************
*** 427,440 ****
*/
#if defined(HAVE_MMAP)
! static short_score *scoreboard_image=NULL;
static void setup_shared_mem(void)
{
caddr_t m;
#if defined(MAP_ANON) || defined(MAP_FILE)
/* BSD style */
! m = mmap((caddr_t)0, HARD_SERVER_LIMIT*sizeof(short_score),
PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
if (m == (caddr_t)-1)
{
--- 428,441 ----
*/
#if defined(HAVE_MMAP)
! static scoreboard *scoreboard_image=NULL;
static void setup_shared_mem(void)
{
caddr_t m;
#if defined(MAP_ANON) || defined(MAP_FILE)
/* BSD style */
! m = mmap((caddr_t)0, SCOREBOARD_SIZE,
PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
if (m == (caddr_t)-1)
{
***************
*** 453,459 ****
fprintf(stderr, "httpd: Could not open /dev/zero\n");
exit(1);
}
! m = mmap((caddr_t)0, HARD_SERVER_LIMIT*sizeof(short_score),
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (m == (caddr_t)-1)
{
--- 454,460 ----
fprintf(stderr, "httpd: Could not open /dev/zero\n");
exit(1);
}
! m = mmap((caddr_t)0, SCOREBOARD_SIZE,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (m == (caddr_t)-1)
{
***************
*** 463,486 ****
}
close(fd);
#endif
! scoreboard_image = (short_score *)m;
}
#elif defined(HAVE_SHMGET)
! static short_score *scoreboard_image=NULL;
static key_t shmkey = IPC_PRIVATE;
static int shmid = -1;
static void setup_shared_mem(void)
{
- int score_size = HARD_SERVER_LIMIT*sizeof(short_score);
char errstr[MAX_STRING_LEN];
struct shmid_ds shmbuf;
#ifdef MOVEBREAK
char *obrk;
#endif
! if ((shmid = shmget(shmkey, score_size, IPC_CREAT|SHM_R|SHM_W)) == -1)
{
perror("shmget");
fprintf(stderr, "httpd: Could not call shmget\n");
--- 464,487 ----
}
close(fd);
#endif
! scoreboard_image = (scoreboard *)m;
! scoreboard_image->global.exit_generation=0;
}
#elif defined(HAVE_SHMGET)
! static scoreboard *scoreboard_image=NULL;
static key_t shmkey = IPC_PRIVATE;
static int shmid = -1;
static void setup_shared_mem(void)
{
char errstr[MAX_STRING_LEN];
struct shmid_ds shmbuf;
#ifdef MOVEBREAK
char *obrk;
#endif
! if ((shmid = shmget(shmkey, SCOREBOARD_SIZE, IPC_CREAT|SHM_R|SHM_W)) == -1)
{
perror("shmget");
fprintf(stderr, "httpd: Could not call shmget\n");
***************
*** 507,514 ****
}
#endif
! #define BADSHMAT ((short_score*)(-1))
! if ((scoreboard_image = (short_score*)shmat(shmid, 0, 0)) == BADSHMAT)
{
perror("shmat");
fprintf(stderr, "httpd: Could not call shmat\n");
--- 508,515 ----
}
#endif
! #define BADSHMAT ((scoreboard *)(-1))
! if ((scoreboard_image = (scoreboard *)shmat(shmid, 0, 0)) == BADSHMAT)
{
perror("shmat");
fprintf(stderr, "httpd: Could not call shmat\n");
***************
*** 554,563 ****
fprintf(stderr, "httpd: Could not move break back\n");
}
#endif
}
#else
! static short_score scoreboard_image[HARD_SERVER_LIMIT];
static int have_scoreboard_fname = 0;
static int scoreboard_fd;
--- 555,566 ----
fprintf(stderr, "httpd: Could not move break back\n");
}
#endif
+ scoreboard_image->global.exit_generation=0;
}
#else
! static scoreboard _scoreboard_image;
! static scoreboard *scoreboard_image=&_scoreboard_image;
static int have_scoreboard_fname = 0;
static int scoreboard_fd;
***************
*** 595,606 ****
/* Called by parent process */
void reinit_scoreboard (pool *p)
{
#if defined(HAVE_SHMGET) || defined(HAVE_MMAP)
if (scoreboard_image == NULL)
{
setup_shared_mem();
}
! memset(scoreboard_image, 0, HARD_SERVER_LIMIT*sizeof(short_score));
#else
scoreboard_fname = server_root_relative (p, scoreboard_fname);
--- 598,614 ----
/* Called by parent process */
void reinit_scoreboard (pool *p)
{
+ int exit_gen=0;
+ if(scoreboard_image)
+ exit_gen=scoreboard_image->global.exit_generation;
+
#if defined(HAVE_SHMGET) || defined(HAVE_MMAP)
if (scoreboard_image == NULL)
{
setup_shared_mem();
}
! memset(scoreboard_image, 0, SCOREBOARD_SIZE);
! scoreboard_image->global.exit_generation=exit_gen;
#else
scoreboard_fname = server_root_relative (p, scoreboard_fname);
***************
*** 614,622 ****
exit (1);
}
! memset ((char*)scoreboard_image, 0, sizeof(scoreboard_image));
force_write (scoreboard_fd, (char*)scoreboard_image,
! sizeof(scoreboard_image));
#endif
}
--- 622,631 ----
exit (1);
}
! memset ((char*)scoreboard_image, 0, sizeof(*scoreboard_image));
! scoreboard_image->global.exit_generation=exit_gen;
force_write (scoreboard_fd, (char*)scoreboard_image,
! sizeof(*scoreboard_image));
#endif
}
***************
*** 659,665 ****
#if !defined(HAVE_MMAP) && !defined(HAVE_SHMGET)
lseek (scoreboard_fd, 0L, 0);
force_read (scoreboard_fd, (char*)scoreboard_image,
! sizeof(scoreboard_image));
#endif
}
--- 668,674 ----
#if !defined(HAVE_MMAP) && !defined(HAVE_SHMGET)
lseek (scoreboard_fd, 0L, 0);
force_read (scoreboard_fd, (char*)scoreboard_image,
! sizeof(*scoreboard_image));
#endif
}
***************
*** 671,677 ****
if (child_num < 0)
return -1;
! memcpy(&new_score_rec,&scoreboard_image[child_num],sizeof new_score_rec);
new_score_rec.pid = getpid();
old_status = new_score_rec.status;
new_score_rec.status = status;
--- 680,686 ----
if (child_num < 0)
return -1;
! memcpy(&new_score_rec,&scoreboard_image->servers[child_num],sizeof new_score_rec);
new_score_rec.pid = getpid();
old_status = new_score_rec.status;
new_score_rec.status = status;
***************
*** 702,708 ****
#endif
#if defined(HAVE_MMAP) || defined(HAVE_SHMGET)
! memcpy(&scoreboard_image[child_num], &new_score_rec, sizeof(short_score));
#else
lseek (scoreboard_fd, (long)child_num * sizeof(short_score), 0);
force_write (scoreboard_fd, (char*)&new_score_rec, sizeof(short_score));
--- 711,717 ----
#endif
#if defined(HAVE_MMAP) || defined(HAVE_SHMGET)
! memcpy(&scoreboard_image->servers[child_num], &new_score_rec, sizeof new_score_rec);
#else
lseek (scoreboard_fd, (long)child_num * sizeof(short_score), 0);
force_write (scoreboard_fd, (char*)&new_score_rec, sizeof(short_score));
***************
*** 711,722 ****
return old_status;
}
int get_child_status (int child_num)
{
if (child_num<0 || child_num>=HARD_SERVER_LIMIT)
return -1;
else
! return scoreboard_image[child_num].status;
}
int count_busy_servers ()
--- 720,741 ----
return old_status;
}
+ void update_scoreboard_global()
+ {
+ #if !defined(HAVE_MMAP) && !defined(HAVE_SHMGET)
+ lseek(scoreboard_fd,
+ (char *)&scoreboard_image->global-(char *)scoreboard_image,0);
+ force_write(scoreboard_fd,(char *)&scoreboard_image->global,
+ sizeof scoreboard_image->global);
+ #endif
+ }
+
int get_child_status (int child_num)
{
if (child_num<0 || child_num>=HARD_SERVER_LIMIT)
return -1;
else
! return scoreboard_image->servers[child_num].status;
}
int count_busy_servers ()
***************
*** 725,742 ****
int res = 0;
for (i = 0; i < HARD_SERVER_LIMIT; ++i)
! if (scoreboard_image[i].status == SERVER_BUSY_READ ||
! scoreboard_image[i].status == SERVER_BUSY_WRITE ||
! scoreboard_image[i].status == SERVER_BUSY_KEEPALIVE ||
! scoreboard_image[i].status == SERVER_BUSY_LOG ||
! scoreboard_image[i].status == SERVER_BUSY_DNS)
++res;
return res;
}
short_score get_scoreboard_info(int i)
{
! return (scoreboard_image[i]);
}
#if defined(STATUS)
--- 744,772 ----
int res = 0;
for (i = 0; i < HARD_SERVER_LIMIT; ++i)
! if (scoreboard_image->servers[i].status == SERVER_BUSY_READ ||
! scoreboard_image->servers[i].status == SERVER_BUSY_WRITE ||
! scoreboard_image->servers[i].status == SERVER_BUSY_KEEPALIVE ||
! scoreboard_image->servers[i].status == SERVER_BUSY_LOG ||
! scoreboard_image->servers[i].status == SERVER_BUSY_DNS)
++res;
return res;
}
+ int count_live_servers()
+ {
+ int i;
+ int res = 0;
+
+ for (i = 0; i < HARD_SERVER_LIMIT; ++i)
+ if (scoreboard_image->servers[i].status != SERVER_DEAD)
+ ++res;
+ return res;
+ }
+
short_score get_scoreboard_info(int i)
{
! return (scoreboard_image->servers[i]);
}
#if defined(STATUS)
***************
*** 776,783 ****
int res = 0;
for (i = 0; i < HARD_SERVER_LIMIT; ++i)
! if (scoreboard_image[i].status == SERVER_READY
! || scoreboard_image[i].status == SERVER_STARTING)
++res;
return res;
--- 806,813 ----
int res = 0;
for (i = 0; i < HARD_SERVER_LIMIT; ++i)
! if (scoreboard_image->servers[i].status == SERVER_READY
! || scoreboard_image->servers[i].status == SERVER_STARTING)
++res;
return res;
***************
*** 788,794 ****
int i;
for (i = 0; i < HARD_SERVER_LIMIT; ++i)
! if (scoreboard_image[i].status == SERVER_DEAD)
return i;
return -1;
--- 818,824 ----
int i;
for (i = 0; i < HARD_SERVER_LIMIT; ++i)
! if (scoreboard_image->servers[i].status == SERVER_DEAD)
return i;
return -1;
***************
*** 799,805 ****
int i;
for (i = 0; i < HARD_SERVER_LIMIT; ++i)
! if (scoreboard_image[i].pid == pid)
return i;
return -1;
--- 829,835 ----
int i;
for (i = 0; i < HARD_SERVER_LIMIT; ++i)
! if (scoreboard_image->servers[i].pid == pid)
return i;
return -1;
***************
*** 812,821 ****
sync_scoreboard_image();
for (i = 0; i < HARD_SERVER_LIMIT; ++i) {
! int pid = scoreboard_image[i].pid;
if (pid != my_pid && pid != 0)
! waitpid (scoreboard_image[i].pid, &status, 0);
}
}
--- 842,851 ----
sync_scoreboard_image();
for (i = 0; i < HARD_SERVER_LIMIT; ++i) {
! int pid = scoreboard_image->servers[i].pid;
if (pid != my_pid && pid != 0)
! waitpid (scoreboard_image->servers[i].pid, &status, 0);
}
}
***************
*** 984,992 ****
--- 1014,1026 ----
}
}
+ static int is_graceful;
+ static int generation;
+
void restart() {
signal (SIGALRM, SIG_IGN);
alarm (0);
+ is_graceful=0;
#if defined(NEXT) || defined(USE_LONGJMP)
longjmp(restart_buffer,1);
#else
***************
*** 994,999 ****
--- 1028,1045 ----
#endif
}
+ void graceful_restart()
+ {
+ scoreboard_image->global.exit_generation=generation;
+ is_graceful=1;
+ update_scoreboard_global();
+ #if defined(NEXT) || defined(USE_LONGJMP)
+ longjmp(restart_buffer,1);
+ #else
+ siglongjmp(restart_buffer,1);
+ #endif
+ }
+
void set_signals() {
#ifndef NO_USE_SIGACTION
struct sigaction sa;
***************
*** 1006,1011 ****
--- 1052,1058 ----
#ifdef NO_USE_SIGACTION
signal(SIGTERM,(void (*)())sig_term);
signal(SIGHUP,(void (*)())restart);
+ signal(SIGINT,(void (*)())graceful_restart);
#else
memset(&sa,0,sizeof sa);
sa.sa_handler=(void (*)())sig_term;
***************
*** 1014,1019 ****
--- 1061,1069 ----
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(SIGINT,&sa,NULL) < 0)
+ log_unixerr("sigaction(SIGINT)", NULL, NULL, server_conf);
#endif
}
***************
*** 1166,1171 ****
--- 1216,1225 ----
clear_pool (ptrans);
sync_scoreboard_image();
+
+ fprintf(stderr,"%d check %d %d\n",getpid(),scoreboard_image->global.exit_generation,generation);
+ if(scoreboard_image->global.exit_generation >= generation)
+ exit(0);
if ((count_idle_servers() >= daemons_max_free)
|| (max_requests_per_child > 0
***************
*** 1250,1255 ****
--- 1304,1314 ----
#if defined(STATUS)
if (r) increment_counts(child_num,r,0);
#endif
+ sync_scoreboard_image();
+ if(scoreboard_image->global.exit_generation >= generation)
+ exit(0);
+ fprintf(stderr,"%d check %d %d\n",getpid(),scoreboard_image->global.exit_generation,generation);
+
}
#if 0
if (bytes_in_pool (ptrans) > 80000)
***************
*** 1297,1303 ****
exit(1);
}
! note_cleanups_for_fd (pconf, s); /* arrange to close on exec or restart */
if((setsockopt(s, SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one)))
== -1) {
--- 1356,1362 ----
exit(1);
}
! /* note_cleanups_for_fd (pconf, s); /* arrange to close on exec or restart */
if((setsockopt(s, SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one)))
== -1) {
***************
*** 1342,1347 ****
--- 1401,1449 ----
return s;
}
+ static listen_rec *old_listeners;
+
+ static void copy_listeners()
+ {
+ listen_rec *lr;
+
+ assert(old_listeners == NULL);
+ for(lr=listeners ; lr ; lr=lr->next)
+ {
+ listen_rec *nr=malloc(sizeof *nr);
+ *nr=*lr;
+ nr->next=old_listeners;
+ assert(!nr->used);
+ old_listeners=nr;
+ }
+ }
+
+ static int find_listener(listen_rec *lr)
+ {
+ listen_rec *or;
+
+ for(or=old_listeners ; or ; or=or->next)
+ if(!memcmp(&or->local_addr,&lr->local_addr,sizeof or->local_addr))
+ {
+ or->used=1;
+ return or->fd;
+ }
+ return -1;
+ }
+
+ static void close_unused_listeners()
+ {
+ listen_rec *or,*next;
+
+ for(or=old_listeners ; or ; or=next)
+ {
+ next=or->next;
+ if(!or->used)
+ close(or->fd);
+ free(or);
+ }
+ old_listeners=NULL;
+ }
/*****************************************************************
* Executive routines.
***************
*** 1352,1357 ****
--- 1454,1460 ----
void standalone_main(int argc, char **argv)
{
struct sockaddr_in sa_server;
+ int saved_sd;
standalone = 1;
sd = listenmaxfd = -1;
***************
*** 1364,1372 ****
sigsetjmp(restart_buffer,1);
#endif
signal (SIGHUP, SIG_IGN); /* Until we're done (re)reading config */
! if(!one_process)
{
#ifndef NO_KILLPG
if (killpg(pgrp,SIGHUP) < 0) /* Kill 'em off */
--- 1467,1477 ----
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 */
***************
*** 1376,1386 ****
log_unixerr ("killpg SIGHUP", NULL, NULL, server_conf);
}
! if (sd != -1 || listenmaxfd != -1) {
reclaim_child_processes(); /* Not when just starting up */
log_error ("SIGHUP received. Attempting to restart", server_conf);
}
restart_time = time(NULL);
clear_pool (pconf);
ptrans = make_sub_pool (pconf);
--- 1481,1495 ----
log_unixerr ("killpg SIGHUP", NULL, NULL, server_conf);
}
! if(is_graceful)
! log_error("SIGINT received. Doing graceful restart",server_conf);
! 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();
+ saved_sd=sd;
restart_time = time(NULL);
clear_pool (pconf);
ptrans = make_sub_pool (pconf);
***************
*** 1395,1406 ****
if (listeners == NULL)
{
! 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
{
listen_rec *lr;
--- 1504,1520 ----
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;
} else
{
listen_rec *lr;
***************
*** 1410,1419 ****
FD_ZERO(&listenfds);
for (lr=listeners; lr != NULL; lr=lr->next)
{
! fd = make_sock(pconf, &lr->local_addr);
FD_SET(fd, &listenfds);
if (fd > listenmaxfd) listenmaxfd = fd;
}
sd = -1;
}
--- 1524,1538 ----
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);
! fprintf(stderr,"listening on %d\n",fd);
FD_SET(fd, &listenfds);
if (fd > listenmaxfd) listenmaxfd = fd;
+ lr->fd=fd;
}
+ close_unused_listeners();
sd = -1;
}
***************
*** 1451,1457 ****
--- 1570,1586 ----
(void)update_child_status(child_slot,SERVER_STARTING,
(request_rec*)NULL);
make_child(server_conf, child_slot);
+
}
+
+ /*
+ if(scoreboard_image->global.please_exit && !count_live_servers())
+ #ifdef NEXT
+ longjmp(restart_buffer,1);
+ #else
+ siglongjmp(restart_buffer,1);
+ #endif
+ */
}
} /* standalone_main */
Index: httpd.h
===================================================================
RCS file: /export/home/cvs/apache/src/httpd.h,v
retrieving revision 1.34
diff -c -r1.34 httpd.h
*** httpd.h 1996/06/17 21:38:21 1.34
--- httpd.h 1996/06/25 19:51:40
***************
*** 498,503 ****
--- 498,505 ----
struct listen_rec {
listen_rec *next;
struct sockaddr_in local_addr; /* local IP address and port */
+ int fd;
+ int used; /* Only used during restart */
/* more stuff here, like which protocol is bound to the port */
};
Index: scoreboard.h
===================================================================
RCS file: /export/home/cvs/apache/src/scoreboard.h,v
retrieving revision 1.12
diff -c -r1.12 scoreboard.h
*** scoreboard.h 1996/06/07 17:39:26 1.12
--- scoreboard.h 1996/06/25 19:51:47
***************
*** 90,94 ****
--- 90,109 ----
#endif
} short_score;
+ typedef struct
+ {
+ int exit_generation; /* Set by the main process if a graceful
+ restart is required */
+ } global_score;
+
+ typedef struct
+ {
+ short_score servers[HARD_SERVER_LIMIT];
+ global_score global;
+ } scoreboard;
+
+ #define SCOREBOARD_SIZE sizeof(scoreboard)
+
extern void sync_scoreboard_image(void);
short_score get_scoreboard_info(int x);
+
--
Ben Laurie Phone: +44 (181) 994 6435
Freelance Consultant and Fax: +44 (181) 994 6472
Technical Director Email: ben@algroup.co.uk
A.L. Digital Ltd, URL: http://www.algroup.co.uk
London, England.