[Pound/epel7: 2/2] Add a couple of robustness/reliability fixes, submitted upstream

Lubomir Rintel lkundrak at fedoraproject.org
Wed Apr 23 14:17:13 UTC 2014


commit d6e5a1ceddf115052995aeffa56af94929de1307
Author: Lubomir Rintel <lkundrak at v3.sk>
Date:   Tue Apr 8 17:05:36 2014 +0200

    Add a couple of robustness/reliability fixes, submitted upstream

 ...-Do-not-return-NULL-data-from-get_thr_arg.patch |   27 +
 ...eaders-to-the-backend-as-soon-as-they-are.patch |   40 +
 ...-Signal-an-error-if-a-terminating-chunk-i.patch |   46 +
 ...e-period-option-wait-for-all-worker-threa.patch |  188 ++++
 ...load-configuration-on-SIGHUP-SIGINT-witho.patch |  970 ++++++++++++++++++++
 Pound.spec                                         |   29 +-
 6 files changed, 1299 insertions(+), 1 deletions(-)
---
diff --git a/0001-Do-not-return-NULL-data-from-get_thr_arg.patch b/0001-Do-not-return-NULL-data-from-get_thr_arg.patch
new file mode 100644
index 0000000..7f8ec82
--- /dev/null
+++ b/0001-Do-not-return-NULL-data-from-get_thr_arg.patch
@@ -0,0 +1,27 @@
+From 2d25c2a5a16076339c35ba7541ded5007854931b Mon Sep 17 00:00:00 2001
+From: Miroslav Spousta <miroslav.spousta at gooddata.com>
+Date: Sat, 8 Jun 2013 22:34:13 +0200
+Subject: [PATCH 1/3] Do not return NULL data from get_thr_arg
+
+get_thr_arg() always waits for non-NULL argument. It prevents "NULL
+get_thr_arg" log message.
+---
+ pound.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/pound.c b/pound.c
+index 3643f70..42a869d 100644
+--- a/pound.c
++++ b/pound.c
+@@ -156,7 +156,7 @@ get_thr_arg(void)
+     thr_arg *res;
+ 
+     (void)pthread_mutex_lock(&arg_mut);
+-    if(first == NULL)
++    while(first == NULL)
+         (void)pthread_cond_wait(&arg_cond, &arg_mut);
+     if((res = first) != NULL)
+         if((first = first->next) == NULL)
+-- 
+1.8.3.1
+
diff --git a/0001-Flush-the-headers-to-the-backend-as-soon-as-they-are.patch b/0001-Flush-the-headers-to-the-backend-as-soon-as-they-are.patch
new file mode 100644
index 0000000..73134e1
--- /dev/null
+++ b/0001-Flush-the-headers-to-the-backend-as-soon-as-they-are.patch
@@ -0,0 +1,40 @@
+From 413b3ca3dd56ca174b4dc732793584ed1e5db9ba Mon Sep 17 00:00:00 2001
+From: Lubomir Rintel <lubo.rintel at gooddata.com>
+Date: Wed, 21 Mar 2012 12:46:07 +0100
+Subject: [PATCH] Flush the headers to the backend as soon as they are received
+
+No need to wait and backend can process them as we read body potentially
+slightly increasing performance. Also, if this is a new connection and backend
+has aggressive first-byte timeout (such as Varnish in default configuration),
+this will prevent the backend from time-ing out when a client has a long delay
+between headers and body (as is customary for busy Internet Exporer <= 8).
+---
+ http.c |   12 ++++++++++++
+ 1 files changed, 12 insertions(+), 0 deletions(-)
+
+diff --git a/http.c b/http.c
+index bb2ce8b..8ce704a 100755
+--- a/http.c
++++ b/http.c
+@@ -1103,6 +1103,18 @@ do_http(thr_arg *arg)
+             BIO_puts(be, "\r\n");
+         }
+ 
++        /* flush to the back-end */
++        if(cur_backend->be_type == 0 && BIO_flush(be) != 1) {
++            str_be(buf, MAXBUF - 1, cur_backend);
++            end_req = cur_time();
++            addr2str(caddr, MAXBUF - 1, &from_host, 1);
++            logmsg(LOG_NOTICE, "(%lx) e500 for %s error flush to %s/%s: %s (%.3f sec)",
++                pthread_self(), caddr, buf, request, strerror(errno), (end_req - start_req) / 1000000.0);
++            err_reply(cl, h500, lstn->err500);
++            clean_all();
++            return;
++        }
++
+         if(cl_11 && chunked) {
+             /* had Transfer-encoding: chunked so read/write all the chunks (HTTP/1.1 only) */
+             if(copy_chunks(cl, be, NULL, cur_backend->be_type, lstn->max_req)) {
+-- 
+1.7.1
+
diff --git a/0001-copy_chunks-Signal-an-error-if-a-terminating-chunk-i.patch b/0001-copy_chunks-Signal-an-error-if-a-terminating-chunk-i.patch
new file mode 100644
index 0000000..a2de118
--- /dev/null
+++ b/0001-copy_chunks-Signal-an-error-if-a-terminating-chunk-i.patch
@@ -0,0 +1,46 @@
+From 10d15bb63b86931e4668a9b12ac8ad7bc7a7bbbe Mon Sep 17 00:00:00 2001
+From: Lubomir Rintel <lubo.rintel at gooddata.com>
+Date: Wed, 23 Apr 2014 15:44:37 +0200
+Subject: [PATCH] copy_chunks: Signal an error if a terminating chunk is
+ missing
+
+This means that the client disconnected abruptly, without completing the
+request. If we proceed waiting for reply from backend without ever completing
+the request we'll just wait forever.
+
+Note that this has some security implications as it provides a way for the
+client to hit and run, wasting a backend connection while disconnecting his one
+(which leads to denial of service).
+---
+ http.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/http.c b/http.c
+index 5076872..f4f3bea 100755
+--- a/http.c
++++ b/http.c
+@@ -184,13 +184,19 @@ copy_chunks(BIO *const cl, BIO *const be, LONG *res_bytes, const int no_write, c
+     regmatch_t  matches[2];
+     int         res;
+ 
+-    for(tot_size = 0L;;) {
++    for(tot_size = 0L, cont = -1L;;) {
++
+         if((res = get_line(cl, buf, MAXBUF)) < 0) {
+             logmsg(LOG_NOTICE, "(%lx) chunked read error: %s", pthread_self(), strerror(errno));
+             return -1;
+-        } else if(res > 0)
++        } else if(res > 0) {
+             /* EOF */
++            if (cont != 0) {
++                logmsg(LOG_NOTICE, "(%lx) unexpected EOF: no terminating chunk", pthread_self());
++                return -1;
++            }
+             return 0;
++        }
+         if(!regexec(&CHUNK_HEAD, buf, 2, matches, 0))
+             cont = STRTOL(buf, NULL, 16);
+         else {
+-- 
+1.8.3.1
+
diff --git a/0002-Remove-Grace-period-option-wait-for-all-worker-threa.patch b/0002-Remove-Grace-period-option-wait-for-all-worker-threa.patch
new file mode 100644
index 0000000..167f44a
--- /dev/null
+++ b/0002-Remove-Grace-period-option-wait-for-all-worker-threa.patch
@@ -0,0 +1,188 @@
+From cb410bbe77e366571df1144851d95b52de5a5c1b Mon Sep 17 00:00:00 2001
+From: Miroslav Spousta <miroslav.spousta at gooddata.com>
+Date: Sun, 9 Jun 2013 23:12:39 +0200
+Subject: [PATCH 2/3] Remove Grace period option, wait for all worker threads
+ to finish
+
+When worker is required to shut down (by SIGHUP/SIGINT), it waits for all busy
+threads to finish. Number of running threads is monitored every 10 seconds.
+During the shutdown, control socket is temporary renamed (pid is appended to
+the socket name), so that it is still accessible and correct socket is removed
+on exit.
+---
+ config.c |  7 +------
+ pound.8  | 10 ----------
+ pound.c  | 35 ++++++++++++++++++++++++++++++-----
+ pound.h  |  1 -
+ 4 files changed, 31 insertions(+), 22 deletions(-)
+ mode change 100644 => 100755 config.c
+ mode change 100644 => 100755 pound.8
+ mode change 100644 => 100755 pound.c
+
+diff --git a/config.c b/config.c
+old mode 100644
+new mode 100755
+index 077fa60..c39be84
+--- a/config.c
++++ b/config.c
+@@ -79,7 +79,7 @@ static regex_t  Err414, Err500, Err501, Err503, MaxRequest, HeadRemove, RewriteL
+ static regex_t  Service, ServiceName, URL, HeadRequire, HeadDeny, BackEnd, Emergency, Priority, HAport, HAportAddr;
+ static regex_t  Redirect, RedirectN, TimeOut, Session, Type, TTL, ID, DynScale;
+ static regex_t  ClientCert, AddHeader, DisableSSLv2, SSLAllowClientRenegotiation, SSLHonorCipherOrder, Ciphers;
+-static regex_t  CAlist, VerifyList, CRLlist, NoHTTPS11, Grace, Include, ConnTO, IgnoreCase, HTTPS, HTTPSCert;
++static regex_t  CAlist, VerifyList, CRLlist, NoHTTPS11, Include, ConnTO, IgnoreCase, HTTPS, HTTPSCert;
+ static regex_t  Disabled, Threads, CNName, Anonymise;
+ 
+ static regmatch_t   matches[5];
+@@ -1277,8 +1277,6 @@ parse_file(void)
+                         def_facility = facilitynames[i].c_val;
+                         break;
+                     }
+-        } else if(!regexec(&Grace, lin, 4, matches, 0)) {
+-            grace = atoi(lin + matches[1].rm_so);
+         } else if(!regexec(&LogLevel, lin, 4, matches, 0)) {
+             log_level = atoi(lin + matches[1].rm_so);
+         } else if(!regexec(&Client, lin, 4, matches, 0)) {
+@@ -1378,7 +1376,6 @@ config_parse(const int argc, char **const argv)
+     || regcomp(&Threads, "^[ \t]*Threads[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&LogFacility, "^[ \t]*LogFacility[ \t]+([a-z0-9-]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&LogLevel, "^[ \t]*LogLevel[ \t]+([0-5])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+-    || regcomp(&Grace, "^[ \t]*Grace[ \t]+([0-9]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&Alive, "^[ \t]*Alive[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&SSLEngine, "^[ \t]*SSLEngine[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&Control, "^[ \t]*Control[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+@@ -1514,7 +1511,6 @@ config_parse(const int argc, char **const argv)
+     numthreads = 128;
+     alive_to = 30;
+     daemonize = 1;
+-    grace = 30;
+ 
+     services = NULL;
+     listeners = NULL;
+@@ -1540,7 +1536,6 @@ config_parse(const int argc, char **const argv)
+     regfree(&Threads);
+     regfree(&LogFacility);
+     regfree(&LogLevel);
+-    regfree(&Grace);
+     regfree(&Alive);
+     regfree(&SSLEngine);
+     regfree(&Control);
+diff --git a/pound.8 b/pound.8
+old mode 100644
+new mode 100755
+index bda2fd8..08c6a22
+--- a/pound.8
++++ b/pound.8
+@@ -296,16 +296,6 @@ wait for a connection to the back-end (in seconds). Default: the
+ .B TimeOut
+ value. This value can be overridden for specific back-ends.
+ .TP
+-\fBGrace\fR value
+-How long should
+-.B Pound
+-continue to answer existing connections after a receiving and INT or HUP
+-signal (default: 30 seconds). The configured listeners are closed
+-immediately. You can bypass this behaviour by stopping
+-.B Pound
+-with a TERM or QUIT signal, in which case the program exits without any
+-delay.
+-.TP
+ \fBSSLEngine\fR "name"
+ Use an OpenSSL hardware acceleration card called \fIname\fR. Available
+ only if OpenSSL-engine is installed on your system.
+diff --git a/pound.c b/pound.c
+old mode 100644
+new mode 100755
+index 42a869d..09c5f16
+--- a/pound.c
++++ b/pound.c
+@@ -27,6 +27,9 @@
+ 
+ #include    "pound.h"
+ 
++/* while in shutdown, check number of running threads every 10 seconds */
++#define     RUNNING_CHECK_PERIOD 10
++
+ /* common variables */
+ char        *user,              /* user to run as */
+             *group,             /* group to run as */
+@@ -39,7 +42,6 @@ int         alive_to,           /* check interval for resurrection */
+             daemonize,          /* run as daemon */
+             log_facility,       /* log facility to use */
+             print_log,          /* print log messages to stdout/stderr */
+-            grace,              /* grace period before shutdown */
+             control_sock;       /* control socket */
+ 
+ SERVICE     *services;          /* global services (if any) */
+@@ -112,6 +114,7 @@ static thr_arg          *first = NULL, *last = NULL;
+ static pthread_cond_t   arg_cond;
+ static pthread_mutex_t  arg_mut;
+ int                     numthreads;
++static int              waiting = 0;
+ 
+ static void
+ init_thr_arg(void)
+@@ -156,8 +159,10 @@ get_thr_arg(void)
+     thr_arg *res;
+ 
+     (void)pthread_mutex_lock(&arg_mut);
++    waiting++;
+     while(first == NULL)
+         (void)pthread_cond_wait(&arg_cond, &arg_mut);
++    waiting--;
+     if((res = first) != NULL)
+         if((first = first->next) == NULL)
+             last = NULL;
+@@ -487,13 +492,33 @@ main(const int argc, char **argv)
+             /* and start working */
+             for(;;) {
+                 if(shut_down) {
+-                    logmsg(LOG_NOTICE, "shutting down...");
++                    int finished;
++
++                    logmsg(LOG_NOTICE, "shutting down (%d)...", getpid());
+                     for(lstn = listeners; lstn; lstn = lstn->next)
+                         close(lstn->sock);
+-                    if(grace > 0) {
+-                        sleep(grace);
+-                        logmsg(LOG_NOTICE, "grace period expired - exiting...");
++                    /* rename control file (append pid) */
++                    if(ctrl_name != NULL) {
++                        char *ctrl_tmp = malloc(strlen(ctrl_name)+11);
++                        sprintf(ctrl_tmp, "%s.%d", ctrl_name, getpid());
++                        rename(ctrl_name, ctrl_tmp);
++                        free(ctrl_name);
++                        ctrl_name = ctrl_tmp;
++                    }
++                    /* wait for all threads to be finished */
++                    finished = 0;
++                    while(!finished) {
++                        int running;
++                        (void)pthread_mutex_lock(&arg_mut);
++                        running = numthreads-waiting;
++                        finished = !first && !running;
++                        (void)pthread_mutex_unlock(&arg_mut);
++                        if(!finished) {
++                            logmsg(LOG_INFO, "%d thread(s) still running...", running);
++                            sleep(RUNNING_CHECK_PERIOD);
++                        }
+                     }
++                    logmsg(LOG_NOTICE, "no threads running - exiting...");
+                     if(ctrl_name != NULL)
+                         (void)unlink(ctrl_name);
+                     exit(0);
+diff --git a/pound.h b/pound.h
+index add7ae5..492413c 100755
+--- a/pound.h
++++ b/pound.h
+@@ -272,7 +272,6 @@ extern int  numthreads,         /* number of worker threads */
+             daemonize,          /* run as daemon */
+             log_facility,       /* log facility to use */
+             print_log,          /* print log messages to stdout/stderr */
+-            grace,              /* grace period before shutdown */
+             control_sock;       /* control socket */
+ 
+ extern regex_t  HEADER,     /* Allowed header */
+-- 
+1.8.3.1
+
diff --git a/0003-Allow-to-reload-configuration-on-SIGHUP-SIGINT-witho.patch b/0003-Allow-to-reload-configuration-on-SIGHUP-SIGINT-witho.patch
new file mode 100644
index 0000000..bb39ee4
--- /dev/null
+++ b/0003-Allow-to-reload-configuration-on-SIGHUP-SIGINT-witho.patch
@@ -0,0 +1,970 @@
+From 08576ea0e6423d648ae4698eff7e87b86076074c Mon Sep 17 00:00:00 2001
+From: Miroslav Spousta <miroslav.spousta at gooddata.com>
+Date: Fri, 14 Jun 2013 16:55:31 +0200
+Subject: [PATCH 3/3] Allow to reload configuration on SIGHUP/SIGINT without
+ service outage
+
+When SIGHUP/SIGINT is received, signal old child to shut down, reload
+configuration and fork a new child while reusing opened listeners (if
+possible). Configuration reload only works when pound is configured with
+--enable-super option. Supervisor also keeps track of all running children and
+kill them when signaled with SIGTERM/SIGQUIT.
+---
+ config.c |  17 +-
+ pound.8  |   9 +
+ pound.c  | 586 ++++++++++++++++++++++++++++++++++++---------------------------
+ pound.h  |  31 ++++
+ svc.c    | 139 +++++++++++++++
+ 5 files changed, 520 insertions(+), 262 deletions(-)
+
+diff --git a/config.c b/config.c
+index c39be84..a4b8d51 100755
+--- a/config.c
++++ b/config.c
+@@ -719,10 +719,10 @@ parse_HTTP(void)
+     memset(res, 0, sizeof(LISTENER));
+     res->to = clnt_to;
+     res->rewr_loc = 1;
+-    res->err414 = "Request URI is too long";
+-    res->err500 = "An internal server error occurred. Please try again later.";
+-    res->err501 = "This method may not be used.";
+-    res->err503 = "The service is not available. Please try again later.";
++    res->err414 = strdup("Request URI is too long");
++    res->err500 = strdup("An internal server error occurred. Please try again later.");
++    res->err501 = strdup("This method may not be used.");
++    res->err503 = strdup("The service is not available. Please try again later.");
+     res->log_level = log_level;
+     if(regcomp(&res->verb, xhttp[0], REG_ICASE | REG_NEWLINE | REG_EXTENDED))
+         conf_err("xHTTP bad default pattern - aborted");
+@@ -922,10 +922,10 @@ parse_HTTPS(void)
+ 
+     res->to = clnt_to;
+     res->rewr_loc = 1;
+-    res->err414 = "Request URI is too long";
+-    res->err500 = "An internal server error occurred. Please try again later.";
+-    res->err501 = "This method may not be used.";
+-    res->err503 = "The service is not available. Please try again later.";
++    res->err414 = strdup("Request URI is too long");
++    res->err500 = strdup("An internal server error occurred. Please try again later.");
++    res->err501 = strdup("This method may not be used.");
++    res->err503 = strdup("The service is not available. Please try again later.");
+     res->allow_client_reneg = 0;
+     res->disable_ssl_v2 = 0;
+     res->log_level = log_level;
+@@ -1437,6 +1437,7 @@ config_parse(const int argc, char **const argv)
+         exit(1);
+     }
+ 
++    optind = 1;
+     opterr = 0;
+     check_only = 0;
+     conf_name = F_CONF;
+diff --git a/pound.8 b/pound.8
+index 08c6a22..e31fa60 100755
+--- a/pound.8
++++ b/pound.8
+@@ -912,6 +912,15 @@ server is re-opened as necessary.
+ attempts to resolve the names of the hosts that appear in various requests and/or responses.
+ That means it need a functioning resolver of some kind (be it /etc/hosts, DNS or something
+ else).
++.PP
++When
++.B Pound
++is compiled to work in supervised mode (default), it supports configuration
++reload without service outage. When SIGHUP/SIGINT is delivered to the
++supervisor process, new worker child process is created while keeping old child
++for servicing current connections. Listening socket is then passed to the new
++child. SIGKILL/SIGQUIT terminates supervisor and all worker processes.
++
+ .SH EXAMPLES
+ To translate HTTPS requests to a local HTTP server (assuming your network address
+ is 123.123.123.123):
+diff --git a/pound.c b/pound.c
+index 09c5f16..1de7013 100755
+--- a/pound.c
++++ b/pound.c
+@@ -47,6 +47,9 @@ int         alive_to,           /* check interval for resurrection */
+ SERVICE     *services;          /* global services (if any) */
+ 
+ LISTENER    *listeners;         /* all available listeners */
++LISTENER    *prev_listeners;    /* saved listeners */
++
++PID         *children;          /* pid of workers */
+ 
+ regex_t HEADER,             /* Allowed header */
+         CHUNK_HEAD,         /* chunk header line */
+@@ -193,34 +196,31 @@ get_thr_qlen(void)
+ static RETSIGTYPE
+ h_term(const int sig)
+ {
+-    logmsg(LOG_NOTICE, "received signal %d - exiting...", sig);
+     if(son > 0)
+-        kill(son, sig);
+-    if(ctrl_name != NULL)
+-        (void)unlink(ctrl_name);
++        signal_all(children, sig);
++    else
++        if(ctrl_name != NULL)
++            (void)unlink(ctrl_name);
+     exit(0);
+ }
+ 
+ /*
+- * handle SIGHUP/SIGINT - exit after grace period
++ * handle SIGHUP/SIGINT - shut down worker (and spawn new one, if possible)
+  */
+ static RETSIGTYPE
+ h_shut(const int sig)
+ {
+-    int         status;
+-    LISTENER    *lstn;
+-
+-    logmsg(LOG_NOTICE, "received signal %d - shutting down...", sig);
+     if(son > 0) {
+-        for(lstn = listeners; lstn; lstn = lstn->next)
+-            close(lstn->sock);
+         kill(son, sig);
+-        (void)wait(&status);
+-        if(ctrl_name != NULL)
+-            (void)unlink(ctrl_name);
+-        exit(0);
+-    } else
+-        shut_down = 1;
++        son = 0;
++    }
++    shut_down = 1;
++}
++
++static RETSIGTYPE
++h_child(const int sig)
++{
++    /* just wake-up from sigsuspend() */
+ }
+ 
+ /*
+@@ -236,6 +236,7 @@ main(const int argc, char **argv)
+     int                 n_listeners, i, clnt_length, clnt;
+     struct pollfd       *polls;
+     LISTENER            *lstn;
++    LISTENER            *prev_lstn;
+     pthread_t           thr;
+     pthread_attr_t      attr;
+     struct sched_param  sp;
+@@ -247,11 +248,11 @@ main(const int argc, char **argv)
+ #ifndef SOL_TCP
+     struct protoent     *pe;
+ #endif
++    int                 daemon;
+ 
+-    print_log = 0;
++    polls = NULL;
++    daemon = 0;
+     (void)umask(077);
+-    control_sock = -1;
+-    log_facility = -1;
+     logmsg(LOG_NOTICE, "starting...");
+ 
+     signal(SIGHUP, h_shut);
+@@ -259,6 +260,7 @@ main(const int argc, char **argv)
+     signal(SIGTERM, h_term);
+     signal(SIGQUIT, h_term);
+     signal(SIGPIPE, SIG_IGN);
++    signal(SIGCHLD, h_child);
+ 
+     srandom(getpid());
+ 
+@@ -310,274 +312,350 @@ main(const int argc, char **argv)
+     SOL_TCP = pe->p_proto;
+ #endif
+ 
+-    /* read config */
+-    config_parse(argc, argv);
+-
+-    if(log_facility != -1)
+-        openlog("pound", LOG_CONS | LOG_NDELAY, LOG_DAEMON);
+-    if(ctrl_name != NULL) {
+-        struct sockaddr_un  ctrl;
+-
+-        memset(&ctrl, 0, sizeof(ctrl));
+-        ctrl.sun_family = AF_UNIX;
+-        strncpy(ctrl.sun_path, ctrl_name, sizeof(ctrl.sun_path) - 1);
+-        (void)unlink(ctrl.sun_path);
+-        if((control_sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+-            logmsg(LOG_ERR, "Control \"%s\" create: %s", ctrl.sun_path, strerror(errno));
+-            exit(1);
+-        }
+-        if(bind(control_sock, (struct sockaddr *)&ctrl, (socklen_t)sizeof(ctrl)) < 0) {
+-            logmsg(LOG_ERR, "Control \"%s\" bind: %s", ctrl.sun_path, strerror(errno));
+-            exit(1);
++    for(;;) {
++        /* free previous values and re-initialize */
++        free(user);
++        free(group);
++        free(root_jail);
++        free(ctrl_name);
++
++        print_log = 0;
++        control_sock = -1;
++        log_facility = -1;
++
++        /* preserve listeners */
++        prev_listeners = listeners;
++        listeners = NULL;
++    
++        /* read config */
++        config_parse(argc, argv);
++        if(shut_down)
++            print_log = 0;
++    
++        if(log_facility != -1)
++            openlog("pound", LOG_CONS | LOG_NDELAY, LOG_DAEMON);
++        else
++            closelog();
++    
++        if(ctrl_name != NULL) {
++            struct sockaddr_un  ctrl;
++    
++            if(control_sock >= 0)
++                close(control_sock);
++    
++            memset(&ctrl, 0, sizeof(ctrl));
++            ctrl.sun_family = AF_UNIX;
++            strncpy(ctrl.sun_path, ctrl_name, sizeof(ctrl.sun_path) - 1);
++            (void)unlink(ctrl.sun_path);
++            if((control_sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
++                logmsg(LOG_ERR, "Control \"%s\" create: %s", ctrl.sun_path, strerror(errno));
++                exit(1);
++            }
++            if(bind(control_sock, (struct sockaddr *)&ctrl, (socklen_t)sizeof(ctrl)) < 0) {
++                logmsg(LOG_ERR, "Control \"%s\" bind: %s", ctrl.sun_path, strerror(errno));
++                exit(1);
++            }
++            listen(control_sock, 512);
+         }
+-        listen(control_sock, 512);
+-    }
+-
+-    /* open listeners */
+-    for(lstn = listeners, n_listeners = 0; lstn; lstn = lstn->next, n_listeners++) {
+-        int opt;
+-
+-        /* prepare the socket */
+-        if((lstn->sock = socket(lstn->addr.ai_family == AF_INET? PF_INET: PF_INET6, SOCK_STREAM, 0)) < 0) {
+-            addr2str(tmp, MAXBUF - 1, &lstn->addr, 0);
+-            logmsg(LOG_ERR, "HTTP socket %s create: %s - aborted", tmp, strerror(errno));
+-            exit(1);
++    
++        /* open listeners */
++        for(lstn = listeners, n_listeners = 0; lstn; lstn = lstn->next, n_listeners++) {
++            int opt;
++
++            /* try to re-use listener socket */
++            for(prev_lstn = prev_listeners; prev_lstn; prev_lstn = prev_lstn->next) {
++                if(prev_lstn->sock >= 0 && !addrinfo_cmp(&prev_lstn->addr, &lstn->addr))
++                    break;
++            }
++            if(prev_lstn && prev_lstn->sock >= 0) {
++                char addr[MAXBUF];
++                /* reuse listener socket */
++                lstn->sock = prev_lstn->sock;
++                prev_lstn->sock = -1;
++                addr2str(addr, sizeof(addr), &prev_lstn->addr, 0);
++                logmsg(LOG_INFO, "reusing listener socket for %s", addr);
++            } else {
++                /* prepare the socket */
++                if((lstn->sock = socket(lstn->addr.ai_family == AF_INET? PF_INET: PF_INET6, SOCK_STREAM, 0)) < 0) {
++                    addr2str(tmp, MAXBUF - 1, &lstn->addr, 0);
++                    logmsg(LOG_ERR, "HTTP socket %s create: %s - aborted", tmp, strerror(errno));
++                    exit(1);
++                }
++                opt = 1;
++                setsockopt(lstn->sock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt));
++                if(bind(lstn->sock, lstn->addr.ai_addr, (socklen_t)lstn->addr.ai_addrlen) < 0) {
++                    addr2str(tmp, MAXBUF - 1, &lstn->addr, 0);
++                    logmsg(LOG_ERR, "HTTP socket bind %s: %s - aborted", tmp, strerror(errno));
++                    exit(1);
++                }
++                listen(lstn->sock, 512);
++            }
+         }
+-        opt = 1;
+-        setsockopt(lstn->sock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt));
+-        if(bind(lstn->sock, lstn->addr.ai_addr, (socklen_t)lstn->addr.ai_addrlen) < 0) {
+-            addr2str(tmp, MAXBUF - 1, &lstn->addr, 0);
+-            logmsg(LOG_ERR, "HTTP socket bind %s: %s - aborted", tmp, strerror(errno));
+-            exit(1);
++        /* close remaining old listeners and free structures */
++        while(prev_listeners) {
++            LISTENER *lstn = prev_listeners;
++            prev_listeners = prev_listeners->next;
++            if(lstn->sock >= 0)
++                close(lstn->sock);
++            free_listener(lstn);
+         }
+-        listen(lstn->sock, 512);
+-    }
+-
+-    /* alloc the poll structures */
+-    if((polls = (struct pollfd *)calloc(n_listeners, sizeof(struct pollfd))) == NULL) {
+-        logmsg(LOG_ERR, "Out of memory for poll - aborted");
+-        exit(1);
+-    }
+-    for(lstn = listeners, i = 0; lstn; lstn = lstn->next, i++)
+-        polls[i].fd = lstn->sock;
+-
+-    /* set uid if necessary */
+-    if(user) {
+-        struct passwd   *pw;
+-
+-        if((pw = getpwnam(user)) == NULL) {
+-            logmsg(LOG_ERR, "no such user %s - aborted", user);
++    
++        /* alloc the poll structures */
++        free(polls);
++        if((polls = (struct pollfd *)calloc(n_listeners, sizeof(struct pollfd))) == NULL) {
++            logmsg(LOG_ERR, "Out of memory for poll - aborted");
+             exit(1);
+         }
+-        user_id = pw->pw_uid;
+-    }
+-
+-    /* set gid if necessary */
+-    if(group) {
+-        struct group    *gr;
+-
+-        if((gr = getgrnam(group)) == NULL) {
+-            logmsg(LOG_ERR, "no such group %s - aborted", group);
+-            exit(1);
++        for(lstn = listeners, i = 0; lstn; lstn = lstn->next, i++)
++            polls[i].fd = lstn->sock;
++    
++        /* set uid if necessary */
++        if(user) {
++            struct passwd   *pw;
++    
++            if((pw = getpwnam(user)) == NULL) {
++                logmsg(LOG_ERR, "no such user %s - aborted", user);
++                exit(1);
++            }
++            user_id = pw->pw_uid;
+         }
+-        group_id = gr->gr_gid;
+-    }
+-
+-    /* Turn off verbose messages (if necessary) */
+-    print_log = 0;
+-
+-    if(daemonize) {
+-        /* daemonize - make ourselves a subprocess. */
+-        switch (fork()) {
+-            case 0:
+-                if(log_facility != -1) {
+-                    close(0);
+-                    close(1);
+-                    close(2);
+-                }
+-                break;
+-            case -1:
+-                logmsg(LOG_ERR, "fork: %s - aborted", strerror(errno));
++    
++        /* set gid if necessary */
++        if(group) {
++            struct group    *gr;
++    
++            if((gr = getgrnam(group)) == NULL) {
++                logmsg(LOG_ERR, "no such group %s - aborted", group);
+                 exit(1);
+-            default:
+-                exit(0);
++            }
++            group_id = gr->gr_gid;
+         }
++    
++        /* Turn off verbose messages (if necessary) */
++        print_log = 0;
++    
++        if(!daemon && daemonize) {
++            /* daemonize - make ourselves a subprocess. */
++            switch (fork()) {
++                case 0:
++                    if(log_facility != -1) {
++                        close(0);
++                        close(1);
++                        close(2);
++                    }
++                    break;
++                case -1:
++                    logmsg(LOG_ERR, "fork: %s - aborted", strerror(errno));
++                    exit(1);
++                default:
++                    exit(0);
++            }
++            daemon = 1;
+ #ifdef  HAVE_SETSID
+-        (void) setsid();
++            (void) setsid();
+ #endif
+-    }
+-
+-    /* record pid in file */
+-    if((fpid = fopen(pid_name, "wt")) != NULL) {
+-        fprintf(fpid, "%d\n", getpid());
+-        fclose(fpid);
+-    } else
+-        logmsg(LOG_NOTICE, "Create \"%s\": %s", pid_name, strerror(errno));
+-
+-    /* chroot if necessary */
+-    if(root_jail) {
+-        if(chroot(root_jail)) {
+-            logmsg(LOG_ERR, "chroot: %s - aborted", strerror(errno));
+-            exit(1);
+         }
+-        if(chdir("/")) {
+-            logmsg(LOG_ERR, "chroot/chdir: %s - aborted", strerror(errno));
+-            exit(1);
++    
++        /* record pid in file */
++        if(!fpid) {
++            if((fpid = fopen(pid_name, "wt")) != NULL) {
++                fprintf(fpid, "%d\n", getpid());
++                fclose(fpid);
++            } else
++                logmsg(LOG_NOTICE, "Create \"%s\": %s", pid_name, strerror(errno));
+         }
+-    }
+ 
+-    if(group)
+-        if(setgid(group_id) || setegid(group_id)) {
+-            logmsg(LOG_ERR, "setgid: %s - aborted", strerror(errno));
+-            exit(1);
+-        }
+-    if(user)
+-        if(setuid(user_id) || seteuid(user_id)) {
+-            logmsg(LOG_ERR, "setuid: %s - aborted", strerror(errno));
+-            exit(1);
+-        }
++        shut_down = 0;
+ 
+-    /* split off into monitor and working process if necessary */
+-    for(;;) {
++        /* split off into monitor and working process if necessary */
++        while(!shut_down) {
+ #ifdef  UPER
+-        if((son = fork()) > 0) {
+-            int status;
+-
+-            (void)wait(&status);
+-            if(WIFEXITED(status))
+-                logmsg(LOG_ERR, "MONITOR: worker exited normally %d, restarting...", WEXITSTATUS(status));
+-            else if(WIFSIGNALED(status))
+-                logmsg(LOG_ERR, "MONITOR: worker exited on signal %d, restarting...", WTERMSIG(status));
+-            else
+-                logmsg(LOG_ERR, "MONITOR: worker exited (stopped?) %d, restarting...", status);
+-        } else if (son == 0) {
++            if((son = fork()) > 0) {
++                sigset_t mask, oldmask;
++
++                insert_pid(&children, son);
++               
++                sigemptyset(&mask);
++                sigaddset(&mask, SIGHUP);
++                sigaddset(&mask, SIGINT);
++                sigaddset(&mask, SIGCHLD);
++
++                sigprocmask(SIG_BLOCK, &mask, &oldmask);
++                while(!shut_down) {
++                    int status, pid;
++
++                    while((pid = waitpid(-1, &status, WNOHANG)) > 0) {
++                        /* we only oversee youngest son, older ones are ignored */
++                        if(pid == son) {
++                            if(WIFEXITED(status))
++                                logmsg(LOG_ERR, "MONITOR: worker %d exited normally %d, restarting...", pid, WEXITSTATUS(status));
++                            else if(WIFSIGNALED(status))
++                                logmsg(LOG_ERR, "MONITOR: worker %d exited on signal %d, restarting...", pid, WTERMSIG(status));
++                            else
++                                logmsg(LOG_ERR, "MONITOR: worker %d exited (stopped?) %d, restarting...", pid, status);
++                        } else {
++                                logmsg(LOG_INFO, "worker %d exited", pid);
++                        }
++                        remove_pid(&children, pid);
++                    }
++
++                    /* wait for children or SIGHUP/INT */
++                    sigsuspend(&oldmask);
++                }
++                /* SIGHUP/INT: reload configuration */
++                sigprocmask(SIG_UNBLOCK, &mask, NULL);
++                logmsg(LOG_NOTICE, "config reload...");
++            } else if (son == 0) {
+ #endif
++                /* chroot if necessary */
++                if(root_jail) {
++                    if(chroot(root_jail)) {
++                        logmsg(LOG_ERR, "chroot: %s - aborted", strerror(errno));
++                        exit(1);
++                    }
++                    if(chdir("/")) {
++                        logmsg(LOG_ERR, "chroot/chdir: %s - aborted", strerror(errno));
++                        exit(1);
++                    }
++                }
+ 
+-            /* thread stuff */
+-            pthread_attr_init(&attr);
+-            pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
++                if(group)
++                    if(setgid(group_id) || setegid(group_id)) {
++                        logmsg(LOG_ERR, "setgid: %s - aborted", strerror(errno));
++                        exit(1);
++                    }
++                if(user)
++                    if(setuid(user_id) || seteuid(user_id)) {
++                        logmsg(LOG_ERR, "setuid: %s - aborted", strerror(errno));
++                        exit(1);
++                    }
++
++                /* thread stuff */
++                pthread_attr_init(&attr);
++                pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ 
+ #ifdef  NEED_STACK
+-            /* set new stack size - necessary for OpenBSD/FreeBSD and Linux NPTL */
+-            if(pthread_attr_setstacksize(&attr, 1 << 18)) {
+-                logmsg(LOG_ERR, "can't set stack size - aborted");
+-                exit(1);
+-            }
++                /* set new stack size - necessary for OpenBSD/FreeBSD and Linux NPTL */
++                if(pthread_attr_setstacksize(&attr, 1 << 18)) {
++                    logmsg(LOG_ERR, "can't set stack size - aborted");
++                    exit(1);
++                }
+ #endif
+-            /* start timer */
+-            if(pthread_create(&thr, &attr, thr_timer, NULL)) {
+-                logmsg(LOG_ERR, "create thr_resurect: %s - aborted", strerror(errno));
+-                exit(1);
+-            }
+-
+-            /* start the controlling thread (if needed) */
+-            if(control_sock >= 0 && pthread_create(&thr, &attr, thr_control, NULL)) {
+-                logmsg(LOG_ERR, "create thr_control: %s - aborted", strerror(errno));
+-                exit(1);
+-            }
+-
+-            /* pause to make sure the service threads were started */
+-            sleep(1);
++                /* start timer */
++                if(pthread_create(&thr, &attr, thr_timer, NULL)) {
++                    logmsg(LOG_ERR, "create thr_resurect: %s - aborted", strerror(errno));
++                    exit(1);
++                }
+ 
+-            /* create the worker threads */
+-            for(i = 0; i < numthreads; i++)
+-                if(pthread_create(&thr, &attr, thr_http, NULL)) {
+-                    logmsg(LOG_ERR, "create thr_http: %s - aborted", strerror(errno));
++                /* start the controlling thread (if needed) */
++                if(control_sock >= 0 && pthread_create(&thr, &attr, thr_control, NULL)) {
++                    logmsg(LOG_ERR, "create thr_control: %s - aborted", strerror(errno));
+                     exit(1);
+                 }
+ 
+-            /* pause to make sure at least some of the worker threads were started */
+-            sleep(1);
+-
+-            /* and start working */
+-            for(;;) {
+-                if(shut_down) {
+-                    int finished;
+-
+-                    logmsg(LOG_NOTICE, "shutting down (%d)...", getpid());
+-                    for(lstn = listeners; lstn; lstn = lstn->next)
+-                        close(lstn->sock);
+-                    /* rename control file (append pid) */
+-                    if(ctrl_name != NULL) {
+-                        char *ctrl_tmp = malloc(strlen(ctrl_name)+11);
+-                        sprintf(ctrl_tmp, "%s.%d", ctrl_name, getpid());
+-                        rename(ctrl_name, ctrl_tmp);
+-                        free(ctrl_name);
+-                        ctrl_name = ctrl_tmp;
++                /* pause to make sure the service threads were started */
++                sleep(1);
++
++                /* create the worker threads */
++                for(i = 0; i < numthreads; i++)
++                    if(pthread_create(&thr, &attr, thr_http, NULL)) {
++                        logmsg(LOG_ERR, "create thr_http: %s - aborted", strerror(errno));
++                        exit(1);
+                     }
+-                    /* wait for all threads to be finished */
+-                    finished = 0;
+-                    while(!finished) {
+-                        int running;
+-                        (void)pthread_mutex_lock(&arg_mut);
+-                        running = numthreads-waiting;
+-                        finished = !first && !running;
+-                        (void)pthread_mutex_unlock(&arg_mut);
+-                        if(!finished) {
+-                            logmsg(LOG_INFO, "%d thread(s) still running...", running);
+-                            sleep(RUNNING_CHECK_PERIOD);
++
++                /* pause to make sure at least some of the worker threads were started */
++                sleep(1);
++
++                /* and start working */
++                for(;;) {
++                    if(shut_down) {
++                        int finished;
++
++                        logmsg(LOG_NOTICE, "shutting down (%d)...", getpid());
++                        for(lstn = listeners; lstn; lstn = lstn->next)
++                            close(lstn->sock);
++                        /* rename control file (append pid) */
++                        if(ctrl_name != NULL) {
++                            char *ctrl_tmp = malloc(strlen(ctrl_name)+11);
++                            sprintf(ctrl_tmp, "%s.%d", ctrl_name, getpid());
++                            rename(ctrl_name, ctrl_tmp);
++                            free(ctrl_name);
++                            ctrl_name = ctrl_tmp;
+                         }
++                        /* wait for all threads to be finished */
++                        finished = 0;
++                        while(!finished) {
++                            int running;
++                            (void)pthread_mutex_lock(&arg_mut);
++                            running = numthreads-waiting;
++                            finished = !first && !running;
++                            (void)pthread_mutex_unlock(&arg_mut);
++                            if(!finished) {
++                                logmsg(LOG_INFO, "%d thread(s) still running...", running);
++                                sleep(RUNNING_CHECK_PERIOD);
++                            }
++                        }
++                        logmsg(LOG_NOTICE, "no threads running - exiting...");
++                        if(ctrl_name != NULL)
++                            (void)unlink(ctrl_name);
++                        exit(0);
+                     }
+-                    logmsg(LOG_NOTICE, "no threads running - exiting...");
+-                    if(ctrl_name != NULL)
+-                        (void)unlink(ctrl_name);
+-                    exit(0);
+-                }
+-                for(lstn = listeners, i = 0; i < n_listeners; lstn = lstn->next, i++) {
+-                    polls[i].events = POLLIN | POLLPRI;
+-                    polls[i].revents = 0;
+-                }
+-                if(poll(polls, n_listeners, -1) < 0) {
+-                    logmsg(LOG_WARNING, "poll: %s", strerror(errno));
+-                } else {
+-                    for(lstn = listeners, i = 0; lstn; lstn = lstn->next, i++) {
+-                        if(polls[i].revents & (POLLIN | POLLPRI)) {
+-                            memset(&clnt_addr, 0, sizeof(clnt_addr));
+-                            clnt_length = sizeof(clnt_addr);
+-                            if((clnt = accept(lstn->sock, (struct sockaddr *)&clnt_addr,
+-                                (socklen_t *)&clnt_length)) < 0) {
+-                                logmsg(LOG_WARNING, "HTTP accept: %s", strerror(errno));
+-                            } else if(((struct sockaddr_in *)&clnt_addr)->sin_family == AF_INET
+-                                   || ((struct sockaddr_in *)&clnt_addr)->sin_family == AF_INET6) {
+-                                thr_arg arg;
+-
+-                                if(lstn->disabled) {
+-                                    /*
+-                                    addr2str(tmp, MAXBUF - 1, &clnt_addr, 1);
+-                                    logmsg(LOG_WARNING, "HTTP disabled listener from %s", tmp);
+-                                    */
+-                                    close(clnt);
+-                                }
+-                                arg.sock = clnt;
+-                                arg.lstn = lstn;
+-                                if((arg.from_host.ai_addr = (struct sockaddr *)malloc(clnt_length)) == NULL) {
+-                                    logmsg(LOG_WARNING, "HTTP arg address: malloc");
++                    for(lstn = listeners, i = 0; i < n_listeners; lstn = lstn->next, i++) {
++                        polls[i].events = POLLIN | POLLPRI;
++                        polls[i].revents = 0;
++                    }
++                    if(poll(polls, n_listeners, -1) < 0) {
++                        logmsg(LOG_WARNING, "poll: %s", strerror(errno));
++                    } else {
++                        for(lstn = listeners, i = 0; lstn; lstn = lstn->next, i++) {
++                            if(polls[i].revents & (POLLIN | POLLPRI)) {
++                                memset(&clnt_addr, 0, sizeof(clnt_addr));
++                                clnt_length = sizeof(clnt_addr);
++                                if((clnt = accept(lstn->sock, (struct sockaddr *)&clnt_addr,
++                                    (socklen_t *)&clnt_length)) < 0) {
++                                    logmsg(LOG_WARNING, "HTTP accept: %s", strerror(errno));
++                                } else if(((struct sockaddr_in *)&clnt_addr)->sin_family == AF_INET
++                                       || ((struct sockaddr_in *)&clnt_addr)->sin_family == AF_INET6) {
++                                    thr_arg arg;
++
++                                    if(lstn->disabled) {
++                                        /*
++                                        addr2str(tmp, MAXBUF - 1, &clnt_addr, 1);
++                                        logmsg(LOG_WARNING, "HTTP disabled listener from %s", tmp);
++                                        */
++                                        close(clnt);
++                                    }
++                                    arg.sock = clnt;
++                                    arg.lstn = lstn;
++                                    if((arg.from_host.ai_addr = (struct sockaddr *)malloc(clnt_length)) == NULL) {
++                                        logmsg(LOG_WARNING, "HTTP arg address: malloc");
++                                        close(clnt);
++                                        continue;
++                                    }
++                                    memcpy(arg.from_host.ai_addr, &clnt_addr, clnt_length);
++                                    arg.from_host.ai_addrlen = clnt_length;
++                                    if(((struct sockaddr_in *)&clnt_addr)->sin_family == AF_INET)
++                                        arg.from_host.ai_family = AF_INET;
++                                    else
++                                        arg.from_host.ai_family = AF_INET6;
++                                    if(put_thr_arg(&arg))
++                                        close(clnt);
++                                } else {
++                                    /* may happen on FreeBSD, I am told */
++                                    logmsg(LOG_WARNING, "HTTP connection prematurely closed by peer");
+                                     close(clnt);
+-                                    continue;
+                                 }
+-                                memcpy(arg.from_host.ai_addr, &clnt_addr, clnt_length);
+-                                arg.from_host.ai_addrlen = clnt_length;
+-                                if(((struct sockaddr_in *)&clnt_addr)->sin_family == AF_INET)
+-                                    arg.from_host.ai_family = AF_INET;
+-                                else
+-                                    arg.from_host.ai_family = AF_INET6;
+-                                if(put_thr_arg(&arg))
+-                                    close(clnt);
+-                            } else {
+-                                /* may happen on FreeBSD, I am told */
+-                                logmsg(LOG_WARNING, "HTTP connection prematurely closed by peer");
+-                                close(clnt);
+                             }
+                         }
+                     }
+                 }
+-            }
+ #ifdef  UPER
+-        } else {
+-            /* failed to spawn son */
+-            logmsg(LOG_ERR, "Can't fork worker (%s) - aborted", strerror(errno));
+-            exit(1);
+-        }
++            } else {
++                /* failed to spawn son */
++                logmsg(LOG_ERR, "Can't fork worker (%s) - aborted", strerror(errno));
++                exit(1);
++            }
+ #endif
++        }
+     }
+ }
+diff --git a/pound.h b/pound.h
+index 492413c..8e6b1aa 100755
+--- a/pound.h
++++ b/pound.h
+@@ -416,6 +416,12 @@ typedef struct _listener {
+ extern LISTENER         *listeners; /* all available listeners */
+ #endif /* NO_EXTERNALS */
+ 
++/* pid list item definition */
++typedef struct _pid {
++    pid_t               pid;
++    struct _pid         *next;
++}   PID;
++
+ typedef struct _thr_arg {
+     int             sock;
+     LISTENER        *lstn;
+@@ -508,6 +514,11 @@ extern int cpURL(char *, char *, int);
+ extern void addr2str(char *, const int, const struct addrinfo *, const int);
+ 
+ /*
++ * Compare two addrinfo strctures, return 0 on match
++ */
++extern int addrinfo_cmp(const struct addrinfo *a, const struct addrinfo *b);
++
++/*
+  * Return a string representation for a back-end address
+  */
+ #define str_be(BUF, LEN, BE)    addr2str((BUF), (LEN), &(BE)->addr, 0)
+@@ -639,3 +650,23 @@ extern void *thr_timer(void *);
+  * listens to client requests and calls the appropriate functions
+  */
+ extern void *thr_control(void *);
++
++/*
++ * free listener structure (and all linked structures)
++ */
++extern void free_listener(LISTENER *lstn);
++
++/*
++ * insert pid into list
++ */
++void insert_pid(PID **list, pid_t pid);
++
++/*
++ * remove pid from the list
++ */
++void remove_pid(PID **list, pid_t pid);
++
++/*
++ * signal all processes in the list
++ */
++void signal_all(PID *list, int signal);
+diff --git a/svc.c b/svc.c
+index 22a6711..8f16827 100755
+--- a/svc.c
++++ b/svc.c
+@@ -305,6 +305,18 @@ addr2str(char *const res, const int res_len, const struct addrinfo *addr, const
+ }
+ 
+ /*
++ * Compare two addrinfo strctures, return 0 on match
++ */
++int
++addrinfo_cmp(const struct addrinfo *a, const struct addrinfo *b)
++{
++    return a->ai_flags == b->ai_flags && a->ai_family == b->ai_family
++        && a->ai_socktype == b->ai_socktype && a->ai_protocol == b->ai_protocol
++        && a->ai_addrlen == b->ai_addrlen && !memcmp(a->ai_addr, b->ai_addr, a->ai_addrlen)
++        ? 0 : 1;
++}
++
++/*
+  * Parse a URL, possibly decoding hexadecimal-encoded characters
+  */
+ int
+@@ -1823,3 +1835,130 @@ SSLINFO_callback(const SSL *ssl, int where, int rc)
+        *reneg_state = RENEG_REJECT;
+     }
+ }
++
++/*
++ * free linked list of matchers
++ */
++void
++free_matchers(MATCHER *matchers)
++{
++    while (matchers) {
++        MATCHER *m = matchers;
++        matchers = matchers->next;
++        regfree(&m->pat);
++        free(m);
++    }
++}
++
++/*
++ * free linked list of backends
++ */
++void
++free_backends(BACKEND *backends) {
++    while (backends) {
++        BACKEND *be = backends;
++        backends = backends->next;
++        free(be->url);
++        SSL_CTX_free(be->ctx);
++        pthread_mutex_destroy(&be->mut);
++        free(be);
++    }
++}
++
++/*
++ * free linked list of services
++ */
++void
++free_services(SERVICE *services)
++{
++    while (services) {
++        SERVICE *svc = services;
++        services = services->next;
++        free_matchers(svc->url);
++        free_matchers(svc->req_head);
++        free_matchers(svc->deny_head);
++        free_backends(svc->backends);
++        free_backends(svc->emergency);
++        pthread_mutex_destroy(&svc->mut);
++        regfree(&svc->sess_start);
++        regfree(&svc->sess_pat);
++#if OPENSSL_VERSION_NUMBER >= 0x10000000L
++        LHM_lh_free(TABNODE, svc->sessions);
++#else
++        lh_free(svc->sessions);
++#endif
++        free(svc);
++    }
++}
++
++/*
++ * free linked list of pound contexts
++ */
++void
++free_contexts(POUND_CTX *contexts)
++{
++    while (contexts) {
++        POUND_CTX *ctx = contexts;
++        contexts = contexts->next;
++        free(ctx->server_name);
++        SSL_CTX_free(ctx->ctx);
++        free(ctx);
++    }
++}
++
++/*
++ * free listener structure (and all linked structures)
++ */
++void
++free_listener(LISTENER *lstn)
++{
++    free_contexts(lstn->ctx);
++    free(lstn->add_head);
++    regfree(&lstn->verb);
++    regfree(&lstn->url_pat);
++    free(lstn->err414);
++    free(lstn->err500);
++    free(lstn->err501);
++    free(lstn->err503);
++    free_matchers(lstn->head_off);
++    free_services(lstn->services);
++    free(lstn);
++}
++
++/*
++ * insert pid into list
++ */
++void insert_pid(PID **list, pid_t pid) {
++    PID *item = (PID*)malloc(sizeof(PID));
++    item->pid = pid;
++    item->next = *list;
++    *list = item;
++}
++
++/*
++ * remove pid from the list
++ */
++void remove_pid(PID **list, pid_t pid) {
++    PID *prev = NULL, *cur = *list;
++    while (cur) {
++        if (cur->pid == pid) {
++            if (prev)
++                prev->next = cur->next;
++            else
++                *list = cur->next;
++            free(cur);
++        }
++        prev = cur;
++        cur = cur->next;
++    }
++}
++
++/*
++ * signal all processes in the list
++ */
++void signal_all(PID *list, int signal) {
++    while (list) {
++        kill(list->pid, signal);
++        list = list->next;
++    }
++}
+-- 
+1.8.3.1
+
diff --git a/Pound.spec b/Pound.spec
index e386a10..adbe25d 100644
--- a/Pound.spec
+++ b/Pound.spec
@@ -6,7 +6,7 @@
 
 Name:        Pound
 Version:     2.7
-Release:     0.1%{?alpha:.%{alpha}}%{?dist}
+Release:     0.1%{?alpha:.%{alpha}}%{?dist}.1
 Epoch:       1
 Summary:     Reverse proxy and load balancer
 Group:       System Environment/Daemons
@@ -35,6 +35,24 @@ Source1:  pound.service
 Source2:  pound.cfg
 Patch0:   pound-remove-owner.patch
 
+# Internet Explorer reliability fix
+# http://www.apsis.ch/pound/pound_list/archive/2014/2014-04/1398260962000#1398260962000
+Patch1:   0001-Flush-the-headers-to-the-backend-as-soon-as-they-are.patch
+
+# Logging verbosity fix
+# http://www.apsis.ch/pound/pound_list/archive/2014/2014-04/1398260973000#1398260973000
+Patch2:   0001-Do-not-return-NULL-data-from-get_thr_arg.patch
+
+# Restart robustness fixes
+# http://www.apsis.ch/pound/pound_list/archive/2014/2014-04/1398261217000#1398261217000
+Patch3:   0002-Remove-Grace-period-option-wait-for-all-worker-threa.patch
+# http://www.apsis.ch/pound/pound_list/archive/2014/2014-04/1398261218000#1398261218000
+Patch4:   0003-Allow-to-reload-configuration-on-SIGHUP-SIGINT-witho.patch
+
+# DoS security fix
+# http://www.apsis.ch/pound/pound_list/archive/2014/2014-04/1398260922000#1398260922000
+Patch5:   0001-copy_chunks-Signal-an-error-if-a-terminating-chunk-i.patch
+
 %description
 The Pound program is a reverse proxy, load balancer and
 HTTPS front-end for Web server(s). Pound was developed
@@ -48,6 +66,12 @@ give away
 %setup -q -n %{name}-%{version}%{?alpha}
 %patch0 -p1 -b .remove-owner
 
+%patch1 -p1
+%patch2 -p1
+%patch3 -p1
+%patch4 -p1
+%patch5 -p1
+
 %build
 %configure
 make %{?_smp_mflags}
@@ -118,6 +142,9 @@ exit 0
 %attr(-,%{pound_user},%{pound_group}) %dir %{pound_home}
 
 %changelog
+* Wed Apr 23 2014 Lubomir Rintel (GoodData) <lubo.rintel at gooddata.com> - 1:2.7-0.1.c.1
+- Add a couple of robustness/reliability fixes, submitted upstream
+
 * Wed Apr 23 2014 Lubomir Rintel (GoodData) <lubo.rintel at gooddata.com> - 1:2.7-0.1.c
 - Rebased to new upstream release
 - Bump epoch; the prerelease tag was added incorrectly. Sigh.


More information about the scm-commits mailing list