From: Miroslav Spousta <[email protected]>

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(-)
 mode change 100644 => 100755 svc.c

diff --git a/config.c b/config.c
index 30037c9..cb972a8 100755
--- a/config.c
+++ b/config.c
@@ -734,10 +734,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");
@@ -937,10 +937,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->log_level = log_level;
     if(regcomp(&res->verb, xhttp[0], REG_ICASE | REG_NEWLINE | REG_EXTENDED))
@@ -1464,6 +1464,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 b665ebe..afffd2e 100755
--- a/pound.8
+++ b/pound.8
@@ -945,6 +945,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 15ed71c..de5f8b1 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;
@@ -509,6 +515,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)
@@ -640,3 +651,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
old mode 100644
new mode 100755
index 0bad4e8..d8f4313
--- 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
@@ -1838,3 +1850,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.9.3


--
To unsubscribe send an email with subject unsubscribe to [email protected].
Please contact [email protected] for questions.

Reply via email to