]> git.sur5r.net Git - openldap/commitdiff
ITS#6625 concurrency patch from Doug Leavitt @ Oracle .com
authorHoward Chu <hyc@openldap.org>
Fri, 22 Oct 2010 21:45:48 +0000 (21:45 +0000)
committerHoward Chu <hyc@openldap.org>
Fri, 22 Oct 2010 21:45:48 +0000 (21:45 +0000)
19 files changed:
doc/drafts/draft-zeilenga-ldap-c-api-concurrency-xx.txt [new file with mode: 0644]
doc/man/man3/ldap_dup.3 [new file with mode: 0644]
doc/man/man3/ldap_dup.3.links [new file with mode: 0644]
doc/man/man3/ldap_get_option.3
include/ldap.h
include/ldap_int_thread.h
libraries/libldap/abandon.c
libraries/libldap/cyrus.c
libraries/libldap/init.c
libraries/libldap/ldap-int.h
libraries/libldap/open.c
libraries/libldap/options.c
libraries/libldap/request.c
libraries/libldap/result.c
libraries/libldap/unbind.c
tests/progs/Makefile.in
tests/progs/slapd-mtread.c [new file with mode: 0644]
tests/scripts/defines.sh
tests/scripts/test060-mt-hot [new file with mode: 0755]

diff --git a/doc/drafts/draft-zeilenga-ldap-c-api-concurrency-xx.txt b/doc/drafts/draft-zeilenga-ldap-c-api-concurrency-xx.txt
new file mode 100644 (file)
index 0000000..271556a
--- /dev/null
@@ -0,0 +1,714 @@
+INTERNET-DRAFT                                        Kurt D. Zeilenga
+Intended Category: Standards Track                 OpenLDAP Foundation
+Extends: draft-ietf-ldapext-ldap-c-api-03.txt
+Expires: 28 March 2000
+                                                     28 September 1999
+
+                    LDAP C API Concurrency Extensions
+              <draft-zeilenga-ldap-c-api-concurrency-00.txt>
+
+1.   Status of this Memo
+
+  This document is an Internet-Draft and is in full conformance with all
+  provisions of Section 10 of RFC2026.
+
+  This draft document will be submitted to the RFC Editor as a Standards
+  Track document. Distribution of this memo is unlimited.  Technical
+  discussion of this document will take place on the IETF LDAP Extension
+  Working Group mailing list <ietf-ldapext@netscape.com>.  Please send
+  editorial comments directly to the author <Kurt@OpenLDAP.org>.
+
+  Internet-Drafts are working documents of the Internet Engineering Task
+  Force (IETF), its areas, and its working groups.  Note that other
+  groups may also distribute working documents as Internet-Drafts.
+
+  Internet-Drafts are draft documents valid for a maximum of six months
+  and may be updated, replaced, or obsoleted by other documents at any
+  time.  It is inappropriate to use Internet-Drafts as reference
+  material or to cite them other than as "work in progress."
+
+  The list of current Internet-Drafts can be accessed at
+  http://www.ietf.org/ietf/1id-abstracts.txt
+
+  The list of Internet-Draft Shadow Directories can be accessed at
+  http://www.ietf.org/shadow.html.
+
+  Copyright 1999, The Internet Society.  All Rights Reserved.
+
+  Please see the Copyright section near the end of this document for
+  more information.
+
+2.   Abstract
+
+  This document defines extensions to the LDAP C API to support use in
+  concurrent execution environments.  The document describes and defines
+
+Zeilenga                                                        [Page 1]
+
+INTERNET-DRAFT      LDAP C API Concurrency Extensions  28 September 1999
+
+  requirements for multiple concurrency levels: thread safe, session
+  thread safe, and operation thread safe.
+
+  The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+  "SHOULD", "SHOULD NOT", "RECOMMENDED",  and "MAY" in this document are
+  to be interpreted as described in RFC 2119 [KEYW].
+
+3.   Introduction
+
+  This document extends the LDAP C API [CAPI] specification to support
+  use in concurrent execution environments.  The extensions add powerful
+  concurrent processing capabilities to the simple to use CAPI.  This
+  document provides an overview of different levels of concurrent
+  execution support and offers a number of CAPI "features" to provide
+  capabilities at these levels.
+
+  The remainder of this section describes three levels of concurrent
+  execution: thread safe, session thread safe, operation thread safe
+  APIs.
+
+3.1. Thread Safe
+
+  An implementation which allows applications to safely execute in
+  concurrent execution environments where the application provides
+  necessary synchronization to ensure serialization of CAPI usage is
+  considered to be "thread safe."   Applications may execute non-CAPI
+  calls in concurrent execution contexts when using thread safe
+  implementations.
+
+3.2. Session Thread Safe
+
+  A "thread safe" implementation which allows CAPI calls associated with
+  different LDAP sessions to proceed asychronously is considered to be
+  "session thread safe."
+
+3.3. Operation Thread Safe
+
+  A "session thread safe" implementation which allows CAPI calls
+  associated with different LDAP operations to proceed asychronously is
+  considered to be "operation thread safe".
+
+4.   Basic Thread Safe Feature
+
+Zeilenga                                                        [Page 2]
+
+INTERNET-DRAFT      LDAP C API Concurrency Extensions  28 September 1999
+
+  This section details requirements for the thread safe CAPI feature.
+  Implementations fulfilling these requirements are said to support the
+  LDAP_API_FEATURE_THREAD_SAFE feature and SHOULD advertise this support
+  as detailed below.  This feature SHOULD be provided by
+  implementations.
+
+  Implementations of this feature MUST implement the LDAP error handling
+  extension [ERRNO].
+
+  Implementations of this feature MUST allow non-CAPI calls to proceed
+  asynchronously.
+
+  Implementations of this feature MUST NOT use any non-thread safe call
+  or mechanism provided by C environment or operating system.  An
+  example of non-reentrant calls is the UNIX strtok() function.  Example
+  of a non-reentrant mechanism is global (i.e.: non-thread specific)
+  errno.
+
+5.   Session Thread Safe Feature
+
+  This section details requirements for the session thread safe CAPI
+  feature.  Implementations fulfilling these requirements are said to
+  support the LDAP_API_FEATURE_SESSION_THREAD_SAFE feature and SHOULD
+  advertise this support as detailed below.  This feature is
+  RECOMMENDED.
+
+5.1. Prerequisite Features
+
+  Implementations providing this feature MUST provide and advertise both
+  LDAP_API_FEATURE_CONTEXT_SPECIFIC_ERRNO [ERRNO] and
+  LDAP_API_FEATURE_THREAD_SAFE.
+
+5.2. Atomic Session Handles
+
+  Implementations providing this feature SHOULD ensure that operations
+  upon a given session handle are atomic.  Implementations which provide
+  atomic session handles SHOULD advertise the feature
+  LDAP_API_FEATURE_ATOMIC_SESSION_HANDLES.
+
+5.3. Concurrency Requirements
+
+  Implementations providing this feature MUST not restrict CAPI calls
+  acting upon a given LDAP session to a particular execution context.
+  Applications MAY use a session handle on any thread.  Applications
+
+Zeilenga                                                        [Page 3]
+
+INTERNET-DRAFT      LDAP C API Concurrency Extensions  28 September 1999
+
+  MUST NOT assume that operations upon a session are atomic.
+
+  Implementations providing this feature MUST allow CAPI calls acting
+  upon different LDAP sessions to safely proceed asynchronously.
+
+  Implementations providing this feature MUST allow CAPI calls not
+  acting upon an LDAP session to safely proceed asynchronously.
+
+6.   Operation Thread Safe Feature
+
+  This section details requirements for the operation thread safe CAPI
+  feature based upon a duplicate session handles mechanism.
+
+  Implementations fulfilling these requirements are said to support the
+  LDAP_API_FEATURE_DUPLICATE_SESSION_HANDLES feature and SHOULD
+  advertise this support as detailed below.  This feature is OPTIONAL.
+
+6.1. Prerequisite Features
+
+  Implementations of this feature MUST provide and advertise
+  LDAP_API_FEATURE_CONTEXT_SPECIFIC_ERRNO [ERRNO],
+  LDAP_API_FEATURE_THREAD_SAFE, LDAP_API_FEATURE_SESSION_THREAD_SAFE,
+  and LDAP_API_FEATURE_ATOMIC_SESSION_HANDLES.
+
+6.2. Duplicated Session Handles
+
+  Implementations of this feature MUST support duplicated session
+  handles.
+
+  As defined in CAPI, a session handle refers to an LDAP session
+  encompassing connections with one or more servers, associated message
+  results, a set of properties (options), and state information.  This
+  feature provides a mechanism for a handle to be duplicated.  A session
+  handle and its duplicates are considered siblings.  Each sibling
+  session handle refers to the same LDAP session and message results.
+  Some properties and state are specific to a handle and others shared
+  between siblings as detailed below.
+
+  CAPI calls made on a handle are atomic.  Calls made on sibling (or
+  other) handles MAY proceed asynchronously.
+
+  Session handles are duplicated using ldap_dup() and destroyed using
+  ldap_destroy().  Use of duplicated session handles with CAPI calls
+  have the following semantics detailed in the sections below.
+
+Zeilenga                                                        [Page 4]
+
+INTERNET-DRAFT      LDAP C API Concurrency Extensions  28 September 1999
+
+6.2.1.    Creating and Destroying duplicated sessions
+
+  Implementations of this feature are required to provide two new
+  routines:      LDAP *ldap_dup( ld );      int ldap_destroy( ld );
+
+  Parameters are:      ld      The session handle
+
+  The ldap_dup() function returns a duplicate of a session handle.  The
+  returned session handle may be used concurrently with the original
+  session handle as described below. ldap_dup returns NULL if it is not
+  able to duplicate the session handle and sets LDAP_OPT_ERROR_NUMBER
+  and ldap_errno indicating the nature of the failure.
+
+  The ldap_destroy() function destroys the session handle.  If the
+  session handle has no siblings, ldap_destroy behaves exactly like
+  ldap_unbind.  If the session handle has siblings, the resources
+  assocated with the handle are released and the siblings remain valid.
+  ldap_destroy() returns LDAP_SUCCESS or an error number indicating the
+  nature of failure.  Regardless of returned value, the handle SHOULD be
+  considered invalid and MUST not be used in subsequent calls.  Attempts
+  to use a destroyed session handle MUST NOT result in
+  LDAP_INVALID_SESSION error being reported.  Implementations SHOULD
+  report LDAP_PARAM_ERROR in such cases.
+
+6.2.2.    ldap_unbind and siblings
+
+  When ldap_unbind() is called on a session handle with siblings, the
+  siblings become invalid.  The siblings must be destroyed using
+  ldap_destroy().  All attempts to obtain the siblings'
+  LDAP_OPT_ERROR_NUMBER will return LDAP_INVALID_SESSION.  Any use other
+  than ldap_destroy() or reading LDAP_OPT_ERROR_NUMBER will fail with an
+  LDAP_INVALID_SESSION error being reported.
+
+6.2.3.    ldap_result()
+
+  Message queues are shared between siblings.  Results of operations on
+  a duplicated session handles are accessible to all sibling session
+  handles.
+
+  Applications desiring results associated with a specific operation
+  SHOULD provide the appropriate msgid to ldap_result().  Applications
+  SHOULD avoid calling ldap_result() with LDAP_RES_ANY as such may
+  "steal" and return results which an operation on a sibling requires to
+  complete.
+
+Zeilenga                                                        [Page 5]
+
+INTERNET-DRAFT      LDAP C API Concurrency Extensions  28 September 1999
+
+6.2.4.    Session Options
+
+  The following CAPI options access values shared between siblings:
+
+       LDAP_OPT_API_INFO      LDAP_OPT_DESC      LDAP_OPT_REFERRALS
+       LDAP_OPT_PROTOCOL_VERSION      LDAP_OPT_API_FEATURE_INFO
+       LDAP_OPT_HOST_NAME
+
+  The following CAPI options access values specific to a sibling:
+
+       LDAP_OPT_DEREF      LDAP_OPT_SIZELIMIT      LDAP_OPT_TIMELIMIT
+       LDAP_OPT_RESTART      LDAP_OPT_CLIENT_CONTROLS
+       LDAP_OPT_SERVER_CONTROLS      LDAP_OPT_ERROR_NUMBER
+       LDAP_OPT_ERROR_STRING      LDAP_OPT_MATCHED_DN
+
+6.2.4.1.  LDAP_OPT_SESSION_REFCNT
+
+  In addition, implementations MUST provide the READ-ONLY, shared
+  LDAP_OPT_SESSION_REFCNT option.  LDAP_OPT_SESSION_REFCNT returns the
+  reference count associated with the supplied session handle argument.
+  The session handle argument is required. The outvalue argument should
+  be a pointer to an integer.  Example use:
+
+      int refcount(LDAP *ld) {
+
+      #ifdef LDAP_OPT_SESSION_REFCNT
+
+        if(ld != NULL) {
+          int refcnt, rc;
+          rc = ldap_get_option(ld,
+              LDAP_OPT_SESSION_REFCNT, &refcnt);
+
+          if(rc == LDAP_OPT_SUCCESS) {
+            return refcnt;
+          }
+        }
+
+      #endif
+
+        return -1;
+      }
+
+7.   Advertising Features
+
+  This document REQUIRES that supported features with the name in the
+  form LDAP_API_FEATURE_x be advertised to consumers of the CAPI as
+
+Zeilenga                                                        [Page 6]
+
+INTERNET-DRAFT      LDAP C API Concurrency Extensions  28 September 1999
+
+  follows:
+
+       SHOULD provide the macro LDAP_API_FEATURE_x with the value
+       of 1000 + revision number of this draft (i.e.: 1000+0 for
+       this 0 revision of the draft).
+
+       MUST provide the CAPI extension "x" when returning API
+       information upon LDAP_OPT_API_INFO option access, and
+
+       MUST provide feature info for "x" via LDAP_OPT_FEATURE_INFO
+       option mechanism.  The feature version provided MUST      match
+  the value LDAP_API_FEATURE_x macro
+
+  where x is replaced appropriately.
+
+  As implementations may not provide macros for all features,
+  applications SHOULD use LDAP_OPT_API_INFO to determine which features
+  are provided by a given implementation.
+
+8.   Changes to the C API specification
+
+8.1. New Symbols
+
+  This extension introduces the following macros:
+
+       LDAP_API_FEATURE_ATOMIC_SESSION_HANDLES
+       LDAP_API_FEATURE_DUPLICATE_SESSION_HANDLES
+       LDAP_API_FEATURE_SESSION_THREAD_SAFE
+       LDAP_API_FEATURE_THREAD_SAFE
+       LDAP_API_FEATURE_OPERATION_THREAD_SAFE      LDAP_INVALID_SESSION
+       LDAP_OPT_SESSION_REFCNT
+
+  This extension introduces these new functions:
+
+       ldap_destroy()      ldap_dup()
+
+  This extension introduces no new typedefs nor structure names.
+
+8.2. Duplicated Session Handles
+
+  This extension introduces duplicated session handles and requirements
+  for handling duplicated session handles.  Semantics of non-duplicated
+  session handles are not affected by this introduction.  However, the
+  semantics of calls upon duplicate session handles differs as described
+  in the extension.
+
+Zeilenga                                                        [Page 7]
+
+INTERNET-DRAFT      LDAP C API Concurrency Extensions  28 September 1999
+
+9.   Security Considerations
+
+  None taken, none given.
+
+10.  Copyright
+
+  Copyright 1999, The Internet Society.  All Rights Reserved.
+
+  This document and translations of it may be copied and furnished to
+  others, and derivative works that comment on or otherwise explain it
+  or assist in its implementation may be prepared, copied, published and
+  distributed, in whole or in part, without restriction of any kind,
+  provided that the above copyright notice and this paragraph are
+  included on all such copies and derivative works.  However, this
+  document itself may not be modified in any way, such as by removing
+  the copyright notice or references to the Internet Society or other
+  Internet organizations, except as needed for the  purpose of
+  developing Internet standards in which case the procedures for
+  copyrights defined in the Internet Standards process must be followed,
+  or as required to translate it into languages other than English.
+
+  The limited permissions granted above are perpetual and will not be
+  revoked by the Internet Society or its successors or assigns.
+
+  This document and the information contained herein is provided on an
+  "AS IS" basis and THE AUTHORS, THE INTERNET SOCIETY, AND THE INTERNET
+  ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
+  INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+  INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+  WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+11.  Bibliography
+
+  [CAPI]  M. Smith, T. Howes, A. Herron, M. Wahl, A. Anantha,      "The
+  C LDAP Application Program Interface", INTERNET-DRAFT,      <draft-
+  ietf-ldapext-ldap-c-api-03.txt> + LDAPext discussions,      June 1999.
+
+  [ERRNO] K. Zeilenga, "LDAP C API Error Reporting Extension",
+       INTERNET-DRAFT, <draft-zeilenga-ldap-c-api-errno-00.txt>,
+       June 1999.
+
+  [KEYW]  S. Bradner, "Key words for use in RFCs to Indicate
+       Requirement Levels", RFC 2119, March 1997.
+
+Zeilenga                                                        [Page 8]
+
+INTERNET-DRAFT      LDAP C API Concurrency Extensions  28 September 1999
+
+  [LDAP]  M. Wahl, T. Howes, S. Kille, "Lightweight Directory
+       Access Protocol (v3)", RFC 2251, December 1997.
+
+13.  Author's Address
+
+  Kurt D. Zeilenga
+  OpenLDAP Foundation
+  <Kurt@OpenLDAP.org>
+
+  This document expires on 28 March 2000.
+
+Zeilenga                                                        [Page 9]
+
+    ---------------------------------------------------------------------
+
+INTERNET-DRAFT                                        Kurt D. Zeilenga
+Intended Category: Standards Track                 OpenLDAP Foundation
+Extends: draft-ietf-ldapext-ldap-c-api-03.txt
+Expires: 28 March 2000
+                                                     28 September 1999
+
+                   LDAP C API Error Reporting Extension
+                 <draft-zeilenga-ldap-c-api-errno-00.txt>
+
+1.   Status of this Memo
+
+  This document is an Internet-Draft and is in full conformance with all
+  provisions of Section 10 of RFC2026.
+
+  This draft document will be submitted to the RFC Editor as a Standards
+  Track document. Distribution of this memo is unlimited.  Technical
+  discussion of this document will take place on the IETF LDAP Extension
+  Working Group mailing list <ietf-ldapext@netscape.com>.  Please send
+  editorial comments directly to the author <Kurt@OpenLDAP.org>.
+
+  Internet-Drafts are working documents of the Internet Engineering Task
+  Force (IETF), its areas, and its working groups.  Note that other
+  groups may also distribute working documents as Internet-Drafts.
+
+  Internet-Drafts are draft documents valid for a maximum of six months
+  and may be updated, replaced, or obsoleted by other documents at any
+  time.  It is inappropriate to use Internet-Drafts as reference
+  material or to cite them other than as ``work in progress.''
+
+  The list of current Internet-Drafts can be accessed at
+  http://www.ietf.org/ietf/1id-abstracts.txt
+
+  The list of Internet-Draft Shadow Directories can be accessed at
+  http://www.ietf.org/shadow.html.
+
+  Copyright 1999, The Internet Society.  All Rights Reserved.
+
+  Please see the Copyright section near the end of this document for
+  more information.
+
+2.   Abstract
+
+  This document defines a manatory extension to the LDAP C API to
+  provide error reporting for all API calls.  The mechanism is
+  nonintrusive and can, optionally, support concurrent execution
+  environments.
+
+Zeilenga                                                        [Page 1]
+
+INTERNET-DRAFT    LDAP C API Error Reporting Extension 28 September 1999
+
+  The key words ``MUST'', ``MUST NOT'', ``REQUIRED'', ``SHALL'', ``SHALL
+  NOT'', ``SHOULD'', ``SHOULD NOT'', ``RECOMMENDED'',  and ``MAY'' in
+  this document are to be interpreted as described in RFC 2119 [KEYW].
+
+3.   Background and Intent of Use
+
+  The LDAP [LDAP] C API [CAPI] provides an interface which (due to
+  legacy compatibiity issues) does not provide a consistent mechanism
+  for reporting errors.  A large number of the calls within the
+  specification have no mechanism to indicate the nature of a failure.
+  The usefulness of a CAPI without a consistent, easy to use, error
+  reporting mechanism is limited.
+
+  This document defines an mandatory extension to the CAPI.  All
+  implementations of the CAPI MUST provide this extension.
+
+  The extension details additional requirements for error reporting.
+  Implementations MUST fulfill all other CAPI error reporting
+  requirements.
+
+4.   Error Handling Extension
+
+  This extension provides a mechanism that applications MAY use to
+  obtain an LDAP error number indicating the nature of the failure
+  associated with the last failed CAPI call.
+
+  Implementations MUST provide access to an LDAP error number (CAPI,
+  Section 9) resulting from the last failed CAPI call via the symbol
+  ldap_errno.  The last failed CAPI call may be within the global
+  context or within the current execution context.
+
+  The ldap_errno MUST evaluate to a modifiable lvalue that has type
+  'int', the value of which is set to a LDAP error number.  It is
+  unspecified whether ldap_errno is a macro or an identifier declared
+  with external linkage.  If a macro definition is suppressed in order
+  to access an actual object, or a program defines an identifier with
+  the name ldap_errno, the behavior is undefined.
+
+  Applications MUST access ldap_errno within the same concurrent
+  execution context, commonly a thread, in which the failure occurred.
+  The value of ldap_errno is LDAP_SUCCESS (0) if no API failure has
+  occurred within the supported context and the user has not assigned a
+  value within the supported context.
+
+Zeilenga                                                        [Page 2]
+
+INTERNET-DRAFT    LDAP C API Error Reporting Extension 28 September 1999
+
+  Implementations SHALL NOT update the ldap_errno value upon successful
+  CAPI call completion.
+
+  Implementations providing a current execution context specific
+  ldap_errno MUST advertise the feature LDAP_API_CONTEXT_SPECIFIC_ERRNO
+  as described in Section 6.  Implementation of
+  LDAP_API_CONTEXT_SPECIFIC_ERRNO is RECOMMENDED.
+
+4.1. Reporting Server Errors
+
+  It is not a CAPI failure for a server to return an error number.
+  Implementations SHALL NOT assign error results returned by servers to
+  ldap_errno.
+
+4.2. Implementation Specific Reporting
+
+  The CAPI specification stated that the caller may obtain an indication
+  of failure of certain calls (see listed below) using implementation
+  specific and/or operating system specific requirements.
+  Implementations are NOT REQUIRED to support any implementation
+  specific and/or operating system mechanism for ANY call detailed by
+  the CAPI specification or its extensions.
+
+  Affected calls include ldap_init(), ldap_open(), and ber_*().
+
+4.3. Example
+
+  The following is an example showing how an application may obtain the
+  error information resulting from a failed CAPI calls:
+
+    int msgid;
+    LDAP *ld = ldap_init("localhost", 389);
+
+    if(ld == NULL) {
+      printf("ldap_init failed, ldap_errno=%d (%s)\n",
+        ldap_errno, ldap_err2string(ldap_errno));
+
+      printf("unable to initialize LDAP session\n");
+      return -1;
+    }
+
+    msgid = ldap_simple_bind(ld, NULL, NULL);
+
+    if(msgid == -1) {
+      int err = ldap_errno;
+
+      if (err != LDAP_SUCCESS ) {
+
+Zeilenga                                                        [Page 3]
+
+INTERNET-DRAFT    LDAP C API Error Reporting Extension 28 September 1999
+
+        /* API failure */
+        printf("ldap_simple_bind failure: ldap_errno=%d (%s)\n",
+          err, ldap_err2string(err));
+
+      } else {
+        int lderr, rc;
+
+        printf("ldap_simple_bind failed\n");
+
+        rc = ldap_get_option(ld,
+          LDAP_OPT_ERROR_NUMBER, &lderr);
+
+        if(rc == LDAP_OPT_SUCCESS) {
+          printf("  reason=%d (%s)\n",
+            lderr, ldap_err2string(lderr));
+
+        } else {
+          printf("ldap_get_option failed, ldap_errno=%d (%s)\n",
+            ldap_errno, ldap_err2string(ldap_errno));        }
+      }
+
+      goto unbind;
+    }
+
+    /* ... */
+
+    unbind: if(ldap_unbind(ld) != 0) {
+      printf("ldap_unbind failed, ldap_errno=%d (%s)\n",
+        ldap_errno, ldap_error2str(ldap_errno));
+
+      return -1;
+    }
+    return 0;
+
+5.   Advertising Features
+
+  This document REQUIRES that supported features with the name in the
+  form LDAP_API_FEATURE_x be advertised to consumers of the CAPI as
+  follows:
+
+       SHOULD provide the macro LDAP_API_FEATURE_x with the value
+       of 1000 + revision number of this draft (i.e.: 1000+0 for
+       this 0 revision of the draft).
+
+       MUST provide the CAPI extension "x" when returning API
+       information upon LDAP_OPT_API_INFO option access, and
+
+Zeilenga                                                        [Page 4]
+
+INTERNET-DRAFT    LDAP C API Error Reporting Extension 28 September 1999
+
+       MUST provide feature info for "x" via LDAP_OPT_FEATURE_INFO
+       option mechanism.  The feature version provided MUST      match
+  the value LDAP_API_FEATURE_x macro
+
+  where x is replaced appropriately.
+
+  As implementations may not provide macros for all features,
+  applications SHOULD use LDAP_OPT_API_INFO to determine which features
+  are provided by a given implementation.
+
+6.   Changes to the LDAP C API
+
+  This section provides a summary of changes to the CAPI specification.
+
+6.1. LDAP_API_VERSION
+
+  LDAP_API_VERSION should be set to the RFC number of this extension if
+  and when it is published as a Standards Track RFC.  (see purpose of
+  this draft above).
+
+  Until such time as this document is published as an RFC,
+  implementations should use the value specified by CAPI plus 100 + 10 *
+  the number of this draft.
+
+  For the third draft of CAPI and this 0 revision of draft, the value of
+  2103 ((2000+3) + (100+10*0)) should be used.
+
+6.2. New Symbols
+
+  This extension introduces two new symbols:
+       LDAP_API_FEATURE_CONTEXT_SPECIFIC_ERRNO      ldap_errno
+
+  LDAP_API_FEATURE_CONTEXT_SPECIFIC_ERRNO is a macro.  ldap_errno MAY be
+  a MACRO.
+
+  This extension indroductes no new functions, typedefs, or structure
+  names.
+
+6.3. Implementation/System Specific Error Handling
+
+  This extensions removes any requirements that implementations to use
+  implementation and/or operating system specific error reporting
+  mechanisms.
+
+Zeilenga                                                        [Page 5]
+
+INTERNET-DRAFT    LDAP C API Error Reporting Extension 28 September 1999
+
+7.   Security Considerations
+
+  None taken, none given.
+
+8.   Copyright
+
+  Copyright 1999, The Internet Society.  All Rights Reserved.
+
+  This document and translations of it may be copied and furnished to
+  others, and derivative works that comment on or otherwise explain it
+  or assist in its implementation may be prepared, copied, published and
+  distributed, in whole or in part, without restriction of any kind,
+  provided that the above copyright notice and this paragraph are
+  included on all such copies and derivative works.  However, this
+  document itself may not be modified in any way, such as by removing
+  the copyright notice or references to the Internet Society or other
+  Internet organizations, except as needed for the  purpose of
+  developing Internet standards in which case the procedures for
+  copyrights defined in the Internet Standards process must be followed,
+  or as required to translate it into languages other than English.
+
+  The limited permissions granted above are perpetual and will not be
+  revoked by the Internet Society or its successors or assigns.
+
+  This document and the information contained herein is provided on an
+  "AS IS" basis and THE AUTHORS, THE INTERNET SOCIETY, AND THE INTERNET
+  ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
+  INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+  INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+  WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+9.   Bibliography
+
+  [CAPI]  M. Smith, T. Howes, A. Herron, M. Wahl, A. Anantha,
+          "The C LDAP Application Program Interface", INTERNET-DRAFT,
+          <draft-ietf-ldapext-ldap-c-api-03.txt> + LDAPext discussions,
+       June 1999.
+
+  [KEYW]  S. Bradner, "Key words for use in RFCs to Indicate
+          Requirement Levels", RFC 2119, March 1997.
+
+  [LDAP]  M. Wahl, T. Howes, S. Kille, "Lightweight Directory
+          Access Protocol (v3)", RFC 2251, December 1997.
+
+Zeilenga                                                        [Page 6]
+
+INTERNET-DRAFT    LDAP C API Error Reporting Extension 28 September 1999
+
+10.  Author's Address
+
+  Kurt D. Zeilenga
+  OpenLDAP Foundation
+  <Kurt@OpenLDAP.org>
+
+  This document expires on 28 March 2000.
+
+Zeilenga                                                        [Page 7]
diff --git a/doc/man/man3/ldap_dup.3 b/doc/man/man3/ldap_dup.3
new file mode 100644 (file)
index 0000000..39b3eed
--- /dev/null
@@ -0,0 +1,126 @@
+.TH LDAP_OPEN 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2010 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply.  See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_dup, ldap_destroy, \- Duplicate and destroy LDAP session handles
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+LDAP *ldap_dup(
+.RS
+.ft B
+LDAP *\fIold\fB );
+.RE
+.LP
+.ft B
+int ldap_destroy(
+.RS
+.ft B
+LDAP *\fIold\fB );
+.RE
+.SH DESCRIPTION
+.LP
+.B ldap_dup()
+duplicates an existing LDAP
+.RB ( "LDAP *" )
+session handle.
+The new session handle may be used concurrently with the
+original session handle.
+In a threaded environment, different threads may execute concurrent
+requests on the same connection/session without fear of contamination.
+Each session handle manages its own private error results.
+.LP
+.B ldap_destroy()
+destroys an existing session handle.
+.LP
+The
+.B ldap_dup()
+and
+.B ldap_destroy()
+functions are used in conjunction with a "thread safe" version
+of
+.B libldap
+.RB ( libldap_r )
+to enable operation thread safe API calls, so that a single session
+may be simultaneously used across multiple threads with consistent
+error handling.
+.LP
+When a session is created through the use of one of the session creation
+functions including
+.BR ldap_open (3),
+.BR ldap_init (3),
+.BR ldap_initialize (3)
+or
+.BR ldap_init_fd (3)
+an
+.B "LDAP *"
+session handle is returned to the application.
+The session handle may be shared amongst threads, however the
+error codes are unique to a session handle.
+Multiple threads performing different operations using the same
+session handle will result in inconsistent error codes and
+return values.
+.LP
+To prevent this confusion,
+.B ldap_dup()
+is used duplicate an existing session handle so that multiple threads
+can share the session, and maintain consistent error information
+and results.
+.LP
+The message queues for a session are shared between sibling session handles.
+Results of operations on a sibling session handles are accessible
+to all the sibling session handles.
+Applications desiring results associated with a specific operation
+should provide the appropriate msgid to
+.BR ldap_result() .
+Applications should avoid calling
+.B ldap_result()
+with
+.B LDAP_RES_ANY
+as that may "steal" and return results in the calling thread
+that another operation in a different thread, using a
+different session handle, may require to complete.
+.LP
+When
+.B ldap_unbind()
+is called on a session handle with siblings, all the 
+siblings become invalid.
+.LP
+Siblings must be destroyed using
+.BR ldap_destroy() .
+Session handle resources associated with the original
+.RB ( "LDAP *" )
+will be freed when the last session handle is destroyed or when
+.B ldap_unbind()
+is called, if no other session handles currently exist.
+.SH ERRORS
+If an error occurs,
+.B ldap_dup()
+will return NULL and 
+.I errno
+should be set appropriately.
+.B ldap_destroy()
+will directly return the LDAP code associated to the error (or
+.I LDAP_SUCCESS
+in case of success);
+.I errno
+should be set as well whenever appropriate.
+.SH SEE ALSO
+.BR ldap_open (3),
+.BR ldap_init (3),
+.BR ldap_initialize (3),
+.BR ldap_init_fd (3),
+.BR errno (3)
+.SH ACKNOWLEDGEMENTS
+This work is based on the previously proposed
+.B LDAP C API Concurrency Extensions
+draft
+.BR ( draft-zeilenga-ldap-c-api-concurrency-00.txt )
+effort.
+.so ../Project
diff --git a/doc/man/man3/ldap_dup.3.links b/doc/man/man3/ldap_dup.3.links
new file mode 100644 (file)
index 0000000..1d77f93
--- /dev/null
@@ -0,0 +1 @@
+ldap_destroy.3
index e3c89662f04273b5575fa34964649d5264b9d13e..5733025f3c4fea36df0267337a52367855afbeca 100644 (file)
@@ -322,6 +322,15 @@ must be
 the library duplicates the controls passed via
 .BR invalue .
 .TP
+.B LDAP_OPT_SESSION_REFCNT
+Returns the reference count associated with the LDAP handle passed in as
+.BR ld ;
+.BR outvalue
+must be a
+.BR "int *" .
+This is a read-only, handle-specific option.
+This option is OpenLDAP specific.
+.TP
 .B LDAP_OPT_SIZELIMIT
 Sets/gets the value that defines the maximum number of entries
 to be returned by a search operation.
index fa6e4176ecc50b248b68314c42fd12c27c795a59..4af25991bf1791add98e6cbe28b6f348f8bbf8e3 100644 (file)
@@ -59,7 +59,9 @@ LDAP_BEGIN_DECL
                defined( LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE ) )
        /* -lldap may or may not be thread safe */
        /* -lldap_r, if available, is always thread safe */
-#      define  LDAP_API_FEATURE_THREAD_SAFE 1
+#      define  LDAP_API_FEATURE_THREAD_SAFE            1
+#      define  LDAP_API_FEATURE_SESSION_THREAD_SAFE    1
+#      define  LDAP_API_FEATURE_OPERATION_THREAD_SAFE  1
 #endif
 #if defined( LDAP_THREAD_SAFE ) && \
        defined( LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE )
@@ -135,6 +137,7 @@ LDAP_BEGIN_DECL
 #define LDAP_OPT_DEFBASE               0x5009  /* searchbase */
 #define        LDAP_OPT_CONNECT_ASYNC          0x5010  /* create connections asynchronously */
 #define        LDAP_OPT_CONNECT_CB                     0x5011  /* connection callbacks */
+#define        LDAP_OPT_SESSION_REFCNT         0x5012  /* session reference count */
 
 /* OpenLDAP TLS options */
 #define LDAP_OPT_X_TLS                         0x6000
@@ -1519,6 +1522,10 @@ ldap_initialize LDAP_P((
        LDAP **ldp,
        LDAP_CONST char *url ));
 
+LDAP_F( LDAP * )
+ldap_dup LDAP_P((
+       LDAP *old ));
+
 /*
  * in tls.c
  */
@@ -1931,6 +1938,10 @@ ldap_unbind_ext_s LDAP_P((
        LDAPControl             **serverctrls,
        LDAPControl             **clientctrls));
 
+LDAP_F( int )
+ldap_destroy LDAP_P((
+       LDAP                    *ld));
+
 #if LDAP_DEPRECATED
 LDAP_F( int )
 ldap_unbind LDAP_P(( /* deprecated, use ldap_unbind_ext */
index d2430f31ac967460b15e9933327702d301fc8fde..108574272f528b19fd82722a3f8bc53213e58477 100644 (file)
@@ -69,6 +69,11 @@ typedef pthread_key_t                ldap_int_thread_key_t;
 typedef pthread_rwlock_t ldap_int_thread_rdwr_t;
 #endif
 
+#ifndef        LDO_MUTEX_NULL
+#define LDO_MUTEX_NULL ,PTHREAD_MUTEX_INITIALIZER
+#define MUTEX_FIRSTCREATE(m)
+#endif
+
 LDAP_END_DECL
 
 #elif defined ( HAVE_MACH_CTHREADS )
@@ -91,6 +96,11 @@ typedef struct mutex         ldap_int_thread_mutex_t;
 typedef struct condition       ldap_int_thread_cond_t;
 typedef cthread_key_t          ldap_int_thread_key_t;
 
+#ifndef        LDO_MUTEX_NULL
+#define LDO_MUTEX_NULL ,MUTEX_INITIALIZER
+#define MUTEX_FIRSTCREATE(m)
+#endif
+
 LDAP_END_DECL
 
 #elif defined( HAVE_GNU_PTH )
@@ -115,6 +125,11 @@ typedef pth_key_t  ldap_int_thread_key_t;
 typedef pth_rwlock_t ldap_int_thread_rdwr_t;
 #endif
 
+#ifndef        LDO_MUTEX_NULL
+#define LDO_MUTEX_NULL ,PTH_MUTEX_INIT
+#define MUTEX_FIRSTCREATE(m)
+#endif
+
 LDAP_END_DECL
 
 #elif defined( HAVE_THR )
@@ -143,7 +158,10 @@ typedef thread_key_t       ldap_int_thread_key_t;
 #define LDAP_THREAD_HAVE_SETCONCURRENCY 1
 #endif
 
-LDAP_END_DECL
+#ifndef        LDO_MUTEX_NULL
+#define LDO_MUTEX_NULL ,DEFAULTMUTEX
+#define MUTEX_FIRSTCREATE(m)
+#endif
 
 #elif defined(HAVE_NT_THREADS)
 /*************************************
@@ -162,6 +180,11 @@ typedef HANDLE     ldap_int_thread_mutex_t;
 typedef HANDLE ldap_int_thread_cond_t;
 typedef DWORD  ldap_int_thread_key_t;
 
+#ifndef        LDO_MUTEX_NULL
+#define LDO_MUTEX_NULL         ,(HANDLE)0
+#define MUTEX_FIRSTCREATE(m)   (!m ? 0 : ldap_pvt_thread_mutex_init(&m) )
+#endif
+
 LDAP_END_DECL
 
 #else
@@ -186,6 +209,11 @@ typedef int                        ldap_int_thread_key_t;
 #define LDAP_THREAD_HAVE_TPOOL 1
 typedef int                    ldap_int_thread_pool_t;
 
+#ifndef        LDO_MUTEX_NULL
+#define LDO_MUTEX_NULL
+#define MUTEX_FIRSTCREATE(m)
+#endif
+
 LDAP_END_DECL
 
 #endif /* no threads support */
index e230099186c2586d4aff2c237151e688666f2b7f..7cda97849a7ed0f020d416ba44b64058f61bbf38 100644 (file)
@@ -206,7 +206,7 @@ start_again:;
                         *              LDAP_NEXT_MSGID(ld, i);
                         */
 
-                       i = ++(ld)->ld_msgid;
+                       LDAP_NEXT_MSGID(ld, i);
 #ifdef LDAP_CONNECTIONLESS
                        if ( LDAP_IS_UDP(ld) ) {
                                struct sockaddr sa = {0};
@@ -216,11 +216,14 @@ start_again:;
                        if ( LDAP_IS_UDP(ld) && ld->ld_options.ldo_version ==
                                LDAP_VERSION2 )
                        {
-                               char *dn = ld->ld_options.ldo_cldapdn;
+                               char *dn;
+                               LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
+                               dn = ld->ld_options.ldo_cldapdn;
                                if (!dn) dn = "";
                                err = ber_printf( ber, "{isti",  /* '}' */
                                        i, dn,
                                        LDAP_REQ_ABANDON, msgid );
+                               LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
                        } else
 #endif
                        {
@@ -287,10 +290,7 @@ start_again:;
                }
        }
 
-       /* ld_abandoned is actually protected by the ld_res_mutex;
-        * give up the ld_req_mutex and get the other */
-       LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
-       LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
+       LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
 
        /* use bisection */
        i = 0;
@@ -304,8 +304,7 @@ start_again:;
                ld->ld_errno = LDAP_SUCCESS;
        }
 
-       LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
-       LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+       LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
        return( ld->ld_errno );
 }
 
index 015eec0b17f91700e1dca450324cc37637b6f500..a25304be821569a832f8184601d9166667f6a119 100644 (file)
@@ -416,7 +416,7 @@ ldap_int_sasl_bind(
                void    *ssl;
 
                rc = 0;
-               LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+               LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
                ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
 
                if ( sd == AC_SOCKET_INVALID ) {
@@ -434,7 +434,7 @@ ldap_int_sasl_bind(
                                }
                        }
                }   
-               LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+               LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
                if( rc != 0 ) return ld->ld_errno;
 
                oldctx = ld->ld_defconn->lconn_sasl_authctx;
index 16d8e9a3830c7ad4008681ff4095e4e1741b0ae9..afdf893ba0ab4e547a0e6e66c69c85d7b43d2bf9 100644 (file)
@@ -36,7 +36,7 @@
 #include "lutil.h"
 
 struct ldapoptions ldap_int_global_options =
-       { LDAP_UNINITIALIZED, LDAP_DEBUG_NONE };  
+       { LDAP_UNINITIALIZED, LDAP_DEBUG_NONE LDO_MUTEX_NULL };  
 
 #define ATTR_NONE      0
 #define ATTR_BOOL      1
@@ -510,6 +510,13 @@ ldap_int_destroy_global_options(void)
  */
 void ldap_int_initialize_global_options( struct ldapoptions *gopts, int *dbglvl )
 {
+       MUTEX_FIRSTCREATE(gopts->ldo_mutex);
+       LDAP_MUTEX_LOCK( &gopts->ldo_mutex );
+       if (gopts->ldo_valid == LDAP_INITIALIZED) {
+               /* someone else got here first */
+               LDAP_MUTEX_UNLOCK( &gopts->ldo_mutex );
+               return;
+       }
        if (dbglvl)
            gopts->ldo_debug = *dbglvl;
        else
@@ -573,6 +580,7 @@ void ldap_int_initialize_global_options( struct ldapoptions *gopts, int *dbglvl
        gopts->ldo_keepalive_idle = 0;
 
        gopts->ldo_valid = LDAP_INITIALIZED;
+       LDAP_MUTEX_UNLOCK( &gopts->ldo_mutex );
        return;
 }
 
index e4c760aea025328dd68bc7009b43e3f716c4a12c..e818900bcb76ad4a3ddd95ae8faf5053b7d6afe6 100644 (file)
@@ -174,6 +174,7 @@ typedef struct ldaplist {
 /*
  * structure representing get/set'able options
  * which have global defaults.
+ * Protect access to this struct with ldo_mutex
  */
 struct ldapoptions {
        short ldo_valid;
@@ -182,6 +183,12 @@ struct ldapoptions {
 #define LDAP_VALID_SESSION     0x2
 #define LDAP_TRASHED_SESSION   0xFF
        int   ldo_debug;
+#ifdef LDAP_R_COMPILE
+       ldap_pvt_thread_mutex_t ldo_mutex;
+#else
+#define LDO_MUTEX_NULL
+#define MUTEX_FIRSTCREATE(m)
+#endif
 #ifdef LDAP_CONNECTIONLESS
 #define        LDAP_IS_UDP(ld)         ((ld)->ld_options.ldo_is_udp)
        void*                   ldo_peer;       /* struct sockaddr* */
@@ -361,24 +368,27 @@ typedef struct ldapreqinfo {
  * structure representing an ldap connection
  */
 
-struct ldap {
-       Sockbuf         *ld_sb;         /* socket descriptor & buffer */
+struct ldap_common {
+       Sockbuf         *ldc_sb;        /* socket descriptor & buffer */
+#define ld_sb                  ldc->ldc_sb
 
-       struct ldapoptions ld_options;
+       /* protected by ldo_mutex */
+       struct ldapoptions ldc_options;
+#define ld_options             ldc->ldc_options
 
 #define ld_valid               ld_options.ldo_valid
 #define ld_debug               ld_options.ldo_debug
 
 #define ld_deref               ld_options.ldo_deref
-#define ld_timelimit   ld_options.ldo_timelimit
-#define ld_sizelimit   ld_options.ldo_sizelimit
+#define ld_timelimit           ld_options.ldo_timelimit
+#define ld_sizelimit           ld_options.ldo_sizelimit
 
-#define ld_defbinddn   ld_options.ldo_defbinddn
+#define ld_defbinddn           ld_options.ldo_defbinddn
 #define ld_defbase             ld_options.ldo_defbase
 #define ld_defhost             ld_options.ldo_defhost
 #define ld_defport             ld_options.ldo_defport
 
-#define ld_refhoplimit ld_options.ldo_refhoplimit
+#define ld_refhoplimit         ld_options.ldo_refhoplimit
 
 #define ld_sctrls              ld_options.ldo_sctrls
 #define ld_cctrls              ld_options.ldo_cctrls
@@ -390,36 +400,79 @@ struct ldap {
 #define ld_urllist_params      ld_options.ldo_urllist_params
 
 #define ld_version             ld_options.ldo_version
+#ifdef LDAP_R_COMPILE
+#define        ld_ldopts_mutex         ld_options.ldo_mutex
+#endif
 
-       unsigned short  ld_lberoptions;
+       unsigned short  ldc_lberoptions;
+#define        ld_lberoptions          ldc->ldc_lberoptions
 
-       ber_int_t       ld_errno;
-       char    *ld_error;
-       char    *ld_matched;
-       char    **ld_referrals;
-       ber_len_t               ld_msgid;
+       /* protected by msgid_mutex */
+       ber_len_t               ldc_msgid;
+#define        ld_msgid                ldc->ldc_msgid
 
        /* do not mess with these */
-       LDAPRequest     *ld_requests;   /* list of outstanding requests */
-       LDAPMessage     *ld_responses;  /* list of outstanding responses */
+       /* protected by req_mutex */
+       LDAPRequest     *ldc_requests;  /* list of outstanding requests */
+       /* protected by res_mutex */
+       LDAPMessage     *ldc_responses; /* list of outstanding responses */
+#define        ld_requests             ldc->ldc_requests
+#define        ld_responses            ldc->ldc_responses
 
 #ifdef LDAP_R_COMPILE
-       ldap_pvt_thread_mutex_t ld_conn_mutex;
-       ldap_pvt_thread_mutex_t ld_req_mutex;
-       ldap_pvt_thread_mutex_t ld_res_mutex;
+       ldap_pvt_thread_mutex_t ldc_msgid_mutex;
+       ldap_pvt_thread_mutex_t ldc_conn_mutex;
+       ldap_pvt_thread_mutex_t ldc_req_mutex;
+       ldap_pvt_thread_mutex_t ldc_res_mutex;
+       ldap_pvt_thread_mutex_t ldc_abandon_mutex;
+#define        ld_msgid_mutex          ldc->ldc_msgid_mutex
+#define        ld_conn_mutex           ldc->ldc_conn_mutex
+#define        ld_req_mutex            ldc->ldc_req_mutex
+#define        ld_res_mutex            ldc->ldc_res_mutex
+#define        ld_abandon_mutex        ldc->ldc_abandon_mutex
 #endif
 
-       ber_len_t       ld_nabandoned;
-       ber_int_t       *ld_abandoned;  /* array of abandoned requests */
+       /* protected by abandon_mutex */
+       ber_len_t       ldc_nabandoned;
+       ber_int_t       *ldc_abandoned; /* array of abandoned requests */
+#define        ld_nabandoned           ldc->ldc_nabandoned
+#define        ld_abandoned            ldc->ldc_abandoned
 
-       LDAPCache       *ld_cache;      /* non-null if cache is initialized */
+       /* unused by libldap */
+       LDAPCache       *ldc_cache;     /* non-null if cache is initialized */
+#define        ld_cache                ldc->ldc_cache
 
        /* do not mess with the rest though */
 
-       LDAPConn        *ld_defconn;    /* default connection */
-       LDAPConn        *ld_conns;      /* list of server connections */
-       void            *ld_selectinfo; /* platform specifics for select */
+       /* protected by conn_mutex */
+       LDAPConn        *ldc_defconn;   /* default connection */
+#define        ld_defconn              ldc->ldc_defconn
+       LDAPConn        *ldc_conns;     /* list of server connections */
+#define        ld_conns                ldc->ldc_conns
+       void            *ldc_selectinfo;/* platform specifics for select */
+#define        ld_selectinfo           ldc->ldc_selectinfo
+
+       /* ldap_common refcnt - free only if 0 */
+#ifdef LDAP_R_COMPILE
+       ldap_pvt_thread_mutex_t ldc_mutex;
+#define        ld_ldcmutex             ldc->ldc_mutex
+#endif
+       /* protected by ldc_mutex */
+       unsigned int            ldc_refcnt;
+#define        ld_ldcrefcnt            ldc->ldc_refcnt
+};
+
+struct ldap {
+       /* thread shared */
+       struct ldap_common      *ldc;
+
+       /* thread specific */
+       ber_int_t               ld_errno;
+       char                    *ld_error;
+       char                    *ld_matched;
+       char                    **ld_referrals;
 };
+
 #define LDAP_VALID(ld)         ( (ld)->ld_valid == LDAP_VALID_SESSION )
 #define LDAP_TRASHED(ld)       ( (ld)->ld_valid == LDAP_TRASHED_SESSION )
 #define LDAP_TRASH(ld)         ( (ld)->ld_valid = LDAP_TRASHED_SESSION )
@@ -448,9 +501,9 @@ LDAP_V( ldap_pvt_thread_mutex_t ) ldap_int_gssapi_mutex;
 
 #ifdef LDAP_R_COMPILE
 #define        LDAP_NEXT_MSGID(ld, id) \
-       LDAP_MUTEX_LOCK( &(ld)->ld_req_mutex ); \
+       LDAP_MUTEX_LOCK( &(ld)->ld_msgid_mutex ); \
        id = ++(ld)->ld_msgid; \
-       LDAP_MUTEX_UNLOCK( &(ld)->ld_req_mutex )
+       LDAP_MUTEX_UNLOCK( &(ld)->ld_msgid_mutex )
 #else
 #define        LDAP_NEXT_MSGID(ld, id) id = ++(ld)->ld_msgid
 #endif
@@ -584,8 +637,11 @@ LDAP_F (ber_int_t) ldap_send_initial_request( LDAP *ld, ber_tag_t msgtype,
 LDAP_F (BerElement *) ldap_alloc_ber_with_options( LDAP *ld );
 LDAP_F (void) ldap_set_ber_options( LDAP *ld, BerElement *ber );
 
-LDAP_F (int) ldap_send_server_request( LDAP *ld, BerElement *ber, ber_int_t msgid, LDAPRequest *parentreq, LDAPURLDesc **srvlist, LDAPConn *lc, LDAPreqinfo *bind );
-LDAP_F (LDAPConn *) ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, int connect, LDAPreqinfo *bind );
+LDAP_F (int) ldap_send_server_request( LDAP *ld, BerElement *ber,
+       ber_int_t msgid, LDAPRequest *parentreq, LDAPURLDesc **srvlist,
+       LDAPConn *lc, LDAPreqinfo *bind, int noconn, int m_res );
+LDAP_F (LDAPConn *) ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist,
+       int use_ldsb, int connect, LDAPreqinfo *bind, int m_req, int m_res );
 LDAP_F (LDAPRequest *) ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid );
 LDAP_F (void) ldap_return_request( LDAP *ld, LDAPRequest *lr, int freeit );
 LDAP_F (void) ldap_free_request( LDAP *ld, LDAPRequest *lr );
index 1bc86e39a718d160eee2a8f03180bb135328b5c7..4a2ad21add0a567e78321b9dc2df352e4ee6023e 100644 (file)
 #include "ldap-int.h"
 #include "ldap_log.h"
 
-/* Caller should hold the req_mutex if simultaneous accesses are possible */
+/* Caller must hold the conn_mutex since simultaneous accesses are possible */
 int ldap_open_defconn( LDAP *ld )
 {
        ld->ld_defconn = ldap_new_connection( ld,
-               &ld->ld_options.ldo_defludp, 1, 1, NULL );
+               &ld->ld_options.ldo_defludp, 1, 1, NULL, 0, 0 );
 
        if( ld->ld_defconn == NULL ) {
                ld->ld_errno = LDAP_SERVER_DOWN;
@@ -74,7 +74,9 @@ ldap_open( LDAP_CONST char *host, int port )
                return( NULL );
        }
 
+       LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
        rc = ldap_open_defconn( ld );
+       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
 
        if( rc < 0 ) {
                ldap_ld_free( ld, 0, NULL, NULL );
@@ -114,8 +116,19 @@ ldap_create( LDAP **ldp )
                return( LDAP_NO_MEMORY );
        }
    
+       if ( (ld->ldc = (struct ldap_common *) LDAP_CALLOC( 1,
+                       sizeof(struct ldap_common) )) == NULL ) {
+               LDAP_FREE( (char *)ld );
+               return( LDAP_NO_MEMORY );
+       }
        /* copy the global options */
+       LDAP_MUTEX_LOCK( &gopts->ldo_mutex );
        AC_MEMCPY(&ld->ld_options, gopts, sizeof(ld->ld_options));
+#ifdef LDAP_R_COMPILE
+       /* Properly initialize the structs mutex */
+       ldap_pvt_thread_mutex_init( &(ld->ld_ldopts_mutex) );
+#endif
+       LDAP_MUTEX_UNLOCK( &gopts->ldo_mutex );
 
        ld->ld_valid = LDAP_VALID_SESSION;
 
@@ -159,10 +172,14 @@ ldap_create( LDAP **ldp )
        if ( ld->ld_sb == NULL ) goto nomem;
 
 #ifdef LDAP_R_COMPILE
+       ldap_pvt_thread_mutex_init( &ld->ld_msgid_mutex );
+       ldap_pvt_thread_mutex_init( &ld->ld_conn_mutex );
        ldap_pvt_thread_mutex_init( &ld->ld_req_mutex );
        ldap_pvt_thread_mutex_init( &ld->ld_res_mutex );
-       ldap_pvt_thread_mutex_init( &ld->ld_conn_mutex );
+       ldap_pvt_thread_mutex_init( &ld->ld_abandon_mutex );
+       ldap_pvt_thread_mutex_init( &ld->ld_ldcmutex );
 #endif
+       ld->ld_ldcrefcnt = 1;
        *ldp = ld;
        return LDAP_SUCCESS;
 
@@ -265,8 +282,9 @@ ldap_init_fd(
                }
        }
 
+       LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
        /* Attach the passed socket as the LDAP's connection */
-       conn = ldap_new_connection( ld, NULL, 1, 0, NULL);
+       conn = ldap_new_connection( ld, NULL, 1, 0, NULL, 0, 0 );
        if( conn == NULL ) {
                ldap_unbind_ext( ld, NULL, NULL );
                return( LDAP_NO_MEMORY );
@@ -276,6 +294,7 @@ ldap_init_fd(
        ber_sockbuf_ctrl( conn->lconn_sb, LBER_SB_OPT_SET_FD, &fd );
        ld->ld_defconn = conn;
        ++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */
+       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
 
        switch( proto ) {
        case LDAP_PROTO_TCP:
@@ -331,6 +350,7 @@ ldap_init_fd(
        return LDAP_SUCCESS;
 }
 
+/* Protected by ld_conn_mutex */
 int
 ldap_int_open_connection(
        LDAP *ld,
@@ -434,8 +454,9 @@ ldap_open_internal_connection( LDAP **ldp, ber_socket_t *fdp )
        int rc;
        LDAPConn *c;
        LDAPRequest *lr;
+       LDAP    *ld;
 
-       rc = ldap_create( ldp );
+       rc = ldap_create( &ld );
        if( rc != LDAP_SUCCESS ) {
                *ldp = NULL;
                return( rc );
@@ -444,7 +465,7 @@ ldap_open_internal_connection( LDAP **ldp, ber_socket_t *fdp )
        /* Make it appear that a search request, msgid 0, was sent */
        lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ));
        if( lr == NULL ) {
-               ldap_unbind_ext( *ldp, NULL, NULL );
+               ldap_unbind_ext( ld, NULL, NULL );
                *ldp = NULL;
                return( LDAP_NO_MEMORY );
        }
@@ -453,13 +474,15 @@ ldap_open_internal_connection( LDAP **ldp, ber_socket_t *fdp )
        lr->lr_status = LDAP_REQST_INPROGRESS;
        lr->lr_res_errno = LDAP_SUCCESS;
        /* no mutex lock needed, we just created this ld here */
-       (*ldp)->ld_requests = lr;
+       ld->ld_requests = lr;
 
+       LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
        /* Attach the passed socket as the *LDAP's connection */
-       c = ldap_new_connection( *ldp, NULL, 1, 0, NULL);
+       c = ldap_new_connection( ld, NULL, 1, 0, NULL, 0, 0 );
        if( c == NULL ) {
-               ldap_unbind_ext( *ldp, NULL, NULL );
+               ldap_unbind_ext( ld, NULL, NULL );
                *ldp = NULL;
+               LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
                return( LDAP_NO_MEMORY );
        }
        ber_sockbuf_ctrl( c->lconn_sb, LBER_SB_OPT_SET_FD, fdp );
@@ -469,15 +492,39 @@ ldap_open_internal_connection( LDAP **ldp, ber_socket_t *fdp )
 #endif
        ber_sockbuf_add_io( c->lconn_sb, &ber_sockbuf_io_tcp,
          LBER_SBIOD_LEVEL_PROVIDER, NULL );
-       (*ldp)->ld_defconn = c;
+       ld->ld_defconn = c;
+       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
 
        /* Add the connection to the *LDAP's select pool */
-       ldap_mark_select_read( *ldp, c->lconn_sb );
-       ldap_mark_select_write( *ldp, c->lconn_sb );
+       ldap_mark_select_read( ld, c->lconn_sb );
+       ldap_mark_select_write( ld, c->lconn_sb );
 
        /* Make this connection an LDAP V3 protocol connection */
        rc = LDAP_VERSION3;
-       ldap_set_option( *ldp, LDAP_OPT_PROTOCOL_VERSION, &rc );
+       ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &rc );
+       *ldp = ld;
 
        return( LDAP_SUCCESS );
 }
+
+LDAP *
+ldap_dup( LDAP *old )
+{
+       LDAP                    *ld;
+
+       if ( old == NULL ) {
+               return( NULL );
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "ldap_dup\n", 0, 0, 0 );
+
+       if ( (ld = (LDAP *) LDAP_CALLOC( 1, sizeof(LDAP) )) == NULL ) {
+               return( NULL );
+       }
+   
+       LDAP_MUTEX_LOCK( &old->ld_ldcmutex );
+       ld->ldc = old->ldc;
+       old->ld_ldcrefcnt++;
+       LDAP_MUTEX_UNLOCK( &old->ld_ldcmutex );
+       return ( ld );
+}
index 69db0c6651d02021f194a83cff2671749de7bdd5..a75d4598c248170e3084315645e401c7c5e239d8 100644 (file)
@@ -96,6 +96,7 @@ ldap_get_option(
        void    *outvalue)
 {
        struct ldapoptions *lo;
+       int rc = LDAP_OPT_ERROR;
 
        /* Get pointer to global option structure */
        lo = LDAP_INT_GLOBAL_OPT();   
@@ -122,19 +123,21 @@ ldap_get_option(
                return LDAP_OPT_ERROR;
        }
 
+       LDAP_MUTEX_LOCK( &lo->ldo_mutex );
+
        switch(option) {
        case LDAP_OPT_API_INFO: {
                        struct ldapapiinfo *info = (struct ldapapiinfo *) outvalue;
 
                        if(info == NULL) {
                                /* outvalue must point to an apiinfo structure */
-                               return LDAP_OPT_ERROR;
+                               break;  /* LDAP_OPT_ERROR */
                        }
 
                        if(info->ldapai_info_version != LDAP_API_INFO_VERSION) {
                                /* api info version mismatch */
                                info->ldapai_info_version = LDAP_API_INFO_VERSION;
-                               return LDAP_OPT_ERROR;
+                               break;  /* LDAP_OPT_ERROR */
                        }
 
                        info->ldapai_api_version = LDAP_API_VERSION;
@@ -158,7 +161,8 @@ ldap_get_option(
                        info->ldapai_vendor_name = LDAP_STRDUP(LDAP_VENDOR_NAME);
                        info->ldapai_vendor_version = LDAP_VENDOR_VERSION;
 
-                       return LDAP_OPT_SUCCESS;
+                       rc = LDAP_OPT_SUCCESS;
+                       break;
                } break;
 
        case LDAP_OPT_DESC:
@@ -168,74 +172,86 @@ ldap_get_option(
                } 
 
                ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, outvalue );
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_SOCKBUF:
                if( ld == NULL ) break;
                *(Sockbuf **)outvalue = ld->ld_sb;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_TIMEOUT:
                /* the caller has to free outvalue ! */
                if ( lo->ldo_tm_api.tv_sec < 0 ) {
                        *(void **)outvalue = NULL;
                } else if ( ldap_int_timeval_dup( outvalue, &lo->ldo_tm_api ) != 0 ) {
-                       return LDAP_OPT_ERROR;
+                       break;  /* LDAP_OPT_ERROR */
                }
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
                
        case LDAP_OPT_NETWORK_TIMEOUT:
                /* the caller has to free outvalue ! */
                if ( lo->ldo_tm_net.tv_sec < 0 ) {
                        *(void **)outvalue = NULL;
                } else if ( ldap_int_timeval_dup( outvalue, &lo->ldo_tm_net ) != 0 ) {
-                       return LDAP_OPT_ERROR;
+                       break;  /* LDAP_OPT_ERROR */
                }
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_DEREF:
                * (int *) outvalue = lo->ldo_deref;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_SIZELIMIT:
                * (int *) outvalue = lo->ldo_sizelimit;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_TIMELIMIT:
                * (int *) outvalue = lo->ldo_timelimit;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_REFERRALS:
                * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_REFERRALS);
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
                
        case LDAP_OPT_RESTART:
                * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_RESTART);
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_PROTOCOL_VERSION:
                * (int *) outvalue = lo->ldo_version;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_SERVER_CONTROLS:
                * (LDAPControl ***) outvalue =
                        ldap_controls_dup( lo->ldo_sctrls );
-
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_CLIENT_CONTROLS:
                * (LDAPControl ***) outvalue =
                        ldap_controls_dup( lo->ldo_cctrls );
-
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_HOST_NAME:
                * (char **) outvalue = ldap_url_list2hosts(lo->ldo_defludp);
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_URI:
                * (char **) outvalue = ldap_url_list2urls(lo->ldo_defludp);
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_DEFBASE:
                if( lo->ldo_defbase == NULL ) {
@@ -243,12 +259,13 @@ ldap_get_option(
                } else {
                        * (char **) outvalue = LDAP_STRDUP(lo->ldo_defbase);
                }
-
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_CONNECT_ASYNC:
                * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_CONNECT_ASYNC);
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_CONNECT_CB:
                {
@@ -263,7 +280,8 @@ ldap_get_option(
                                }
                        }
                }
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_RESULT_CODE:
                if(ld == NULL) {
@@ -271,7 +289,8 @@ ldap_get_option(
                        break;
                } 
                * (int *) outvalue = ld->ld_errno;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_DIAGNOSTIC_MESSAGE:
                if(ld == NULL) {
@@ -284,8 +303,8 @@ ldap_get_option(
                } else {
                        * (char **) outvalue = LDAP_STRDUP(ld->ld_error);
                }
-
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_MATCHED_DN:
                if(ld == NULL) {
@@ -298,8 +317,8 @@ ldap_get_option(
                } else {
                        * (char **) outvalue = LDAP_STRDUP( ld->ld_matched );
                }
-
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_REFERRAL_URLS:
                if(ld == NULL) {
@@ -312,28 +331,31 @@ ldap_get_option(
                } else {
                        * (char ***) outvalue = ldap_value_dup(ld->ld_referrals);
                }
-
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_API_FEATURE_INFO: {
                        LDAPAPIFeatureInfo *info = (LDAPAPIFeatureInfo *) outvalue;
                        int i;
 
-                       if(info == NULL) return LDAP_OPT_ERROR;
+                       if(info == NULL)
+                               break;  /* LDAP_OPT_ERROR */
 
                        if(info->ldapaif_info_version != LDAP_FEATURE_INFO_VERSION) {
                                /* api info version mismatch */
                                info->ldapaif_info_version = LDAP_FEATURE_INFO_VERSION;
-                               return LDAP_OPT_ERROR;
+                               break;  /* LDAP_OPT_ERROR */
                        }
 
-                       if(info->ldapaif_name == NULL) return LDAP_OPT_ERROR;
+                       if(info->ldapaif_name == NULL)
+                               break;  /* LDAP_OPT_ERROR */
 
                        for(i=0; features[i].ldapaif_name != NULL; i++) {
                                if(!strcmp(info->ldapaif_name, features[i].ldapaif_name)) {
                                        info->ldapaif_version =
                                                features[i].ldapaif_version;
-                                       return LDAP_OPT_SUCCESS;
+                                       rc = LDAP_OPT_SUCCESS;
+                                       break;
                                }
                        }
                }
@@ -341,41 +363,54 @@ ldap_get_option(
 
        case LDAP_OPT_DEBUG_LEVEL:
                * (int *) outvalue = lo->ldo_debug;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
        
+       case LDAP_OPT_SESSION_REFCNT:
+               * (int *) outvalue = ld->ld_ldcrefcnt;
+               rc = LDAP_OPT_SUCCESS;
+               break;
+
        case LDAP_OPT_X_KEEPALIVE_IDLE:
                * (int *) outvalue = lo->ldo_keepalive_idle;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_X_KEEPALIVE_PROBES:
                * (int *) outvalue = lo->ldo_keepalive_probes;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_X_KEEPALIVE_INTERVAL:
                * (int *) outvalue = lo->ldo_keepalive_interval;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        default:
 #ifdef HAVE_TLS
                if ( ldap_pvt_tls_get_option( ld, option, outvalue ) == 0 ) {
-                       return LDAP_OPT_SUCCESS;
+                       rc = LDAP_OPT_SUCCESS;
+                       break;
                }
 #endif
 #ifdef HAVE_CYRUS_SASL
                if ( ldap_int_sasl_get_option( ld, option, outvalue ) == 0 ) {
-                       return LDAP_OPT_SUCCESS;
+                       rc = LDAP_OPT_SUCCESS;
+                       break;
                }
 #endif
 #ifdef HAVE_GSSAPI
                if ( ldap_int_gssapi_get_option( ld, option, outvalue ) == 0 ) {
-                       return LDAP_OPT_SUCCESS;
+                       rc = LDAP_OPT_SUCCESS;
+                       break;
                }
 #endif
                /* bad param */
                break;
        }
 
-       return LDAP_OPT_ERROR;
+       LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+       return ( rc );
 }
 
 int
@@ -386,6 +421,7 @@ ldap_set_option(
 {
        struct ldapoptions *lo;
        int *dbglvl = NULL;
+       int rc = LDAP_OPT_ERROR;
 
        /* Get pointer to global option structure */
        lo = LDAP_INT_GLOBAL_OPT();
@@ -416,14 +452,19 @@ ldap_set_option(
                lo = &ld->ld_options;
        }
 
-       switch(option) {
+       LDAP_MUTEX_LOCK( &lo->ldo_mutex );
+
+       switch ( option ) {
+
+       /* options with boolean values */
        case LDAP_OPT_REFERRALS:
                if(invalue == LDAP_OPT_OFF) {
                        LDAP_BOOL_CLR(lo, LDAP_BOOL_REFERRALS);
                } else {
                        LDAP_BOOL_SET(lo, LDAP_BOOL_REFERRALS);
                }
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_RESTART:
                if(invalue == LDAP_OPT_OFF) {
@@ -431,7 +472,8 @@ ldap_set_option(
                } else {
                        LDAP_BOOL_SET(lo, LDAP_BOOL_RESTART);
                }
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_CONNECT_ASYNC:
                if(invalue == LDAP_OPT_OFF) {
@@ -439,11 +481,10 @@ ldap_set_option(
                } else {
                        LDAP_BOOL_SET(lo, LDAP_BOOL_CONNECT_ASYNC);
                }
-               return LDAP_OPT_SUCCESS;
-       }
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        /* options which can withstand invalue == NULL */
-       switch ( option ) {
        case LDAP_OPT_SERVER_CONTROLS: {
                        LDAPControl *const *controls =
                                (LDAPControl *const *) invalue;
@@ -453,16 +494,19 @@ ldap_set_option(
 
                        if( controls == NULL || *controls == NULL ) {
                                lo->ldo_sctrls = NULL;
-                               return LDAP_OPT_SUCCESS;
+                               rc = LDAP_OPT_SUCCESS;
+                               break;
                        }
                                
                        lo->ldo_sctrls = ldap_controls_dup( controls );
 
                        if(lo->ldo_sctrls == NULL) {
                                /* memory allocation error ? */
-                               break;
+                               break;  /* LDAP_OPT_ERROR */
                        }
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_CLIENT_CONTROLS: {
                        LDAPControl *const *controls =
@@ -473,22 +517,25 @@ ldap_set_option(
 
                        if( controls == NULL || *controls == NULL ) {
                                lo->ldo_cctrls = NULL;
-                               return LDAP_OPT_SUCCESS;
+                               rc = LDAP_OPT_SUCCESS;
+                               break;
                        }
                                
                        lo->ldo_cctrls = ldap_controls_dup( controls );
 
                        if(lo->ldo_cctrls == NULL) {
                                /* memory allocation error ? */
-                               break;
+                               break;  /* LDAP_OPT_ERROR */
                        }
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
 
        case LDAP_OPT_HOST_NAME: {
                        const char *host = (const char *) invalue;
                        LDAPURLDesc *ludlist = NULL;
-                       int rc = LDAP_OPT_SUCCESS;
+                       rc = LDAP_OPT_SUCCESS;
 
                        if(host != NULL) {
                                rc = ldap_url_parsehosts( &ludlist, host,
@@ -519,13 +566,13 @@ ldap_set_option(
                                        ldap_free_urllist(lo->ldo_defludp);
                                lo->ldo_defludp = ludlist;
                        }
-                       return rc;
+                       break;
                }
 
        case LDAP_OPT_URI: {
                        const char *urls = (const char *) invalue;
                        LDAPURLDesc *ludlist = NULL;
-                       int rc = LDAP_OPT_SUCCESS;
+                       rc = LDAP_OPT_SUCCESS;
 
                        if(urls != NULL) {
                                rc = ldap_url_parselist_ext(&ludlist, urls, NULL,
@@ -578,7 +625,7 @@ ldap_set_option(
                                        ldap_free_urllist(lo->ldo_defludp);
                                lo->ldo_defludp = ludlist;
                        }
-                       return rc;
+                       break;
                }
 
        case LDAP_OPT_DEFBASE: {
@@ -587,24 +634,32 @@ ldap_set_option(
 
                        if ( newbase != NULL ) {
                                defbase = LDAP_STRDUP( newbase );
-                               if ( defbase == NULL ) return LDAP_NO_MEMORY;
+                               if ( defbase == NULL ) {
+                                       rc = LDAP_NO_MEMORY;
+                                       break;
+                               }
 
                        } else if ( ld != NULL ) {
                                defbase = LDAP_STRDUP( ldap_int_global_options.ldo_defbase );
-                               if ( defbase == NULL ) return LDAP_NO_MEMORY;
+                               if ( defbase == NULL ) {
+                                       rc = LDAP_NO_MEMORY;
+                                       break;
+                               }
                        }
                        
                        if ( lo->ldo_defbase != NULL )
                                LDAP_FREE( lo->ldo_defbase );
                        lo->ldo_defbase = defbase;
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_DIAGNOSTIC_MESSAGE: {
                        const char *err = (const char *) invalue;
 
                        if(ld == NULL) {
                                /* need a struct ldap */
-                               return LDAP_OPT_ERROR;
+                               break;  /* LDAP_OPT_ERROR */
                        }
 
                        if( ld->ld_error ) {
@@ -615,14 +670,16 @@ ldap_set_option(
                        if ( err ) {
                                ld->ld_error = LDAP_STRDUP(err);
                        }
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_MATCHED_DN: {
                        const char *matched = (const char *) invalue;
 
                        if (ld == NULL) {
                                /* need a struct ldap */
-                               return LDAP_OPT_ERROR;
+                               break;  /* LDAP_OPT_ERROR */
                        }
 
                        if( ld->ld_matched ) {
@@ -633,14 +690,16 @@ ldap_set_option(
                        if ( matched ) {
                                ld->ld_matched = LDAP_STRDUP( matched );
                        }
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_REFERRAL_URLS: {
                        char *const *referrals = (char *const *) invalue;
                        
                        if(ld == NULL) {
                                /* need a struct ldap */
-                               return LDAP_OPT_ERROR;
+                               break;  /* LDAP_OPT_ERROR */
                        }
 
                        if( ld->ld_referrals ) {
@@ -650,38 +709,52 @@ ldap_set_option(
                        if ( referrals ) {
                                ld->ld_referrals = ldap_value_dup(referrals);
                        }
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        /* Only accessed from inside this function by ldap_set_rebind_proc() */
        case LDAP_OPT_REBIND_PROC: {
                        lo->ldo_rebind_proc = (LDAP_REBIND_PROC *)invalue;              
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
        case LDAP_OPT_REBIND_PARAMS: {
                        lo->ldo_rebind_params = (void *)invalue;                
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        /* Only accessed from inside this function by ldap_set_nextref_proc() */
        case LDAP_OPT_NEXTREF_PROC: {
                        lo->ldo_nextref_proc = (LDAP_NEXTREF_PROC *)invalue;            
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
        case LDAP_OPT_NEXTREF_PARAMS: {
                        lo->ldo_nextref_params = (void *)invalue;               
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        /* Only accessed from inside this function by ldap_set_urllist_proc() */
        case LDAP_OPT_URLLIST_PROC: {
                        lo->ldo_urllist_proc = (LDAP_URLLIST_PROC *)invalue;            
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
        case LDAP_OPT_URLLIST_PARAMS: {
                        lo->ldo_urllist_params = (void *)invalue;               
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        /* read-only options */
        case LDAP_OPT_API_INFO:
        case LDAP_OPT_DESC:
        case LDAP_OPT_SOCKBUF:
        case LDAP_OPT_API_FEATURE_INFO:
-               return LDAP_OPT_ERROR;
+               break;  /* LDAP_OPT_ERROR */
 
        /* options which cannot withstand invalue == NULL */
        case LDAP_OPT_DEREF:
@@ -698,25 +771,29 @@ ldap_set_option(
        case LDAP_OPT_X_KEEPALIVE_INTERVAL :
                if(invalue == NULL) {
                        /* no place to set from */
-                       return LDAP_OPT_ERROR;
+                       LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+                       return ( LDAP_OPT_ERROR );
                }
                break;
 
        default:
 #ifdef HAVE_TLS
                if ( ldap_pvt_tls_set_option( ld, option, (void *)invalue ) == 0 )
-                       return LDAP_OPT_SUCCESS;
+                       LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+                       return ( LDAP_OPT_SUCCESS );
 #endif
 #ifdef HAVE_CYRUS_SASL
                if ( ldap_int_sasl_set_option( ld, option, (void *)invalue ) == 0 )
-                       return LDAP_OPT_SUCCESS;
+                       LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+                       return ( LDAP_OPT_SUCCESS );
 #endif
 #ifdef HAVE_GSSAPI
                if ( ldap_int_gssapi_set_option( ld, option, (void *)invalue ) == 0 )
-                       return LDAP_OPT_SUCCESS;
+                       LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+                       return ( LDAP_OPT_SUCCESS );
 #endif
                /* bad param */
-               return LDAP_OPT_ERROR;
+               break;  /* LDAP_OPT_ERROR */
        }
 
        /* options which cannot withstand invalue == NULL */
@@ -725,31 +802,38 @@ ldap_set_option(
        case LDAP_OPT_DEREF:
                /* FIXME: check value for protocol compliance? */
                lo->ldo_deref = * (const int *) invalue;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_SIZELIMIT:
                /* FIXME: check value for protocol compliance? */
                lo->ldo_sizelimit = * (const int *) invalue;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_TIMELIMIT:
                /* FIXME: check value for protocol compliance? */
                lo->ldo_timelimit = * (const int *) invalue;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_TIMEOUT: {
                        const struct timeval *tv = 
                                (const struct timeval *) invalue;
 
                        lo->ldo_tm_api = *tv;
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_NETWORK_TIMEOUT: {
                        const struct timeval *tv = 
                                (const struct timeval *) invalue;
 
                        lo->ldo_tm_net = *tv;
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_PROTOCOL_VERSION: {
                        int vers = * (const int *) invalue;
@@ -758,7 +842,9 @@ ldap_set_option(
                                break;
                        }
                        lo->ldo_version = vers;
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_RESULT_CODE: {
                        int err = * (const int *) invalue;
@@ -769,11 +855,14 @@ ldap_set_option(
                        }
 
                        ld->ld_errno = err;
-               } return LDAP_OPT_SUCCESS;
+               }
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_DEBUG_LEVEL:
                lo->ldo_debug = * (const int *) invalue;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
 
        case LDAP_OPT_CONNECT_CB:
                {
@@ -784,19 +873,24 @@ ldap_set_option(
                        ll->ll_next = lo->ldo_conn_cbs;
                        lo->ldo_conn_cbs = ll;
                }
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
        case LDAP_OPT_X_KEEPALIVE_IDLE:
                lo->ldo_keepalive_idle = * (const int *) invalue;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
        case LDAP_OPT_X_KEEPALIVE_PROBES :
                lo->ldo_keepalive_probes = * (const int *) invalue;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
        case LDAP_OPT_X_KEEPALIVE_INTERVAL :
                lo->ldo_keepalive_interval = * (const int *) invalue;
-               return LDAP_OPT_SUCCESS;
+               rc = LDAP_OPT_SUCCESS;
+               break;
        
        }
-       return LDAP_OPT_ERROR;
+       LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+       return ( rc );
 }
 
 int
index 66e334829a195360ef64f38c67e8b12e7e76f523..10e399ab94322084a9582ab3e1effd6e4f4c7f39 100644 (file)
 #include "ldap-int.h"
 #include "lber.h"
 
+/* used by ldap_send_server_request and ldap_new_connection */
+#ifdef LDAP_R_COMPILE
+#define LDAP_CONN_LOCK_IF(nolock) \
+       { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); }
+#define LDAP_CONN_UNLOCK_IF(nolock) \
+       { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); }
+#define LDAP_REQ_LOCK_IF(nolock) \
+       { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); }
+#define LDAP_REQ_UNLOCK_IF(nolock) \
+       { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); }
+#define LDAP_RES_LOCK_IF(nolock) \
+       { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); }
+#define LDAP_RES_UNLOCK_IF(nolock) \
+       { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); }
+#else
+#define LDAP_CONN_LOCK_IF(nolock)
+#define LDAP_CONN_UNLOCK_IF(nolock)
+#define LDAP_REQ_LOCK_IF(nolock)
+#define LDAP_REQ_UNLOCK_IF(nolock)
+#define LDAP_RES_LOCK_IF(nolock)
+#define LDAP_RES_UNLOCK_IF(nolock)
+#endif
+
 static LDAPConn *find_connection LDAP_P(( LDAP *ld, LDAPURLDesc *srv, int any ));
 static void use_connection LDAP_P(( LDAP *ld, LDAPConn *lc ));
 static void ldap_free_request_int LDAP_P(( LDAP *ld, LDAPRequest *lr ));
@@ -82,10 +105,12 @@ ldap_alloc_ber_with_options( LDAP *ld )
 void
 ldap_set_ber_options( LDAP *ld, BerElement *ber )
 {
+       /* ld_lberoptions is constant, hence no lock */
        ber->ber_options = ld->ld_lberoptions;
 }
 
 
+/* sets needed mutexes - no mutexes set to this point */
 ber_int_t
 ldap_send_initial_request(
        LDAP *ld,
@@ -98,15 +123,15 @@ ldap_send_initial_request(
 
        Debug( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n", 0, 0, 0 );
 
-       LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+       LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
        if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, NULL ) == -1 ) {
                /* not connected yet */
                rc = ldap_open_defconn( ld );
 
        }
-       LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
        if( rc < 0 ) {
                ber_free( ber, 1 );
+               LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
                return( -1 );
        } else if ( rc == 0 ) {
                Debug( LDAP_DEBUG_TRACE,
@@ -117,27 +142,33 @@ ldap_send_initial_request(
 #ifdef LDAP_CONNECTIONLESS
        if (LDAP_IS_UDP(ld)) {
                if (msgtype == LDAP_REQ_BIND) {
+                       LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
                        if (ld->ld_options.ldo_cldapdn)
                                ldap_memfree(ld->ld_options.ldo_cldapdn);
                        ld->ld_options.ldo_cldapdn = ldap_strdup(dn);
                        ber_free( ber, 1 );
+                       LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
+                       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
                        return 0;
                }
                if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH)
                {
                        ber_free( ber, 1 );
+                       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
                        return LDAP_PARAM_ERROR;
                }
        }
 #endif
        LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
        rc = ldap_send_server_request( ld, ber, msgid, NULL,
-               NULL, NULL, NULL );
+               NULL, NULL, NULL, 0, 0 );
        LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
        return(rc);
 }
 
 
+/* protected by conn_mutex */
 int
 ldap_int_flush_request(
        LDAP *ld,
@@ -145,6 +176,7 @@ ldap_int_flush_request(
 {
        LDAPConn *lc = lr->lr_conn;
 
+       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
        if ( ber_flush2( lc->lconn_sb, lr->lr_ber, LBER_FLUSH_FREE_NEVER ) != 0 ) {
                if ( sock_errno() == EAGAIN ) {
                        /* need to continue write later */
@@ -171,6 +203,13 @@ ldap_int_flush_request(
        return 0;
 }
 
+/*
+ * protected by req_mutex
+ * if m_noconn then protect using conn_lock
+ * else already protected with conn_lock
+ * if m_res then also protected by res_mutex
+ */
+
 int
 ldap_send_server_request(
        LDAP *ld,
@@ -179,16 +218,20 @@ ldap_send_server_request(
        LDAPRequest *parentreq,
        LDAPURLDesc **srvlist,
        LDAPConn *lc,
-       LDAPreqinfo *bind )
+       LDAPreqinfo *bind,
+       int m_noconn,
+       int m_res )
 {
        LDAPRequest     *lr;
        int             incparent, rc;
 
+       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
        Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 );
 
        incparent = 0;
        ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
 
+       LDAP_CONN_LOCK_IF(m_noconn);
        if ( lc == NULL ) {
                if ( srvlist == NULL ) {
                        lc = ld->ld_defconn;
@@ -200,7 +243,8 @@ ldap_send_server_request(
                                        incparent = 1;
                                        ++parentreq->lr_outrefcnt;
                                }
-                               lc = ldap_new_connection( ld, srvlist, 0, 1, bind );
+                               lc = ldap_new_connection( ld, srvlist, 0,
+                                       1, bind, 1, m_res );
                        }
                }
        }
@@ -223,11 +267,13 @@ ldap_send_server_request(
                        /* async only occurs if a network timeout is set */
 
                        /* honor network timeout */
+                       LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
                        if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec )
                        {
                                /* caller will have to call again */
                                ld->ld_errno = LDAP_X_CONNECTING;
                        }
+                       LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
                        /* fallthru */
 
                default:
@@ -246,6 +292,7 @@ ldap_send_server_request(
                        /* Forget about the bind */
                        --parentreq->lr_outrefcnt; 
                }
+               LDAP_CONN_UNLOCK_IF(m_noconn);
                return( -1 );
        }
 
@@ -255,10 +302,13 @@ ldap_send_server_request(
        if ( LDAP_IS_UDP( ld )) {
                BerElement tmpber = *ber;
                ber_rewind( &tmpber );
+               LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
                rc = ber_write( &tmpber, ld->ld_options.ldo_peer,
                        sizeof( struct sockaddr ), 0 );
+               LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
                if ( rc == -1 ) {
                        ld->ld_errno = LDAP_ENCODING_ERROR;
+                       LDAP_CONN_UNLOCK_IF(m_noconn);
                        return rc;
                }
        }
@@ -276,7 +326,10 @@ ldap_send_server_request(
        {
                rc = -1;
        }
-       if ( rc ) return rc;
+       if ( rc ) {
+               LDAP_CONN_UNLOCK_IF(m_noconn);
+               return rc;
+       }
 
        lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) );
        if ( lr == NULL ) {
@@ -287,6 +340,7 @@ ldap_send_server_request(
                        /* Forget about the bind */
                        --parentreq->lr_outrefcnt; 
                }
+               LDAP_CONN_UNLOCK_IF(m_noconn);
                return( -1 );
        } 
        lr->lr_msgid = msgid;
@@ -345,6 +399,7 @@ ldap_send_server_request(
                msgid = -1;
        }
 
+       LDAP_CONN_UNLOCK_IF(m_noconn);
        return( msgid );
 }
 
@@ -375,18 +430,17 @@ find_tls_ext( LDAPURLDesc *srv )
 }
 
 /*
- * caller must hold ld_req_mutex or be exclusive user of ld
- * if ( connect != 0 ) or ( bind != NULL ) caller must also hold
- * ld_req_mutex and ld_res_mutex
+ * always protected by conn_mutex
+ * optionally protected by req_mutex and res_mutex
  */
-
 LDAPConn *
 ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
-       int connect, LDAPreqinfo *bind )
+       int connect, LDAPreqinfo *bind, int m_req, int m_res )
 {
        LDAPConn        *lc;
        int             async = 0;
 
+       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
        Debug( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n",
                use_ldsb, connect, (bind != NULL) );
        /*
@@ -445,15 +499,10 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
        }
 
        lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED;
-       LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
        lc->lconn_next = ld->ld_conns;
        ld->ld_conns = lc;
-       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
 
        if ( connect ) {
-               LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
-               LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
-
 #ifdef HAVE_TLS
                if ( lc->lconn_server->lud_exts ) {
                        int rc, ext = find_tls_ext( lc->lconn_server );
@@ -464,11 +513,13 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
                                ++lc->lconn_refcnt;     /* avoid premature free */
                                ld->ld_defconn = lc;
 
-                               LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
-                               LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
+                               LDAP_REQ_UNLOCK_IF(m_req);
+                               LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+                               LDAP_RES_UNLOCK_IF(m_res);
                                rc = ldap_start_tls_s( ld, NULL, NULL );
-                               LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
-                               LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+                               LDAP_RES_LOCK_IF(m_res);
+                               LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+                               LDAP_REQ_LOCK_IF(m_req);
                                ld->ld_defconn = savedefconn;
                                --lc->lconn_refcnt;
 
@@ -485,9 +536,6 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
                int             err = 0;
                LDAPConn        *savedefconn;
 
-               LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
-               LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
-
                /* Set flag to prevent additional referrals
                 * from being processed on this
                 * connection until the bind has completed
@@ -507,13 +555,15 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
                                ld->ld_defconn = lc;
 
                                Debug( LDAP_DEBUG_TRACE, "Call application rebind_proc\n", 0, 0, 0);
-                               LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
-                               LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
+                               LDAP_REQ_UNLOCK_IF(m_req);
+                               LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+                               LDAP_RES_UNLOCK_IF(m_res);
                                err = (*ld->ld_rebind_proc)( ld,
                                        bind->ri_url, bind->ri_request, bind->ri_msgid,
                                        ld->ld_rebind_params );
-                               LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
-                               LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+                               LDAP_RES_LOCK_IF(m_res);
+                               LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+                               LDAP_REQ_LOCK_IF(m_req);
 
                                ld->ld_defconn = savedefconn;
                                --lc->lconn_refcnt;
@@ -538,8 +588,9 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
                                "anonymous rebind via ldap_sasl_bind(\"\")\n",
                                0, 0, 0);
 
-                       LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
-                       LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
+                       LDAP_REQ_UNLOCK_IF(m_req);
+                       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+                       LDAP_RES_UNLOCK_IF(m_res);
                        rc = ldap_sasl_bind( ld, "", LDAP_SASL_SIMPLE, &passwd,
                                NULL, NULL, &msgid );
                        if ( rc != LDAP_SUCCESS ) {
@@ -583,8 +634,9 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
                                        }
                                }
                        }
-                       LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
-                       LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+                       LDAP_RES_LOCK_IF(m_res);
+                       LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+                       LDAP_REQ_LOCK_IF(m_req);
                        ld->ld_defconn = savedefconn;
                        --lc->lconn_refcnt;
 
@@ -596,11 +648,11 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
                if ( lc != NULL )
                        lc->lconn_rebind_inprogress = 0;
        }
-
        return( lc );
 }
 
 
+/* protected by ld_conn_mutex */
 static LDAPConn *
 find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
 /*
@@ -613,7 +665,7 @@ find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
        int lcu_port, lsu_port;
        int found = 0;
 
-       LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
        for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
                lcu = lc->lconn_server;
                lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme,
@@ -637,35 +689,34 @@ find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
                if ( found )
                        break;
        }
-       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
        return lc;
 }
 
 
-/*
- * NOTE: the caller holds ld_req_mutex
- */
 
+/* protected by ld_conn_mutex */
 static void
 use_connection( LDAP *ld, LDAPConn *lc )
 {
+       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
        ++lc->lconn_refcnt;
        lc->lconn_lastused = time( NULL );
 }
 
 
+/* protected by ld_conn_mutex */
 void
 ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
 {
        LDAPConn        *tmplc, *prevlc;
 
+       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
        Debug( LDAP_DEBUG_TRACE,
                "ldap_free_connection %d %d\n",
                force, unbind, 0 );
 
        if ( force || --lc->lconn_refcnt <= 0 ) {
                /* remove from connections list first */
-               LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
 
                for ( prevlc = NULL, tmplc = ld->ld_conns;
                        tmplc != NULL;
@@ -684,7 +735,6 @@ ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
                        }
                        prevlc = tmplc;
                }
-               LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
 
                /* process connection callbacks */
                {
@@ -693,19 +743,23 @@ ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
                        ldap_conncb *cb;
 
                        lo = &ld->ld_options;
+                       LDAP_MUTEX_LOCK( &lo->ldo_mutex );
                        if ( lo->ldo_conn_cbs ) {
                                for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
                                        cb = ll->ll_data;
                                        cb->lc_del( ld, lc->lconn_sb, cb );
                                }
                        }
+                       LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
                        lo = LDAP_INT_GLOBAL_OPT();
+                       LDAP_MUTEX_LOCK( &lo->ldo_mutex );
                        if ( lo->ldo_conn_cbs ) {
                                for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
                                        cb = ll->ll_data;
                                        cb->lc_del( ld, lc->lconn_sb, cb );
                                }
                        }
+                       LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
                }
 
                if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
@@ -773,6 +827,7 @@ ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
 }
 
 
+/* Protects self with ld_conn_mutex */
 #ifdef LDAP_DEBUG
 void
 ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
@@ -781,6 +836,7 @@ ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
        char            timebuf[32];
 
        Debug( LDAP_DEBUG_TRACE, "** ld %p Connection%s:\n", (void *)ld, all ? "s" : "", 0 );
+       LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
        for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) {
                if ( lc->lconn_server != NULL ) {
                        Debug( LDAP_DEBUG_TRACE, "* host: %s  port: %d%s\n",
@@ -817,9 +873,11 @@ ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
                        break;
                }
        }
+       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
 }
 
 
+/* protected by req_mutex and res_mutex */
 void
 ldap_dump_requests_and_responses( LDAP *ld )
 {
@@ -868,9 +926,11 @@ ldap_dump_requests_and_responses( LDAP *ld )
 }
 #endif /* LDAP_DEBUG */
 
+/* protected by req_mutex */
 static void
 ldap_free_request_int( LDAP *ld, LDAPRequest *lr )
 {
+       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
        /* if lr_refcnt > 0, the request has been looked up 
         * by ldap_find_request_by_msgid(); if in the meanwhile
         * the request is free()'d by someone else, just decrease
@@ -921,11 +981,11 @@ ldap_free_request_int( LDAP *ld, LDAPRequest *lr )
        LDAP_FREE( lr );
 }
 
+/* protected by req_mutex */
 void
 ldap_free_request( LDAP *ld, LDAPRequest *lr )
 {
        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
-
        Debug( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n",
                lr->lr_origid, lr->lr_msgid, 0 );
 
@@ -993,6 +1053,8 @@ static int ldap_int_nextref(
  *  (OUT) hadrefp = 1 if sucessfully followed referral
  *
  * Return value - number of referrals followed
+ *
+ * Protected by res_mutex, conn_mutex and req_mutex    (try_read1msg)
  */
 int
 ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp )
@@ -1008,11 +1070,14 @@ ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char *
        LDAPreqinfo  rinfo;
        LDAP_NEXTREF_PROC       *nextref_proc = ld->ld_nextref_proc ? ld->ld_nextref_proc : ldap_int_nextref;
 
+       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
+       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
+       Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n", 0, 0, 0 );
+
        ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
        *hadrefp = 0;
 
-       Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n", 0, 0, 0 );
-
        unfollowed = NULL;
        rc = count = 0;
 
@@ -1184,10 +1249,8 @@ ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char *
                /* Send the new request to the server - may require a bind */
                rinfo.ri_msgid = origreq->lr_origid;
                rinfo.ri_url = refarray[i];
-               LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
                rc = ldap_send_server_request( ld, ber, id,
-                       origreq, &srv, NULL, &rinfo );
-               LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+                       origreq, &srv, NULL, &rinfo, 0, 1 );
                if ( rc < 0 ) {
                        /* Failure, try next referral in the list */
                        Debug( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n", 
@@ -1208,6 +1271,7 @@ ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char *
                                if ( lc == NULL ) {
                                        ld->ld_errno = LDAP_OPERATIONS_ERROR;
                                        rc = -1;
+                                       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
                                        goto done;
                                }
                        }
@@ -1253,6 +1317,7 @@ done:
 
 /*
  * XXX merging of errors in this routine needs to be improved
+ * Protected by res_mutex, conn_mutex and req_mutex    (try_read1msg)
  */
 int
 ldap_chase_referrals( LDAP *ld,
@@ -1270,6 +1335,9 @@ ldap_chase_referrals( LDAP *ld,
        LDAPreqinfo  rinfo;
        LDAPConn        *lc;
 
+       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
+       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
        Debug( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n", 0, 0, 0 );
 
        ld->ld_errno = LDAP_SUCCESS;    /* optimistic */
@@ -1367,11 +1435,8 @@ ldap_chase_referrals( LDAP *ld,
 
                rinfo.ri_msgid = origreq->lr_origid;
 
-               LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
                rc = ldap_send_server_request( ld, ber, id,
-                       lr, &srv, NULL, &rinfo );
-               LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
-
+                       lr, &srv, NULL, &rinfo, 0, 1 );
                LDAP_FREE( rinfo.ri_url );
 
                if( rc >= 0 ) {
@@ -1561,12 +1626,12 @@ re_encode_request( LDAP *ld,
 }
 
 
+/* protected by req_mutex */
 LDAPRequest *
 ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid )
 {
        LDAPRequest     *lr;
 
-       LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
        for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
                if ( lr->lr_status == LDAP_REQST_COMPLETED ) {
                        continue;       /* Skip completed requests */
@@ -1576,17 +1641,16 @@ ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid )
                        break;
                }
        }
-       LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
 
        return( lr );
 }
 
+/* protected by req_mutex */
 void
 ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit )
 {
        LDAPRequest     *lr;
 
-       LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
        for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
                if ( lr == lrx ) {
                        if ( lr->lr_refcnt > 0 ) {
@@ -1607,5 +1671,4 @@ ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit )
        } else if ( freeit ) {
                ldap_free_request( ld, lrx );
        }
-       LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
 }
index 29c0b6f55e9e8afcb4c1eeecc3caa718dad1b11e..a50d7c2a2971234132f6f8f0df143680f99f132d 100644 (file)
@@ -66,8 +66,8 @@
 #include "ldap_log.h"
 #include "lutil.h"
 
-static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid, int *idx ));
-static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid, int idx ));
+static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
+static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
 static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout,
        LDAPMessage **result ));
 static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid,
@@ -120,6 +120,7 @@ ldap_result(
        return rc;
 }
 
+/* protected by res_mutex */
 static LDAPMessage *
 chkResponseList(
        LDAP *ld,
@@ -144,12 +145,10 @@ chkResponseList(
 
        lastlm = &ld->ld_responses;
        for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
-               int     idx;
-
                nextlm = lm->lm_next;
                ++cnt;
 
-               if ( ldap_abandoned( ld, lm->lm_msgid, &idx ) ) {
+               if ( ldap_abandoned( ld, lm->lm_msgid ) ) {
                        Debug( LDAP_DEBUG_ANY,
                                "response list msg abandoned, "
                                "msgid %d message type %s\n",
@@ -164,7 +163,7 @@ chkResponseList(
                        default:
                                /* there's no need to keep the id
                                 * in the abandoned list any longer */
-                               ldap_mark_abandoned( ld, lm->lm_msgid, idx );
+                               ldap_mark_abandoned( ld, lm->lm_msgid );
                                break;
                        }
 
@@ -231,6 +230,7 @@ chkResponseList(
        return lm;
 }
 
+/* protected by res_mutex */
 static int
 wait4msg(
        LDAP *ld,
@@ -284,9 +284,7 @@ wait4msg(
                if ( ldap_debug & LDAP_DEBUG_TRACE ) {
                        Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
                                (void *)ld, msgid, all );
-                       LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
                        ldap_dump_connection( ld, ld->ld_conns, 1 );
-                       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
                        LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
                        ldap_dump_requests_and_responses( ld );
                        LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
@@ -308,7 +306,6 @@ wait4msg(
                                        break;
                                }
                        }
-                       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
 
                        if ( !lc_ready ) {
                                int err;
@@ -328,6 +325,7 @@ wait4msg(
                                {
                                        ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
                                                LDAP_TIMEOUT);
+                                       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
                                        return( rc );
                                }
 
@@ -349,8 +347,6 @@ wait4msg(
                                {
                                        ldap_int_flush_request( ld, ld->ld_requests );
                                }
-                               LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
-                               LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
                                for ( lc = ld->ld_conns;
                                        rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL;
                                        lc = lnext )
@@ -360,25 +356,22 @@ wait4msg(
                                        {
                                                /* Don't let it get freed out from under us */
                                                ++lc->lconn_refcnt;
-                                               LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
                                                rc = try_read1msg( ld, msgid, all, lc, result );
                                                lnext = lc->lconn_next;
 
                                                /* Only take locks if we're really freeing */
                                                if ( lc->lconn_refcnt <= 1 ) {
-                                                       LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
                                                        ldap_free_connection( ld, lc, 0, 1 );
-                                                       LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
                                                } else {
                                                        --lc->lconn_refcnt;
                                                }
-                                               LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
                                        } else {
                                                lnext = lc->lconn_next;
                                        }
                                }
-                               LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+                               LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
                        }
+                       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
                }
 
                if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
@@ -432,6 +425,7 @@ wait4msg(
 }
 
 
+/* protected by res_mutex, conn_mutex and req_mutex */
 static ber_tag_t
 try_read1msg(
        LDAP *ld,
@@ -443,7 +437,6 @@ try_read1msg(
        BerElement      *ber;
        LDAPMessage     *newmsg, *l, *prev;
        ber_int_t       id;
-       int             idx;
        ber_tag_t       tag;
        ber_len_t       len;
        int             foundit = 0;
@@ -461,6 +454,8 @@ try_read1msg(
        assert( lc != NULL );
        
        LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
+       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
 
        Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
                (void *)ld, msgid, all );
@@ -535,7 +530,7 @@ nextresp3:
        
        /* if it's been abandoned, toss it */
        if ( id > 0 ) {
-               if ( ldap_abandoned( ld, id, &idx ) ) {
+               if ( ldap_abandoned( ld, id ) ) {
                        /* the message type */
                        tag = ber_peek_tag( ber, &len );
                        switch ( tag ) {
@@ -548,7 +543,7 @@ nextresp3:
                        default:
                                /* there's no need to keep the id
                                 * in the abandoned list any longer */
-                               ldap_mark_abandoned( ld, id, idx );
+                               ldap_mark_abandoned( ld, id );
                                break;
                        }
 
@@ -1337,35 +1332,37 @@ ldap_msgdelete( LDAP *ld, int msgid )
  *
  * return the location of the message id in the array of abandoned
  * message ids, or -1
- *
- * expects ld_res_mutex to be locked
  */
 static int
-ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp )
+ldap_abandoned( LDAP *ld, ber_int_t msgid )
 {
-       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
-
-       assert( idxp != NULL );
+       int     ret, idx;
        assert( msgid >= 0 );
 
-       return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
+       LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
+       ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
+       LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
+       return ret;
 }
 
 /*
  * ldap_mark_abandoned
- *
- * expects ld_res_mutex to be locked
  */
 static int
-ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx )
+ldap_mark_abandoned( LDAP *ld, ber_int_t msgid )
 {
-       LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
+       int     ret, idx;
 
-       /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
-       assert( idx >= 0 );
-       assert( (unsigned) idx < ld->ld_nabandoned );
-       assert( ld->ld_abandoned[ idx ] == msgid );
-
-       return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
+       assert( msgid >= 0 );
+       LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
+       ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
+       if (ret <= 0) {         /* error or already deleted by another thread */
+               LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
+               return ret;
+       }
+       /* still in abandoned array, so delete */
+       ret = ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
                msgid, idx );
+       LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
+       return ret;
 }
index 4ddfdfc7a976d1b02429a7deff83907671bddb5d..5b43eee34a74cf8b02ac33deb15cc0fc35cf08b8 100644 (file)
@@ -80,18 +80,44 @@ ldap_ld_free(
        LDAPMessage     *lm, *next;
        int             err = LDAP_SUCCESS;
 
+       LDAP_MUTEX_LOCK( &ld->ld_ldcmutex );
+       /* Someone else is still using this ld. */
+       if (ld->ld_ldcrefcnt > 1) {     /* but not last thread */
+               /* clean up self only */
+               ld->ld_ldcrefcnt--;
+               if ( ld->ld_error != NULL ) {
+                       LDAP_FREE( ld->ld_error );
+                       ld->ld_error = NULL;
+               }
+
+               if ( ld->ld_matched != NULL ) {
+                       LDAP_FREE( ld->ld_matched );
+                       ld->ld_matched = NULL;
+               }
+               if ( ld->ld_referrals != NULL) {
+                       LDAP_VFREE(ld->ld_referrals);
+                       ld->ld_referrals = NULL;
+               }  
+               LDAP_MUTEX_UNLOCK( &ld->ld_ldcmutex );
+               LDAP_FREE( (char *) ld );
+               return( err );
+       }
+
+       /* This ld is the last thread. */
+
        /* free LDAP structure and outstanding requests/responses */
        LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
        while ( ld->ld_requests != NULL ) {
                ldap_free_request( ld, ld->ld_requests );
        }
+       LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+       LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
 
        /* free and unbind from all open connections */
        while ( ld->ld_conns != NULL ) {
                ldap_free_connection( ld, ld->ld_conns, 1, close );
        }
-       LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
-
+       LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
        LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
        for ( lm = ld->ld_responses; lm != NULL; lm = next ) {
                next = lm->lm_next;
@@ -103,6 +129,7 @@ ldap_ld_free(
                ld->ld_abandoned = NULL;
        }
        LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
+       LDAP_MUTEX_LOCK( &ld->ld_ldopts_mutex );
 
        /* final close callbacks */
        {
@@ -126,7 +153,7 @@ ldap_ld_free(
                ld->ld_matched = NULL;
        }
 
-       if( ld->ld_referrals != NULL) {
+       if ( ld->ld_referrals != NULL) {
                LDAP_VFREE(ld->ld_referrals);
                ld->ld_referrals = NULL;
        }  
@@ -188,22 +215,35 @@ ldap_ld_free(
                ldap_controls_free( ld->ld_options.ldo_cctrls );
                ld->ld_options.ldo_cctrls = NULL;
        }
+       LDAP_MUTEX_UNLOCK( &ld->ld_ldopts_mutex );
 
        ber_sockbuf_free( ld->ld_sb );   
    
 #ifdef LDAP_R_COMPILE
+       ldap_pvt_thread_mutex_destroy( &ld->ld_msgid_mutex );
+       ldap_pvt_thread_mutex_destroy( &ld->ld_conn_mutex );
        ldap_pvt_thread_mutex_destroy( &ld->ld_req_mutex );
        ldap_pvt_thread_mutex_destroy( &ld->ld_res_mutex );
-       ldap_pvt_thread_mutex_destroy( &ld->ld_conn_mutex );
+       ldap_pvt_thread_mutex_destroy( &ld->ld_abandon_mutex );
+       ldap_pvt_thread_mutex_destroy( &ld->ld_ldopts_mutex );
+       ldap_pvt_thread_mutex_unlock( &ld->ld_ldcmutex );
+       ldap_pvt_thread_mutex_destroy( &ld->ld_ldcmutex );
 #endif
 #ifndef NDEBUG
        LDAP_TRASH(ld);
 #endif
+       LDAP_FREE( (char *) ld->ldc );
        LDAP_FREE( (char *) ld );
    
        return( err );
 }
 
+int
+ldap_destroy( LDAP *ld )
+{
+       return ( ldap_ld_free( ld, 1, NULL, NULL ) );
+}
+
 int
 ldap_unbind_s( LDAP *ld )
 {
@@ -233,7 +273,7 @@ ldap_send_unbind(
                return( ld->ld_errno );
        }
 
-       id = ++(ld)->ld_msgid;
+       LDAP_NEXT_MSGID(ld, id);
 
        /* fill it in */
        if ( ber_printf( ber, "{itn" /*}*/, id,
index a08e57f72932debf6a4d9d72870ce4b1960bba7a..d956c184cf19aafaf0dc00c2ed0413e2c7ba86ce 100644 (file)
 ## <http://www.OpenLDAP.org/license.html>.
 
 PROGRAMS = slapd-tester slapd-search slapd-read slapd-addel slapd-modrdn \
-               slapd-modify slapd-bind ldif-filter
+               slapd-modify slapd-bind slapd-mtread ldif-filter
 
 SRCS     = slapd-common.c \
                slapd-tester.c slapd-search.c slapd-read.c slapd-addel.c \
-               slapd-modrdn.c slapd-modify.c slapd-bind.c ldif-filter.c
+               slapd-modrdn.c slapd-modify.c slapd-bind.c slapd-mtread.c \
+               ldif-filter.c
 
 LDAP_INCDIR= ../../include
 LDAP_LIBDIR= ../../libraries
 
 XLIBS    = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLUTIL_A) $(LDAP_LIBLBER_LA)
+XRLIBS    = $(LDAP_LIBLDAP_R_LA) $(LDAP_LIBLUTIL_A) $(LDAP_LIBLBER_LA)
 XXLIBS  = $(SECURITY_LIBS) $(LUTIL_LIBS)
+RLIBS = $(XRLIBS) $(XXLIBS) $(AC_LIBS) $(XXXLIBS)
+
 
 OBJS     = slapd-common.o
 
@@ -57,3 +61,6 @@ slapd-bind: slapd-bind.o $(OBJS) $(XLIBS)
 ldif-filter: ldif-filter.o $(XLIBS)
        $(LTLINK) -o $@ ldif-filter.o $(LIBS)
 
+slapd-mtread: slapd-mtread.o $(OBJS) $(XRLIBS)
+       $(LTLINK) -o $@ slapd-mtread.o $(OBJS) $(RLIBS)
+
diff --git a/tests/progs/slapd-mtread.c b/tests/progs/slapd-mtread.c
new file mode 100644 (file)
index 0000000..3ef8b5e
--- /dev/null
@@ -0,0 +1,758 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2009 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Spanier for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * This tool is a MT reader.  It behaves like slapd-read however
+ * with one or more threads simultaneously using the same connection.
+ * If -M is enabled, then M threads will also perfrom write operations.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "ldap_pvt_thread.h"
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+
+#include "ldap.h"
+#include "lutil.h"
+
+#include "ldap_pvt.h"
+
+#include "slapd-common.h"
+
+#define MAXCONN        512
+#define LOOPS  100
+#define RETRIES        0
+#define DEFAULT_BASE   "ou=people,dc=example,dc=com"
+
+static int
+whoami()
+{
+       int me = ldap_pvt_thread_self();
+       return (me);
+}
+
+static void
+do_conn( char *uri, char *manager, struct berval *passwd,
+       LDAP **ld, int nobind, int maxretries, int conn_num );
+
+static void
+do_read( LDAP *ld, char *entry,
+       char **attrs, int noattrs, int nobind, int maxloop,
+       int maxretries, int delay, int force, int chaserefs, int idx );
+
+static void
+do_random( LDAP *ld,
+       char *sbase, char *filter, char **attrs, int noattrs, int nobind,
+       int innerloop, int maxretries, int delay, int force, int chaserefs,
+       int idx );
+
+static void *
+do_onethread( void *arg );
+
+static void *
+do_onerwthread( void *arg );
+
+#define MAX_THREAD     1024
+int    rt_pass[MAX_THREAD];
+int    rt_fail[MAX_THREAD];
+int    rwt_pass[MAX_THREAD];
+int    rwt_fail[MAX_THREAD];
+
+/*
+ * Shared globals (command line args)
+ */
+LDAP           *ld = NULL;
+char           *entry = NULL;
+char           *filter  = NULL;
+int            loops = LOOPS;
+int            outerloops = 1;
+int            retries = RETRIES;
+int            delay = 0;
+int            force = 0;
+int            chaserefs = 0;
+char           *srchattrs[] = { "1.1", NULL };
+char           **attrs = srchattrs;
+int            noattrs = 0;
+int            nobind = 0;
+int            threads = 1;
+int            rwthreads = 0;
+int            verbose = 0;
+
+int            noconns = 1;
+LDAP           **lds = NULL;
+
+static void
+thread_error(char *string)
+{
+       char            thrstr[BUFSIZ];
+
+       snprintf(thrstr, BUFSIZ, "error on tid: %d: %s", whoami(), string);
+       tester_error( thrstr );
+}
+
+static void
+thread_output(char *string)
+{
+       char            thrstr[BUFSIZ];
+
+       snprintf(thrstr, BUFSIZ, "tid: %d says: %s", whoami(), string);
+       tester_error( thrstr );
+}
+
+static void
+thread_verbose(char *string)
+{
+       char            thrstr[BUFSIZ];
+
+       if (!verbose)
+               return;
+       snprintf(thrstr, BUFSIZ, "tid: %d says: %s", whoami(), string);
+       tester_error( thrstr );
+}
+
+static void
+usage( char *name )
+{
+        fprintf( stderr,
+               "usage: %s "
+               "-H <uri> | ([-h <host>] -p <port>) "
+               "-D <manager> "
+               "-w <passwd> "
+               "-e <entry> "
+               "[-A] "
+               "[-C] "
+               "[-F] "
+               "[-N] "
+               "[-v] "
+               "[-c connections] "
+               "[-f filter] "
+               "[-i <ignore>] "
+               "[-l <loops>] "
+               "[-L <outerloops>] "
+               "[-m threads] "
+               "[-M threads] "
+               "[-r <maxretries>] "
+               "[-t <delay>] "
+               "[-T <attrs>] "
+               "[<attrs>] "
+               "\n",
+               name );
+       exit( EXIT_FAILURE );
+}
+
+int
+main( int argc, char **argv )
+{
+       int             i;
+       char            *uri = NULL;
+       char            *host = "localhost";
+       int             port = -1;
+       char            *manager = NULL;
+       struct berval   passwd = { 0, NULL };
+       ldap_pvt_thread_t       rtid[MAX_THREAD], rwtid[MAX_THREAD];
+       char            outstr[BUFSIZ];
+       int             ptpass;
+       int             testfail = 0;
+
+
+       tester_init( "slapd-mtread", TESTER_READ );
+
+       /* by default, tolerate referrals and no such object */
+       tester_ignore_str2errlist( "REFERRAL,NO_SUCH_OBJECT" );
+
+       while ( (i = getopt( argc, argv, "ACc:D:e:Ff:H:h:i:L:l:M:m:p:r:t:T:w:v" )) != EOF ) {
+               switch ( i ) {
+               case 'A':
+                       noattrs++;
+                       break;
+
+               case 'C':
+                       chaserefs++;
+                       break;
+
+               case 'H':               /* the server uri */
+                       uri = strdup( optarg );
+                       break;
+
+               case 'h':               /* the servers host */
+                       host = strdup( optarg );
+
+               case 'i':
+                       tester_ignore_str2errlist( optarg );
+                       break;
+
+               case 'N':
+                       nobind++;
+                       break;
+
+               case 'v':
+                       verbose++;
+                       break;
+
+               case 'p':               /* the servers port */
+                       if ( lutil_atoi( &port, optarg ) != 0 ) {
+                               usage( argv[0] );
+                       }
+                       break;
+
+               case 'D':               /* the servers manager */
+                       manager = strdup( optarg );
+                       break;
+
+               case 'w':               /* the server managers password */
+                       passwd.bv_val = strdup( optarg );
+                       passwd.bv_len = strlen( optarg );
+                       memset( optarg, '*', passwd.bv_len );
+                       break;
+
+               case 'c':               /* the number of connections */
+                       if ( lutil_atoi( &noconns, optarg ) != 0 ) {
+                               usage( argv[0] );
+                       }
+                       break;
+
+               case 'e':               /* DN to search for */
+                       entry = strdup( optarg );
+                       break;
+
+               case 'f':               /* the search request */
+                       filter = strdup( optarg );
+                       break;
+
+               case 'F':
+                       force++;
+                       break;
+
+               case 'l':               /* the number of loops */
+                       if ( lutil_atoi( &loops, optarg ) != 0 ) {
+                               usage( argv[0] );
+                       }
+                       break;
+
+               case 'L':               /* the number of outerloops */
+                       if ( lutil_atoi( &outerloops, optarg ) != 0 ) {
+                               usage( argv[0] );
+                       }
+                       break;
+
+               case 'M':               /* the number of R/W threads */
+                       if ( lutil_atoi( &rwthreads, optarg ) != 0 ) {
+                               usage( argv[0] );
+                       }
+                       if (rwthreads > MAX_THREAD)
+                               rwthreads = MAX_THREAD;
+                       break;
+
+               case 'm':               /* the number of threads */
+                       if ( lutil_atoi( &threads, optarg ) != 0 ) {
+                               usage( argv[0] );
+                       }
+                       if (threads > MAX_THREAD)
+                               threads = MAX_THREAD;
+                       break;
+
+               case 'r':               /* the number of retries */
+                       if ( lutil_atoi( &retries, optarg ) != 0 ) {
+                               usage( argv[0] );
+                       }
+                       break;
+
+               case 't':               /* delay in seconds */
+                       if ( lutil_atoi( &delay, optarg ) != 0 ) {
+                               usage( argv[0] );
+                       }
+                       break;
+
+               case 'T':
+                       attrs = ldap_str2charray( optarg, "," );
+                       if ( attrs == NULL ) {
+                               usage( argv[0] );
+                       }
+                       break;
+
+               default:
+                       usage( argv[0] );
+                       break;
+               }
+       }
+
+       if (( entry == NULL ) || ( port == -1 && uri == NULL ))
+               usage( argv[0] );
+
+       if ( *entry == '\0' ) {
+               fprintf( stderr, "%s: invalid EMPTY entry DN.\n",
+                               argv[0] );
+               exit( EXIT_FAILURE );
+       }
+
+       if ( argv[optind] != NULL ) {
+               attrs = &argv[optind];
+       }
+
+       if (noconns < 1)
+               noconns = 1;
+       if (noconns > MAXCONN)
+               noconns = MAXCONN;
+       lds = (LDAP **) calloc( sizeof(LDAP *), noconns);
+       if (lds == NULL) {
+               fprintf( stderr, "%s: Memory error: calloc noconns.\n",
+                               argv[0] );
+               exit( EXIT_FAILURE );
+       }
+
+       uri = tester_uri( uri, host, port );
+       /* One connection and one connection only */
+       do_conn( uri, manager, &passwd, &ld, nobind, retries, 0 );
+       lds[0] = ld;
+       for(i = 1; i < noconns; i++) {
+               do_conn( uri, manager, &passwd, &lds[i], nobind, retries, i );
+       }
+
+       ldap_pvt_thread_initialize();
+
+       snprintf(outstr, BUFSIZ, "MT Test Start: conns: %d (%s)", noconns, uri);
+       tester_error(outstr);
+       snprintf(outstr, BUFSIZ, "Threads: RO: %d RW: %d", threads, rwthreads);
+       tester_error(outstr);
+
+       /* Set up read only threads */
+       for ( i = 0; i < threads; i++ ) {
+               ldap_pvt_thread_create( &rtid[i], 0, do_onethread, (void*)i);
+               snprintf(outstr, BUFSIZ, "Created RO thread %d [%d]", i, rtid[i]);
+               thread_verbose(outstr);
+       }
+       /* Set up read/write threads */
+       for ( i = 0; i < rwthreads; i++ ) {
+               ldap_pvt_thread_create( &rwtid[i], 0, do_onerwthread, (void*)i);
+               snprintf(outstr, BUFSIZ, "Created RW thread %d [%d]", i, rwtid[i]);
+               thread_verbose(outstr);
+       }
+
+       ptpass =  outerloops * loops;
+
+       /* wait for read only threads to complete */
+       for ( i = 0; i < threads; i++ )
+               ldap_pvt_thread_join(rtid[i], NULL);
+       /* wait for read/write threads to complete */
+       for ( i = 0; i < rwthreads; i++ )
+               ldap_pvt_thread_join(rwtid[i], NULL);
+
+       for(i = 0; i < noconns; i++) {
+               if ( lds[i] != NULL ) {
+                       ldap_unbind_ext( lds[i], NULL, NULL );
+               }
+       }
+       free( lds );
+
+       for ( i = 0; i < threads; i++ ) {
+               snprintf(outstr, BUFSIZ, "RO thread %d pass=%d fail=%d", i,
+                       rt_pass[i], rt_fail[i]);
+               tester_error(outstr);
+               if (rt_fail[i] != 0 || rt_pass[i] != ptpass) {
+                       snprintf(outstr, BUFSIZ, "FAIL RO thread %d", i);
+                       tester_error(outstr);
+                       testfail++;
+               }
+       }
+       for ( i = 0; i < rwthreads; i++ ) {
+               snprintf(outstr, BUFSIZ, "RW thread %d pass=%d fail=%d", i,
+                       rwt_pass[i], rwt_fail[i]);
+               tester_error(outstr);
+               if (rwt_fail[i] != 0 || rwt_pass[i] != ptpass) {
+                       snprintf(outstr, BUFSIZ, "FAIL RW thread %d", i);
+                       tester_error(outstr);
+                       testfail++;
+               }
+       }
+       snprintf(outstr, BUFSIZ, "MT Test complete" );
+       tester_error(outstr);
+
+       if (testfail)
+               exit( EXIT_FAILURE );
+       exit( EXIT_SUCCESS );
+}
+
+static void *
+do_onethread( void *arg )
+{
+       int             i, j, thisconn;
+       LDAP            **mlds;
+       int             me = whoami();
+       char            thrstr[BUFSIZ];
+       int             rc, refcnt = 0;
+       int             myidx = (int)arg;
+
+       mlds = (LDAP **) calloc( sizeof(LDAP *), noconns);
+       if (mlds == NULL) {
+               thread_error( "Memory error: thread calloc for noconns" );
+               exit( EXIT_FAILURE );
+       }
+
+       for ( j = 0; j < outerloops; j++ ) {
+               for(i = 0; i < noconns; i++) {
+                       mlds[i] = ldap_dup(lds[i]);
+                       if (mlds[i] == NULL) {
+                               thread_error( "ldap_dup error" );
+                       }
+               }
+               rc = ldap_get_option(mlds[0], LDAP_OPT_SESSION_REFCNT, &refcnt);
+               snprintf(thrstr, BUFSIZ,
+                       "RO Thread: %d conns: %d refcnt: %d (rc = %d)",
+                       me, noconns, refcnt, rc);
+               thread_verbose(thrstr);
+
+               thisconn = (me + j) % noconns;
+               if (thisconn < 0 || thisconn >= noconns)
+                       thisconn = 0;
+               if (mlds[thisconn] == NULL) {
+                       thread_error("(failed to dup)");
+                       tester_perror( "ldap_dup", "(failed to dup)" );
+                       exit( EXIT_FAILURE );
+               }
+               snprintf(thrstr, BUFSIZ, "Thread %d using conn %d", me, thisconn);
+               thread_verbose(thrstr);
+               if ( filter != NULL ) {
+                       do_random( mlds[thisconn], entry, filter, attrs,
+                               noattrs, nobind, loops, retries, delay, force,
+                               chaserefs, myidx );
+
+               } else {
+                       do_read( mlds[thisconn], entry, attrs,
+                               noattrs, nobind, loops, retries, delay, force,
+                               chaserefs, myidx );
+               }
+               for(i = 0; i < noconns; i++) {
+                       (void) ldap_destroy(mlds[i]);
+                       mlds[i] = NULL;
+               }
+       }
+       free( mlds );
+       return( NULL );
+}
+
+static void *
+do_onerwthread( void *arg )
+{
+       int             i, j, thisconn;
+       LDAP            **mlds, *ld;
+       int             me = whoami();
+       char            thrstr[BUFSIZ];
+       char            dn[256], uids[32], cns[32], *base;
+       LDAPMod         *attrp[5], attrs[4];
+       char            *oc_vals[] = { "top", "OpenLDAPperson", NULL };
+       char            *cn_vals[] = { NULL, NULL };
+       char            *sn_vals[] = { NULL, NULL };
+       char            *uid_vals[] = { NULL, NULL };
+       int             ret;
+       int             adds = 0;
+       int             dels = 0;
+       int             rc, refcnt = 0;
+       int             myidx = (int)arg;
+
+       mlds = (LDAP **) calloc( sizeof(LDAP *), noconns);
+       if (mlds == NULL) {
+               thread_error( "Memory error: thread calloc for noconns" );
+               exit( EXIT_FAILURE );
+       }
+
+       snprintf(uids, 32, "rwtest%04.4d", me);
+       snprintf(cns, 32, "rwtest%04.4d", me);
+       /* add setup */
+       for (i = 0; i < 4; i++) {
+               attrp[i] = &attrs[i];
+               attrs[i].mod_op = 0;
+       }
+       attrp[4] = NULL;
+       attrs[0].mod_type = "objectClass";
+       attrs[0].mod_values = oc_vals;
+       attrs[1].mod_type = "cn";
+       attrs[1].mod_values = cn_vals;
+       cn_vals[0] = &cns[0];
+       attrs[2].mod_type = "sn";
+       attrs[2].mod_values = sn_vals;
+       sn_vals[0] = &cns[0];
+       attrs[3].mod_type = "uid";
+       attrs[3].mod_values = uid_vals;
+       uid_vals[0] = &uids[0];
+
+       for ( j = 0; j < outerloops; j++ ) {
+               for(i = 0; i < noconns; i++) {
+                       mlds[i] = ldap_dup(lds[i]);
+                       if (mlds[i] == NULL) {
+                               thread_error( "ldap_dup error" );
+                       }
+               }
+               rc = ldap_get_option(mlds[0], LDAP_OPT_SESSION_REFCNT, &refcnt);
+               snprintf(thrstr, BUFSIZ,
+                       "RW Thread: %d conns: %d refcnt: %d (rc = %d)",
+                       me, noconns, refcnt, rc);
+               thread_verbose(thrstr);
+
+               thisconn = (me + j) % noconns;
+               if (thisconn < 0 || thisconn >= noconns)
+                       thisconn = 0;
+               if (mlds[thisconn] == NULL) {
+                       thread_error("(failed to dup)");
+                       tester_perror( "ldap_dup", "(failed to dup)" );
+                       exit( EXIT_FAILURE );
+               }
+               snprintf(thrstr, BUFSIZ, "START RW Thread %d using conn %d",
+                       me, thisconn);
+               thread_verbose(thrstr);
+
+               ld = mlds[thisconn];
+               if (entry != NULL)
+                       base = entry;
+               else
+                       base = DEFAULT_BASE;
+               snprintf(dn, 256, "cn=%s,%s", cns, base);
+
+               adds = 0;
+               dels = 0;
+               for (i = 0; i < loops; i++) {
+                       ret = ldap_add_ext_s(ld, dn, &attrp[0], NULL, NULL);
+                       if (ret == LDAP_SUCCESS) {
+                               adds++;
+                               ret = ldap_delete_ext_s(ld, dn, NULL, NULL);
+                               if (ret == LDAP_SUCCESS) {
+                                       dels++;
+                                       rwt_pass[myidx]++;
+                               } else {
+                                       thread_output(ldap_err2string(ret));
+                                       rwt_fail[myidx]++;
+                               }
+                       } else {
+                               thread_output(ldap_err2string(ret));
+                               rwt_fail[myidx]++;
+                       }
+               }
+
+               snprintf(thrstr, BUFSIZ,
+                       "INNER STOP RW Thread %d using conn %d (%d/%d)",
+                       me, thisconn, adds, dels);
+               thread_verbose(thrstr);
+
+               for(i = 0; i < noconns; i++) {
+                       (void) ldap_destroy(mlds[i]);
+                       mlds[i] = NULL;
+               }
+       }
+
+       free( mlds );
+       return( NULL );
+}
+
+static void
+do_conn( char *uri, char *manager, struct berval *passwd,
+       LDAP **ldp, int nobind, int maxretries, int conn_num )
+{
+       LDAP    *ld = NULL;
+       int     version = LDAP_VERSION3;
+       int     i = 0, do_retry = maxretries;
+       int     rc = LDAP_SUCCESS;
+       char    thrstr[BUFSIZ];
+
+retry:;
+       ldap_initialize( &ld, uri );
+       if ( ld == NULL ) {
+               snprintf( thrstr, BUFSIZ, "connection: %d", conn_num );
+               tester_error( thrstr );
+               tester_perror( "ldap_initialize", NULL );
+               exit( EXIT_FAILURE );
+       }
+
+       (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version ); 
+       (void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
+               chaserefs ? LDAP_OPT_ON : LDAP_OPT_OFF );
+
+       if ( do_retry == maxretries ) {
+               snprintf( thrstr, BUFSIZ, "do_conn #%d\n", conn_num );
+               thread_verbose( thrstr );
+       }
+
+       if ( nobind == 0 ) {
+               rc = ldap_sasl_bind_s( ld, manager, LDAP_SASL_SIMPLE, passwd, NULL, NULL, NULL );
+               if ( rc != LDAP_SUCCESS ) {
+                       snprintf( thrstr, BUFSIZ, "connection: %d", conn_num );
+                       tester_error( thrstr );
+                       tester_ldap_error( ld, "ldap_sasl_bind_s", NULL );
+                       switch ( rc ) {
+                       case LDAP_BUSY:
+                       case LDAP_UNAVAILABLE:
+                               if ( do_retry > 0 ) {
+                                       ldap_unbind_ext( ld, NULL, NULL );
+                                       ld = NULL;
+                                       do_retry--;
+                                       if ( delay != 0 ) {
+                                           sleep( delay );
+                                       }
+                                       goto retry;
+                               }
+                       /* fallthru */
+                       default:
+                               break;
+                       }
+                       exit( EXIT_FAILURE );
+               }
+       }
+       *ldp = ld;
+}
+
+static void
+do_random( LDAP *ld,
+       char *sbase, char *filter, char **srchattrs, int noattrs, int nobind,
+       int innerloop, int maxretries, int delay, int force, int chaserefs,
+       int idx )
+{
+       int     i = 0, do_retry = maxretries;
+       char    *attrs[ 2 ];
+       int     rc = LDAP_SUCCESS;
+       int     nvalues = 0;
+       char    **values = NULL;
+       LDAPMessage *res = NULL, *e = NULL;
+       char    thrstr[BUFSIZ];
+
+       attrs[ 0 ] = LDAP_NO_ATTRS;
+       attrs[ 1 ] = NULL;
+
+       snprintf( thrstr, BUFSIZ,
+                       "Read(%d): base=\"%s\", filter=\"%s\".\n",
+                       innerloop, sbase, filter );
+       thread_verbose( thrstr );
+
+       rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE,
+               filter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res );
+       switch ( rc ) {
+       case LDAP_SIZELIMIT_EXCEEDED:
+       case LDAP_TIMELIMIT_EXCEEDED:
+       case LDAP_SUCCESS:
+               nvalues = ldap_count_entries( ld, res );
+               if ( nvalues == 0 ) {
+                       if ( rc ) {
+                               tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+                       }
+                       break;
+               }
+
+               values = malloc( ( nvalues + 1 ) * sizeof( char * ) );
+               for ( i = 0, e = ldap_first_entry( ld, res ); e != NULL; i++, e = ldap_next_entry( ld, e ) )
+               {
+                       values[ i ] = ldap_get_dn( ld, e );
+               }
+               values[ i ] = NULL;
+
+               ldap_msgfree( res );
+
+               if ( do_retry == maxretries ) {
+                       snprintf( thrstr, BUFSIZ,
+                               "Read base=\"%s\" filter=\"%s\" got %d values.\n",
+                               sbase, filter, nvalues );
+                       thread_verbose( thrstr );
+               }
+
+               for ( i = 0; i < innerloop; i++ ) {
+                       int     r = ((double)nvalues)*rand()/(RAND_MAX + 1.0);
+
+                       do_read( ld, values[ r ],
+                               srchattrs, noattrs, nobind, 1, maxretries,
+                               delay, force, chaserefs, idx );
+               }
+               for( i = 0; i < nvalues; i++) {
+                       if (values[i] != NULL)
+                               free( values[i] );
+               }
+               free( values );
+               break;
+
+       default:
+               tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+               break;
+       }
+
+       snprintf( thrstr, BUFSIZ, "Search done (%d).\n", rc );
+       thread_verbose( thrstr );
+}
+
+static void
+do_read( LDAP *ld, char *entry,
+       char **attrs, int noattrs, int nobind, int maxloop,
+       int maxretries, int delay, int force, int chaserefs, int idx )
+{
+       int     i = 0, do_retry = maxretries;
+       int     rc = LDAP_SUCCESS;
+       char    thrstr[BUFSIZ];
+
+retry:;
+       if ( do_retry == maxretries ) {
+               snprintf( thrstr, BUFSIZ, "Read(%d): entry=\"%s\".\n",
+                       maxloop, entry );
+               thread_verbose( thrstr );
+       }
+
+       snprintf(thrstr, BUFSIZ, "tid: %d LD %x cnt: %d (retried %d) (%s)", \
+                whoami(), ld, maxloop, (do_retry - maxretries), entry);
+       thread_verbose( thrstr );
+
+       for ( ; i < maxloop; i++ ) {
+               LDAPMessage *res = NULL;
+
+               rc = ldap_search_ext_s( ld, entry, LDAP_SCOPE_BASE,
+                               NULL, attrs, noattrs, NULL, NULL, NULL,
+                               LDAP_NO_LIMIT, &res );
+               if ( res != NULL ) {
+                       ldap_msgfree( res );
+               }
+
+               if ( rc == 0 ) {
+                       rt_pass[idx]++;
+               } else {
+                       int             first = tester_ignore_err( rc );
+                       char            buf[ BUFSIZ ];
+
+                       rt_fail[idx]++;
+                       snprintf( buf, sizeof( buf ), "ldap_search_ext_s(%s)", entry );
+
+                       /* if ignore.. */
+                       if ( first ) {
+                               /* only log if first occurrence */
+                               if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
+                                       tester_ldap_error( ld, buf, NULL );
+                               }
+                               continue;
+                       }
+
+                       /* busy needs special handling */
+                       tester_ldap_error( ld, buf, NULL );
+                       if ( rc == LDAP_BUSY && do_retry > 0 ) {
+                               do_retry--;
+                               goto retry;
+                       }
+                       break;
+               }
+       }
+}
index 48d8c9a3e9803993cf8e24b2fe722ba11aa3c334..4f29c464afb7bbac98a6bc7bb393c23ec2d4f507 100755 (executable)
@@ -192,6 +192,7 @@ LDAPCOMPARE="$CLIENTDIR/ldapcompare $TOOLARGS"
 LDAPEXOP="$CLIENTDIR/ldapexop $TOOLARGS"
 SLAPDTESTER=$PROGDIR/slapd-tester
 LDIFFILTER=$PROGDIR/ldif-filter
+SLAPDMTREAD=$PROGDIR/slapd-mtread
 LVL=${SLAPD_DEBUG-0x4105}
 LOCALHOST=localhost
 BASEPORT=${SLAPD_BASEPORT-9010}
@@ -311,6 +312,8 @@ SLAVE2OUT=$SERVER3OUT
 SLAVEFLT=$SERVER2FLT
 SLAVE2FLT=$SERVER3FLT
 
+MTREADOUT=$TESTDIR/mtread.out
+
 # original outputs for cmp
 PROXYCACHEOUT=$DATADIR/proxycache.out
 REFERRALOUT=$DATADIR/referrals.out
diff --git a/tests/scripts/test060-mt-hot b/tests/scripts/test060-mt-hot
new file mode 100755 (executable)
index 0000000..fff6c72
--- /dev/null
@@ -0,0 +1,289 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2009 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+#
+# Populate and start up slapd server with some random data
+#
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND $MONITORDB < $MCONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+       echo "slapadd failed ($RC)!"
+       exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+. $CONFFILTER $BACKEND $MONITORDB < $CONF > $CONF1
+$SLAPINDEX -f $CONF1
+RC=$?
+if test $RC != 0 ; then
+       echo "warning: slapindex failed ($RC)"
+       echo "  assuming no indexing support"
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+echo $SLAPD -f $CONF1 -h $URI1 -d $LVL $TIMING
+$SLAPD -f $CONF1 -h $URI1 -d $LVL $TIMING > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+    echo PID $PID
+    read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+# Perform a basic search, make sure of a functional setup
+echo "Testing basic monitor search..."
+for i in 0 1 2 3 4 5; do
+       $LDAPSEARCH -s base -b "$MONITORDN" -h $LOCALHOST -p $PORT1 \
+               '(objectclass=*)' > /dev/null 2>&1
+       RC=$?
+       if test $RC = 0 ; then
+               break
+       fi
+       echo "Waiting 5 seconds for slapd to start..."
+       sleep 5
+done
+
+if test $RC != 0 ; then
+       echo "mt-hot read failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+cat /dev/null > $MTREADOUT
+
+echo "Monitor searches"
+# Perform a basic single threaded search on a single connection
+THR=1
+OUTER=1
+INNER=50000
+echo "Testing basic mt-hot search: $THR threads ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$MONITORDN" \
+       -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$MONITORDN" -f "(objectclass=*)" \
+       -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "slapd-mtread failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+# Perform a basic multi-threaded search on a single connection
+THR=5
+OUTER=1
+INNER=10000
+echo "Testing basic mt-hot search: $THR threads ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$MONITORDN" \
+       -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$MONITORDN" -f "(objectclass=*)" \
+       -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "slapd-mtread failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+# Perform a basic multi-threaded search on a single connection
+THR=100
+OUTER=5
+INNER=100
+echo "Testing basic mt-hot search: $THR threads ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$MONITORDN" \
+       -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$MONITORDN" -f "(objectclass=*)" \
+       -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "slapd-mtread failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+# Perform a single threaded random DB search on a single connection
+echo "Random searches"
+THR=1
+OUTER=1
+INNER=50000
+echo "Testing random mt-hot search: $THR threads ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$BASEDN" -f "(objectclass=*)" \
+       -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$BASEDN" -f "(objectclass=*)" \
+       -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "slapd-mtread failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+# Perform a multi-threaded random DB search on a single connection
+THR=5
+OUTER=1
+INNER=10000
+echo "Testing random mt-hot search: $THR threads ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$BASEDN" -f "(objectclass=*)" \
+       -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$BASEDN" -f "(objectclass=*)" \
+       -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "slapd-mtread failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+# Perform a multi-threaded random DB search on a single connection
+THR=100
+OUTER=5
+INNER=100
+echo "Testing random mt-hot search: $THR threads ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$BASEDN" -f "(objectclass=*)" \
+       -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$BASEDN" -f "(objectclass=*)" \
+       -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "slapd-mtread failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+# Perform a basic multi-threaded search using multiple connections
+echo "Multiple threads and connection searches"
+CONN=5
+THR=5
+OUTER=1
+INNER=10000
+echo "Testing basic mt-hot search: $THR threads $CONN conns ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$MONITORDN" \
+       -c $CONN -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$MONITORDN" -f "(objectclass=*)" \
+       -c $CONN -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "slapd-mtread failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+# Perform a basic multi-threaded search using multiple connections
+CONN=5
+THR=50
+OUTER=5
+INNER=1000
+echo "Testing basic mt-hot search: $THR threads $CONN conns ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$MONITORDN" \
+       -c $CONN -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$MONITORDN" -f "(objectclass=*)" \
+       -c $CONN -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "slapd-mtread failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+# Perform a multi-threaded random DB search using multiple connections
+CONN=5
+THR=100
+OUTER=5
+INNER=100
+echo "Testing random mt-hot search: $THR threads $CONN conns ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$BASEDN" -f "(objectclass=*)" \
+       -c $CONN -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$BASEDN" -f "(objectclass=*)" \
+       -c $CONN -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "slapd-mtread failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+# Perform a multi-threaded random reads and writes using single connection
+CONN=1
+THR=10
+WTHR=10
+OUTER=5
+INNER=100
+echo "Testing random mt-hot r/w search: $THR read threads $WTHR write threads $CONN conns ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$BASEDN" -f "(&(!(cn=rwtest*))(objectclass=*))" \
+       -c $CONN -m $THR -M $WTHR -L $OUTER -l $INNER
+$SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$BASEDN" -f "(&(!(cn=rwtest*))(objectclass=*))" \
+       -c $CONN -m $THR -M $WTHR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "slapd-mtread failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+# Perform a multi-threaded random reads and writes using multiple connections
+CONN=5
+THR=10
+WTHR=10
+OUTER=5
+INNER=100
+echo "Testing random mt-hot r/w search: $THR read threads $WTHR write threads $CONN conns ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$BASEDN" -f "(&(!(cn=rwtest*))(objectclass=*))" \
+       -c $CONN -m $THR -M $WTHR -L $OUTER -l $INNER
+$SLAPDMTREAD -h $LOCALHOST -p $PORT1 -D "$MANAGERDN" -w $PASSWD \
+       -e "$BASEDN" -f "(&(!(cn=rwtest*))(objectclass=*))" \
+       -c $CONN -m $THR -M $WTHR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "slapd-mtread failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+exit 0