]> granicus.if.org Git - apache/commitdiff
Add "AcceptErrorsNonFatal" directive
authorEric Covener <covener@apache.org>
Thu, 4 Jan 2018 15:10:45 +0000 (15:10 +0000)
committerEric Covener <covener@apache.org>
Thu, 4 Jan 2018 15:10:45 +0000 (15:10 +0000)
This tweaks accept() failure processing by having ap_unixd_accept
pass more errors up, and having the MPM's check against a macro
to see if they are in a whitelist of non ENETDOWN/EMFILE kind
of potential process-wide errors.

Default behavior is still to exit.

edit: MMN bump in 1820099.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1820098 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
docs/manual/mod/mpm_common.xml
include/ap_listen.h
os/unix/unixd.c
server/listen.c
server/mpm/event/event.c
server/mpm/motorz/motorz.c
server/mpm/prefork/prefork.c
server/mpm/worker/worker.c

diff --git a/CHANGES b/CHANGES
index 518f1f04f3c81573e5698d5b4b36a2a18cc27ff4..5e90aefc4f9abd8fefb5cfeb35b64ac223a06a3b 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,11 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.1
 
+
+  *) core: Add "AcceptErrorsNonFatal" to allow ECONNREFUSED, ECONNABORTED, and
+     ECONNRESET during the client accept() to not trigger graceful shutdown of
+     the child process.  [Eric Covener]
+
   *) mod_md v1.1.7:
      - MDMustStaple was unable to create the necessary OpenSSL OBJ identifier on some platforms, 
        possibly because this fails if the OID is already configured in ```openssl.cnf```, see
index 2978b20794ec714f378e0b16f6b2fe304359e03f..d1e09eeed5129a3a81747c3a497db9b10240eec1 100644 (file)
@@ -873,4 +873,35 @@ client connections</description>
 </usage>
 </directivesynopsis>
 
+<directivesynopsis>
+<name>AcceptErrorsNonFatal</name>
+<description>Treat some errors accepting a new connection as non-fatal
+to the httpd process.</description>
+<syntax>AcceptErrorsNonFatal ON</syntax>
+<default>OFF (ECONNREFUSED, ECONNABORTED, ECONNRESET cause the process to 
+         exit)</default>
+<contextlist><context>server config</context></contextlist>
+<modulelist><module>event</module><module>worker</module>
+<module>prefork</module>
+</modulelist>
+<compatibility>2.5.1 and later</compatibility>
+
+<usage>
+    <p>The <directive>AcceptErrorsNonFatal</directive> alters the servers
+    behavior under some rare errors that may occur while accepting a new 
+    client connection.  By default, the child process handling a request
+    will gracefully exit when nearly any socket error occurs during the
+    accept() system call.  This is to ensure a potentially unhealthy
+    child process does not try to take on more new connections.</p>
+
+    <p>With <directive>AcceptErrorsNonFatal</directive> set to "ON",
+    the process will <em>not</em> begin to exit if the accept() error is
+    ECONNREFUSED, ECONNABORTED, or ECONNRESET.</p>
+    <note>Some third-party firwewall software components may inject errors
+    into accept() processing, using return codes not specified by the 
+    operating system</note>
+</usage>
+</directivesynopsis>
+
 </modulesynopsis>
index 58c2574ff40e916790ac8d8ce512f275c996d4db..9cbaaa491097451b2da932939c1cdc8c426b2b41 100644 (file)
@@ -71,6 +71,12 @@ struct ap_listen_rec {
     const char* protocol;
 
     ap_slave_t *slave;
+
+    /**
+     * Allow the accept_func to return a wider set of return codes
+     */
+    int use_specific_errors;
+
 };
 
 /**
@@ -79,6 +85,9 @@ struct ap_listen_rec {
 AP_DECLARE_DATA extern ap_listen_rec *ap_listeners;
 AP_DECLARE_DATA extern int ap_num_listen_buckets;
 AP_DECLARE_DATA extern int ap_have_so_reuseport;
+AP_DECLARE_DATA extern int ap_accept_errors_nonfatal;
+
+AP_DECLARE(int) ap_accept_error_is_nonfatal(apr_status_t rv);
 
 /**
  * Setup all of the defaults for the listener list
@@ -143,6 +152,10 @@ AP_DECLARE_NONSTD(const char *) ap_set_receive_buffer_size(cmd_parms *cmd,
                                                            void *dummy,
                                                            const char *arg);
 
+AP_DECLARE_NONSTD(const char *) ap_set_accept_errors_nonfatal(cmd_parms *cmd,
+                                                           void *dummy,
+                                                           int flag);
+
 #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)"), \
@@ -153,8 +166,9 @@ AP_INIT_TAKE_ARGV("Listen", ap_set_listener, NULL, RSRC_CONF, \
 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, \
-              RSRC_CONF, "Receive buffer size in bytes")
-
+              RSRC_CONF, "Receive buffer size in bytes"), \
+AP_INIT_FLAG("AcceptErrorsNonFatal", ap_set_accept_errors_nonfatal, NULL, \
+              RSRC_CONF, "Some accept() errors are not fatal to the process")
 #ifdef __cplusplus
 }
 #endif
index 43645f09daf803d1d735f73a5b293417c7a2d2e5..b939355289504067e7c48dd2f471d6b844910182 100644 (file)
@@ -320,6 +320,12 @@ AP_DECLARE(apr_status_t) ap_unixd_accept(void **accepted, ap_listen_rec *lr,
     if (APR_STATUS_IS_EINTR(status)) {
         return status;
     }
+  
+    /* Let the caller handle slightly more varied return values */
+    if (lr->use_specific_errors && ap_accept_error_is_nonfatal(status)) { 
+        return status;
+    }
+
     /* Our old behaviour here was to continue after accept()
      * errors.  But this leads us into lots of troubles
      * because most of the errors are quite fatal.  For
index 2d1db6dc291f863fdc66e052fcbbdd529cb1ab33..56308e21cc06839fb845eb22fa65145269df6453 100644 (file)
@@ -59,6 +59,9 @@ static ap_listen_rec **ap_listen_buckets;
  */
 AP_DECLARE_DATA int ap_have_so_reuseport = -1;
 
+/* Whether some accept() errors are non-fatal to the process */
+AP_DECLARE_DATA int ap_accept_errors_nonfatal = 0;
+
 static ap_listen_rec *old_listeners;
 static int ap_listenbacklog;
 static int ap_listencbratio;
@@ -793,6 +796,7 @@ AP_DECLARE(int) ap_setup_listeners(server_rec *s)
     }
 
     for (lr = ap_listeners; lr; lr = lr->next) {
+        lr->use_specific_errors = ap_accept_errors_nonfatal;
         num_listeners++;
         found = 0;
         for (ls = s; ls && !found; ls = ls->next) {
@@ -1004,6 +1008,14 @@ AP_DECLARE(void) ap_listen_pre_config(void)
     }
 }
 
+AP_DECLARE(int) ap_accept_error_is_nonfatal(apr_status_t status)
+{
+
+   return APR_STATUS_IS_ECONNREFUSED(status)
+             || APR_STATUS_IS_ECONNABORTED(status)
+             || APR_STATUS_IS_ECONNRESET(status);
+}
+
 AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
                                                 int argc, char *const argv[])
 {
@@ -1128,6 +1140,14 @@ AP_DECLARE_NONSTD(const char *) ap_set_send_buffer_size(cmd_parms *cmd,
     return NULL;
 }
 
+AP_DECLARE_NONSTD(const char *) ap_set_accept_errors_nonfatal(cmd_parms *cmd,
+                                                           void *dummy,
+                                                           int flag)
+{
+   ap_accept_errors_nonfatal = flag;
+   return NULL;
+}
+
 AP_DECLARE_NONSTD(const char *) ap_set_receive_buffer_size(cmd_parms *cmd,
                                                            void *dummy,
                                                            const char *arg)
index 8d33442b8c61a7fb25afe66a92e54820dbfe93fe..6ccc2084454e392c2306ff43d72980b122a0d4f7 100644 (file)
@@ -2068,6 +2068,10 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
                         resource_shortage = 1;
                         signal_threads(ST_GRACEFUL);
                     }
+                    else if (ap_accept_error_is_nonfatal(rc)) { 
+                        ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, ap_server_conf, 
+                                     "accept() on client socket failed");
+                    }
 
                     if (csd != NULL) {
                         conns_this_child--;
index d8d95222952df33ad952bfa6448bd58b632e44bc..be8f3020e4d3847598fa73d6e8382cb3142c047e 100644 (file)
@@ -202,6 +202,11 @@ static apr_status_t motorz_io_accept(motorz_core_t *mz, motorz_sb_t *sb)
                      "motorz_io_accept failed");
         clean_child_exit(APEXIT_CHILDSICK);
     }
+    else if (ap_accept_error_is_nonfatal(rv)) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf,
+                "accept() on client socket failed");
+    }
+
     else {
         motorz_conn_t *scon = apr_pcalloc(ptrans, sizeof(motorz_conn_t));
         scon->pool = ptrans;
index 649cc97d63b7096777a80d607f2014e3f2324978..e20c516c7ed95839cbfa3d316bea8483efede3f6 100644 (file)
@@ -600,7 +600,12 @@ static void child_main(int child_num_arg, int child_bucket)
             /* resource shortage or should-not-occur occurred */
             clean_child_exit(APEXIT_CHILDSICK);
         }
-        else if (status != APR_SUCCESS) {
+        else if (ap_accept_error_is_nonfatal(rc)) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, ap_server_conf,
+                    "accept() on client socket failed");
+        }
+
+        if (status != APR_SUCCESS) {
             continue;
         }
 
index c87c003c7e5c7912b6c3bbaee41e028c9e089ecb..9a971ebd5765cb7d6f71d1e25e673950c8c684d7 100644 (file)
@@ -699,6 +699,10 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy)
                 resource_shortage = 1;
                 signal_threads(ST_GRACEFUL);
             }
+            else if (ap_accept_error_is_nonfatal(rv)) {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf,
+                        "accept() on client socket failed");
+            }
             if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(my_bucket->mutex)))
                 != APR_SUCCESS) {