]> git.sur5r.net Git - openldap/blobdiff - libraries/libldap/abandon.c
ITS#6262
[openldap] / libraries / libldap / abandon.c
index 24880ab4f9c566e6067a84db1f5a836f944636da..d8cb33128dd6631d808b85d83cbd042dc42dba72 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 1998-2004 The OpenLDAP Foundation.
+ * Copyright 1998-2010 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 /* Portions  Copyright (c) 1990 Regents of the University of Michigan.
  * All rights reserved.
  */
-/* Portions Copyright (C) The Internet Society (1997).
- * ASN.1 fragments are from RFC 2251; see RFC for full legal notices.
- */
-
-/*
- * An abandon request looks like this:
- *     AbandonRequest ::= MessageID
- */
 
 #include "portable.h"
 
 
 #include "ldap-int.h"
 
-static int do_abandon LDAP_P((
+/*
+ * An abandon request looks like this:
+ *             AbandonRequest ::= [APPLICATION 16] MessageID
+ * and has no response.  (Source: RFC 4511)
+ */
+#include "lutil.h"
+
+static int
+do_abandon(
        LDAP *ld,
        ber_int_t origid,
        ber_int_t msgid,
        LDAPControl **sctrls,
-       LDAPControl **cctrls));
+       int sendabandon );
 
 /*
  * ldap_abandon_ext - perform an ldap extended abandon operation.
@@ -66,24 +66,24 @@ ldap_abandon_ext(
        LDAPControl **sctrls,
        LDAPControl **cctrls )
 {
-       int rc;
-#ifdef NEW_LOGGING
-       LDAP_LOG ( OPERATION, ARGS, "ldap_abandon_ext %d\n", msgid, 0, 0 );
-#else
+       int     rc;
+
        Debug( LDAP_DEBUG_TRACE, "ldap_abandon_ext %d\n", msgid, 0, 0 );
-#endif
 
        /* check client controls */
 #ifdef LDAP_R_COMPILE
        ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
 #endif
+
        rc = ldap_int_client_controls( ld, cctrls );
-       if( rc == LDAP_SUCCESS )
-               rc = do_abandon( ld, msgid, msgid, sctrls, cctrls );
+       if ( rc == LDAP_SUCCESS ) {
+               rc = do_abandon( ld, msgid, msgid, sctrls, 1 );
+       }
 
 #ifdef LDAP_R_COMPILE
        ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
 #endif
+
        return rc;
 }
 
@@ -102,48 +102,67 @@ ldap_abandon_ext(
 int
 ldap_abandon( LDAP *ld, int msgid )
 {
-#ifdef NEW_LOGGING
-       LDAP_LOG ( OPERATION, ARGS, "ldap_abandon %d\n", msgid, 0, 0 );
-#else
        Debug( LDAP_DEBUG_TRACE, "ldap_abandon %d\n", msgid, 0, 0 );
-#endif
        return ldap_abandon_ext( ld, msgid, NULL, NULL ) == LDAP_SUCCESS
                ? 0 : -1;
 }
 
 
+int
+ldap_pvt_discard(
+       LDAP *ld,
+       ber_int_t msgid )
+{
+       int     rc;
+
+#ifdef LDAP_R_COMPILE
+       ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
+#endif
+
+       rc = do_abandon( ld, msgid, msgid, NULL, 0 );
+
+#ifdef LDAP_R_COMPILE
+       ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
+#endif
+
+       return rc;
+}
+
 static int
 do_abandon(
        LDAP *ld,
        ber_int_t origid,
        ber_int_t msgid,
        LDAPControl **sctrls,
-       LDAPControl **cctrls)
+       int sendabandon )
 {
        BerElement      *ber;
-       int             i, err, sendabandon;
-       ber_int_t *old_abandon;
+       int             i, err;
        Sockbuf         *sb;
        LDAPRequest     *lr;
 
-#ifdef NEW_LOGGING
-       LDAP_LOG ( OPERATION, ARGS, "do_abandon %d, msgid %d\n", origid, msgid, 0 );
-#else
        Debug( LDAP_DEBUG_TRACE, "do_abandon origid %d, msgid %d\n",
                origid, msgid, 0 );
-#endif
-
-       sendabandon = 1;
 
        /* find the request that we are abandoning */
-       for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
-               if ( lr->lr_msgid == msgid ) {  /* this message */
+start_again:;
+       lr = ld->ld_requests;
+       while ( lr != NULL ) {
+               /* this message */
+               if ( lr->lr_msgid == msgid ) {
                        break;
                }
-               if ( lr->lr_origid == msgid ) {/* child:  abandon it */
-                       (void) do_abandon( ld,
-                               msgid, lr->lr_msgid, sctrls, cctrls );
+
+               /* child: abandon it */
+               if ( lr->lr_origid == msgid && !lr->lr_abandoned ) {
+                       (void)do_abandon( ld, lr->lr_origid, lr->lr_msgid,
+                               sctrls, sendabandon );
+
+                       /* restart, as lr may now be dangling... */
+                       goto start_again;
                }
+
+               lr = lr->lr_next;
        }
 
        if ( lr != NULL ) {
@@ -158,9 +177,9 @@ do_abandon(
                }
        }
 
-/* ldap_msgdelete locks the res_mutex. Give up the req_mutex
- * while we're in there.
- */
+       /* ldap_msgdelete locks the res_mutex. Give up the req_mutex
       * while we're in there.
       */
 #ifdef LDAP_R_COMPILE
        ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
 #endif
@@ -173,47 +192,60 @@ do_abandon(
                return LDAP_SUCCESS;
        }
 
+       /* fetch again the request that we are abandoning */
+       if ( lr != NULL ) {
+               for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
+                       /* this message */
+                       if ( lr->lr_msgid == msgid ) {
+                               break;
+                       }
+               }
+       }
+
        err = 0;
        if ( sendabandon ) {
-               if( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, NULL ) == -1 ) {
+               if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, NULL ) == -1 ) {
                        /* not connected */
                        err = -1;
                        ld->ld_errno = LDAP_SERVER_DOWN;
 
-               } else if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
-                       /* BER element alocation failed */
+               } else if ( ( ber = ldap_alloc_ber_with_options( ld ) ) == NULL ) {
+                       /* BER element allocation failed */
                        err = -1;
                        ld->ld_errno = LDAP_NO_MEMORY;
 
                } else {
-       /*
-        * We already have the mutex in LDAP_R_COMPILE, so
-        * don't try to get it again.
-        *              LDAP_NEXT_MSGID(ld, i);
-        */
+                       /*
+                        * We already have the mutex in LDAP_R_COMPILE, so
+                        * don't try to get it again.
+                        *              LDAP_NEXT_MSGID(ld, i);
+                        */
+
                        i = ++(ld)->ld_msgid;
 #ifdef LDAP_CONNECTIONLESS
                        if ( LDAP_IS_UDP(ld) ) {
-                           err = ber_write( ber, ld->ld_options.ldo_peer,
-                               sizeof(struct sockaddr), 0);
+                               struct sockaddr sa = {0};
+                               /* dummy, filled with ldo_peer in request.c */
+                               err = ber_write( ber, &sa, sizeof(sa), 0 );
                        }
                        if ( LDAP_IS_UDP(ld) && ld->ld_options.ldo_version ==
-                               LDAP_VERSION2) {
-                           char *dn = ld->ld_options.ldo_cldapdn;
-                           if (!dn) dn = "";
-                           err = ber_printf( ber, "{isti",  /* '}' */
-                               i, dn,
-                               LDAP_REQ_ABANDON, msgid );
+                               LDAP_VERSION2 )
+                       {
+                               char *dn = ld->ld_options.ldo_cldapdn;
+                               if (!dn) dn = "";
+                               err = ber_printf( ber, "{isti",  /* '}' */
+                                       i, dn,
+                                       LDAP_REQ_ABANDON, msgid );
                        } else
 #endif
                        {
-                           /* create a message to send */
-                           err = ber_printf( ber, "{iti",  /* '}' */
-                               i,
-                               LDAP_REQ_ABANDON, msgid );
+                               /* create a message to send */
+                               err = ber_printf( ber, "{iti",  /* '}' */
+                                       i,
+                                       LDAP_REQ_ABANDON, msgid );
                        }
 
-                       if( err == -1 ) {
+                       if ( err == -1 ) {
                                /* encoding error */
                                ld->ld_errno = LDAP_ENCODING_ERROR;
 
@@ -228,7 +260,7 @@ do_abandon(
                                        /* close '{' */
                                        err = ber_printf( ber, /*{*/ "N}" );
 
-                                       if( err == -1 ) {
+                                       if ( err == -1 ) {
                                                /* encoding error */
                                                ld->ld_errno = LDAP_ENCODING_ERROR;
                                        }
@@ -241,12 +273,13 @@ do_abandon(
                        } else {
                                /* send the message */
                                if ( lr != NULL ) {
+                                       assert( lr->lr_conn != NULL );
                                        sb = lr->lr_conn->lconn_sb;
                                } else {
                                        sb = ld->ld_sb;
                                }
 
-                               if ( ber_flush( sb, ber, 1 ) != 0 ) {
+                               if ( ber_flush2( sb, ber, LBER_FLUSH_FREE_ALWAYS ) != 0 ) {
                                        ld->ld_errno = LDAP_SERVER_DOWN;
                                        err = -1;
                                } else {
@@ -260,34 +293,176 @@ do_abandon(
                if ( sendabandon || lr->lr_status == LDAP_REQST_WRITING ) {
                        ldap_free_connection( ld, lr->lr_conn, 0, 1 );
                }
+
                if ( origid == msgid ) {
                        ldap_free_request( ld, lr );
+
+               } else {
+                       lr->lr_abandoned = 1;
                }
        }
 
+#ifdef LDAP_R_COMPILE
+       /* ld_abandoned is actually protected by the ld_res_mutex;
+        * give up the ld_req_mutex and get the other */
+       ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
+       ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
+#endif
+
+       /* use bisection */
        i = 0;
-       if ( ld->ld_abandoned != NULL ) {
-               for ( ; ld->ld_abandoned[i] != -1; i++ )
-                       ;       /* NULL */
+       if ( ld->ld_nabandoned == 0 ||
+               ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &i ) == 0 )
+       {
+               ldap_int_bisect_insert( &ld->ld_abandoned, &ld->ld_nabandoned, msgid, i );
        }
 
-       old_abandon = ld->ld_abandoned;
+       if ( err != -1 ) {
+               ld->ld_errno = LDAP_SUCCESS;
+       }
+
+#ifdef LDAP_R_COMPILE
+       ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
+       ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
+#endif
+       return( ld->ld_errno );
+}
+
+/*
+ * ldap_int_bisect_find
+ *
+ * args:
+ *     v:      array of length n (in)
+ *     n:      length of array v (in)
+ *     id:     value to look for (in)
+ *     idxp:   pointer to location of value/insert point
+ *
+ * return:
+ *     0:      not found
+ *     1:      found
+ *     -1:     error
+ */
+int
+ldap_int_bisect_find( ber_int_t *v, ber_len_t n, ber_int_t id, int *idxp )
+{
+       int             begin,
+                       end,
+                       rc = 0;
+
+       assert( id >= 0 );
+
+       begin = 0;
+       end = n - 1;
+
+               if ( n <= 0 || id < v[ begin ] ) {
+                       *idxp = 0;
+
+               } else if ( id > v[ end ] ) {
+                       *idxp = n;
+
+               } else {
+                       int             pos;
+                       ber_int_t       curid;
+       
+                       do {
+                               pos = (begin + end)/2;
+                               curid = v[ pos ];
+       
+                               if ( id < curid ) {
+                                       end = pos - 1;
+       
+                               } else if ( id > curid ) {
+                                       begin = ++pos;
+       
+                               } else {
+                                       /* already abandoned? */
+                                       rc = 1;
+                                       break;
+                               }
+                       } while ( end >= begin );
+       
+                       *idxp = pos;
+               }
+
+       return rc;
+}
 
-       ld->ld_abandoned = (ber_int_t *) LDAP_REALLOC( (char *)
-               ld->ld_abandoned, (i + 2) * sizeof(ber_int_t) );
-               
-       if ( ld->ld_abandoned == NULL ) {
-               ld->ld_abandoned = old_abandon;
-               ld->ld_errno = LDAP_NO_MEMORY;
-               return( ld->ld_errno );
+/*
+ * ldap_int_bisect_insert
+ *
+ * args:
+ *     vp:     pointer to array of length *np (in/out)
+ *     np:     pointer to length of array *vp (in/out)
+ *     id:     value to insert (in)
+ *     idx:    location of insert point (as computed by ldap_int_bisect_find())
+ *
+ * return:
+ *     0:      inserted
+ *     -1:     error
+ */
+int
+ldap_int_bisect_insert( ber_int_t **vp, ber_len_t *np, int id, int idx )
+{
+       ber_int_t       *v;
+       ber_len_t       n;
+       int             i;
+
+       assert( vp != NULL );
+       assert( np != NULL );
+       assert( idx >= 0 );
+       assert( (unsigned) idx <= *np );
+
+       n = *np;
+
+       v = ber_memrealloc( *vp, sizeof( ber_int_t ) * ( n + 1 ) );
+       if ( v == NULL ) {
+               return -1;
        }
+       *vp = v;
 
-       ld->ld_abandoned[i] = msgid;
-       ld->ld_abandoned[i + 1] = -1;
+       for ( i = n; i > idx; i-- ) {
+               v[ i ] = v[ i - 1 ];
+       }
+       v[ idx ] = id;
+       ++(*np);
 
-       if ( err != -1 ) {
-               ld->ld_errno = LDAP_SUCCESS;
+       return 0;
+}
+
+/*
+ * ldap_int_bisect_delete
+ *
+ * args:
+ *     vp:     pointer to array of length *np (in/out)
+ *     np:     pointer to length of array *vp (in/out)
+ *     id:     value to delete (in)
+ *     idx:    location of value to delete (as computed by ldap_int_bisect_find())
+ *
+ * return:
+ *     0:      deleted
+ */
+int
+ldap_int_bisect_delete( ber_int_t **vp, ber_len_t *np, int id, int idx )
+{
+       ber_int_t       *v;
+       ber_len_t       i, n;
+
+       assert( vp != NULL );
+       assert( np != NULL );
+       assert( idx >= 0 );
+       assert( (unsigned) idx < *np );
+
+       v = *vp;
+
+       assert( v[ idx ] == id );
+
+       --(*np);
+       n = *np;
+
+       for ( i = idx; i < n; i++ ) {
+               v[ i ] = v[ i + 1 ];
        }
 
-       return( ld->ld_errno );
+       return 0;
 }
+