]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/connection.c
Merge remote branch 'origin/mdb.master'
[openldap] / servers / slapd / connection.c
index 9e2130d6de68917c410a1674d445a1ecbc230e0a..7f9cd8adbf3713228220bd48f65062ce66f985cf 100644 (file)
@@ -1,7 +1,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 1998-2009 The OpenLDAP Foundation.
+ * Copyright 1998-2012 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "lutil.h"
 #include "slap.h"
 
+#ifdef LDAP_CONNECTIONLESS
+#include "../../libraries/liblber/lber-int.h"  /* ber_int_sb_read() */
+#endif
+
 #ifdef LDAP_SLAPI
 #include "slapi/slapi.h"
 #endif
@@ -48,33 +52,19 @@ static ldap_pvt_thread_mutex_t connections_mutex;
 static Connection *connections = NULL;
 
 static ldap_pvt_thread_mutex_t conn_nextid_mutex;
-static unsigned long conn_nextid = 0;
+static unsigned long conn_nextid = SLAPD_SYNC_SYNCCONN_OFFSET;
 
 static const char conn_lost_str[] = "connection lost";
 
-/* structure state (protected by connections_mutex) */
-#define SLAP_C_UNINITIALIZED   0x00    /* MUST BE ZERO (0) */
-#define SLAP_C_UNUSED                  0x01
-#define SLAP_C_USED                            0x02
-#define        SLAP_C_PENDING                  0x03
-
-/* connection state (protected by c_mutex ) */
-#define SLAP_C_INVALID                 0x00    /* MUST BE ZERO (0) */
-#define SLAP_C_INACTIVE                        0x01    /* zero threads */
-#define SLAP_C_ACTIVE                  0x02    /* one or more threads */
-#define SLAP_C_BINDING                 0x03    /* binding */
-#define SLAP_C_CLOSING                 0x04    /* closing */
-#define SLAP_C_CLIENT                  0x05    /* outbound client conn */
-
 const char *
 connection_state2str( int state )
 {
        switch( state ) {
        case SLAP_C_INVALID:    return "!";
        case SLAP_C_INACTIVE:   return "|";
+       case SLAP_C_CLOSING:    return "C";
        case SLAP_C_ACTIVE:             return "";
        case SLAP_C_BINDING:    return "B";
-       case SLAP_C_CLOSING:    return "C";
        case SLAP_C_CLIENT:             return "L";
        }
 
@@ -218,9 +208,12 @@ int connections_shutdown(void)
  */
 int connections_timeout_idle(time_t now)
 {
-       int i = 0;
-       int connindex;
+       int i = 0, writers = 0;
+       ber_socket_t connindex;
        Connection* c;
+       time_t old;
+
+       old = slapd_get_writetime();
 
        for( c = connection_first( &connindex );
                c != NULL;
@@ -235,18 +228,55 @@ int connections_timeout_idle(time_t now)
                        continue;
                }
 
-               if( difftime( c->c_activitytime+global_idletimeout, now) < 0 ) {
+               if( global_idletimeout && 
+                       difftime( c->c_activitytime+global_idletimeout, now) < 0 ) {
                        /* close it */
                        connection_closing( c, "idletimeout" );
                        connection_close( c );
                        i++;
+                       continue;
+               }
+               if ( c->c_writewaiter && global_writetimeout ) {
+                       writers = 1;
+                       if( difftime( c->c_activitytime+global_writetimeout, now) < 0 ) {
+                               /* close it */
+                               connection_closing( c, "writetimeout" );
+                               connection_close( c );
+                               i++;
+                               continue;
+                       }
                }
        }
        connection_done( c );
+       if ( old && !writers )
+               slapd_clr_writetime( old );
 
        return i;
 }
 
+/* Drop all client connections */
+void connections_drop()
+{
+       Connection* c;
+       ber_socket_t connindex;
+
+       for( c = connection_first( &connindex );
+               c != NULL;
+               c = connection_next( c, &connindex ) )
+       {
+               /* Don't close a slow-running request or a persistent
+                * outbound connection.
+                */
+               if(( c->c_n_ops_executing && !c->c_writewaiter)
+                       || c->c_conn_state == SLAP_C_CLIENT ) {
+                       continue;
+               }
+               connection_closing( c, "dropping" );
+               connection_close( c );
+       }
+       connection_done( c );
+}
+
 static Connection* connection_get( ber_socket_t s )
 {
        Connection *c;
@@ -270,12 +300,11 @@ static Connection* connection_get( ber_socket_t s )
                if( c->c_struct_state != SLAP_C_USED ) {
                        /* connection must have been closed due to resched */
 
-                       assert( c->c_conn_state == SLAP_C_INVALID );
-                       assert( c->c_sd == AC_SOCKET_INVALID );
-
                        Debug( LDAP_DEBUG_CONNS,
                                "connection_get(%d): connection not used\n",
                                s, 0, 0 );
+                       assert( c->c_conn_state == SLAP_C_INVALID );
+                       assert( c->c_sd == AC_SOCKET_INVALID );
 
                        ldap_pvt_thread_mutex_unlock( &c->c_mutex );
                        return NULL;
@@ -541,9 +570,9 @@ Connection * connection_init(
        slap_sasl_external( c, ssf, authid );
 
        slapd_add_internal( s, 1 );
-       ldap_pvt_thread_mutex_unlock( &c->c_mutex );
 
        backend_connection_init(c);
+       ldap_pvt_thread_mutex_unlock( &c->c_mutex );
 
        return c;
 }
@@ -680,19 +709,15 @@ connection_destroy( Connection *c )
        }
 }
 
-int connection_state_closing( Connection *c )
+int connection_valid( Connection *c )
 {
        /* c_mutex must be locked by caller */
 
-       int state;
        assert( c != NULL );
-       assert( c->c_struct_state == SLAP_C_USED );
-
-       state = c->c_conn_state;
 
-       assert( state != SLAP_C_INVALID );
-
-       return state == SLAP_C_CLOSING;
+       return c->c_struct_state == SLAP_C_USED &&
+               c->c_conn_state >= SLAP_C_ACTIVE &&
+               c->c_conn_state <= SLAP_C_CLIENT;
 }
 
 static void connection_abandon( Connection *c )
@@ -701,7 +726,6 @@ static void connection_abandon( Connection *c )
 
        Operation *o, *next, op = {0};
        Opheader ohdr = {0};
-       SlapReply rs = {0};
 
        op.o_hdr = &ohdr;
        op.o_conn = c;
@@ -709,6 +733,8 @@ static void connection_abandon( Connection *c )
        op.o_tag = LDAP_REQ_ABANDON;
 
        for ( o = LDAP_STAILQ_FIRST( &c->c_ops ); o; o=next ) {
+               SlapReply rs = {REP_RESULT};
+
                next = LDAP_STAILQ_NEXT( o, o_next );
                op.orn_msgid = o->o_msgid;
                o->o_abandon = 1;
@@ -737,11 +763,39 @@ static void connection_abandon( Connection *c )
        }
 }
 
+static void
+connection_wake_writers( Connection *c )
+{
+       /* wake write blocked operations */
+       ldap_pvt_thread_mutex_lock( &c->c_write1_mutex );
+       if ( c->c_writers > 0 ) {
+               c->c_writers = -c->c_writers;
+               ldap_pvt_thread_cond_broadcast( &c->c_write1_cv );
+               ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex );
+               if ( c->c_writewaiter ) {
+                       ldap_pvt_thread_mutex_lock( &c->c_write2_mutex );
+                       ldap_pvt_thread_cond_signal( &c->c_write2_cv );
+                       slapd_clr_write( c->c_sd, 1 );
+                       ldap_pvt_thread_mutex_unlock( &c->c_write2_mutex );
+               }
+               ldap_pvt_thread_mutex_lock( &c->c_write1_mutex );
+               while ( c->c_writers ) {
+                       ldap_pvt_thread_cond_wait( &c->c_write1_cv, &c->c_write1_mutex );
+               }
+               ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex );
+       } else {
+               ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex );
+               slapd_clr_write( c->c_sd, 1 );
+       }
+}
+
 void connection_closing( Connection *c, const char *why )
 {
        assert( connections != NULL );
        assert( c != NULL );
-       assert( c->c_struct_state == SLAP_C_USED );
+
+       if ( c->c_struct_state != SLAP_C_USED ) return;
+
        assert( c->c_conn_state != SLAP_C_INVALID );
 
        /* c_mutex must be locked by caller */
@@ -761,26 +815,7 @@ void connection_closing( Connection *c, const char *why )
                connection_abandon( c );
 
                /* wake write blocked operations */
-               ldap_pvt_thread_mutex_lock( &c->c_write1_mutex );
-               if ( c->c_writers > 0 ) {
-                       c->c_writers = -c->c_writers;
-                       ldap_pvt_thread_cond_broadcast( &c->c_write1_cv );
-                       ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex );
-                       if ( c->c_writewaiter ) {
-                               ldap_pvt_thread_mutex_lock( &c->c_write2_mutex );
-                               ldap_pvt_thread_cond_signal( &c->c_write2_cv );
-                               slapd_clr_write( c->c_sd, 1 );
-                               ldap_pvt_thread_mutex_unlock( &c->c_write2_mutex );
-                       }
-                       ldap_pvt_thread_mutex_lock( &c->c_write1_mutex );
-                       while ( c->c_writers ) {
-                               ldap_pvt_thread_cond_wait( &c->c_write1_cv, &c->c_write1_mutex );
-                       }
-                       ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex );
-               } else {
-                       ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex );
-                       slapd_clr_write( c->c_sd, 1 );
-               }
+               connection_wake_writers( c );
 
        } else if( why == NULL && c->c_close_reason == conn_lost_str ) {
                /* Client closed connection after doing Unbind. */
@@ -793,7 +828,9 @@ connection_close( Connection *c )
 {
        assert( connections != NULL );
        assert( c != NULL );
-       assert( c->c_struct_state == SLAP_C_USED );
+
+       if ( c->c_struct_state != SLAP_C_USED ) return;
+
        assert( c->c_conn_state == SLAP_C_CLOSING );
 
        /* NOTE: c_mutex should be locked by caller */
@@ -827,6 +864,17 @@ unsigned long connections_nextid(void)
        return id;
 }
 
+/*
+ * Loop through the connections:
+ *
+ *     for (c = connection_first(&i); c; c = connection_next(c, &i)) ...;
+ *     connection_done(c);
+ *
+ * 'i' is the cursor, initialized by connection_first().
+ * 'c_mutex' is locked in the returned connection.  The functions must
+ * be passed the previous return value so they can unlock it again.
+ */
+
 Connection* connection_first( ber_socket_t *index )
 {
        assert( connections != NULL );
@@ -843,6 +891,7 @@ Connection* connection_first( ber_socket_t *index )
        return connection_next(NULL, index);
 }
 
+/* Next connection in loop, see connection_first() */
 Connection* connection_next( Connection *c, ber_socket_t *index )
 {
        assert( connections != NULL );
@@ -891,6 +940,7 @@ Connection* connection_next( Connection *c, ber_socket_t *index )
        return c;
 }
 
+/* End connection loop, see connection_first() */
 void connection_done( Connection *c )
 {
        assert( connections != NULL );
@@ -1006,7 +1056,7 @@ conn_counter_init( Operation *op, void *ctx )
 static void *
 connection_operation( void *ctx, void *arg_v )
 {
-       int rc = LDAP_OTHER;
+       int rc = LDAP_OTHER, cancel;
        Operation *op = arg_v;
        SlapReply rs = {REP_RESULT};
        ber_tag_t tag = op->o_tag;
@@ -1110,22 +1160,32 @@ operations_error:
                INCR_OP_COMPLETED( opidx );
        }
 
-       if ( op->o_cancel == SLAP_CANCEL_REQ ) {
-               if ( rc == SLAPD_ABANDON ) {
-                       op->o_cancel = SLAP_CANCEL_ACK;
-               } else {
-                       op->o_cancel = LDAP_TOO_LATE;
+       ldap_pvt_thread_mutex_lock( &conn->c_mutex );
+
+       if ( opidx == SLAP_OP_BIND && conn->c_conn_state == SLAP_C_BINDING )
+               conn->c_conn_state = SLAP_C_ACTIVE;
+
+       cancel = op->o_cancel;
+       if ( cancel != SLAP_CANCEL_NONE && cancel != SLAP_CANCEL_DONE ) {
+               if ( cancel == SLAP_CANCEL_REQ ) {
+                       op->o_cancel = rc == SLAPD_ABANDON
+                               ? SLAP_CANCEL_ACK : LDAP_TOO_LATE;
                }
-       }
 
-       while ( op->o_cancel != SLAP_CANCEL_NONE &&
-               op->o_cancel != SLAP_CANCEL_DONE )
-       {
-               ldap_pvt_thread_yield();
+               do {
+                       /* Fake a cond_wait with thread_yield, then
+                        * verify the result properly mutex-protected.
+                        */
+                       ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
+                       do {
+                               ldap_pvt_thread_yield();
+                       } while ( (cancel = op->o_cancel) != SLAP_CANCEL_NONE
+                                       && cancel != SLAP_CANCEL_DONE );
+                       ldap_pvt_thread_mutex_lock( &conn->c_mutex );
+               } while ( (cancel = op->o_cancel) != SLAP_CANCEL_NONE
+                               && cancel != SLAP_CANCEL_DONE );
        }
 
-       ldap_pvt_thread_mutex_lock( &conn->c_mutex );
-
        ber_set_option( op->o_ber, LBER_OPT_BER_MEMCTX, &memctx_null );
 
        LDAP_STAILQ_REMOVE( &conn->c_ops, op, Operation, o_next);
@@ -1244,6 +1304,11 @@ int connection_read_activate( ber_socket_t s )
        if ( rc )
                return rc;
 
+       /* Don't let blocked writers block a pause request */
+       if ( connections[s].c_writewaiter &&
+               ldap_pvt_thread_pool_pausing( &connection_pool ))
+               connection_wake_writers( &connections[s] );
+
        rc = ldap_pvt_thread_pool_submit( &connection_pool,
                connection_read_thread, (void *)(long)s );
 
@@ -1256,24 +1321,6 @@ int connection_read_activate( ber_socket_t s )
        return rc;
 }
 
-void
-connection_hangup( ber_socket_t s )
-{
-       Connection *c;
-
-       c = connection_get( s );
-       if ( c ) {
-               if ( c->c_conn_state == SLAP_C_CLIENT ) {
-                       connection_return( c );
-                       connection_read_activate( s );
-               } else {
-                       connection_closing( c, "connection lost" );
-                       connection_close( c );
-                       connection_return( c );
-               }
-       }
-}
-
 static int
 connection_read( ber_socket_t s, conn_readinfo *cri )
 {
@@ -1356,6 +1403,11 @@ connection_read( ber_socket_t s, conn_readinfo *cri )
                            c->c_connid, (int) s, c->c_tls_ssf, c->c_ssf, 0 );
                        slap_sasl_external( c, c->c_tls_ssf, &authid );
                        if ( authid.bv_val ) free( authid.bv_val );
+               } else if ( rc == 1 && ber_sockbuf_ctrl( c->c_sb,
+                       LBER_SB_OPT_NEEDS_WRITE, NULL )) {      /* need to retry */
+                       slapd_set_write( s, 1 );
+                       connection_return( c );
+                       return 0;
                }
 
                /* if success and data is ready, fall thru to data input loop */
@@ -1460,12 +1512,20 @@ connection_input( Connection *conn , conn_readinfo *cri )
 #ifdef LDAP_CONNECTIONLESS
        if ( conn->c_is_udp ) {
                char peername[sizeof("IP=255.255.255.255:65336")];
+               const char *peeraddr_string = NULL;
 
                len = ber_int_sb_read(conn->c_sb, &peeraddr, sizeof(struct sockaddr));
                if (len != sizeof(struct sockaddr)) return 1;
 
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+               char addr[INET_ADDRSTRLEN];
+               peeraddr_string = inet_ntop( AF_INET, &peeraddr.sa_in_addr.sin_addr,
+                          addr, sizeof(addr) );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+               peeraddr_string = inet_ntoa( peeraddr.sa_in_addr.sin_addr );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
                sprintf( peername, "IP=%s:%d",
-                       inet_ntoa( peeraddr.sa_in_addr.sin_addr ),
+                        peeraddr_string,
                        (unsigned) ntohs( peeraddr.sa_in_addr.sin_port ) );
                Statslog( LDAP_DEBUG_STATS,
                        "conn=%lu UDP request from %s (%s) accepted.\n",
@@ -1511,8 +1571,8 @@ connection_input( Connection *conn , conn_readinfo *cri )
 #ifdef LDAP_CONNECTIONLESS
        if( conn->c_is_udp ) {
                if( tag == LBER_OCTETSTRING ) {
-                       ber_get_stringa( ber, &cdn );
-                       tag = ber_peek_tag(ber, &len);
+                       if ( (tag = ber_get_stringa( ber, &cdn )) != LBER_ERROR )
+                               tag = ber_peek_tag( ber, &len );
                }
                if( tag != LDAP_REQ_ABANDON && tag != LDAP_REQ_SEARCH ) {
                        Debug( LDAP_DEBUG_ANY, "invalid req for UDP 0x%lx\n", tag, 0, 0 );
@@ -1530,6 +1590,9 @@ connection_input( Connection *conn , conn_readinfo *cri )
        ctx = cri->ctx;
        op = slap_op_alloc( ber, msgid, tag, conn->c_n_ops_received++, ctx );
 
+       Debug( LDAP_DEBUG_TRACE, "op tag 0x%lx, time %ld\n", tag,
+               (long) op->o_time, 0);
+
        op->o_conn = conn;
        /* clear state if the connection is being reused from inactive */
        if ( conn->c_conn_state == SLAP_C_INACTIVE ) {
@@ -1715,8 +1778,6 @@ static int connection_bind_cleanup_cb( Operation *op, SlapReply *rs )
 static int connection_bind_cb( Operation *op, SlapReply *rs )
 {
        ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
-       if ( op->o_conn->c_conn_state == SLAP_C_BINDING )
-               op->o_conn->c_conn_state = SLAP_C_ACTIVE;
        op->o_conn->c_sasl_bind_in_progress =
                ( rs->sr_err == LDAP_SASL_BIND_IN_PROGRESS );
 
@@ -1844,8 +1905,6 @@ int connection_write(ber_socket_t s)
 
        assert( connections != NULL );
 
-       slapd_clr_write( s, 0 );
-
        c = connection_get( s );
        if( c == NULL ) {
                Debug( LDAP_DEBUG_ANY,
@@ -1854,12 +1913,24 @@ int connection_write(ber_socket_t s)
                return -1;
        }
 
+       slapd_clr_write( s, 0 );
+
+#ifdef HAVE_TLS
+       if ( c->c_is_tls && c->c_needs_tls_accept ) {
+               connection_return( c );
+               connection_read_activate( s );
+               return 0;
+       }
+#endif
+
        c->c_n_write++;
 
        Debug( LDAP_DEBUG_TRACE,
                "connection_write(%d): waking output for id=%lu\n",
                s, c->c_connid, 0 );
+       ldap_pvt_thread_mutex_lock( &c->c_write2_mutex );
        ldap_pvt_thread_cond_signal( &c->c_write2_cv );
+       ldap_pvt_thread_mutex_unlock( &c->c_write2_mutex );
 
        if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_READ, NULL ) ) {
                slapd_set_read( s, 1 );
@@ -1967,6 +2038,8 @@ connection_fake_init2(
        conn->c_send_ldap_result = slap_send_ldap_result;
        conn->c_send_search_entry = slap_send_search_entry;
        conn->c_send_search_reference = slap_send_search_reference;
+       conn->c_send_ldap_extended = slap_send_ldap_extended;
+       conn->c_send_ldap_intermediate = slap_send_ldap_intermediate;
        conn->c_listener = (Listener *)&dummy_list;
        conn->c_peer_domain = slap_empty_bv;
        conn->c_peer_name = slap_empty_bv;