--- /dev/null
+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]
--- /dev/null
+.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
--- /dev/null
+ldap_destroy.3
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.
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 )
#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
LDAP **ldp,
LDAP_CONST char *url ));
+LDAP_F( LDAP * )
+ldap_dup LDAP_P((
+ LDAP *old ));
+
/*
* in tls.c
*/
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 */
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 )
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 )
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 )
#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)
/*************************************
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
#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 */
* 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};
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
{
}
}
- /* 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;
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 );
}
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 ) {
}
}
}
- 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;
#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
*/
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
gopts->ldo_keepalive_idle = 0;
gopts->ldo_valid = LDAP_INITIALIZED;
+ LDAP_MUTEX_UNLOCK( &gopts->ldo_mutex );
return;
}
/*
* structure representing get/set'able options
* which have global defaults.
+ * Protect access to this struct with ldo_mutex
*/
struct ldapoptions {
short ldo_valid;
#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* */
* 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
#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 )
#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
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 );
#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;
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 );
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;
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;
}
}
+ 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 );
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:
return LDAP_SUCCESS;
}
+/* Protected by ld_conn_mutex */
int
ldap_int_open_connection(
LDAP *ld,
int rc;
LDAPConn *c;
LDAPRequest *lr;
+ LDAP *ld;
- rc = ldap_create( ldp );
+ rc = ldap_create( &ld );
if( rc != LDAP_SUCCESS ) {
*ldp = NULL;
return( rc );
/* 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 );
}
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 );
#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 );
+}
void *outvalue)
{
struct ldapoptions *lo;
+ int rc = LDAP_OPT_ERROR;
/* Get pointer to global option structure */
lo = LDAP_INT_GLOBAL_OPT();
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;
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:
}
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 ) {
} 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:
{
}
}
}
- return LDAP_OPT_SUCCESS;
+ rc = LDAP_OPT_SUCCESS;
+ break;
case LDAP_OPT_RESULT_CODE:
if(ld == NULL) {
break;
}
* (int *) outvalue = ld->ld_errno;
- return LDAP_OPT_SUCCESS;
+ rc = LDAP_OPT_SUCCESS;
+ break;
case LDAP_OPT_DIAGNOSTIC_MESSAGE:
if(ld == NULL) {
} 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) {
} 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) {
} 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;
}
}
}
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
{
struct ldapoptions *lo;
int *dbglvl = NULL;
+ int rc = LDAP_OPT_ERROR;
/* Get pointer to global option structure */
lo = LDAP_INT_GLOBAL_OPT();
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) {
} 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) {
} 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;
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 =
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,
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,
ldap_free_urllist(lo->ldo_defludp);
lo->ldo_defludp = ludlist;
}
- return rc;
+ break;
}
case LDAP_OPT_DEFBASE: {
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 ) {
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 ) {
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 ) {
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:
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 */
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;
break;
}
lo->ldo_version = vers;
- } return LDAP_OPT_SUCCESS;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
case LDAP_OPT_RESULT_CODE: {
int err = * (const int *) invalue;
}
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:
{
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
#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 ));
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,
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,
#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,
{
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 */
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,
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;
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 );
}
}
}
/* 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:
/* Forget about the bind */
--parentreq->lr_outrefcnt;
}
+ LDAP_CONN_UNLOCK_IF(m_noconn);
return( -1 );
}
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;
}
}
{
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 ) {
/* Forget about the bind */
--parentreq->lr_outrefcnt;
}
+ LDAP_CONN_UNLOCK_IF(m_noconn);
return( -1 );
}
lr->lr_msgid = msgid;
msgid = -1;
}
+ LDAP_CONN_UNLOCK_IF(m_noconn);
return( msgid );
}
}
/*
- * 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) );
/*
}
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 );
++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;
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
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;
"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 ) {
}
}
}
- 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;
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 )
/*
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,
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;
}
prevlc = tmplc;
}
- LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
/* process connection callbacks */
{
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 ) {
}
+/* Protects self with ld_conn_mutex */
#ifdef LDAP_DEBUG
void
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",
break;
}
}
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
}
+/* protected by req_mutex and res_mutex */
void
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
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 );
* (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 )
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;
/* 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",
if ( lc == NULL ) {
ld->ld_errno = LDAP_OPERATIONS_ERROR;
rc = -1;
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
goto 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,
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 */
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 ) {
}
+/* 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 */
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 ) {
} else if ( freeit ) {
ldap_free_request( ld, lrx );
}
- LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
}
#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,
return rc;
}
+/* protected by res_mutex */
static LDAPMessage *
chkResponseList(
LDAP *ld,
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",
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;
}
return lm;
}
+/* protected by res_mutex */
static int
wait4msg(
LDAP *ld,
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 );
break;
}
}
- LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
if ( !lc_ready ) {
int err;
{
ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
LDAP_TIMEOUT);
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
return( rc );
}
{
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 )
{
/* 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 ) {
}
+/* protected by res_mutex, conn_mutex and req_mutex */
static ber_tag_t
try_read1msg(
LDAP *ld,
BerElement *ber;
LDAPMessage *newmsg, *l, *prev;
ber_int_t id;
- int idx;
ber_tag_t tag;
ber_len_t len;
int foundit = 0;
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 );
/* 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 ) {
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;
}
*
* 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;
}
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;
ld->ld_abandoned = NULL;
}
LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
+ LDAP_MUTEX_LOCK( &ld->ld_ldopts_mutex );
/* final close callbacks */
{
ld->ld_matched = NULL;
}
- if( ld->ld_referrals != NULL) {
+ if ( ld->ld_referrals != NULL) {
LDAP_VFREE(ld->ld_referrals);
ld->ld_referrals = NULL;
}
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 )
{
return( ld->ld_errno );
}
- id = ++(ld)->ld_msgid;
+ LDAP_NEXT_MSGID(ld, id);
/* fill it in */
if ( ber_printf( ber, "{itn" /*}*/, id,
## <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
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)
+
--- /dev/null
+/* $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;
+ }
+ }
+}
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}
SLAVEFLT=$SERVER2FLT
SLAVE2FLT=$SERVER3FLT
+MTREADOUT=$TESTDIR/mtread.out
+
# original outputs for cmp
PROXYCACHEOUT=$DATADIR/proxycache.out
REFERRALOUT=$DATADIR/referrals.out
--- /dev/null
+#! /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