Hi,

attached patch adds new "FreeListen" directive. The difference between "Listen" and "FreeListen" is that "FreeListen" sets the IP_FREEBIND socket option on platforms where this is available.

It is therefore possible to start the server even when particular IP address set in the "FreeListen" is not configured yet.

This is needed for httpd startup with systemd when one wants to use particular IP address to bind. There is no way how to start httpd after the IP address has been configured in systemd and according to systemd developers, the applications should become more robust to handle network changes like that. The full reasoning is explained here [1].

The patch needs latest APR-trunk currently, but it could be rewritten to set IP_FREEBIND directly instead of using APR API (We use that way for REUSEADDR socket option).

Do you think FreeListen is good name for this feature, or would you name/implement it differently?

[1] https://www.freedesktop.org/wiki/Software/systemd/NetworkTarget/

Regards,
Jan Kaluza
Index: docs/manual/mod/mpm_common.xml
===================================================================
--- docs/manual/mod/mpm_common.xml	(revision 1733461)
+++ docs/manual/mod/mpm_common.xml	(working copy)
@@ -104,6 +104,31 @@
 </directivesynopsis>
 
 <directivesynopsis>
+<name>FreeListen</name>
+<description>IP addresses and ports that the server
+listens to</description>
+<syntax>FreeListen [<var>IP-address</var>:]<var>portnumber</var> [<var>protocol</var>]</syntax>
+<contextlist><context>server config</context></contextlist>
+<modulelist><module>event</module><module>worker</module>
+<module>prefork</module><module>mpm_winnt</module>
+<module>mpm_netware</module><module>mpmt_os2</module>
+</modulelist>
+
+<usage>
+    <p>The <directive>FreeListen</directive> directive has the same meaning
+    as the <directive>Listen</directive> directive. The difference between
+    these two directives is that <directive>FreeListen</directive> directive
+    sets the <code>IP_FREEBIND</code> socket option on platforms where this
+    option is available.</p>
+
+    <p>It is therefore possible to start the server even when particular IP
+    address set in the <directive>FreeListen</directive> directive is not
+    configured yet.</p>
+</usage>
+<seealso><directive module="mpm_common">Listen</directive></seealso>
+</directivesynopsis>
+
+<directivesynopsis>
 <name>GracefulShutdownTimeout</name>
 <description>Specify a timeout after which a gracefully shutdown server
 will exit.</description>
@@ -241,6 +266,7 @@
     </note>
 
 </usage>
+<seealso><directive module="mpm_common">FreeListen</directive></seealso>
 <seealso><a href="../dns-caveats.html">DNS Issues</a></seealso>
 <seealso><a href="../bind.html">Setting which addresses and ports Apache HTTP Server
     uses</a></seealso>
Index: include/ap_listen.h
===================================================================
--- include/ap_listen.h	(revision 1733461)
+++ include/ap_listen.h	(working copy)
@@ -71,6 +71,10 @@
     const char* protocol;
 
     ap_slave_t *slave;
+    /**
+     * Whether use APR_SO_FREEBIND.
+     */
+    int free_bind;
 };
 
 /**
@@ -148,8 +152,10 @@
   "Maximum length of the queue of pending connections, as used by listen(2)"), \
 AP_INIT_TAKE1("ListenCoresBucketsRatio", ap_set_listencbratio, NULL, RSRC_CONF, \
   "Ratio between the number of CPU cores (online) and the number of listeners buckets"), \
-AP_INIT_TAKE_ARGV("Listen", ap_set_listener, NULL, RSRC_CONF, \
+AP_INIT_TAKE_ARGV("Listen", ap_set_listener, (void *) 0, RSRC_CONF, \
   "A port number or a numeric IP address and a port number, and an optional protocol"), \
+AP_INIT_TAKE_ARGV("FreeListen", ap_set_listener, (void *) 1, RSRC_CONF, \
+  "A port number or a numeric IP address and a port number, and an optional protocol"), \
 AP_INIT_TAKE1("SendBufferSize", ap_set_send_buffer_size, NULL, RSRC_CONF, \
   "Send buffer size in bytes"), \
 AP_INIT_TAKE1("ReceiveBufferSize", ap_set_receive_buffer_size, NULL, \
Index: server/listen.c
===================================================================
--- server/listen.c	(revision 1733461)
+++ server/listen.c	(working copy)
@@ -68,7 +68,8 @@
 #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, int do_bind_listen)
+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;
@@ -162,6 +163,21 @@
     }
 #endif
 
+   if (server->free_bind) {
+#if defined(APR_SO_FREEBIND)
+        stat = apr_socket_opt_set(s, APR_SO_FREEBIND, one);
+        if (stat != APR_SUCCESS) {
+            ap_log_perror(APLOG_MARK, APLOG_WARNING, stat, p, APLOGNO(00071)
+                          "make_sock: failed to set set IP_FREEBIND socket option");
+            return stat;
+        }
+#else
+        ap_log_perror(APLOG_MARK, APLOG_WARNING, stat, p, APLOGNO(00071)
+                        "make_sock: FreeListen not supported by the system");
+        return APR_ENOTIMPL;
+#endif
+    }
+
     if (do_bind_listen) {
 #if APR_HAVE_IPV6
         if (server->bind_addr->family == APR_INET6) {
@@ -348,6 +364,7 @@
     rec = apr_palloc(process->pool, sizeof(ap_listen_rec));
     rec->active = 0;
     rec->next = 0;
+    rec->free_bind = 0;
 
     rv = apr_os_sock_make(&rec->sd, &si, process->pool);
     if (rv != APR_SUCCESS) {
@@ -406,7 +423,7 @@
 
 static const char *alloc_listener(process_rec *process, char *addr,
                                   apr_port_t port, const char* proto,
-                                  void *slave)
+                                  void *slave, int free_bind)
 {
     ap_listen_rec **walk, *last;
     apr_status_t status;
@@ -470,6 +487,7 @@
         new->active = 0;
         new->next = 0;
         new->bind_addr = sa;
+        new->free_bind = free_bind;
         new->protocol = apr_pstrdup(process->pool, proto);
 
         /* Go to the next sockaddr. */
@@ -827,6 +845,7 @@
                 duplr = apr_palloc(p, sizeof(ap_listen_rec));
                 duplr->slave = NULL;
                 duplr->protocol = apr_pstrdup(p, lr->protocol);
+                duplr->free_bind = lr->free_bind;
                 hostname = apr_pstrdup(p, lr->bind_addr->hostname);
                 port = lr->bind_addr->port;
                 apr_sockaddr_info_get(&sa, hostname, APR_UNSPEC, port, 0, p);
@@ -1011,7 +1030,8 @@
     }
 #endif
 
-    return alloc_listener(cmd->server->process, host, port, proto, NULL);
+    return alloc_listener(cmd->server->process, host, port, proto, NULL,
+                          cmd->cmd->cmd_data != NULL);
 }
 
 AP_DECLARE_NONSTD(const char *) ap_set_listenbacklog(cmd_parms *cmd,

Reply via email to