diff --git a/CHANGES b/CHANGES index 042b5d4fc5978218f6acf71d09378f8b06950888..dd9bbfe16adbb7e96b0642bedfe0fed4b6fd4bbe 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 e3e76914aed1b9904d378d44781779a2eca4d959..06197ed4dd9a4e17582bfe563ad6cadd9a9e64e4 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: <integer> 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: <integer> 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 <path> -.\"Specify the path to the directory containing the Unicode character -.\"tables. The default path is DATADIR/ucdata. +.TP +.B olcWriteTimeout: <integer> +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 5753beba2cc920fc09dcc22a2742b1d0354cb6a6..ec888c9f4af4d01eeca8123cd5fc973fead950e8 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 <integer> 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 <filename> Read additional configuration information from the given file before @@ -985,6 +987,12 @@ The default is 1. .\".B ucdata-path <path> .\"Specify the path to the directory containing the Unicode character .\"tables. The default path is DATADIR/ucdata. +.TP +.B writetimeout <integer> +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 37c4ff6f3cbb5cb3089a62cc4b77a2572a3b5aff..bb59aa6969d7c4a495d6d12a47081a2f191ba89d 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 1e18315f8c62a3dbb3181f65d487d8de2cfbd472..1f651d1c400efc91b87f91ceed044a2b8f531e55 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 7cfcd7b927cfad80ea4e4ba20fb26fbe1f2a5bb7..4e5ccd5fbdd1890216de452c14855885e2837a3b 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 a7cd6d983736bfe3bd6c22b2cbfd6130d827ad26..23193d149846e0efe5b42c7b78ccecaf57010f47 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 982fb8d0ff59b1602a6ae2737fb2fea7e9a5d5ef..a56a5fc011466ae84f9236b86d51e606361adf85 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 406b7d6347ec54cc9d3dc7ce98f1e04bb210b495..8bbddad96f2e0af28c724dc3491b9ea4072e4686 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 );