From 0f30db1c46cf576230cd9397f0d5ad2892da7325 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Fri, 22 Oct 2010 21:45:48 +0000 Subject: [PATCH] ITS#6625 concurrency patch from Doug Leavitt @ Oracle .com --- ...aft-zeilenga-ldap-c-api-concurrency-xx.txt | 714 +++++++++++++++++ doc/man/man3/ldap_dup.3 | 126 +++ doc/man/man3/ldap_dup.3.links | 1 + doc/man/man3/ldap_get_option.3 | 9 + include/ldap.h | 13 +- include/ldap_int_thread.h | 30 +- libraries/libldap/abandon.c | 15 +- libraries/libldap/cyrus.c | 4 +- libraries/libldap/init.c | 10 +- libraries/libldap/ldap-int.h | 112 ++- libraries/libldap/open.c | 73 +- libraries/libldap/options.c | 282 ++++--- libraries/libldap/request.c | 169 ++-- libraries/libldap/result.c | 69 +- libraries/libldap/unbind.c | 50 +- tests/progs/Makefile.in | 11 +- tests/progs/slapd-mtread.c | 758 ++++++++++++++++++ tests/scripts/defines.sh | 3 + tests/scripts/test060-mt-hot | 289 +++++++ 19 files changed, 2494 insertions(+), 244 deletions(-) create mode 100644 doc/drafts/draft-zeilenga-ldap-c-api-concurrency-xx.txt create mode 100644 doc/man/man3/ldap_dup.3 create mode 100644 doc/man/man3/ldap_dup.3.links create mode 100644 tests/progs/slapd-mtread.c create mode 100755 tests/scripts/test060-mt-hot diff --git a/doc/drafts/draft-zeilenga-ldap-c-api-concurrency-xx.txt b/doc/drafts/draft-zeilenga-ldap-c-api-concurrency-xx.txt new file mode 100644 index 0000000000..271556acf4 --- /dev/null +++ b/doc/drafts/draft-zeilenga-ldap-c-api-concurrency-xx.txt @@ -0,0 +1,714 @@ +INTERNET-DRAFT Kurt D. Zeilenga +Intended Category: Standards Track OpenLDAP Foundation +Extends: draft-ietf-ldapext-ldap-c-api-03.txt +Expires: 28 March 2000 + 28 September 1999 + + LDAP C API Concurrency Extensions + + +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 . Please send + editorial comments directly to the author . + + 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, + LDAPext discussions, June 1999. + + [ERRNO] K. Zeilenga, "LDAP C API Error Reporting Extension", + INTERNET-DRAFT, , + 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 + + + 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 + + +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 . Please send + editorial comments directly to the author . + + 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, + + 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 + + + This document expires on 28 March 2000. + +Zeilenga [Page 7] diff --git a/doc/man/man3/ldap_dup.3 b/doc/man/man3/ldap_dup.3 new file mode 100644 index 0000000000..39b3eedfc6 --- /dev/null +++ b/doc/man/man3/ldap_dup.3 @@ -0,0 +1,126 @@ +.TH LDAP_OPEN 3 "RELEASEDATE" "OpenLDAP LDVERSION" +.\" $OpenLDAP$ +.\" Copyright 1998-2010 The OpenLDAP Foundation All Rights Reserved. +.\" Copying restrictions apply. See COPYRIGHT/LICENSE. +.SH NAME +ldap_dup, ldap_destroy, \- Duplicate and destroy LDAP session handles +.SH LIBRARY +OpenLDAP LDAP (libldap, \-lldap) +.SH SYNOPSIS +.nf +.ft B +#include +.LP +.ft B +LDAP *ldap_dup( +.RS +.ft B +LDAP *\fIold\fB ); +.RE +.LP +.ft B +int ldap_destroy( +.RS +.ft B +LDAP *\fIold\fB ); +.RE +.SH DESCRIPTION +.LP +.B ldap_dup() +duplicates an existing LDAP +.RB ( "LDAP *" ) +session handle. +The new session handle may be used concurrently with the +original session handle. +In a threaded environment, different threads may execute concurrent +requests on the same connection/session without fear of contamination. +Each session handle manages its own private error results. +.LP +.B ldap_destroy() +destroys an existing session handle. +.LP +The +.B ldap_dup() +and +.B ldap_destroy() +functions are used in conjunction with a "thread safe" version +of +.B libldap +.RB ( libldap_r ) +to enable operation thread safe API calls, so that a single session +may be simultaneously used across multiple threads with consistent +error handling. +.LP +When a session is created through the use of one of the session creation +functions including +.BR ldap_open (3), +.BR ldap_init (3), +.BR ldap_initialize (3) +or +.BR ldap_init_fd (3) +an +.B "LDAP *" +session handle is returned to the application. +The session handle may be shared amongst threads, however the +error codes are unique to a session handle. +Multiple threads performing different operations using the same +session handle will result in inconsistent error codes and +return values. +.LP +To prevent this confusion, +.B ldap_dup() +is used duplicate an existing session handle so that multiple threads +can share the session, and maintain consistent error information +and results. +.LP +The message queues for a session are shared between sibling session handles. +Results of operations on a sibling session handles are accessible +to all the sibling session handles. +Applications desiring results associated with a specific operation +should provide the appropriate msgid to +.BR ldap_result() . +Applications should avoid calling +.B ldap_result() +with +.B LDAP_RES_ANY +as that may "steal" and return results in the calling thread +that another operation in a different thread, using a +different session handle, may require to complete. +.LP +When +.B ldap_unbind() +is called on a session handle with siblings, all the +siblings become invalid. +.LP +Siblings must be destroyed using +.BR ldap_destroy() . +Session handle resources associated with the original +.RB ( "LDAP *" ) +will be freed when the last session handle is destroyed or when +.B ldap_unbind() +is called, if no other session handles currently exist. +.SH ERRORS +If an error occurs, +.B ldap_dup() +will return NULL and +.I errno +should be set appropriately. +.B ldap_destroy() +will directly return the LDAP code associated to the error (or +.I LDAP_SUCCESS +in case of success); +.I errno +should be set as well whenever appropriate. +.SH SEE ALSO +.BR ldap_open (3), +.BR ldap_init (3), +.BR ldap_initialize (3), +.BR ldap_init_fd (3), +.BR errno (3) +.SH ACKNOWLEDGEMENTS +This work is based on the previously proposed +.B LDAP C API Concurrency Extensions +draft +.BR ( draft-zeilenga-ldap-c-api-concurrency-00.txt ) +effort. +.so ../Project diff --git a/doc/man/man3/ldap_dup.3.links b/doc/man/man3/ldap_dup.3.links new file mode 100644 index 0000000000..1d77f93af5 --- /dev/null +++ b/doc/man/man3/ldap_dup.3.links @@ -0,0 +1 @@ +ldap_destroy.3 diff --git a/doc/man/man3/ldap_get_option.3 b/doc/man/man3/ldap_get_option.3 index e3c89662f0..5733025f3c 100644 --- a/doc/man/man3/ldap_get_option.3 +++ b/doc/man/man3/ldap_get_option.3 @@ -322,6 +322,15 @@ must be the library duplicates the controls passed via .BR invalue . .TP +.B LDAP_OPT_SESSION_REFCNT +Returns the reference count associated with the LDAP handle passed in as +.BR ld ; +.BR outvalue +must be a +.BR "int *" . +This is a read-only, handle-specific option. +This option is OpenLDAP specific. +.TP .B LDAP_OPT_SIZELIMIT Sets/gets the value that defines the maximum number of entries to be returned by a search operation. diff --git a/include/ldap.h b/include/ldap.h index fa6e4176ec..4af25991bf 100644 --- a/include/ldap.h +++ b/include/ldap.h @@ -59,7 +59,9 @@ LDAP_BEGIN_DECL defined( LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE ) ) /* -lldap may or may not be thread safe */ /* -lldap_r, if available, is always thread safe */ -# define LDAP_API_FEATURE_THREAD_SAFE 1 +# define LDAP_API_FEATURE_THREAD_SAFE 1 +# define LDAP_API_FEATURE_SESSION_THREAD_SAFE 1 +# define LDAP_API_FEATURE_OPERATION_THREAD_SAFE 1 #endif #if defined( LDAP_THREAD_SAFE ) && \ defined( LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE ) @@ -135,6 +137,7 @@ LDAP_BEGIN_DECL #define LDAP_OPT_DEFBASE 0x5009 /* searchbase */ #define LDAP_OPT_CONNECT_ASYNC 0x5010 /* create connections asynchronously */ #define LDAP_OPT_CONNECT_CB 0x5011 /* connection callbacks */ +#define LDAP_OPT_SESSION_REFCNT 0x5012 /* session reference count */ /* OpenLDAP TLS options */ #define LDAP_OPT_X_TLS 0x6000 @@ -1519,6 +1522,10 @@ ldap_initialize LDAP_P(( LDAP **ldp, LDAP_CONST char *url )); +LDAP_F( LDAP * ) +ldap_dup LDAP_P(( + LDAP *old )); + /* * in tls.c */ @@ -1931,6 +1938,10 @@ ldap_unbind_ext_s LDAP_P(( LDAPControl **serverctrls, LDAPControl **clientctrls)); +LDAP_F( int ) +ldap_destroy LDAP_P(( + LDAP *ld)); + #if LDAP_DEPRECATED LDAP_F( int ) ldap_unbind LDAP_P(( /* deprecated, use ldap_unbind_ext */ diff --git a/include/ldap_int_thread.h b/include/ldap_int_thread.h index d2430f31ac..108574272f 100644 --- a/include/ldap_int_thread.h +++ b/include/ldap_int_thread.h @@ -69,6 +69,11 @@ typedef pthread_key_t ldap_int_thread_key_t; typedef pthread_rwlock_t ldap_int_thread_rdwr_t; #endif +#ifndef LDO_MUTEX_NULL +#define LDO_MUTEX_NULL ,PTHREAD_MUTEX_INITIALIZER +#define MUTEX_FIRSTCREATE(m) +#endif + LDAP_END_DECL #elif defined ( HAVE_MACH_CTHREADS ) @@ -91,6 +96,11 @@ typedef struct mutex ldap_int_thread_mutex_t; typedef struct condition ldap_int_thread_cond_t; typedef cthread_key_t ldap_int_thread_key_t; +#ifndef LDO_MUTEX_NULL +#define LDO_MUTEX_NULL ,MUTEX_INITIALIZER +#define MUTEX_FIRSTCREATE(m) +#endif + LDAP_END_DECL #elif defined( HAVE_GNU_PTH ) @@ -115,6 +125,11 @@ typedef pth_key_t ldap_int_thread_key_t; typedef pth_rwlock_t ldap_int_thread_rdwr_t; #endif +#ifndef LDO_MUTEX_NULL +#define LDO_MUTEX_NULL ,PTH_MUTEX_INIT +#define MUTEX_FIRSTCREATE(m) +#endif + LDAP_END_DECL #elif defined( HAVE_THR ) @@ -143,7 +158,10 @@ typedef thread_key_t ldap_int_thread_key_t; #define LDAP_THREAD_HAVE_SETCONCURRENCY 1 #endif -LDAP_END_DECL +#ifndef LDO_MUTEX_NULL +#define LDO_MUTEX_NULL ,DEFAULTMUTEX +#define MUTEX_FIRSTCREATE(m) +#endif #elif defined(HAVE_NT_THREADS) /************************************* @@ -162,6 +180,11 @@ typedef HANDLE ldap_int_thread_mutex_t; typedef HANDLE ldap_int_thread_cond_t; typedef DWORD ldap_int_thread_key_t; +#ifndef LDO_MUTEX_NULL +#define LDO_MUTEX_NULL ,(HANDLE)0 +#define MUTEX_FIRSTCREATE(m) (!m ? 0 : ldap_pvt_thread_mutex_init(&m) ) +#endif + LDAP_END_DECL #else @@ -186,6 +209,11 @@ typedef int ldap_int_thread_key_t; #define LDAP_THREAD_HAVE_TPOOL 1 typedef int ldap_int_thread_pool_t; +#ifndef LDO_MUTEX_NULL +#define LDO_MUTEX_NULL +#define MUTEX_FIRSTCREATE(m) +#endif + LDAP_END_DECL #endif /* no threads support */ diff --git a/libraries/libldap/abandon.c b/libraries/libldap/abandon.c index e230099186..7cda97849a 100644 --- a/libraries/libldap/abandon.c +++ b/libraries/libldap/abandon.c @@ -206,7 +206,7 @@ start_again:; * LDAP_NEXT_MSGID(ld, i); */ - i = ++(ld)->ld_msgid; + LDAP_NEXT_MSGID(ld, i); #ifdef LDAP_CONNECTIONLESS if ( LDAP_IS_UDP(ld) ) { struct sockaddr sa = {0}; @@ -216,11 +216,14 @@ start_again:; if ( LDAP_IS_UDP(ld) && ld->ld_options.ldo_version == LDAP_VERSION2 ) { - char *dn = ld->ld_options.ldo_cldapdn; + char *dn; + LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); + dn = ld->ld_options.ldo_cldapdn; if (!dn) dn = ""; err = ber_printf( ber, "{isti", /* '}' */ i, dn, LDAP_REQ_ABANDON, msgid ); + LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); } else #endif { @@ -287,10 +290,7 @@ start_again:; } } - /* ld_abandoned is actually protected by the ld_res_mutex; - * give up the ld_req_mutex and get the other */ - LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); - LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); + LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex ); /* use bisection */ i = 0; @@ -304,8 +304,7 @@ start_again:; ld->ld_errno = LDAP_SUCCESS; } - LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); - LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex ); return( ld->ld_errno ); } diff --git a/libraries/libldap/cyrus.c b/libraries/libldap/cyrus.c index 015eec0b17..a25304be82 100644 --- a/libraries/libldap/cyrus.c +++ b/libraries/libldap/cyrus.c @@ -416,7 +416,7 @@ ldap_int_sasl_bind( void *ssl; rc = 0; - LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ); if ( sd == AC_SOCKET_INVALID ) { @@ -434,7 +434,7 @@ ldap_int_sasl_bind( } } } - LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); if( rc != 0 ) return ld->ld_errno; oldctx = ld->ld_defconn->lconn_sasl_authctx; diff --git a/libraries/libldap/init.c b/libraries/libldap/init.c index 16d8e9a383..afdf893ba0 100644 --- a/libraries/libldap/init.c +++ b/libraries/libldap/init.c @@ -36,7 +36,7 @@ #include "lutil.h" struct ldapoptions ldap_int_global_options = - { LDAP_UNINITIALIZED, LDAP_DEBUG_NONE }; + { LDAP_UNINITIALIZED, LDAP_DEBUG_NONE LDO_MUTEX_NULL }; #define ATTR_NONE 0 #define ATTR_BOOL 1 @@ -510,6 +510,13 @@ ldap_int_destroy_global_options(void) */ void ldap_int_initialize_global_options( struct ldapoptions *gopts, int *dbglvl ) { + MUTEX_FIRSTCREATE(gopts->ldo_mutex); + LDAP_MUTEX_LOCK( &gopts->ldo_mutex ); + if (gopts->ldo_valid == LDAP_INITIALIZED) { + /* someone else got here first */ + LDAP_MUTEX_UNLOCK( &gopts->ldo_mutex ); + return; + } if (dbglvl) gopts->ldo_debug = *dbglvl; else @@ -573,6 +580,7 @@ void ldap_int_initialize_global_options( struct ldapoptions *gopts, int *dbglvl gopts->ldo_keepalive_idle = 0; gopts->ldo_valid = LDAP_INITIALIZED; + LDAP_MUTEX_UNLOCK( &gopts->ldo_mutex ); return; } diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h index e4c760aea0..e818900bcb 100644 --- a/libraries/libldap/ldap-int.h +++ b/libraries/libldap/ldap-int.h @@ -174,6 +174,7 @@ typedef struct ldaplist { /* * structure representing get/set'able options * which have global defaults. + * Protect access to this struct with ldo_mutex */ struct ldapoptions { short ldo_valid; @@ -182,6 +183,12 @@ struct ldapoptions { #define LDAP_VALID_SESSION 0x2 #define LDAP_TRASHED_SESSION 0xFF int ldo_debug; +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_t ldo_mutex; +#else +#define LDO_MUTEX_NULL +#define MUTEX_FIRSTCREATE(m) +#endif #ifdef LDAP_CONNECTIONLESS #define LDAP_IS_UDP(ld) ((ld)->ld_options.ldo_is_udp) void* ldo_peer; /* struct sockaddr* */ @@ -361,24 +368,27 @@ typedef struct ldapreqinfo { * structure representing an ldap connection */ -struct ldap { - Sockbuf *ld_sb; /* socket descriptor & buffer */ +struct ldap_common { + Sockbuf *ldc_sb; /* socket descriptor & buffer */ +#define ld_sb ldc->ldc_sb - struct ldapoptions ld_options; + /* protected by ldo_mutex */ + struct ldapoptions ldc_options; +#define ld_options ldc->ldc_options #define ld_valid ld_options.ldo_valid #define ld_debug ld_options.ldo_debug #define ld_deref ld_options.ldo_deref -#define ld_timelimit ld_options.ldo_timelimit -#define ld_sizelimit ld_options.ldo_sizelimit +#define ld_timelimit ld_options.ldo_timelimit +#define ld_sizelimit ld_options.ldo_sizelimit -#define ld_defbinddn ld_options.ldo_defbinddn +#define ld_defbinddn ld_options.ldo_defbinddn #define ld_defbase ld_options.ldo_defbase #define ld_defhost ld_options.ldo_defhost #define ld_defport ld_options.ldo_defport -#define ld_refhoplimit ld_options.ldo_refhoplimit +#define ld_refhoplimit ld_options.ldo_refhoplimit #define ld_sctrls ld_options.ldo_sctrls #define ld_cctrls ld_options.ldo_cctrls @@ -390,36 +400,79 @@ struct ldap { #define ld_urllist_params ld_options.ldo_urllist_params #define ld_version ld_options.ldo_version +#ifdef LDAP_R_COMPILE +#define ld_ldopts_mutex ld_options.ldo_mutex +#endif - unsigned short ld_lberoptions; + unsigned short ldc_lberoptions; +#define ld_lberoptions ldc->ldc_lberoptions - ber_int_t ld_errno; - char *ld_error; - char *ld_matched; - char **ld_referrals; - ber_len_t ld_msgid; + /* protected by msgid_mutex */ + ber_len_t ldc_msgid; +#define ld_msgid ldc->ldc_msgid /* do not mess with these */ - LDAPRequest *ld_requests; /* list of outstanding requests */ - LDAPMessage *ld_responses; /* list of outstanding responses */ + /* protected by req_mutex */ + LDAPRequest *ldc_requests; /* list of outstanding requests */ + /* protected by res_mutex */ + LDAPMessage *ldc_responses; /* list of outstanding responses */ +#define ld_requests ldc->ldc_requests +#define ld_responses ldc->ldc_responses #ifdef LDAP_R_COMPILE - ldap_pvt_thread_mutex_t ld_conn_mutex; - ldap_pvt_thread_mutex_t ld_req_mutex; - ldap_pvt_thread_mutex_t ld_res_mutex; + ldap_pvt_thread_mutex_t ldc_msgid_mutex; + ldap_pvt_thread_mutex_t ldc_conn_mutex; + ldap_pvt_thread_mutex_t ldc_req_mutex; + ldap_pvt_thread_mutex_t ldc_res_mutex; + ldap_pvt_thread_mutex_t ldc_abandon_mutex; +#define ld_msgid_mutex ldc->ldc_msgid_mutex +#define ld_conn_mutex ldc->ldc_conn_mutex +#define ld_req_mutex ldc->ldc_req_mutex +#define ld_res_mutex ldc->ldc_res_mutex +#define ld_abandon_mutex ldc->ldc_abandon_mutex #endif - ber_len_t ld_nabandoned; - ber_int_t *ld_abandoned; /* array of abandoned requests */ + /* protected by abandon_mutex */ + ber_len_t ldc_nabandoned; + ber_int_t *ldc_abandoned; /* array of abandoned requests */ +#define ld_nabandoned ldc->ldc_nabandoned +#define ld_abandoned ldc->ldc_abandoned - LDAPCache *ld_cache; /* non-null if cache is initialized */ + /* unused by libldap */ + LDAPCache *ldc_cache; /* non-null if cache is initialized */ +#define ld_cache ldc->ldc_cache /* do not mess with the rest though */ - LDAPConn *ld_defconn; /* default connection */ - LDAPConn *ld_conns; /* list of server connections */ - void *ld_selectinfo; /* platform specifics for select */ + /* protected by conn_mutex */ + LDAPConn *ldc_defconn; /* default connection */ +#define ld_defconn ldc->ldc_defconn + LDAPConn *ldc_conns; /* list of server connections */ +#define ld_conns ldc->ldc_conns + void *ldc_selectinfo;/* platform specifics for select */ +#define ld_selectinfo ldc->ldc_selectinfo + + /* ldap_common refcnt - free only if 0 */ +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_t ldc_mutex; +#define ld_ldcmutex ldc->ldc_mutex +#endif + /* protected by ldc_mutex */ + unsigned int ldc_refcnt; +#define ld_ldcrefcnt ldc->ldc_refcnt +}; + +struct ldap { + /* thread shared */ + struct ldap_common *ldc; + + /* thread specific */ + ber_int_t ld_errno; + char *ld_error; + char *ld_matched; + char **ld_referrals; }; + #define LDAP_VALID(ld) ( (ld)->ld_valid == LDAP_VALID_SESSION ) #define LDAP_TRASHED(ld) ( (ld)->ld_valid == LDAP_TRASHED_SESSION ) #define LDAP_TRASH(ld) ( (ld)->ld_valid = LDAP_TRASHED_SESSION ) @@ -448,9 +501,9 @@ LDAP_V( ldap_pvt_thread_mutex_t ) ldap_int_gssapi_mutex; #ifdef LDAP_R_COMPILE #define LDAP_NEXT_MSGID(ld, id) \ - LDAP_MUTEX_LOCK( &(ld)->ld_req_mutex ); \ + LDAP_MUTEX_LOCK( &(ld)->ld_msgid_mutex ); \ id = ++(ld)->ld_msgid; \ - LDAP_MUTEX_UNLOCK( &(ld)->ld_req_mutex ) + LDAP_MUTEX_UNLOCK( &(ld)->ld_msgid_mutex ) #else #define LDAP_NEXT_MSGID(ld, id) id = ++(ld)->ld_msgid #endif @@ -584,8 +637,11 @@ LDAP_F (ber_int_t) ldap_send_initial_request( LDAP *ld, ber_tag_t msgtype, LDAP_F (BerElement *) ldap_alloc_ber_with_options( LDAP *ld ); LDAP_F (void) ldap_set_ber_options( LDAP *ld, BerElement *ber ); -LDAP_F (int) ldap_send_server_request( LDAP *ld, BerElement *ber, ber_int_t msgid, LDAPRequest *parentreq, LDAPURLDesc **srvlist, LDAPConn *lc, LDAPreqinfo *bind ); -LDAP_F (LDAPConn *) ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, int connect, LDAPreqinfo *bind ); +LDAP_F (int) ldap_send_server_request( LDAP *ld, BerElement *ber, + ber_int_t msgid, LDAPRequest *parentreq, LDAPURLDesc **srvlist, + LDAPConn *lc, LDAPreqinfo *bind, int noconn, int m_res ); +LDAP_F (LDAPConn *) ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, + int use_ldsb, int connect, LDAPreqinfo *bind, int m_req, int m_res ); LDAP_F (LDAPRequest *) ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid ); LDAP_F (void) ldap_return_request( LDAP *ld, LDAPRequest *lr, int freeit ); LDAP_F (void) ldap_free_request( LDAP *ld, LDAPRequest *lr ); diff --git a/libraries/libldap/open.c b/libraries/libldap/open.c index 1bc86e39a7..4a2ad21add 100644 --- a/libraries/libldap/open.c +++ b/libraries/libldap/open.c @@ -35,11 +35,11 @@ #include "ldap-int.h" #include "ldap_log.h" -/* Caller should hold the req_mutex if simultaneous accesses are possible */ +/* Caller must hold the conn_mutex since simultaneous accesses are possible */ int ldap_open_defconn( LDAP *ld ) { ld->ld_defconn = ldap_new_connection( ld, - &ld->ld_options.ldo_defludp, 1, 1, NULL ); + &ld->ld_options.ldo_defludp, 1, 1, NULL, 0, 0 ); if( ld->ld_defconn == NULL ) { ld->ld_errno = LDAP_SERVER_DOWN; @@ -74,7 +74,9 @@ ldap_open( LDAP_CONST char *host, int port ) return( NULL ); } + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); rc = ldap_open_defconn( ld ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); if( rc < 0 ) { ldap_ld_free( ld, 0, NULL, NULL ); @@ -114,8 +116,19 @@ ldap_create( LDAP **ldp ) return( LDAP_NO_MEMORY ); } + if ( (ld->ldc = (struct ldap_common *) LDAP_CALLOC( 1, + sizeof(struct ldap_common) )) == NULL ) { + LDAP_FREE( (char *)ld ); + return( LDAP_NO_MEMORY ); + } /* copy the global options */ + LDAP_MUTEX_LOCK( &gopts->ldo_mutex ); AC_MEMCPY(&ld->ld_options, gopts, sizeof(ld->ld_options)); +#ifdef LDAP_R_COMPILE + /* Properly initialize the structs mutex */ + ldap_pvt_thread_mutex_init( &(ld->ld_ldopts_mutex) ); +#endif + LDAP_MUTEX_UNLOCK( &gopts->ldo_mutex ); ld->ld_valid = LDAP_VALID_SESSION; @@ -159,10 +172,14 @@ ldap_create( LDAP **ldp ) if ( ld->ld_sb == NULL ) goto nomem; #ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_init( &ld->ld_msgid_mutex ); + ldap_pvt_thread_mutex_init( &ld->ld_conn_mutex ); ldap_pvt_thread_mutex_init( &ld->ld_req_mutex ); ldap_pvt_thread_mutex_init( &ld->ld_res_mutex ); - ldap_pvt_thread_mutex_init( &ld->ld_conn_mutex ); + ldap_pvt_thread_mutex_init( &ld->ld_abandon_mutex ); + ldap_pvt_thread_mutex_init( &ld->ld_ldcmutex ); #endif + ld->ld_ldcrefcnt = 1; *ldp = ld; return LDAP_SUCCESS; @@ -265,8 +282,9 @@ ldap_init_fd( } } + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); /* Attach the passed socket as the LDAP's connection */ - conn = ldap_new_connection( ld, NULL, 1, 0, NULL); + conn = ldap_new_connection( ld, NULL, 1, 0, NULL, 0, 0 ); if( conn == NULL ) { ldap_unbind_ext( ld, NULL, NULL ); return( LDAP_NO_MEMORY ); @@ -276,6 +294,7 @@ ldap_init_fd( ber_sockbuf_ctrl( conn->lconn_sb, LBER_SB_OPT_SET_FD, &fd ); ld->ld_defconn = conn; ++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */ + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); switch( proto ) { case LDAP_PROTO_TCP: @@ -331,6 +350,7 @@ ldap_init_fd( return LDAP_SUCCESS; } +/* Protected by ld_conn_mutex */ int ldap_int_open_connection( LDAP *ld, @@ -434,8 +454,9 @@ ldap_open_internal_connection( LDAP **ldp, ber_socket_t *fdp ) int rc; LDAPConn *c; LDAPRequest *lr; + LDAP *ld; - rc = ldap_create( ldp ); + rc = ldap_create( &ld ); if( rc != LDAP_SUCCESS ) { *ldp = NULL; return( rc ); @@ -444,7 +465,7 @@ ldap_open_internal_connection( LDAP **ldp, ber_socket_t *fdp ) /* Make it appear that a search request, msgid 0, was sent */ lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest )); if( lr == NULL ) { - ldap_unbind_ext( *ldp, NULL, NULL ); + ldap_unbind_ext( ld, NULL, NULL ); *ldp = NULL; return( LDAP_NO_MEMORY ); } @@ -453,13 +474,15 @@ ldap_open_internal_connection( LDAP **ldp, ber_socket_t *fdp ) lr->lr_status = LDAP_REQST_INPROGRESS; lr->lr_res_errno = LDAP_SUCCESS; /* no mutex lock needed, we just created this ld here */ - (*ldp)->ld_requests = lr; + ld->ld_requests = lr; + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); /* Attach the passed socket as the *LDAP's connection */ - c = ldap_new_connection( *ldp, NULL, 1, 0, NULL); + c = ldap_new_connection( ld, NULL, 1, 0, NULL, 0, 0 ); if( c == NULL ) { - ldap_unbind_ext( *ldp, NULL, NULL ); + ldap_unbind_ext( ld, NULL, NULL ); *ldp = NULL; + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); return( LDAP_NO_MEMORY ); } ber_sockbuf_ctrl( c->lconn_sb, LBER_SB_OPT_SET_FD, fdp ); @@ -469,15 +492,39 @@ ldap_open_internal_connection( LDAP **ldp, ber_socket_t *fdp ) #endif ber_sockbuf_add_io( c->lconn_sb, &ber_sockbuf_io_tcp, LBER_SBIOD_LEVEL_PROVIDER, NULL ); - (*ldp)->ld_defconn = c; + ld->ld_defconn = c; + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); /* Add the connection to the *LDAP's select pool */ - ldap_mark_select_read( *ldp, c->lconn_sb ); - ldap_mark_select_write( *ldp, c->lconn_sb ); + ldap_mark_select_read( ld, c->lconn_sb ); + ldap_mark_select_write( ld, c->lconn_sb ); /* Make this connection an LDAP V3 protocol connection */ rc = LDAP_VERSION3; - ldap_set_option( *ldp, LDAP_OPT_PROTOCOL_VERSION, &rc ); + ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &rc ); + *ldp = ld; return( LDAP_SUCCESS ); } + +LDAP * +ldap_dup( LDAP *old ) +{ + LDAP *ld; + + if ( old == NULL ) { + return( NULL ); + } + + Debug( LDAP_DEBUG_TRACE, "ldap_dup\n", 0, 0, 0 ); + + if ( (ld = (LDAP *) LDAP_CALLOC( 1, sizeof(LDAP) )) == NULL ) { + return( NULL ); + } + + LDAP_MUTEX_LOCK( &old->ld_ldcmutex ); + ld->ldc = old->ldc; + old->ld_ldcrefcnt++; + LDAP_MUTEX_UNLOCK( &old->ld_ldcmutex ); + return ( ld ); +} diff --git a/libraries/libldap/options.c b/libraries/libldap/options.c index 69db0c6651..a75d4598c2 100644 --- a/libraries/libldap/options.c +++ b/libraries/libldap/options.c @@ -96,6 +96,7 @@ ldap_get_option( void *outvalue) { struct ldapoptions *lo; + int rc = LDAP_OPT_ERROR; /* Get pointer to global option structure */ lo = LDAP_INT_GLOBAL_OPT(); @@ -122,19 +123,21 @@ ldap_get_option( return LDAP_OPT_ERROR; } + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); + switch(option) { case LDAP_OPT_API_INFO: { struct ldapapiinfo *info = (struct ldapapiinfo *) outvalue; if(info == NULL) { /* outvalue must point to an apiinfo structure */ - return LDAP_OPT_ERROR; + break; /* LDAP_OPT_ERROR */ } if(info->ldapai_info_version != LDAP_API_INFO_VERSION) { /* api info version mismatch */ info->ldapai_info_version = LDAP_API_INFO_VERSION; - return LDAP_OPT_ERROR; + break; /* LDAP_OPT_ERROR */ } info->ldapai_api_version = LDAP_API_VERSION; @@ -158,7 +161,8 @@ ldap_get_option( info->ldapai_vendor_name = LDAP_STRDUP(LDAP_VENDOR_NAME); info->ldapai_vendor_version = LDAP_VENDOR_VERSION; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; } break; case LDAP_OPT_DESC: @@ -168,74 +172,86 @@ ldap_get_option( } ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, outvalue ); - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_SOCKBUF: if( ld == NULL ) break; *(Sockbuf **)outvalue = ld->ld_sb; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_TIMEOUT: /* the caller has to free outvalue ! */ if ( lo->ldo_tm_api.tv_sec < 0 ) { *(void **)outvalue = NULL; } else if ( ldap_int_timeval_dup( outvalue, &lo->ldo_tm_api ) != 0 ) { - return LDAP_OPT_ERROR; + break; /* LDAP_OPT_ERROR */ } - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_NETWORK_TIMEOUT: /* the caller has to free outvalue ! */ if ( lo->ldo_tm_net.tv_sec < 0 ) { *(void **)outvalue = NULL; } else if ( ldap_int_timeval_dup( outvalue, &lo->ldo_tm_net ) != 0 ) { - return LDAP_OPT_ERROR; + break; /* LDAP_OPT_ERROR */ } - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_DEREF: * (int *) outvalue = lo->ldo_deref; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_SIZELIMIT: * (int *) outvalue = lo->ldo_sizelimit; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_TIMELIMIT: * (int *) outvalue = lo->ldo_timelimit; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_REFERRALS: * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_REFERRALS); - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_RESTART: * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_RESTART); - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_PROTOCOL_VERSION: * (int *) outvalue = lo->ldo_version; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_SERVER_CONTROLS: * (LDAPControl ***) outvalue = ldap_controls_dup( lo->ldo_sctrls ); - - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_CLIENT_CONTROLS: * (LDAPControl ***) outvalue = ldap_controls_dup( lo->ldo_cctrls ); - - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_HOST_NAME: * (char **) outvalue = ldap_url_list2hosts(lo->ldo_defludp); - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_URI: * (char **) outvalue = ldap_url_list2urls(lo->ldo_defludp); - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_DEFBASE: if( lo->ldo_defbase == NULL ) { @@ -243,12 +259,13 @@ ldap_get_option( } else { * (char **) outvalue = LDAP_STRDUP(lo->ldo_defbase); } - - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_CONNECT_ASYNC: * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_CONNECT_ASYNC); - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_CONNECT_CB: { @@ -263,7 +280,8 @@ ldap_get_option( } } } - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_RESULT_CODE: if(ld == NULL) { @@ -271,7 +289,8 @@ ldap_get_option( break; } * (int *) outvalue = ld->ld_errno; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_DIAGNOSTIC_MESSAGE: if(ld == NULL) { @@ -284,8 +303,8 @@ ldap_get_option( } else { * (char **) outvalue = LDAP_STRDUP(ld->ld_error); } - - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_MATCHED_DN: if(ld == NULL) { @@ -298,8 +317,8 @@ ldap_get_option( } else { * (char **) outvalue = LDAP_STRDUP( ld->ld_matched ); } - - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_REFERRAL_URLS: if(ld == NULL) { @@ -312,28 +331,31 @@ ldap_get_option( } else { * (char ***) outvalue = ldap_value_dup(ld->ld_referrals); } - - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_API_FEATURE_INFO: { LDAPAPIFeatureInfo *info = (LDAPAPIFeatureInfo *) outvalue; int i; - if(info == NULL) return LDAP_OPT_ERROR; + if(info == NULL) + break; /* LDAP_OPT_ERROR */ if(info->ldapaif_info_version != LDAP_FEATURE_INFO_VERSION) { /* api info version mismatch */ info->ldapaif_info_version = LDAP_FEATURE_INFO_VERSION; - return LDAP_OPT_ERROR; + break; /* LDAP_OPT_ERROR */ } - if(info->ldapaif_name == NULL) return LDAP_OPT_ERROR; + if(info->ldapaif_name == NULL) + break; /* LDAP_OPT_ERROR */ for(i=0; features[i].ldapaif_name != NULL; i++) { if(!strcmp(info->ldapaif_name, features[i].ldapaif_name)) { info->ldapaif_version = features[i].ldapaif_version; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; } } } @@ -341,41 +363,54 @@ ldap_get_option( case LDAP_OPT_DEBUG_LEVEL: * (int *) outvalue = lo->ldo_debug; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; + case LDAP_OPT_SESSION_REFCNT: + * (int *) outvalue = ld->ld_ldcrefcnt; + rc = LDAP_OPT_SUCCESS; + break; + case LDAP_OPT_X_KEEPALIVE_IDLE: * (int *) outvalue = lo->ldo_keepalive_idle; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_X_KEEPALIVE_PROBES: * (int *) outvalue = lo->ldo_keepalive_probes; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_X_KEEPALIVE_INTERVAL: * (int *) outvalue = lo->ldo_keepalive_interval; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; default: #ifdef HAVE_TLS if ( ldap_pvt_tls_get_option( ld, option, outvalue ) == 0 ) { - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; } #endif #ifdef HAVE_CYRUS_SASL if ( ldap_int_sasl_get_option( ld, option, outvalue ) == 0 ) { - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; } #endif #ifdef HAVE_GSSAPI if ( ldap_int_gssapi_get_option( ld, option, outvalue ) == 0 ) { - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; } #endif /* bad param */ break; } - return LDAP_OPT_ERROR; + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( rc ); } int @@ -386,6 +421,7 @@ ldap_set_option( { struct ldapoptions *lo; int *dbglvl = NULL; + int rc = LDAP_OPT_ERROR; /* Get pointer to global option structure */ lo = LDAP_INT_GLOBAL_OPT(); @@ -416,14 +452,19 @@ ldap_set_option( lo = &ld->ld_options; } - switch(option) { + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); + + switch ( option ) { + + /* options with boolean values */ case LDAP_OPT_REFERRALS: if(invalue == LDAP_OPT_OFF) { LDAP_BOOL_CLR(lo, LDAP_BOOL_REFERRALS); } else { LDAP_BOOL_SET(lo, LDAP_BOOL_REFERRALS); } - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_RESTART: if(invalue == LDAP_OPT_OFF) { @@ -431,7 +472,8 @@ ldap_set_option( } else { LDAP_BOOL_SET(lo, LDAP_BOOL_RESTART); } - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_CONNECT_ASYNC: if(invalue == LDAP_OPT_OFF) { @@ -439,11 +481,10 @@ ldap_set_option( } else { LDAP_BOOL_SET(lo, LDAP_BOOL_CONNECT_ASYNC); } - return LDAP_OPT_SUCCESS; - } + rc = LDAP_OPT_SUCCESS; + break; /* options which can withstand invalue == NULL */ - switch ( option ) { case LDAP_OPT_SERVER_CONTROLS: { LDAPControl *const *controls = (LDAPControl *const *) invalue; @@ -453,16 +494,19 @@ ldap_set_option( if( controls == NULL || *controls == NULL ) { lo->ldo_sctrls = NULL; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; } lo->ldo_sctrls = ldap_controls_dup( controls ); if(lo->ldo_sctrls == NULL) { /* memory allocation error ? */ - break; + break; /* LDAP_OPT_ERROR */ } - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_CLIENT_CONTROLS: { LDAPControl *const *controls = @@ -473,22 +517,25 @@ ldap_set_option( if( controls == NULL || *controls == NULL ) { lo->ldo_cctrls = NULL; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; } lo->ldo_cctrls = ldap_controls_dup( controls ); if(lo->ldo_cctrls == NULL) { /* memory allocation error ? */ - break; + break; /* LDAP_OPT_ERROR */ } - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_HOST_NAME: { const char *host = (const char *) invalue; LDAPURLDesc *ludlist = NULL; - int rc = LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; if(host != NULL) { rc = ldap_url_parsehosts( &ludlist, host, @@ -519,13 +566,13 @@ ldap_set_option( ldap_free_urllist(lo->ldo_defludp); lo->ldo_defludp = ludlist; } - return rc; + break; } case LDAP_OPT_URI: { const char *urls = (const char *) invalue; LDAPURLDesc *ludlist = NULL; - int rc = LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; if(urls != NULL) { rc = ldap_url_parselist_ext(&ludlist, urls, NULL, @@ -578,7 +625,7 @@ ldap_set_option( ldap_free_urllist(lo->ldo_defludp); lo->ldo_defludp = ludlist; } - return rc; + break; } case LDAP_OPT_DEFBASE: { @@ -587,24 +634,32 @@ ldap_set_option( if ( newbase != NULL ) { defbase = LDAP_STRDUP( newbase ); - if ( defbase == NULL ) return LDAP_NO_MEMORY; + if ( defbase == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } } else if ( ld != NULL ) { defbase = LDAP_STRDUP( ldap_int_global_options.ldo_defbase ); - if ( defbase == NULL ) return LDAP_NO_MEMORY; + if ( defbase == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } } if ( lo->ldo_defbase != NULL ) LDAP_FREE( lo->ldo_defbase ); lo->ldo_defbase = defbase; - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_DIAGNOSTIC_MESSAGE: { const char *err = (const char *) invalue; if(ld == NULL) { /* need a struct ldap */ - return LDAP_OPT_ERROR; + break; /* LDAP_OPT_ERROR */ } if( ld->ld_error ) { @@ -615,14 +670,16 @@ ldap_set_option( if ( err ) { ld->ld_error = LDAP_STRDUP(err); } - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_MATCHED_DN: { const char *matched = (const char *) invalue; if (ld == NULL) { /* need a struct ldap */ - return LDAP_OPT_ERROR; + break; /* LDAP_OPT_ERROR */ } if( ld->ld_matched ) { @@ -633,14 +690,16 @@ ldap_set_option( if ( matched ) { ld->ld_matched = LDAP_STRDUP( matched ); } - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_REFERRAL_URLS: { char *const *referrals = (char *const *) invalue; if(ld == NULL) { /* need a struct ldap */ - return LDAP_OPT_ERROR; + break; /* LDAP_OPT_ERROR */ } if( ld->ld_referrals ) { @@ -650,38 +709,52 @@ ldap_set_option( if ( referrals ) { ld->ld_referrals = ldap_value_dup(referrals); } - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; /* Only accessed from inside this function by ldap_set_rebind_proc() */ case LDAP_OPT_REBIND_PROC: { lo->ldo_rebind_proc = (LDAP_REBIND_PROC *)invalue; - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_REBIND_PARAMS: { lo->ldo_rebind_params = (void *)invalue; - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; /* Only accessed from inside this function by ldap_set_nextref_proc() */ case LDAP_OPT_NEXTREF_PROC: { lo->ldo_nextref_proc = (LDAP_NEXTREF_PROC *)invalue; - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_NEXTREF_PARAMS: { lo->ldo_nextref_params = (void *)invalue; - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; /* Only accessed from inside this function by ldap_set_urllist_proc() */ case LDAP_OPT_URLLIST_PROC: { lo->ldo_urllist_proc = (LDAP_URLLIST_PROC *)invalue; - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_URLLIST_PARAMS: { lo->ldo_urllist_params = (void *)invalue; - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; /* read-only options */ case LDAP_OPT_API_INFO: case LDAP_OPT_DESC: case LDAP_OPT_SOCKBUF: case LDAP_OPT_API_FEATURE_INFO: - return LDAP_OPT_ERROR; + break; /* LDAP_OPT_ERROR */ /* options which cannot withstand invalue == NULL */ case LDAP_OPT_DEREF: @@ -698,25 +771,29 @@ ldap_set_option( case LDAP_OPT_X_KEEPALIVE_INTERVAL : if(invalue == NULL) { /* no place to set from */ - return LDAP_OPT_ERROR; + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( LDAP_OPT_ERROR ); } break; default: #ifdef HAVE_TLS if ( ldap_pvt_tls_set_option( ld, option, (void *)invalue ) == 0 ) - return LDAP_OPT_SUCCESS; + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( LDAP_OPT_SUCCESS ); #endif #ifdef HAVE_CYRUS_SASL if ( ldap_int_sasl_set_option( ld, option, (void *)invalue ) == 0 ) - return LDAP_OPT_SUCCESS; + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( LDAP_OPT_SUCCESS ); #endif #ifdef HAVE_GSSAPI if ( ldap_int_gssapi_set_option( ld, option, (void *)invalue ) == 0 ) - return LDAP_OPT_SUCCESS; + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( LDAP_OPT_SUCCESS ); #endif /* bad param */ - return LDAP_OPT_ERROR; + break; /* LDAP_OPT_ERROR */ } /* options which cannot withstand invalue == NULL */ @@ -725,31 +802,38 @@ ldap_set_option( case LDAP_OPT_DEREF: /* FIXME: check value for protocol compliance? */ lo->ldo_deref = * (const int *) invalue; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_SIZELIMIT: /* FIXME: check value for protocol compliance? */ lo->ldo_sizelimit = * (const int *) invalue; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_TIMELIMIT: /* FIXME: check value for protocol compliance? */ lo->ldo_timelimit = * (const int *) invalue; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_TIMEOUT: { const struct timeval *tv = (const struct timeval *) invalue; lo->ldo_tm_api = *tv; - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_NETWORK_TIMEOUT: { const struct timeval *tv = (const struct timeval *) invalue; lo->ldo_tm_net = *tv; - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_PROTOCOL_VERSION: { int vers = * (const int *) invalue; @@ -758,7 +842,9 @@ ldap_set_option( break; } lo->ldo_version = vers; - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_RESULT_CODE: { int err = * (const int *) invalue; @@ -769,11 +855,14 @@ ldap_set_option( } ld->ld_errno = err; - } return LDAP_OPT_SUCCESS; + } + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_DEBUG_LEVEL: lo->ldo_debug = * (const int *) invalue; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_CONNECT_CB: { @@ -784,19 +873,24 @@ ldap_set_option( ll->ll_next = lo->ldo_conn_cbs; lo->ldo_conn_cbs = ll; } - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_X_KEEPALIVE_IDLE: lo->ldo_keepalive_idle = * (const int *) invalue; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_X_KEEPALIVE_PROBES : lo->ldo_keepalive_probes = * (const int *) invalue; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; case LDAP_OPT_X_KEEPALIVE_INTERVAL : lo->ldo_keepalive_interval = * (const int *) invalue; - return LDAP_OPT_SUCCESS; + rc = LDAP_OPT_SUCCESS; + break; } - return LDAP_OPT_ERROR; + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( rc ); } int diff --git a/libraries/libldap/request.c b/libraries/libldap/request.c index 66e334829a..10e399ab94 100644 --- a/libraries/libldap/request.c +++ b/libraries/libldap/request.c @@ -53,6 +53,29 @@ #include "ldap-int.h" #include "lber.h" +/* used by ldap_send_server_request and ldap_new_connection */ +#ifdef LDAP_R_COMPILE +#define LDAP_CONN_LOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); } +#define LDAP_CONN_UNLOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); } +#define LDAP_REQ_LOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); } +#define LDAP_REQ_UNLOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); } +#define LDAP_RES_LOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); } +#define LDAP_RES_UNLOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); } +#else +#define LDAP_CONN_LOCK_IF(nolock) +#define LDAP_CONN_UNLOCK_IF(nolock) +#define LDAP_REQ_LOCK_IF(nolock) +#define LDAP_REQ_UNLOCK_IF(nolock) +#define LDAP_RES_LOCK_IF(nolock) +#define LDAP_RES_UNLOCK_IF(nolock) +#endif + static LDAPConn *find_connection LDAP_P(( LDAP *ld, LDAPURLDesc *srv, int any )); static void use_connection LDAP_P(( LDAP *ld, LDAPConn *lc )); static void ldap_free_request_int LDAP_P(( LDAP *ld, LDAPRequest *lr )); @@ -82,10 +105,12 @@ ldap_alloc_ber_with_options( LDAP *ld ) void ldap_set_ber_options( LDAP *ld, BerElement *ber ) { + /* ld_lberoptions is constant, hence no lock */ ber->ber_options = ld->ld_lberoptions; } +/* sets needed mutexes - no mutexes set to this point */ ber_int_t ldap_send_initial_request( LDAP *ld, @@ -98,15 +123,15 @@ ldap_send_initial_request( Debug( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n", 0, 0, 0 ); - LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, NULL ) == -1 ) { /* not connected yet */ rc = ldap_open_defconn( ld ); } - LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); if( rc < 0 ) { ber_free( ber, 1 ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); return( -1 ); } else if ( rc == 0 ) { Debug( LDAP_DEBUG_TRACE, @@ -117,27 +142,33 @@ ldap_send_initial_request( #ifdef LDAP_CONNECTIONLESS if (LDAP_IS_UDP(ld)) { if (msgtype == LDAP_REQ_BIND) { + LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); if (ld->ld_options.ldo_cldapdn) ldap_memfree(ld->ld_options.ldo_cldapdn); ld->ld_options.ldo_cldapdn = ldap_strdup(dn); ber_free( ber, 1 ); + LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); return 0; } if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH) { ber_free( ber, 1 ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); return LDAP_PARAM_ERROR; } } #endif LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); rc = ldap_send_server_request( ld, ber, msgid, NULL, - NULL, NULL, NULL ); + NULL, NULL, NULL, 0, 0 ); LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); return(rc); } +/* protected by conn_mutex */ int ldap_int_flush_request( LDAP *ld, @@ -145,6 +176,7 @@ ldap_int_flush_request( { LDAPConn *lc = lr->lr_conn; + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); if ( ber_flush2( lc->lconn_sb, lr->lr_ber, LBER_FLUSH_FREE_NEVER ) != 0 ) { if ( sock_errno() == EAGAIN ) { /* need to continue write later */ @@ -171,6 +203,13 @@ ldap_int_flush_request( return 0; } +/* + * protected by req_mutex + * if m_noconn then protect using conn_lock + * else already protected with conn_lock + * if m_res then also protected by res_mutex + */ + int ldap_send_server_request( LDAP *ld, @@ -179,16 +218,20 @@ ldap_send_server_request( LDAPRequest *parentreq, LDAPURLDesc **srvlist, LDAPConn *lc, - LDAPreqinfo *bind ) + LDAPreqinfo *bind, + int m_noconn, + int m_res ) { LDAPRequest *lr; int incparent, rc; + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 ); incparent = 0; ld->ld_errno = LDAP_SUCCESS; /* optimistic */ + LDAP_CONN_LOCK_IF(m_noconn); if ( lc == NULL ) { if ( srvlist == NULL ) { lc = ld->ld_defconn; @@ -200,7 +243,8 @@ ldap_send_server_request( incparent = 1; ++parentreq->lr_outrefcnt; } - lc = ldap_new_connection( ld, srvlist, 0, 1, bind ); + lc = ldap_new_connection( ld, srvlist, 0, + 1, bind, 1, m_res ); } } } @@ -223,11 +267,13 @@ ldap_send_server_request( /* async only occurs if a network timeout is set */ /* honor network timeout */ + LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec ) { /* caller will have to call again */ ld->ld_errno = LDAP_X_CONNECTING; } + LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); /* fallthru */ default: @@ -246,6 +292,7 @@ ldap_send_server_request( /* Forget about the bind */ --parentreq->lr_outrefcnt; } + LDAP_CONN_UNLOCK_IF(m_noconn); return( -1 ); } @@ -255,10 +302,13 @@ ldap_send_server_request( if ( LDAP_IS_UDP( ld )) { BerElement tmpber = *ber; ber_rewind( &tmpber ); + LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); rc = ber_write( &tmpber, ld->ld_options.ldo_peer, sizeof( struct sockaddr ), 0 ); + LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); if ( rc == -1 ) { ld->ld_errno = LDAP_ENCODING_ERROR; + LDAP_CONN_UNLOCK_IF(m_noconn); return rc; } } @@ -276,7 +326,10 @@ ldap_send_server_request( { rc = -1; } - if ( rc ) return rc; + if ( rc ) { + LDAP_CONN_UNLOCK_IF(m_noconn); + return rc; + } lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) ); if ( lr == NULL ) { @@ -287,6 +340,7 @@ ldap_send_server_request( /* Forget about the bind */ --parentreq->lr_outrefcnt; } + LDAP_CONN_UNLOCK_IF(m_noconn); return( -1 ); } lr->lr_msgid = msgid; @@ -345,6 +399,7 @@ ldap_send_server_request( msgid = -1; } + LDAP_CONN_UNLOCK_IF(m_noconn); return( msgid ); } @@ -375,18 +430,17 @@ find_tls_ext( LDAPURLDesc *srv ) } /* - * caller must hold ld_req_mutex or be exclusive user of ld - * if ( connect != 0 ) or ( bind != NULL ) caller must also hold - * ld_req_mutex and ld_res_mutex + * always protected by conn_mutex + * optionally protected by req_mutex and res_mutex */ - LDAPConn * ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, - int connect, LDAPreqinfo *bind ) + int connect, LDAPreqinfo *bind, int m_req, int m_res ) { LDAPConn *lc; int async = 0; + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); Debug( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n", use_ldsb, connect, (bind != NULL) ); /* @@ -445,15 +499,10 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, } lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED; - LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); lc->lconn_next = ld->ld_conns; ld->ld_conns = lc; - LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); if ( connect ) { - LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); - LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); - #ifdef HAVE_TLS if ( lc->lconn_server->lud_exts ) { int rc, ext = find_tls_ext( lc->lconn_server ); @@ -464,11 +513,13 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, ++lc->lconn_refcnt; /* avoid premature free */ ld->ld_defconn = lc; - LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); - LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); + LDAP_REQ_UNLOCK_IF(m_req); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + LDAP_RES_UNLOCK_IF(m_res); rc = ldap_start_tls_s( ld, NULL, NULL ); - LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); - LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + LDAP_RES_LOCK_IF(m_res); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + LDAP_REQ_LOCK_IF(m_req); ld->ld_defconn = savedefconn; --lc->lconn_refcnt; @@ -485,9 +536,6 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, int err = 0; LDAPConn *savedefconn; - LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); - LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); - /* Set flag to prevent additional referrals * from being processed on this * connection until the bind has completed @@ -507,13 +555,15 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, ld->ld_defconn = lc; Debug( LDAP_DEBUG_TRACE, "Call application rebind_proc\n", 0, 0, 0); - LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); - LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); + LDAP_REQ_UNLOCK_IF(m_req); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + LDAP_RES_UNLOCK_IF(m_res); err = (*ld->ld_rebind_proc)( ld, bind->ri_url, bind->ri_request, bind->ri_msgid, ld->ld_rebind_params ); - LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); - LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + LDAP_RES_LOCK_IF(m_res); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + LDAP_REQ_LOCK_IF(m_req); ld->ld_defconn = savedefconn; --lc->lconn_refcnt; @@ -538,8 +588,9 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, "anonymous rebind via ldap_sasl_bind(\"\")\n", 0, 0, 0); - LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); - LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); + LDAP_REQ_UNLOCK_IF(m_req); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + LDAP_RES_UNLOCK_IF(m_res); rc = ldap_sasl_bind( ld, "", LDAP_SASL_SIMPLE, &passwd, NULL, NULL, &msgid ); if ( rc != LDAP_SUCCESS ) { @@ -583,8 +634,9 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, } } } - LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); - LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + LDAP_RES_LOCK_IF(m_res); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + LDAP_REQ_LOCK_IF(m_req); ld->ld_defconn = savedefconn; --lc->lconn_refcnt; @@ -596,11 +648,11 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, if ( lc != NULL ) lc->lconn_rebind_inprogress = 0; } - return( lc ); } +/* protected by ld_conn_mutex */ static LDAPConn * find_connection( LDAP *ld, LDAPURLDesc *srv, int any ) /* @@ -613,7 +665,7 @@ find_connection( LDAP *ld, LDAPURLDesc *srv, int any ) int lcu_port, lsu_port; int found = 0; - LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) { lcu = lc->lconn_server; lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme, @@ -637,35 +689,34 @@ find_connection( LDAP *ld, LDAPURLDesc *srv, int any ) if ( found ) break; } - LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); return lc; } -/* - * NOTE: the caller holds ld_req_mutex - */ +/* protected by ld_conn_mutex */ static void use_connection( LDAP *ld, LDAPConn *lc ) { + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); ++lc->lconn_refcnt; lc->lconn_lastused = time( NULL ); } +/* protected by ld_conn_mutex */ void ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind ) { LDAPConn *tmplc, *prevlc; + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); Debug( LDAP_DEBUG_TRACE, "ldap_free_connection %d %d\n", force, unbind, 0 ); if ( force || --lc->lconn_refcnt <= 0 ) { /* remove from connections list first */ - LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); for ( prevlc = NULL, tmplc = ld->ld_conns; tmplc != NULL; @@ -684,7 +735,6 @@ ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind ) } prevlc = tmplc; } - LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); /* process connection callbacks */ { @@ -693,19 +743,23 @@ ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind ) ldap_conncb *cb; lo = &ld->ld_options; + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); if ( lo->ldo_conn_cbs ) { for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) { cb = ll->ll_data; cb->lc_del( ld, lc->lconn_sb, cb ); } } + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); lo = LDAP_INT_GLOBAL_OPT(); + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); if ( lo->ldo_conn_cbs ) { for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) { cb = ll->ll_data; cb->lc_del( ld, lc->lconn_sb, cb ); } } + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); } if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) { @@ -773,6 +827,7 @@ ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind ) } +/* Protects self with ld_conn_mutex */ #ifdef LDAP_DEBUG void ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all ) @@ -781,6 +836,7 @@ ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all ) char timebuf[32]; Debug( LDAP_DEBUG_TRACE, "** ld %p Connection%s:\n", (void *)ld, all ? "s" : "", 0 ); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) { if ( lc->lconn_server != NULL ) { Debug( LDAP_DEBUG_TRACE, "* host: %s port: %d%s\n", @@ -817,9 +873,11 @@ ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all ) break; } } + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); } +/* protected by req_mutex and res_mutex */ void ldap_dump_requests_and_responses( LDAP *ld ) { @@ -868,9 +926,11 @@ ldap_dump_requests_and_responses( LDAP *ld ) } #endif /* LDAP_DEBUG */ +/* protected by req_mutex */ static void ldap_free_request_int( LDAP *ld, LDAPRequest *lr ) { + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); /* if lr_refcnt > 0, the request has been looked up * by ldap_find_request_by_msgid(); if in the meanwhile * the request is free()'d by someone else, just decrease @@ -921,11 +981,11 @@ ldap_free_request_int( LDAP *ld, LDAPRequest *lr ) LDAP_FREE( lr ); } +/* protected by req_mutex */ void ldap_free_request( LDAP *ld, LDAPRequest *lr ) { LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); - Debug( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n", lr->lr_origid, lr->lr_msgid, 0 ); @@ -993,6 +1053,8 @@ static int ldap_int_nextref( * (OUT) hadrefp = 1 if sucessfully followed referral * * Return value - number of referrals followed + * + * Protected by res_mutex, conn_mutex and req_mutex (try_read1msg) */ int ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp ) @@ -1008,11 +1070,14 @@ ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char * LDAPreqinfo rinfo; LDAP_NEXTREF_PROC *nextref_proc = ld->ld_nextref_proc ? ld->ld_nextref_proc : ldap_int_nextref; + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); + Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n", 0, 0, 0 ); + ld->ld_errno = LDAP_SUCCESS; /* optimistic */ *hadrefp = 0; - Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n", 0, 0, 0 ); - unfollowed = NULL; rc = count = 0; @@ -1184,10 +1249,8 @@ ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char * /* Send the new request to the server - may require a bind */ rinfo.ri_msgid = origreq->lr_origid; rinfo.ri_url = refarray[i]; - LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); rc = ldap_send_server_request( ld, ber, id, - origreq, &srv, NULL, &rinfo ); - LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + origreq, &srv, NULL, &rinfo, 0, 1 ); if ( rc < 0 ) { /* Failure, try next referral in the list */ Debug( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n", @@ -1208,6 +1271,7 @@ ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char * if ( lc == NULL ) { ld->ld_errno = LDAP_OPERATIONS_ERROR; rc = -1; + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); goto done; } } @@ -1253,6 +1317,7 @@ done: /* * XXX merging of errors in this routine needs to be improved + * Protected by res_mutex, conn_mutex and req_mutex (try_read1msg) */ int ldap_chase_referrals( LDAP *ld, @@ -1270,6 +1335,9 @@ ldap_chase_referrals( LDAP *ld, LDAPreqinfo rinfo; LDAPConn *lc; + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); Debug( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n", 0, 0, 0 ); ld->ld_errno = LDAP_SUCCESS; /* optimistic */ @@ -1367,11 +1435,8 @@ ldap_chase_referrals( LDAP *ld, rinfo.ri_msgid = origreq->lr_origid; - LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); rc = ldap_send_server_request( ld, ber, id, - lr, &srv, NULL, &rinfo ); - LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); - + lr, &srv, NULL, &rinfo, 0, 1 ); LDAP_FREE( rinfo.ri_url ); if( rc >= 0 ) { @@ -1561,12 +1626,12 @@ re_encode_request( LDAP *ld, } +/* protected by req_mutex */ LDAPRequest * ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid ) { LDAPRequest *lr; - LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) { if ( lr->lr_status == LDAP_REQST_COMPLETED ) { continue; /* Skip completed requests */ @@ -1576,17 +1641,16 @@ ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid ) break; } } - LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); return( lr ); } +/* protected by req_mutex */ void ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit ) { LDAPRequest *lr; - LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) { if ( lr == lrx ) { if ( lr->lr_refcnt > 0 ) { @@ -1607,5 +1671,4 @@ ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit ) } else if ( freeit ) { ldap_free_request( ld, lrx ); } - LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); } diff --git a/libraries/libldap/result.c b/libraries/libldap/result.c index 29c0b6f55e..a50d7c2a29 100644 --- a/libraries/libldap/result.c +++ b/libraries/libldap/result.c @@ -66,8 +66,8 @@ #include "ldap_log.h" #include "lutil.h" -static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid, int *idx )); -static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid, int idx )); +static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid )); +static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid )); static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout, LDAPMessage **result )); static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid, @@ -120,6 +120,7 @@ ldap_result( return rc; } +/* protected by res_mutex */ static LDAPMessage * chkResponseList( LDAP *ld, @@ -144,12 +145,10 @@ chkResponseList( lastlm = &ld->ld_responses; for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) { - int idx; - nextlm = lm->lm_next; ++cnt; - if ( ldap_abandoned( ld, lm->lm_msgid, &idx ) ) { + if ( ldap_abandoned( ld, lm->lm_msgid ) ) { Debug( LDAP_DEBUG_ANY, "response list msg abandoned, " "msgid %d message type %s\n", @@ -164,7 +163,7 @@ chkResponseList( default: /* there's no need to keep the id * in the abandoned list any longer */ - ldap_mark_abandoned( ld, lm->lm_msgid, idx ); + ldap_mark_abandoned( ld, lm->lm_msgid ); break; } @@ -231,6 +230,7 @@ chkResponseList( return lm; } +/* protected by res_mutex */ static int wait4msg( LDAP *ld, @@ -284,9 +284,7 @@ wait4msg( if ( ldap_debug & LDAP_DEBUG_TRACE ) { Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n", (void *)ld, msgid, all ); - LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); ldap_dump_connection( ld, ld->ld_conns, 1 ); - LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); ldap_dump_requests_and_responses( ld ); LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); @@ -308,7 +306,6 @@ wait4msg( break; } } - LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); if ( !lc_ready ) { int err; @@ -328,6 +325,7 @@ wait4msg( { ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN : LDAP_TIMEOUT); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); return( rc ); } @@ -349,8 +347,6 @@ wait4msg( { ldap_int_flush_request( ld, ld->ld_requests ); } - LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); - LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); for ( lc = ld->ld_conns; rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL; lc = lnext ) @@ -360,25 +356,22 @@ wait4msg( { /* Don't let it get freed out from under us */ ++lc->lconn_refcnt; - LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); rc = try_read1msg( ld, msgid, all, lc, result ); lnext = lc->lconn_next; /* Only take locks if we're really freeing */ if ( lc->lconn_refcnt <= 1 ) { - LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); ldap_free_connection( ld, lc, 0, 1 ); - LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); } else { --lc->lconn_refcnt; } - LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); } else { lnext = lc->lconn_next; } } - LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); } + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); } if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) { @@ -432,6 +425,7 @@ wait4msg( } +/* protected by res_mutex, conn_mutex and req_mutex */ static ber_tag_t try_read1msg( LDAP *ld, @@ -443,7 +437,6 @@ try_read1msg( BerElement *ber; LDAPMessage *newmsg, *l, *prev; ber_int_t id; - int idx; ber_tag_t tag; ber_len_t len; int foundit = 0; @@ -461,6 +454,8 @@ try_read1msg( assert( lc != NULL ); LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n", (void *)ld, msgid, all ); @@ -535,7 +530,7 @@ nextresp3: /* if it's been abandoned, toss it */ if ( id > 0 ) { - if ( ldap_abandoned( ld, id, &idx ) ) { + if ( ldap_abandoned( ld, id ) ) { /* the message type */ tag = ber_peek_tag( ber, &len ); switch ( tag ) { @@ -548,7 +543,7 @@ nextresp3: default: /* there's no need to keep the id * in the abandoned list any longer */ - ldap_mark_abandoned( ld, id, idx ); + ldap_mark_abandoned( ld, id ); break; } @@ -1337,35 +1332,37 @@ ldap_msgdelete( LDAP *ld, int msgid ) * * return the location of the message id in the array of abandoned * message ids, or -1 - * - * expects ld_res_mutex to be locked */ static int -ldap_abandoned( LDAP *ld, ber_int_t msgid, int *idxp ) +ldap_abandoned( LDAP *ld, ber_int_t msgid ) { - LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); - - assert( idxp != NULL ); + int ret, idx; assert( msgid >= 0 ); - return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp ); + LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex ); + ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx ); + LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex ); + return ret; } /* * ldap_mark_abandoned - * - * expects ld_res_mutex to be locked */ static int -ldap_mark_abandoned( LDAP *ld, ber_int_t msgid, int idx ) +ldap_mark_abandoned( LDAP *ld, ber_int_t msgid ) { - LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); + int ret, idx; - /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */ - assert( idx >= 0 ); - assert( (unsigned) idx < ld->ld_nabandoned ); - assert( ld->ld_abandoned[ idx ] == msgid ); - - return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned, + assert( msgid >= 0 ); + LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex ); + ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx ); + if (ret <= 0) { /* error or already deleted by another thread */ + LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex ); + return ret; + } + /* still in abandoned array, so delete */ + ret = ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned, msgid, idx ); + LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex ); + return ret; } diff --git a/libraries/libldap/unbind.c b/libraries/libldap/unbind.c index 4ddfdfc7a9..5b43eee34a 100644 --- a/libraries/libldap/unbind.c +++ b/libraries/libldap/unbind.c @@ -80,18 +80,44 @@ ldap_ld_free( LDAPMessage *lm, *next; int err = LDAP_SUCCESS; + LDAP_MUTEX_LOCK( &ld->ld_ldcmutex ); + /* Someone else is still using this ld. */ + if (ld->ld_ldcrefcnt > 1) { /* but not last thread */ + /* clean up self only */ + ld->ld_ldcrefcnt--; + if ( ld->ld_error != NULL ) { + LDAP_FREE( ld->ld_error ); + ld->ld_error = NULL; + } + + if ( ld->ld_matched != NULL ) { + LDAP_FREE( ld->ld_matched ); + ld->ld_matched = NULL; + } + if ( ld->ld_referrals != NULL) { + LDAP_VFREE(ld->ld_referrals); + ld->ld_referrals = NULL; + } + LDAP_MUTEX_UNLOCK( &ld->ld_ldcmutex ); + LDAP_FREE( (char *) ld ); + return( err ); + } + + /* This ld is the last thread. */ + /* free LDAP structure and outstanding requests/responses */ LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); while ( ld->ld_requests != NULL ) { ldap_free_request( ld, ld->ld_requests ); } + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); /* free and unbind from all open connections */ while ( ld->ld_conns != NULL ) { ldap_free_connection( ld, ld->ld_conns, 1, close ); } - LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); - + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); for ( lm = ld->ld_responses; lm != NULL; lm = next ) { next = lm->lm_next; @@ -103,6 +129,7 @@ ldap_ld_free( ld->ld_abandoned = NULL; } LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); + LDAP_MUTEX_LOCK( &ld->ld_ldopts_mutex ); /* final close callbacks */ { @@ -126,7 +153,7 @@ ldap_ld_free( ld->ld_matched = NULL; } - if( ld->ld_referrals != NULL) { + if ( ld->ld_referrals != NULL) { LDAP_VFREE(ld->ld_referrals); ld->ld_referrals = NULL; } @@ -188,22 +215,35 @@ ldap_ld_free( ldap_controls_free( ld->ld_options.ldo_cctrls ); ld->ld_options.ldo_cctrls = NULL; } + LDAP_MUTEX_UNLOCK( &ld->ld_ldopts_mutex ); ber_sockbuf_free( ld->ld_sb ); #ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_destroy( &ld->ld_msgid_mutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_conn_mutex ); ldap_pvt_thread_mutex_destroy( &ld->ld_req_mutex ); ldap_pvt_thread_mutex_destroy( &ld->ld_res_mutex ); - ldap_pvt_thread_mutex_destroy( &ld->ld_conn_mutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_abandon_mutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_ldopts_mutex ); + ldap_pvt_thread_mutex_unlock( &ld->ld_ldcmutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_ldcmutex ); #endif #ifndef NDEBUG LDAP_TRASH(ld); #endif + LDAP_FREE( (char *) ld->ldc ); LDAP_FREE( (char *) ld ); return( err ); } +int +ldap_destroy( LDAP *ld ) +{ + return ( ldap_ld_free( ld, 1, NULL, NULL ) ); +} + int ldap_unbind_s( LDAP *ld ) { @@ -233,7 +273,7 @@ ldap_send_unbind( return( ld->ld_errno ); } - id = ++(ld)->ld_msgid; + LDAP_NEXT_MSGID(ld, id); /* fill it in */ if ( ber_printf( ber, "{itn" /*}*/, id, diff --git a/tests/progs/Makefile.in b/tests/progs/Makefile.in index a08e57f729..d956c184cf 100644 --- a/tests/progs/Makefile.in +++ b/tests/progs/Makefile.in @@ -14,17 +14,21 @@ ## . PROGRAMS = slapd-tester slapd-search slapd-read slapd-addel slapd-modrdn \ - slapd-modify slapd-bind ldif-filter + slapd-modify slapd-bind slapd-mtread ldif-filter SRCS = slapd-common.c \ slapd-tester.c slapd-search.c slapd-read.c slapd-addel.c \ - slapd-modrdn.c slapd-modify.c slapd-bind.c ldif-filter.c + slapd-modrdn.c slapd-modify.c slapd-bind.c slapd-mtread.c \ + ldif-filter.c LDAP_INCDIR= ../../include LDAP_LIBDIR= ../../libraries XLIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLUTIL_A) $(LDAP_LIBLBER_LA) +XRLIBS = $(LDAP_LIBLDAP_R_LA) $(LDAP_LIBLUTIL_A) $(LDAP_LIBLBER_LA) XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS) +RLIBS = $(XRLIBS) $(XXLIBS) $(AC_LIBS) $(XXXLIBS) + OBJS = slapd-common.o @@ -57,3 +61,6 @@ slapd-bind: slapd-bind.o $(OBJS) $(XLIBS) ldif-filter: ldif-filter.o $(XLIBS) $(LTLINK) -o $@ ldif-filter.o $(LIBS) +slapd-mtread: slapd-mtread.o $(OBJS) $(XRLIBS) + $(LTLINK) -o $@ slapd-mtread.o $(OBJS) $(RLIBS) + diff --git a/tests/progs/slapd-mtread.c b/tests/progs/slapd-mtread.c new file mode 100644 index 0000000000..3ef8b5ecf8 --- /dev/null +++ b/tests/progs/slapd-mtread.c @@ -0,0 +1,758 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* 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 +#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 | ([-h ] -p ) " + "-D " + "-w " + "-e " + "[-A] " + "[-C] " + "[-F] " + "[-N] " + "[-v] " + "[-c connections] " + "[-f filter] " + "[-i ] " + "[-l ] " + "[-L ] " + "[-m threads] " + "[-M threads] " + "[-r ] " + "[-t ] " + "[-T ] " + "[] " + "\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; + } + } +} diff --git a/tests/scripts/defines.sh b/tests/scripts/defines.sh index 48d8c9a3e9..4f29c464af 100755 --- a/tests/scripts/defines.sh +++ b/tests/scripts/defines.sh @@ -192,6 +192,7 @@ LDAPCOMPARE="$CLIENTDIR/ldapcompare $TOOLARGS" LDAPEXOP="$CLIENTDIR/ldapexop $TOOLARGS" SLAPDTESTER=$PROGDIR/slapd-tester LDIFFILTER=$PROGDIR/ldif-filter +SLAPDMTREAD=$PROGDIR/slapd-mtread LVL=${SLAPD_DEBUG-0x4105} LOCALHOST=localhost BASEPORT=${SLAPD_BASEPORT-9010} @@ -311,6 +312,8 @@ SLAVE2OUT=$SERVER3OUT SLAVEFLT=$SERVER2FLT SLAVE2FLT=$SERVER3FLT +MTREADOUT=$TESTDIR/mtread.out + # original outputs for cmp PROXYCACHEOUT=$DATADIR/proxycache.out REFERRALOUT=$DATADIR/referrals.out diff --git a/tests/scripts/test060-mt-hot b/tests/scripts/test060-mt-hot new file mode 100755 index 0000000000..fff6c72505 --- /dev/null +++ b/tests/scripts/test060-mt-hot @@ -0,0 +1,289 @@ +#! /bin/sh +# $OpenLDAP$ +## This work is part of OpenLDAP Software . +## +## 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 +## . + +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 -- 2.39.5