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,