[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