]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Merge r1511033, r1528032, r1608686, r1608694, r1608703, r1899784, r1916312, r1916344...
authorRuediger Pluem <rpluem@apache.org>
Wed, 4 Jun 2025 09:41:25 +0000 (09:41 +0000)
committerRuediger Pluem <rpluem@apache.org>
Wed, 4 Jun 2025 09:41:25 +0000 (09:41 +0000)
Add support for systemd socket activation to listener sockets.

* configure.in: Simplify/fix systemd detection: move later to fix
  autoconf warnings; define HAVE_SYSTEMD rather than using CPPFLAGS.

* server/listen.c: Use HAVE_SYSTEMD for systemd #define.

* modules/arch/unix/config5.m4: Update systemd headers check.

* server/listen.c: duplicate sockets correctly when using systemd socket
activation, fix addrlen in getsockname() call.

Follow up r1608686, pass process to alloc_systemd_listener.

* server/listen.c: detect systemd socket activation using sd_listen_fds(),
drop the support for "Listen systemd" and use standard Listen syntax instead.

This allows using the same configuration file with or without socket activation
and allows setting protocol when using socket activation.

Remove libsystemd dependency from main httpd binary

Until this change httpd was linking libsystemd to the main httpd binary. If you want to run lightweight version of httpd in container, sometimes you just want to install
httpd binary with as little dependencies as possible to make container small in size and do not pull uncencessary dependencies and libraries.

This change will move all systemd library calls from listen.c to mod_systemd module and remove systemd linking from the main httpd bin.
Fixed mixed declaration and wrongly declared variable.

Submitted by: Luboš Uhliarik <luhliari redhat.com>

mod_systemd: Axe APR_OPTIONAL_FN redeclarations to avoid compiler warning.

ap_find_systemd_socket() and ap_systemd_listen_fds() are already declared in
"ap_listen.h", so just include them.

mod_systemd: if SELinux is available and enabled, log the SELinux
context at startup, since this may vary when httpd is started via
systemd vs being started directly.

* modules/arch/unix/mod_systemd.c (systemd_post_config):
  Do nothing for the pre-config iteration.
  Log the SELinux context if available.

* modules/arch/unix/config5.m4: Detect libselinux.

Have at least one CI job build mod_systemd.

Fix a cppcheck warning.

Remove some dead code. Updating 'last' is pointless here.

Ensure that ALL fields of the ap_listen_rec structure are initialized

alloc_listener initializes more fields in the created ap_listen_rec structure
than alloc_systemd_listener as it has more data to add to this structure.
Ensure that all fields of the ap_listen_rec structure are initialized at
least with 0 as later code using this structure depends on this.

Reviewed by: rpluem, jorton, ylavic

Github: closes #515

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1926113 13f79535-47bb-0310-9956-ffa450edef68

16 files changed:
CHANGES
STATUS
changes-entries/dav-get-base-path.txt [deleted file]
changes-entries/github 310.txt [deleted file]
changes-entries/h2_v2.0.30.txt [deleted file]
changes-entries/h2_v2.0.32.txt [deleted file]
changes-entries/md_v2.5.1.txt [deleted file]
changes-entries/md_v2.5.2.txt [deleted file]
changes-entries/pr69443.txt [deleted file]
changes-entries/pr69579.txt [deleted file]
changes-entries/pr69590.txt [deleted file]
changes-entries/pr69624.txt [deleted file]
include/ap_listen.h
modules/arch/unix/config5.m4
modules/arch/unix/mod_systemd.c
server/listen.c

diff --git a/CHANGES b/CHANGES
index 529a2c96bf9f44c38056b80e0572a76e9473bc7e..1b3a6c9ce1af3b5eca1b174b92718d96678a4619 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,68 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.4.64
 
+  *) mod_systemd: Systemd socket activation can now be enabled at
+     build time but disabled at run time, if mod_systemd is not
+     loaded.  [Lubos Uhliarik <luhliari redhat.com>]
+
+  *) mod_systemd: Log the SELinux context at startup if available and
+     enabled.  [Joe Orton]
+
+  *) mod_http2: update to version 2.0.32
+     The code setting the connection window size was set wrong,
+     preventing `H2WindowSize` to work.
+     Fixed <https://github.com/icing/mod_h2/issues/300>.
+     [Stefan Eissing, Michael Kaufmann]
+
+  *) mod_http2: update to version 2.0.30
+     - Fixed bug in handling over long response headers. When the 64 KB limit
+       of nghttp2 was exceeded, the request was not reset and the client was
+       left hanging, waiting for it. Now the stream is reset.
+     - Added new directive `H2MaxHeaderBlockLen` to set the limit on response
+       header sizes.
+     - Fixed handling of Timeout vs. KeepAliveTimeout when first request on a
+       connection was reset.
+
+  *) mod_lua: Fix memory handling in LuaOutputFilter. PR 69590.
+     [Guillermo Grandes <guillermo.grandes gmail.com>]
+
+  * mod_proxy_http2: revert r1912193 for detecting broken backend connections
+    as this interferes with backend selection who a node is unresponsive.
+    PR69624.
+
+  *) mod_proxy_balancer: Fix a regression that caused stickysession keys no
+     longer be recognized if they are provided as query parameter in the URL.
+     PR 69443 [Ruediger Pluem]
+
+  *) mod_md: update to version 2.5.2
+     - Fixed TLS-ALPN-01 challenges when multiple `MDPrivateKeys` are specified
+       with EC keys before RSA ones. Fixes #377. [Stefan Eissing]
+     - Fixed missing newlines in the status page output. [Andreas Groth]
+
+  *) mod_dav: Add API to expose DavBasePath setting.  [Joe Orton]
+
+  *) mod_md: update to version 2.5.1
+     - Added support for ACME profiles with new directives MDProfile and
+       MDProfileMandatory.
+     - When installing a custom CA file via `MDCACertificateFile`, also set the
+       libcurl option CURLSSLOPT_NO_REVOKE that suppresses complains by Schannel
+       (when curl is linked with it) about missing CRL/OCSP in certificates.
+     - Fixed handling of corrupted httpd.json and added test 300_30 for it.
+       File is removed on error and written again. Fixes #369.
+     - Added explanation in log for how to proceed when md_store.json could not be
+       parsed and prevented the server start.
+     - restored fixed to #336 and #337 which got lost in a sync with Apache svn
+     - Add Issue Name/Uris to certificate information in md-status handler
+     - MDomains with static certificate files have MDRenewMode "manual", unless
+       "always" is configured.
+
+  *) core: Report invalid Options= argument when parsing AllowOverride
+     directives.
+     Github #310 [Zhou Qingyang <zhou1615 umn.edu>]
+
+  *) scoreboard/mod_http2: record durations of HTTP/2 requests.
+     PR 69579 [Pierre Brochard <pierre.brochard.1982@m4x.org>]
+
 Changes with Apache 2.4.63
 
   *) mod_dav: Update redirect-carefully example BrowserMatch config
diff --git a/STATUS b/STATUS
index de4f4a96db9f3236f36df22e0dd60d1b1846af02..5d3b206e51906e30d60b4cdbec10ed4acd1c6837 100644 (file)
--- a/STATUS
+++ b/STATUS
@@ -183,28 +183,6 @@ PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
         https://patch-diff.githubusercontent.com/raw/apache/httpd/pull/533.diff
      +1 covener, rpluem, jorton
 
-  *) mod_systemd: Add systemd socket activation
-     Trunk version of patch:
-        https://svn.apache.org/r1511033
-        https://svn.apache.org/r1528032
-        https://svn.apache.org/r1608686
-        https://svn.apache.org/r1608694
-        https://svn.apache.org/r1608703
-        https://svn.apache.org/r1838271
-        https://svn.apache.org/r1899784
-        https://svn.apache.org/r1916312
-        https://svn.apache.org/r1916344
-        https://svn.apache.org/r1926091
-     Backport version for 2.4.x of patch:
-       https://patch-diff.githubusercontent.com/raw/apache/httpd/pull/515.diff
-     Can be applied via apply_backport_pr.sh 515
-     +1: rpluem, jorton, ylavic
-     jailletc36: Should the apr_palloc() in alloc_systemd_listener() be
-                 apr_pcalloc() so that all fields are 0?
-                 make_sock() uses the 'flags' field that would be
-                 undefined otherwise.
-     rpluem says: Good catch. Added r1926091 and reset votes.
-
 PATCHES PROPOSED TO BACKPORT FROM TRUNK:
   [ New proposals should be added at the end of the list ]
 
diff --git a/changes-entries/dav-get-base-path.txt b/changes-entries/dav-get-base-path.txt
deleted file mode 100644 (file)
index e24e41d..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-  *) mod_dav: Add API to expose DavBasePath setting.  [Joe Orton]
-
diff --git a/changes-entries/github 310.txt b/changes-entries/github 310.txt
deleted file mode 100644 (file)
index 2d966cd..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-  *) core: Report invalid Options= argument when parsing AllowOverride
-     directives.
-     Github #310 [Zhou Qingyang <zhou1615 umn.edu>]
diff --git a/changes-entries/h2_v2.0.30.txt b/changes-entries/h2_v2.0.30.txt
deleted file mode 100644 (file)
index edd12af..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-  *) mod_http2: update to version 2.0.30
-     - Fixed bug in handling over long response headers. When the 64 KB limit
-       of nghttp2 was exceeded, the request was not reset and the client was
-       left hanging, waiting for it. Now the stream is reset.
-     - Added new directive `H2MaxHeaderBlockLen` to set the limit on response
-       header sizes.
-     - Fixed handling of Timeout vs. KeepAliveTimeout when first request on a
-       connection was reset.
diff --git a/changes-entries/h2_v2.0.32.txt b/changes-entries/h2_v2.0.32.txt
deleted file mode 100644 (file)
index a1597cd..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-  *) mod_http2: update to version 2.0.32
-     The code setting the connection window size was set wrong,
-     preventing `H2WindowSize` to work.
-     Fixed <https://github.com/icing/mod_h2/issues/300>.
-     [Stefan Eissing, Michael Kaufmann]
diff --git a/changes-entries/md_v2.5.1.txt b/changes-entries/md_v2.5.1.txt
deleted file mode 100644 (file)
index 051f2aa..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-  *) mod_md: update to version 2.5.1
-     - Added support for ACME profiles with new directives MDProfile and
-       MDProfileMandatory.
-     - When installing a custom CA file via `MDCACertificateFile`, also set the
-       libcurl option CURLSSLOPT_NO_REVOKE that suppresses complains by Schannel
-       (when curl is linked with it) about missing CRL/OCSP in certificates.
-     - Fixed handling of corrupted httpd.json and added test 300_30 for it.
-       File is removed on error and written again. Fixes #369.
-     - Added explanation in log for how to proceed when md_store.json could not be
-       parsed and prevented the server start.
-     - restored fixed to #336 and #337 which got lost in a sync with Apache svn
-     - Add Issue Name/Uris to certificate information in md-status handler
-     - MDomains with static certificate files have MDRenewMode "manual", unless
-       "always" is configured.
diff --git a/changes-entries/md_v2.5.2.txt b/changes-entries/md_v2.5.2.txt
deleted file mode 100644 (file)
index 1b4922b..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-  *) mod_md: update to version 2.5.2
-     - Fixed TLS-ALPN-01 challenges when multiple `MDPrivateKeys` are specified
-       with EC keys before RSA ones. Fixes #377. [Stefan Eissing]
-     - Fixed missing newlines in the status page output. [Andreas Groth]
diff --git a/changes-entries/pr69443.txt b/changes-entries/pr69443.txt
deleted file mode 100644 (file)
index 97de769..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-  *) mod_proxy_balancer: Fix a regression that caused stickysession keys no
-     longer be recognized if they are provided as query parameter in the URL.
-     PR 69443 [Ruediger Pluem]
diff --git a/changes-entries/pr69579.txt b/changes-entries/pr69579.txt
deleted file mode 100644 (file)
index 0b91a0d..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-  *) scoreboard/mod_http2: record durations of HTTP/2 requests.
-     PR 69579 [Pierre Brochard <pierre.brochard.1982@m4x.org>]
diff --git a/changes-entries/pr69590.txt b/changes-entries/pr69590.txt
deleted file mode 100644 (file)
index ebf3f1a..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-  *) mod_lua: Fix memory handling in LuaOutputFilter. PR 69590.
-     [Guillermo Grandes <guillermo.grandes gmail.com>]
diff --git a/changes-entries/pr69624.txt b/changes-entries/pr69624.txt
deleted file mode 100644 (file)
index 74ee63a..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-  * mod_proxy_http2: revert r1912193 for detecting broken backend connections
-    as this interferes with backend selection who a node is unresponsive.
-    PR69624.
index 58c2574ff40e916790ac8d8ce512f275c996d4db..d5ed9685c54eed61e26a0e046ca9303ca5cdb1fd 100644 (file)
@@ -29,6 +29,7 @@
 #include "apr_network_io.h"
 #include "httpd.h"
 #include "http_config.h"
+#include "apr_optional.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -143,6 +144,15 @@ AP_DECLARE_NONSTD(const char *) ap_set_receive_buffer_size(cmd_parms *cmd,
                                                            void *dummy,
                                                            const char *arg);
 
+#ifdef HAVE_SYSTEMD
+APR_DECLARE_OPTIONAL_FN(int,
+                        ap_find_systemd_socket, (process_rec *, apr_port_t));
+
+APR_DECLARE_OPTIONAL_FN(int,
+                        ap_systemd_listen_fds, (int));
+#endif
+
+
 #define LISTEN_COMMANDS \
 AP_INIT_TAKE1("ListenBacklog", ap_set_listenbacklog, NULL, RSRC_CONF, \
   "Maximum length of the queue of pending connections, as used by listen(2)"), \
index 3d099f86b756ee9b06a8cebe0add1b0a1a47dfc2..7ee06663b602010f6acda87c39ce17aea0238e15 100644 (file)
@@ -23,6 +23,11 @@ APACHE_MODULE(systemd, Systemd support, , , no, [
     AC_MSG_WARN([Your system does not support systemd.])
     enable_systemd="no"
   else
+    AC_CHECK_LIB(selinux, is_selinux_enabled, [
+      AC_DEFINE(HAVE_SELINUX, 1, [Defined if SELinux is supported])
+      APR_ADDTO(MOD_SYSTEMD_LDADD, [-lselinux])
+    ])
+
     APR_ADDTO(MOD_SYSTEMD_LDADD, [$SYSTEMD_LIBS])
   fi
 ])
index c3e7082df1f57ccd065b6234a05105c37a8f6cfa..22482fd6bbcef54beb5fe07aab295ca4eb590225 100644 (file)
@@ -18,6 +18,7 @@
 #include <stdint.h>
 #include <ap_config.h>
 #include "ap_mpm.h"
+#include "ap_listen.h"
 #include <http_core.h>
 #include <httpd.h>
 #include <http_log.h>
 #include "scoreboard.h"
 #include "mpm_common.h"
 
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif
+
 #include "systemd/sd-daemon.h"
 
 #if APR_HAVE_UNISTD_H
@@ -44,6 +49,20 @@ static int systemd_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
     return OK;
 }
 
+#ifdef HAVE_SELINUX
+static void log_selinux_context(void)
+{
+    char *con;
+
+    if (is_selinux_enabled() && getcon(&con) == 0) {
+        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
+                     APLOGNO(10497) "SELinux is enabled; "
+                     "httpd running as context %s", con);
+        freecon(con);
+    }
+}
+#endif
+
 /* Report the service is ready in post_config, which could be during
  * startup or after a reload.  The server could still hit a fatal
  * startup error after this point during ap_run_mpm(), so this is
@@ -51,9 +70,16 @@ static int systemd_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
  * the TCP ports so new connections will not be rejected.  There will
  * always be a possible async failure event simultaneous to the
  * service reporting "ready", so this should be good enough. */
-static int systemd_post_config(apr_pool_t *p, apr_pool_t *plog,
+static int systemd_post_config(apr_pool_t *pconf, apr_pool_t *plog,
                                apr_pool_t *ptemp, server_rec *main_server)
 {
+    if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG)
+        return OK;
+
+#ifdef HAVE_SELINUX
+    log_selinux_context();
+#endif
+
     sd_notify(0, "READY=1\n"
               "STATUS=Configuration loaded.\n");
     return OK;
@@ -96,8 +122,42 @@ static int systemd_monitor(apr_pool_t *p, server_rec *s)
     return DECLINED;
 }
 
+static int ap_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 int ap_systemd_listen_fds(int unset_environment){
+    return sd_listen_fds(unset_environment);
+}
+
 static void systemd_register_hooks(apr_pool_t *p)
 {
+    APR_REGISTER_OPTIONAL_FN(ap_systemd_listen_fds);
+    APR_REGISTER_OPTIONAL_FN(ap_find_systemd_socket);
+
     /* Enable ap_extended_status. */
     ap_hook_pre_config(systemd_pre_config, NULL, NULL, APR_HOOK_LAST);
     /* Signal service is ready. */
index 9577d60269abcedd3d3a586803843bf600c3871f..cb7e59eb34c39e9aad6796f65d14a05f951f4ae0 100644 (file)
@@ -60,9 +60,12 @@ static int ap_listenbacklog;
 static int ap_listencbratio;
 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;
@@ -95,20 +98,6 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
         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, APLOGNO(00069)
-                          "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
@@ -170,21 +159,37 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
     }
 #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 (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_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;
+        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
@@ -278,6 +283,101 @@ static apr_status_t close_listeners_on_exec(void *v)
     return APR_SUCCESS;
 }
 
+#ifdef HAVE_SYSTEMD
+
+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_pcalloc(process->pool, sizeof(ap_listen_rec));
+
+
+    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;
+    APR_OPTIONAL_FN_TYPE(ap_find_systemd_socket) *find_systemd_socket;
+    int fd;
+
+    find_systemd_socket = APR_RETRIEVE_OPTIONAL_FN(ap_find_systemd_socket);
+
+    if (!find_systemd_socket)
+       return "Systemd socket activation is used, but mod_systemd is probably "
+               "not loaded";
+
+    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 = new;
+    }
+    else {
+        last->next = new;
+    }
+
+    return NULL;
+}
+#endif /* HAVE_SYSTEMD */
+
 /* Returns non-zero if socket address SA matches hostname, port and
  * scope_id.  p is used for temporary allocations. */
 static int match_address(const apr_sockaddr_t *sa,
@@ -529,7 +629,7 @@ static int open_listeners(apr_pool_t *pool)
                 }
             }
 #endif
-            if (make_sock(pool, lr) == APR_SUCCESS) {
+            if (make_sock(pool, lr, 1) == APR_SUCCESS) {
                 ++num_open;
             }
             else {
@@ -615,6 +715,9 @@ AP_DECLARE(int) ap_setup_listeners(server_rec *s)
     int num_listeners = 0;
     const char* proto;
     int found;
+#ifdef HAVE_SYSTEMD
+    APR_OPTIONAL_FN_TYPE(ap_systemd_listen_fds) *systemd_listen_fds;
+#endif
 
     for (ls = s; ls; ls = ls->next) {
         proto = ap_get_server_protocol(ls);
@@ -641,8 +744,32 @@ 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 {
+            systemd_listen_fds = APR_RETRIEVE_OPTIONAL_FN(ap_systemd_listen_fds);
+            if (systemd_listen_fds != NULL) {
+                systemd_listen_fds(1);
+            }
+        }        
+    }
+    else
+#endif
+    {
+        if (open_listeners(s->process->pool)) {
+            return 0;
+        }
     }
 
     for (lr = ap_listeners; lr; lr = lr->next) {
@@ -715,24 +842,36 @@ AP_DECLARE(apr_status_t) ap_duplicate_listeners(apr_pool_t *p, server_rec *s,
             char *hostname;
             apr_port_t port;
             apr_sockaddr_t *sa;
-            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;
-            stat = apr_socket_create(&duplr->sd, duplr->bind_addr->family,
-                                     SOCK_STREAM, 0, p);
-            if (stat != APR_SUCCESS) {
-                ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, p, APLOGNO(02640)
-                            "ap_duplicate_listeners: for address %pI, "
-                            "cannot duplicate a new socket!",
-                            duplr->bind_addr);
-                return stat;
+#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);
             }
-            make_sock(p, duplr);
 #if AP_NONBLOCK_WHEN_MULTI_LISTEN
             use_nonblock = (ap_listeners && ap_listeners->next);
             stat = apr_socket_opt_set(duplr->sd, APR_SO_NONBLOCK, use_nonblock);
@@ -851,6 +990,9 @@ AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
     apr_port_t port;
     apr_status_t rv;
     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+#ifdef HAVE_SYSTEMD
+    APR_OPTIONAL_FN_TYPE(ap_systemd_listen_fds) *systemd_listen_fds;
+#endif
 
     if (err != NULL) {
         return err;
@@ -859,6 +1001,16 @@ 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) {
+        systemd_listen_fds = APR_RETRIEVE_OPTIONAL_FN(ap_systemd_listen_fds);
+        if (systemd_listen_fds != NULL) {
+            use_systemd = systemd_listen_fds(0) > 0;
+        } else {
+            use_systemd = 0;
+        }
+    }
+#endif
 
     rv = apr_parse_addr_port(&host, &scope_id, &port, argv[0], cmd->pool);
     if (rv != APR_SUCCESS) {
@@ -894,6 +1046,12 @@ AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
         ap_str_tolower(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,
                           scope_id, NULL, cmd->temp_pool);
 }