From dbdc79f9cd4c6bbfd30626c6674892612e8e5f8a Mon Sep 17 00:00:00 2001 From: Quanah Gibson-Mount Date: Wed, 3 Jun 2009 01:29:14 +0000 Subject: [PATCH] ITS#5836 --- CHANGES | 1 + doc/man/man5/slapd-config.5 | 15 ++++---- doc/man/man5/slapd.conf.5 | 10 +++++- servers/slapd/bconfig.c | 5 ++- servers/slapd/config.c | 1 + servers/slapd/connection.c | 24 +++++++++++-- servers/slapd/daemon.c | 70 +++++++++++++++++++++++++++---------- servers/slapd/proto-slap.h | 3 ++ servers/slapd/result.c | 2 +- 9 files changed, 100 insertions(+), 31 deletions(-) diff --git a/CHANGES b/CHANGES index 042b5d4fc5..dd9bbfe16a 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,7 @@ OpenLDAP 2.4.17 Engineering Fixed liblutil for _GNU_SOURCE (ITS#5464,ITS#5666) Added slapd sasl auxprop support (ITS#6147) Added slapd schema checking tool (ITS#6150) + Added slapd writetimeout keyword (ITS#5836) Fixed slapd assert with closing connections (ITS#6111) Fixed slapd cert validation (ITS#6098) Fixed slapd errno handling (ITS#6037) diff --git a/doc/man/man5/slapd-config.5 b/doc/man/man5/slapd-config.5 index e3e76914ae..06197ed4dd 100644 --- a/doc/man/man5/slapd-config.5 +++ b/doc/man/man5/slapd-config.5 @@ -450,7 +450,9 @@ along with this option. .B olcIdleTimeout: Specify the number of seconds to wait before forcibly closing an idle client connection. A setting of 0 disables this -feature. The default is 0. +feature. The default is 0. You may also want to set the +.B olcWriteTimeout +option. .TP .B olcIndexIntLen: Specify the key length for ordered integer indices. The most significant @@ -753,11 +755,12 @@ The default is 16; the minimum value is 2. Specify the maximum number of threads to use in tool mode. This should not be greater than the number of CPUs in the system. The default is 1. -.\"ucdata-path is obsolete / ignored... -.\".TP -.\".B ucdata-path -.\"Specify the path to the directory containing the Unicode character -.\"tables. The default path is DATADIR/ucdata. +.TP +.B olcWriteTimeout: +Specify the number of seconds to wait before forcibly closing +a connection with an outstanding write. This allows recovery from +various network hang conditions. A setting of 0 disables this +feature. The default is 0. .SH TLS OPTIONS If .B slapd diff --git a/doc/man/man5/slapd.conf.5 b/doc/man/man5/slapd.conf.5 index 5753beba2c..ec888c9f4a 100644 --- a/doc/man/man5/slapd.conf.5 +++ b/doc/man/man5/slapd.conf.5 @@ -472,7 +472,9 @@ along with this option. .B idletimeout Specify the number of seconds to wait before forcibly closing an idle client connection. A idletimeout of 0 disables this -feature. The default is 0. +feature. The default is 0. You may also want to set the +.B writetimeout +option. .TP .B include Read additional configuration information from the given file before @@ -985,6 +987,12 @@ The default is 1. .\".B ucdata-path .\"Specify the path to the directory containing the Unicode character .\"tables. The default path is DATADIR/ucdata. +.TP +.B writetimeout +Specify the number of seconds to wait before forcibly closing +a connection with an outstanding write. This allows recovery from +various network hang conditions. A writetimeout of 0 disables this +feature. The default is 0. .SH TLS OPTIONS If .B slapd diff --git a/servers/slapd/bconfig.c b/servers/slapd/bconfig.c index 37c4ff6f3c..bb59aa6969 100644 --- a/servers/slapd/bconfig.c +++ b/servers/slapd/bconfig.c @@ -714,6 +714,9 @@ static ConfigTable config_back_cf_table[] = { &config_updateref, "( OLcfgDbAt:0.13 NAME 'olcUpdateRef' " "EQUALITY caseIgnoreMatch " "SUP labeledURI )", NULL, NULL }, + { "writetimeout", "timeout", 2, 2, 0, ARG_INT, + &global_writetimeout, "( OLcfgGlAt:88 NAME 'olcWriteTimeout' " + "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL }, { NULL, NULL, 0, 0, 0, ARG_IGNORED, NULL, NULL, NULL, NULL } }; @@ -772,7 +775,7 @@ static ConfigOCs cf_ocs[] = { "olcTLSCACertificatePath $ olcTLSCertificateFile $ " "olcTLSCertificateKeyFile $ olcTLSCipherSuite $ olcTLSCRLCheck $ " "olcTLSRandFile $ olcTLSVerifyClient $ olcTLSDHParamFile $ " - "olcTLSCRLFile $ olcToolThreads $ " + "olcTLSCRLFile $ olcToolThreads $ olcWriteTimeout $ " "olcObjectIdentifier $ olcAttributeTypes $ olcObjectClasses $ " "olcDitContentRules $ olcLdapSyntaxes ) )", Cft_Global }, { "( OLcfgGlOc:2 " diff --git a/servers/slapd/config.c b/servers/slapd/config.c index 1e18315f8c..1f651d1c40 100644 --- a/servers/slapd/config.c +++ b/servers/slapd/config.c @@ -62,6 +62,7 @@ slap_mask_t global_allows = 0; slap_mask_t global_disallows = 0; int global_gentlehup = 0; int global_idletimeout = 0; +int global_writetimeout = 0; char *global_host = NULL; char *global_realm = NULL; char *sasl_host = NULL; diff --git a/servers/slapd/connection.c b/servers/slapd/connection.c index 7cfcd7b927..4e5ccd5fbd 100644 --- a/servers/slapd/connection.c +++ b/servers/slapd/connection.c @@ -222,17 +222,23 @@ int connections_shutdown(void) */ int connections_timeout_idle(time_t now) { - int i = 0; + int i = 0, writers = 0; int connindex; Connection* c; + time_t old; + + old = slapd_get_writetime(); for( c = connection_first( &connindex ); c != NULL; c = connection_next( c, &connindex ) ) { /* Don't timeout a slow-running request or a persistent - * outbound connection */ - if( c->c_n_ops_executing || c->c_conn_state == SLAP_C_CLIENT ) { + * outbound connection. But if it has a writewaiter, see + * if the waiter has been there too long. + */ + if(( c->c_n_ops_executing && !c->c_writewaiter) + || c->c_conn_state == SLAP_C_CLIENT ) { continue; } @@ -241,9 +247,21 @@ int connections_timeout_idle(time_t now) connection_closing( c, "idletimeout" ); connection_close( c ); i++; + continue; + } + if ( c->c_writewaiter ) { + writers = 1; + if( difftime( c->c_activitytime+global_writetimeout, now) < 0 ) { + /* close it */ + connection_closing( c, "writetimeout" ); + connection_close( c ); + i++; + } } } connection_done( c ); + if ( !writers ) + slapd_clr_writetime( old ); return i; } diff --git a/servers/slapd/daemon.c b/servers/slapd/daemon.c index a7cd6d9837..23193d1498 100644 --- a/servers/slapd/daemon.c +++ b/servers/slapd/daemon.c @@ -86,6 +86,8 @@ static ber_socket_t wake_sds[2] ; static int emfile; +static time_t chk_writetime; + static volatile int waking; #ifdef NO_THREADS #define WAKE_LISTENER(w) do { \ @@ -955,6 +957,9 @@ slapd_set_write( ber_socket_t s, int wake ) SLAP_SOCK_SET_WRITE( s ); slap_daemon.sd_nwriters++; } + if (( wake & 2 ) && global_writetimeout ) { + chk_writetime = slap_get_time(); + } ldap_pvt_thread_mutex_unlock( &slap_daemon.sd_mutex ); WAKE_LISTENER(wake); @@ -988,6 +993,25 @@ slapd_set_read( ber_socket_t s, int wake ) WAKE_LISTENER(wake); } +time_t +slapd_get_writetime() +{ + time_t cur; + ldap_pvt_thread_mutex_lock( &slap_daemon.sd_mutex ); + cur = chk_writetime; + ldap_pvt_thread_mutex_unlock( &slap_daemon.sd_mutex ); + return cur; +} + +void +slapd_clr_writetime( time_t old ) +{ + ldap_pvt_thread_mutex_lock( &slap_daemon.sd_mutex ); + if ( chk_writetime == old ) + chk_writetime = 0; + ldap_pvt_thread_mutex_unlock( &slap_daemon.sd_mutex ); +} + static void slapd_close( ber_socket_t s ) { @@ -2035,24 +2059,12 @@ slapd_daemon_task( { int l; time_t last_idle_check = 0; - struct timeval idle; int ebadf = 0; #define SLAPD_IDLE_CHECK_LIMIT 4 if ( global_idletimeout > 0 ) { last_idle_check = slap_get_time(); - /* Set the select timeout. - * Don't just truncate, preserve the fractions of - * seconds to prevent sleeping for zero time. - */ - idle.tv_sec = global_idletimeout / SLAPD_IDLE_CHECK_LIMIT; - idle.tv_usec = global_idletimeout - \ - ( idle.tv_sec * SLAPD_IDLE_CHECK_LIMIT ); - idle.tv_usec *= 1000000 / SLAPD_IDLE_CHECK_LIMIT; - } else { - idle.tv_sec = 0; - idle.tv_usec = 0; } slapd_add( wake_sds[0], 0, NULL ); @@ -2157,14 +2169,34 @@ slapd_daemon_task( now = slap_get_time(); - if ( ( global_idletimeout > 0 ) && - difftime( last_idle_check + - global_idletimeout/SLAPD_IDLE_CHECK_LIMIT, now ) < 0 ) - { - connections_timeout_idle( now ); - last_idle_check = now; + if ( global_idletimeout > 0 || chk_writetime ) { + int check = 0; + /* Set the select timeout. + * Don't just truncate, preserve the fractions of + * seconds to prevent sleeping for zero time. + */ + if ( chk_writetime ) { + tv.tv_sec = global_writetimeout; + tv.tv_usec = global_writetimeout; + if ( difftime( chk_writetime, now ) < 0 ) + check = 2; + } else { + tv.tv_sec = global_idletimeout / SLAPD_IDLE_CHECK_LIMIT; + tv.tv_usec = global_idletimeout - \ + ( tv.tv_sec * SLAPD_IDLE_CHECK_LIMIT ); + tv.tv_usec *= 1000000 / SLAPD_IDLE_CHECK_LIMIT; + if ( difftime( last_idle_check + + global_idletimeout/SLAPD_IDLE_CHECK_LIMIT, now ) < 0 ) + check = 1; + } + if ( check ) { + connections_timeout_idle( now ); + last_idle_check = now; + } + } else { + tv.tv_sec = 0; + tv.tv_usec = 0; } - tv = idle; #ifdef SIGHUP if ( slapd_gentle_shutdown ) { diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h index 982fb8d0ff..a56a5fc011 100644 --- a/servers/slapd/proto-slap.h +++ b/servers/slapd/proto-slap.h @@ -843,6 +843,8 @@ LDAP_SLAPD_F (void) slapd_set_write LDAP_P((ber_socket_t s, int wake)); LDAP_SLAPD_F (void) slapd_clr_write LDAP_P((ber_socket_t s, int wake)); LDAP_SLAPD_F (void) slapd_set_read LDAP_P((ber_socket_t s, int wake)); LDAP_SLAPD_F (int) slapd_clr_read LDAP_P((ber_socket_t s, int wake)); +LDAP_SLAPD_F (void) slapd_clr_writetime LDAP_P((time_t old)); +LDAP_SLAPD_F (time_t) slapd_get_writetime LDAP_P((void)); LDAP_SLAPD_V (volatile sig_atomic_t) slapd_abrupt_shutdown; LDAP_SLAPD_V (volatile sig_atomic_t) slapd_shutdown; @@ -1933,6 +1935,7 @@ LDAP_SLAPD_V (const char) Versionstr[]; LDAP_SLAPD_V (int) global_gentlehup; LDAP_SLAPD_V (int) global_idletimeout; +LDAP_SLAPD_V (int) global_writetimeout; LDAP_SLAPD_V (char *) global_host; LDAP_SLAPD_V (char *) global_realm; LDAP_SLAPD_V (char *) sasl_host; diff --git a/servers/slapd/result.c b/servers/slapd/result.c index 406b7d6347..8bbddad96f 100644 --- a/servers/slapd/result.c +++ b/servers/slapd/result.c @@ -211,7 +211,7 @@ static long send_ldap_ber( /* wait for socket to be write-ready */ ldap_pvt_thread_mutex_lock( &conn->c_write2_mutex ); conn->c_writewaiter = 1; - slapd_set_write( conn->c_sd, 1 ); + slapd_set_write( conn->c_sd, 2 ); ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex ); ldap_pvt_thread_mutex_unlock( &conn->c_mutex ); -- 2.39.5