]> granicus.if.org Git - apache/blobdiff - server/listen.c
Remove APLOG_NOERRNO.
[apache] / server / listen.c
index fc584742aa45e67b445b8b40ee9c05760123a2ee..8bf0b3272ee3e4152a92269790f6eb4bd7abb616 100644 (file)
@@ -1,9 +1,9 @@
-/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
- * applicable.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
  *
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
 #define APR_WANT_STRFUNC
 #include "apr_want.h"
 
-#define CORE_PRIVATE
 #include "ap_config.h"
 #include "httpd.h"
 #include "http_config.h"
 #include "http_core.h"
 #include "ap_listen.h"
 #include "http_log.h"
-#include "mpm.h"
 #include "mpm_common.h"
 
+#ifdef HAVE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
 AP_DECLARE_DATA ap_listen_rec *ap_listeners = NULL;
 
+AP_DECLARE_DATA ap_listen_rec **mpm_listen = NULL;
+AP_DECLARE_DATA int enable_default_listener = 1;
+AP_DECLARE_DATA int num_buckets = 1;
+AP_DECLARE_DATA int have_so_reuseport = 0;
+
 static ap_listen_rec *old_listeners;
 static int ap_listenbacklog;
 static int send_buffer_size;
 static int receive_buffer_size;
+#ifdef HAVE_SYSTEMD
+static int use_systemd = -1;
+#endif
 
 /* TODO: make_sock is just begging and screaming for APR abstraction */
-static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
+static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server, int do_bind_listen)
 {
     apr_socket_t *s = server->sd;
     int one = 1;
@@ -54,7 +68,7 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
 #ifndef WIN32
     stat = apr_socket_opt_set(s, APR_SO_REUSEADDR, one);
     if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
-        ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p,
+        ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(00067)
                       "make_sock: for address %pI, apr_socket_opt_set: (SO_REUSEADDR)",
                       server->bind_addr);
         apr_socket_close(s);
@@ -64,27 +78,13 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
 
     stat = apr_socket_opt_set(s, APR_SO_KEEPALIVE, one);
     if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
-        ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p,
+        ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(00068)
                       "make_sock: for address %pI, apr_socket_opt_set: (SO_KEEPALIVE)",
                       server->bind_addr);
         apr_socket_close(s);
         return stat;
     }
 
-#if APR_HAVE_IPV6
-    if (server->bind_addr->family == APR_INET6) {
-        stat = apr_socket_opt_set(s, APR_IPV6_V6ONLY, v6only_setting);
-        if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
-            ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p,
-                          "make_sock: for address %pI, apr_socket_opt_set: "
-                          "(IPV6_V6ONLY)",
-                          server->bind_addr);
-            apr_socket_close(s);
-            return stat;
-        }
-    }
-#endif
-
     /*
      * To send data over high bandwidth-delay connections at full
      * speed we must force the TCP window to open wide enough to keep the
@@ -107,7 +107,7 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
     if (send_buffer_size) {
         stat = apr_socket_opt_set(s, APR_SO_SNDBUF,  send_buffer_size);
         if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
-            ap_log_perror(APLOG_MARK, APLOG_WARNING, stat, p,
+            ap_log_perror(APLOG_MARK, APLOG_WARNING, stat, p, APLOGNO(00070)
                           "make_sock: failed to set SendBufferSize for "
                           "address %pI, using default",
                           server->bind_addr);
@@ -117,11 +117,10 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
     if (receive_buffer_size) {
         stat = apr_socket_opt_set(s, APR_SO_RCVBUF, receive_buffer_size);
         if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
-            ap_log_perror(APLOG_MARK, APLOG_WARNING, stat, p,
+            ap_log_perror(APLOG_MARK, APLOG_WARNING, stat, p, APLOGNO(00071)
                           "make_sock: failed to set ReceiveBufferSize for "
                           "address %pI, using default",
                           server->bind_addr);
-            strerror(errno);
             /* not a fatal error */
         }
     }
@@ -130,21 +129,60 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
     ap_sock_disable_nagle(s);
 #endif
 
-    if ((stat = apr_socket_bind(s, server->bind_addr)) != APR_SUCCESS) {
-        ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, stat, p,
-                      "make_sock: could not bind to address %pI",
-                      server->bind_addr);
-        apr_socket_close(s);
-        return stat;
+#ifdef SO_REUSEPORT
+    {
+      int thesock;
+      apr_os_sock_get(&thesock, s);
+      if (setsockopt(thesock, SOL_SOCKET, SO_REUSEPORT, (void *)&one, sizeof(int)) < 0) {
+          /* defined by not valid? */
+          if (errno == ENOPROTOOPT) {
+              have_so_reuseport = 0;
+          } /* Check if SO_REUSEPORT is supported by the running Linux Kernel.*/
+          else {
+              ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(02638)
+                      "make_sock: for address %pI, apr_socket_opt_set: (SO_REUSEPORT)",
+                       server->bind_addr);
+              apr_socket_close(s);
+              return errno;
+          }
+      }
+      else {
+          have_so_reuseport = 1;
+      }
     }
+#endif
 
-    if ((stat = apr_socket_listen(s, ap_listenbacklog)) != APR_SUCCESS) {
-        ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, stat, p,
-                      "make_sock: unable to listen for connections "
-                      "on address %pI",
-                      server->bind_addr);
-        apr_socket_close(s);
-        return stat;
+    if (do_bind_listen) {
+#if APR_HAVE_IPV6
+        if (server->bind_addr->family == APR_INET6) {
+            stat = apr_socket_opt_set(s, APR_IPV6_V6ONLY, v6only_setting);
+            if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
+                ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(00069)
+                              "make_sock: for address %pI, apr_socket_opt_set: "
+                              "(IPV6_V6ONLY)",
+                              server->bind_addr);
+                apr_socket_close(s);
+                return stat;
+            }
+        }
+#endif
+
+        if ((stat = apr_socket_bind(s, server->bind_addr)) != APR_SUCCESS) {
+            ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, stat, p, APLOGNO(00072)
+                          "make_sock: could not bind to address %pI",
+                          server->bind_addr);
+            apr_socket_close(s);
+            return stat;
+        }
+
+        if ((stat = apr_socket_listen(s, ap_listenbacklog)) != APR_SUCCESS) {
+            ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, stat, p, APLOGNO(00073)
+                          "make_sock: unable to listen for connections "
+                          "on address %pI",
+                          server->bind_addr);
+            apr_socket_close(s);
+            return stat;
+        }
     }
 
 #ifdef WIN32
@@ -160,8 +198,8 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
      */
     stat = apr_socket_opt_set(s, APR_SO_REUSEADDR, one);
     if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
-        ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p,
-                    "make_sock: for address %pI, apr_socket_opt_set: (SO_REUSEADDR)", 
+        ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(00074)
+                    "make_sock: for address %pI, apr_socket_opt_set: (SO_REUSEADDR)",
                      server->bind_addr);
         apr_socket_close(s);
         return stat;
@@ -169,13 +207,9 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
 #endif
 
     server->sd = s;
-    server->active = 1;
+    server->active = enable_default_listener;
 
-#ifdef MPM_ACCEPT_FUNC
-    server->accept_func = MPM_ACCEPT_FUNC;
-#else
     server->accept_func = NULL;
-#endif
 
     return APR_SUCCESS;
 }
@@ -183,8 +217,7 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
 static const char* find_accf_name(server_rec *s, const char *proto)
 {
     const char* accf;
-    core_server_config *conf = ap_get_module_config(s->module_config,
-                                                    &core_module);
+    core_server_config *conf = ap_get_core_module_config(s->module_config);
     if (!proto) {
         return NULL;
     }
@@ -198,7 +231,7 @@ static const char* find_accf_name(server_rec *s, const char *proto)
     return accf;
 }
 
-static void ap_apply_accept_filter(apr_pool_t *p, ap_listen_rec *lis, 
+static void ap_apply_accept_filter(apr_pool_t *p, ap_listen_rec *lis,
                                            server_rec *server)
 {
     apr_socket_t *s = lis->sd;
@@ -217,21 +250,22 @@ static void ap_apply_accept_filter(apr_pool_t *p, ap_listen_rec *lis,
 
     if (accf) {
 #if APR_HAS_SO_ACCEPTFILTER
-        rv = apr_socket_accept_filter(s, apr_pstrdup(p, accf), 
-                                      apr_pstrdup(p,""));
+        /* In APR 1.x, the 2nd and 3rd parameters are char * instead of 
+         * const char *, so make a copy of those args here.
+         */
+        rv = apr_socket_accept_filter(s, apr_pstrdup(p, accf),
+                                      apr_pstrdup(p, ""));
         if (rv != APR_SUCCESS && !APR_STATUS_IS_ENOTIMPL(rv)) {
-            ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p,
+            ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p, APLOGNO(00075)
                           "Failed to enable the '%s' Accept Filter",
                           accf);
         }
 #else
-#ifdef APR_TCP_DEFER_ACCEPT
-        rv = apr_socket_opt_set(s, APR_TCP_DEFER_ACCEPT, 1);   
+        rv = apr_socket_opt_set(s, APR_TCP_DEFER_ACCEPT, 30);
         if (rv != APR_SUCCESS && !APR_STATUS_IS_ENOTIMPL(rv)) {
-            ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p,
+            ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p, APLOGNO(00076)
                               "Failed to enable APR_TCP_DEFER_ACCEPT");
         }
-#endif
 #endif
     }
 }
@@ -242,8 +276,127 @@ static apr_status_t close_listeners_on_exec(void *v)
     return APR_SUCCESS;
 }
 
-static const char *alloc_listener(process_rec *process, char *addr, 
-                                  apr_port_t port, const char* proto)
+
+#ifdef HAVE_SYSTEMD
+
+static int find_systemd_socket(process_rec * process, apr_port_t port)
+{
+    int fdcount, fd;
+    int sdc = sd_listen_fds(0);
+
+    if (sdc < 0) {
+        ap_log_perror(APLOG_MARK, APLOG_CRIT, sdc, process->pool, APLOGNO(02486)
+                      "find_systemd_socket: Error parsing enviroment, sd_listen_fds returned %d",
+                      sdc);
+        return -1;
+    }
+
+    if (sdc == 0) {
+        ap_log_perror(APLOG_MARK, APLOG_CRIT, sdc, process->pool, APLOGNO(02487)
+                      "find_systemd_socket: At least one socket must be set.");
+        return -1;
+    }
+
+    fdcount = atoi(getenv("LISTEN_FDS"));
+    for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + fdcount; fd++) {
+        if (sd_is_socket_inet(fd, 0, 0, -1, port) > 0) {
+            return fd;
+        }
+    }
+
+    return -1;
+}
+
+static apr_status_t alloc_systemd_listener(process_rec * process,
+                                           int fd, const char *proto,
+                                           ap_listen_rec **out_rec)
+{
+    apr_status_t rv;
+    struct sockaddr sa;
+    socklen_t len = sizeof(struct sockaddr);
+    apr_os_sock_info_t si;
+    ap_listen_rec *rec;
+    *out_rec = NULL;
+
+    memset(&si, 0, sizeof(si));
+
+    rv = getsockname(fd, &sa, &len);
+
+    if (rv != 0) {
+        rv = apr_get_netos_error();
+        ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, process->pool, APLOGNO(02489)
+                      "getsockname on %d failed.", fd);
+        return rv;
+    }
+
+    si.os_sock = &fd;
+    si.family = sa.sa_family;
+    si.local = &sa;
+    si.type = SOCK_STREAM;
+    si.protocol = APR_PROTO_TCP;
+
+    rec = apr_palloc(process->pool, sizeof(ap_listen_rec));
+    rec->active = 0;
+    rec->next = 0;
+
+    rv = apr_os_sock_make(&rec->sd, &si, process->pool);
+    if (rv != APR_SUCCESS) {
+        ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, process->pool, APLOGNO(02490)
+                      "apr_os_sock_make on %d failed.", fd);
+        return rv;
+    }
+
+    rv = apr_socket_addr_get(&rec->bind_addr, APR_LOCAL, rec->sd);
+    if (rv != APR_SUCCESS) {
+        ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, process->pool, APLOGNO(02491)
+                      "apr_socket_addr_get on %d failed.", fd);
+        return rv;
+    }
+
+    rec->protocol = apr_pstrdup(process->pool, proto);
+
+    *out_rec = rec;
+
+    return make_sock(process->pool, rec, 0);
+}
+
+static const char *set_systemd_listener(process_rec *process, apr_port_t port,
+                                        const char *proto)
+{
+    ap_listen_rec *last, *new;
+    apr_status_t rv;
+    int fd = find_systemd_socket(process, port);
+    if (fd < 0) {
+        return "Systemd socket activation is used, but this port is not "
+                "configured in systemd";
+    }
+
+    last = ap_listeners;
+    while (last && last->next) {
+        last = last->next;
+    }
+
+    rv = alloc_systemd_listener(process, fd, proto, &new);
+    if (rv != APR_SUCCESS) {
+        return "Failed to setup socket passed by systemd using socket activation";
+    }
+
+    if (last == NULL) {
+        ap_listeners = last = new;
+    }
+    else {
+        last->next = new;
+        last = new;
+    }
+
+    return NULL;
+}
+
+#endif /* HAVE_SYSTEMD */
+
+static const char *alloc_listener(process_rec *process, char *addr,
+                                  apr_port_t port, const char* proto,
+                                  void *slave)
 {
     ap_listen_rec **walk, *last;
     apr_status_t status;
@@ -278,13 +431,16 @@ static const char *alloc_listener(process_rec *process, char *addr,
     }
 
     if (found_listener) {
+        if (ap_listeners->slave != slave) {
+            return "Cannot define a slave on the same IP:port as a Listener";
+        }
         return NULL;
     }
 
     if ((status = apr_sockaddr_info_get(&sa, addr, APR_UNSPEC, port, 0,
                                         process->pool))
         != APR_SUCCESS) {
-        ap_log_perror(APLOG_MARK, APLOG_CRIT, status, process->pool,
+        ap_log_perror(APLOG_MARK, APLOG_CRIT, status, process->pool, APLOGNO(00077)
                       "alloc_listener: failed to set up sockaddr for %s",
                       addr);
         return "Listen setup failed";
@@ -322,7 +478,7 @@ static const char *alloc_listener(process_rec *process, char *addr,
         }
 #endif
         if (status != APR_SUCCESS) {
-            ap_log_perror(APLOG_MARK, APLOG_CRIT, status, process->pool,
+            ap_log_perror(APLOG_MARK, APLOG_CRIT, status, process->pool, APLOGNO(00078)
                           "alloc_listener: failed to get a socket for %s",
                           addr);
             return "Listen setup failed";
@@ -335,6 +491,7 @@ static const char *alloc_listener(process_rec *process, char *addr,
             last->next = new;
             last = new;
         }
+        new->slave = slave;
     }
 
     return NULL;
@@ -362,6 +519,9 @@ static int open_listeners(apr_pool_t *pool)
     int num_open;
     const char *userdata_key = "ap_open_listeners";
     void *data;
+#if AP_NONBLOCK_WHEN_MULTI_LISTEN
+    int use_nonblock;
+#endif
 
     /* Don't allocate a default listener.  If we need to listen to a
      * port, then the user needs to have a Listen directive in their
@@ -375,20 +535,28 @@ static int open_listeners(apr_pool_t *pool)
         }
         else {
 #if APR_HAVE_IPV6
+            ap_listen_rec *cur;
             int v6only_setting;
+            int skip = 0;
 
             /* If we have the unspecified IPv4 address (0.0.0.0) and
              * the unspecified IPv6 address (::) is next, we need to
              * swap the order of these in the list. We always try to
              * bind to IPv6 first, then IPv4, since an IPv6 socket
              * might be able to receive IPv4 packets if V6ONLY is not
-             * enabled, but never the other way around. */
+             * enabled, but never the other way around.
+             * Note: In some configurations, the unspecified IPv6 address
+             * could be even later in the list.  This logic only corrects
+             * the situation where it is next in the list, such as when
+             * apr_sockaddr_info_get() returns an IPv4 and an IPv6 address,
+             * in that order.
+             */
             if (lr->next != NULL
                 && IS_INADDR_ANY(lr->bind_addr)
                 && lr->bind_addr->port == lr->next->bind_addr->port
                 && IS_IN6ADDR_ANY(lr->next->bind_addr)) {
                 /* Exchange lr and lr->next */
-                ap_listen_rec *next = lr->next;
+                next = lr->next;
                 lr->next = next->next;
                 next->next = lr;
                 if (previous) {
@@ -400,28 +568,36 @@ static int open_listeners(apr_pool_t *pool)
                 lr = next;
             }
 
-            /* If we are trying to bind to 0.0.0.0 and the previous listener
+            /* If we are trying to bind to 0.0.0.0 and a previous listener
              * was :: on the same port and in turn that socket does not have
              * the IPV6_V6ONLY flag set; we must skip the current attempt to
              * listen (which would generate an error). IPv4 will be handled
              * on the established IPv6 socket.
              */
-            if (previous != NULL
-                && IS_INADDR_ANY(lr->bind_addr)
-                && lr->bind_addr->port == previous->bind_addr->port
-                && IS_IN6ADDR_ANY(previous->bind_addr)
-                && apr_socket_opt_get(previous->sd, APR_IPV6_V6ONLY,
-                                      &v6only_setting) == APR_SUCCESS
-                && v6only_setting == 0) {
-
-                /* Remove the current listener from the list */
-                previous->next = lr->next;
-                continue;
+            if (IS_INADDR_ANY(lr->bind_addr) && previous) {
+                for (cur = ap_listeners; cur != lr; cur = cur->next) {
+                    if (lr->bind_addr->port == cur->bind_addr->port
+                        && IS_IN6ADDR_ANY(cur->bind_addr)
+                        && apr_socket_opt_get(cur->sd, APR_IPV6_V6ONLY,
+                                              &v6only_setting) == APR_SUCCESS
+                        && v6only_setting == 0) {
+
+                        /* Remove the current listener from the list */
+                        previous->next = lr->next;
+                        lr = previous; /* maintain current value of previous after
+                                        * post-loop expression is evaluated
+                                        */
+                        skip = 1;
+                        break;
+                    }
+                }
+                if (skip) {
+                    continue;
+                }
             }
 #endif
-            if (make_sock(pool, lr) == APR_SUCCESS) {
+            if (make_sock(pool, lr, enable_default_listener) == APR_SUCCESS) {
                 ++num_open;
-                lr->active = 1;
             }
             else {
 #if APR_HAVE_IPV6
@@ -474,16 +650,15 @@ static int open_listeners(apr_pool_t *pool)
      * is already forgotten about by the time we call accept, we won't
      * be hung until another connection arrives on that port
      */
-    if (ap_listeners && ap_listeners->next) {
-        for (lr = ap_listeners; lr; lr = lr->next) {
-            apr_status_t status;
-
-            status = apr_socket_opt_set(lr->sd, APR_SO_NONBLOCK, 1);
-            if (status != APR_SUCCESS) {
-                ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, status, pool,
-                              "unable to make listening socket non-blocking");
-                return -1;
-            }
+    use_nonblock = (ap_listeners && ap_listeners->next);
+    for (lr = ap_listeners; lr; lr = lr->next) {
+        apr_status_t status;
+
+        status = apr_socket_opt_set(lr->sd, APR_SO_NONBLOCK, use_nonblock);
+        if (status != APR_SUCCESS) {
+            ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, status, pool, APLOGNO(00079)
+                          "unable to control socket non-blocking status");
+            return -1;
         }
     }
 #endif /* AP_NONBLOCK_WHEN_MULTI_LISTEN */
@@ -516,8 +691,8 @@ AP_DECLARE(int) ap_setup_listeners(server_rec *s)
         proto = ap_get_server_protocol(ls);
         if (!proto) {
             found = 0;
-            /* No protocol was set for this vhost, 
-             * use the default for this listener. 
+            /* No protocol was set for this vhost,
+             * use the default for this listener.
              */
             for (addr = ls->addrs; addr && !found; addr = addr->next) {
                 for (lr = ap_listeners; lr; lr = lr->next) {
@@ -537,8 +712,29 @@ AP_DECLARE(int) ap_setup_listeners(server_rec *s)
         }
     }
 
-    if (open_listeners(s->process->pool)) {
-       return 0;
+
+#ifdef HAVE_SYSTEMD
+    if (use_systemd) {
+        const char *userdata_key = "ap_open_systemd_listeners";
+        void *data;
+        /* clear the enviroment on our second run
+        * so that none of our future children get confused.
+        */
+        apr_pool_userdata_get(&data, userdata_key, s->process->pool);
+        if (!data) {
+            apr_pool_userdata_set((const void *)1, userdata_key,
+                                apr_pool_cleanup_null, s->process->pool);
+        }
+        else {
+            sd_listen_fds(1);
+        }        
+    }
+    else
+#endif
+    {
+        if (open_listeners(s->process->pool)) {
+            return 0;
+        }
     }
 
     for (lr = ap_listeners; lr; lr = lr->next) {
@@ -562,14 +758,103 @@ AP_DECLARE(int) ap_setup_listeners(server_rec *s)
     return num_listeners;
 }
 
-AP_DECLARE_NONSTD(void) ap_close_listeners(void) 
+AP_DECLARE(apr_status_t) ap_duplicate_listeners(server_rec *s, apr_pool_t *p,
+                                                  int num_buckets) {
+    int i;
+    apr_status_t stat;
+    int use_nonblock = 0;
+    ap_listen_rec *lr;
+
+    mpm_listen = apr_palloc(p, sizeof(ap_listen_rec*) * num_buckets);
+    for (i = 0; i < num_buckets; i++) {
+        ap_listen_rec *last = NULL;
+        lr = ap_listeners;
+        while (lr) {
+            ap_listen_rec *duplr;
+            char *hostname;
+            apr_port_t port;
+            apr_sockaddr_t *sa;
+#ifdef HAVE_SYSTEMD
+            if (use_systemd) {
+                int thesock;
+                apr_os_sock_get(&thesock, lr->sd);
+                if ((stat = alloc_systemd_listener(s->process, thesock,
+                    lr->protocol, &duplr)) != APR_SUCCESS) {
+                    return stat;
+                }
+            }
+            else
+#endif
+            {
+                duplr  = apr_palloc(p, sizeof(ap_listen_rec));
+                duplr->slave = NULL;
+                duplr->protocol = apr_pstrdup(p, lr->protocol);
+                hostname = apr_pstrdup(p, lr->bind_addr->hostname);
+                port = lr->bind_addr->port;
+                apr_sockaddr_info_get(&sa, hostname, APR_UNSPEC, port, 0, p);
+                duplr->bind_addr = sa;
+                duplr->next = NULL;
+                if ((stat = apr_socket_create(&duplr->sd, duplr->bind_addr->family,
+                                            SOCK_STREAM, 0, p)) != APR_SUCCESS) {
+                    ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, p, APLOGNO(02640)
+                                "ap_duplicate_socket: for address %pI, "
+                                "cannot duplicate a new socket!",
+                                duplr->bind_addr);
+                    return stat;
+                }
+                make_sock(p, duplr, 1);
+            }
+#if AP_NONBLOCK_WHEN_MULTI_LISTEN
+            use_nonblock = (ap_listeners && ap_listeners->next);
+            if ((stat = apr_socket_opt_set(duplr->sd, APR_SO_NONBLOCK, use_nonblock))
+                != APR_SUCCESS) {
+                ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(02641)
+                              "unable to control socket non-blocking status");
+                return stat;
+            }
+#endif
+            ap_apply_accept_filter(p, duplr, s);
+
+            if (last == NULL) {
+                mpm_listen[i] = last = duplr;
+            }
+            else {
+                last->next = duplr;
+                last = duplr;
+            }
+            lr = lr->next;
+        }
+    }
+    return APR_SUCCESS;
+}
+
+AP_DECLARE_NONSTD(void) ap_close_listeners(void)
 {
     ap_listen_rec *lr;
+    int i;
+    for (i = 0; i < num_buckets; i++) {
+        for (lr = mpm_listen[i]; lr; lr = lr->next) {
+            apr_socket_close(lr->sd);
+            lr->active = 0;
+        }
+    }
+}
+
+AP_DECLARE_NONSTD(int) ap_close_selected_listeners(ap_slave_t *slave)
+{
+    ap_listen_rec *lr;
+    int n = 0;
 
     for (lr = ap_listeners; lr; lr = lr->next) {
-        apr_socket_close(lr->sd);
-        lr->active = 0;
+        if (lr->slave != slave) {
+            apr_socket_close(lr->sd);
+            lr->active = 0;
+        }
+        else {
+            ++n;
+        }
     }
+    return n;
 }
 
 AP_DECLARE(void) ap_listen_pre_config(void)
@@ -579,7 +864,6 @@ AP_DECLARE(void) ap_listen_pre_config(void)
     ap_listenbacklog = DEFAULT_LISTENBACKLOG;
 }
 
-
 AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
                                                 int argc, char *const argv[])
 {
@@ -595,6 +879,11 @@ AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
     if (argc < 1 || argc > 2) {
         return "Listen requires 1 or 2 arguments.";
     }
+#ifdef HAVE_SYSTEMD
+    if (use_systemd == -1) {
+        use_systemd = sd_listen_fds(0) > 0;
+    }
+#endif
 
     rv = apr_parse_addr_port(&host, &scope_id, &port, argv[0], cmd->pool);
     if (rv != APR_SUCCESS) {
@@ -615,14 +904,24 @@ AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
     }
 
     if (argc != 2) {
-        proto = "http";
+        if (port == 443) {
+            proto = "https";
+        } else {
+            proto = "http";
+        }
     }
     else {
         proto = apr_pstrdup(cmd->pool, argv[1]);
         ap_str_tolower(proto);
     }
 
-    return alloc_listener(cmd->server->process, host, port, proto);
+#ifdef HAVE_SYSTEMD
+    if (use_systemd) {
+        return set_systemd_listener(cmd->server->process, port, proto);
+    }
+#endif
+
+    return alloc_listener(cmd->server->process, host, port, proto, NULL);
 }
 
 AP_DECLARE_NONSTD(const char *) ap_set_listenbacklog(cmd_parms *cmd,