diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libldap5/sources/ldap/prldap/ldappr-threads.c | |
download | illumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libldap5/sources/ldap/prldap/ldappr-threads.c')
-rw-r--r-- | usr/src/lib/libldap5/sources/ldap/prldap/ldappr-threads.c | 754 |
1 files changed, 754 insertions, 0 deletions
diff --git a/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-threads.c b/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-threads.c new file mode 100644 index 0000000000..b6b8a0e27a --- /dev/null +++ b/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-threads.c @@ -0,0 +1,754 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +/* + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998-1999 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +/* + * Thread callback functions for libldap that use the NSPR (Netscape + * Portable Runtime) thread API. + * + */ + +#ifdef _SOLARIS_SDK +#include <thread.h> +#include <synch.h> +#include <prinit.h> +#include <prthread.h> +#include <syslog.h> +#include <string.h> +#include <sys/types.h> +#include <signal.h> +#include <errno.h> +extern int errno; +#endif /* _SOLARIS_SDK */ + +#include "ldappr-int.h" + +#ifndef _SOLARIS_SDK +/* + * Macros: + */ +/* + * Grow thread private data arrays 10 elements at a time. + */ +#define PRLDAP_TPD_ARRAY_INCREMENT 10 + +/* + * Structures and types: + */ +/* + * Structure used by libldap thread callbacks to maintain error information. + */ +typedef struct prldap_errorinfo { + int plei_lderrno; + char *plei_matched; + char *plei_errmsg; +} PRLDAP_ErrorInfo; + +/* + * Structure used to maintain thread-private data. At the present time, + * only error info. is thread-private. One of these structures is allocated + * for each thread. + */ +typedef struct prldap_tpd_header { + int ptpdh_tpd_count; /* # of data items allocated */ + void **ptpdh_dataitems; /* array of data items */ +} PRLDAP_TPDHeader; + +/* + * Structure used by associate a PRLDAP thread-private data index with an + * LDAP session handle. One of these exists for each active LDAP session + * handle. + */ +typedef struct prldap_tpd_map { + LDAP *prtm_ld; /* non-NULL if in use */ + PRUintn prtm_index; /* index into TPD array */ + struct prldap_tpd_map *prtm_next; +} PRLDAP_TPDMap; + +#ifdef _SOLARIS_SDK +extern mutex_t inited_mutex; +#endif /* _SOLARIS_SDK */ + +/* + * Static Variables: + */ +/* + * prldap_map_list points to all of the PRLDAP_TPDMap structures + * we have ever allocated. We recycle them as we open and close LDAP + * sessions. + */ +static PRLDAP_TPDMap *prldap_map_list = NULL; + + +/* + * The prldap_map_mutex is used to protect access to the prldap_map_list. + */ +static PRLock *prldap_map_mutex = NULL; + +/* + * The prldap_tpd_maxindex value is used to track the largest TPD array + * index we have used. + */ +static PRInt32 prldap_tpd_maxindex = -1; + +/* + * prldap_tpdindex is an NSPR thread private data index we use to + * maintain our own thread-private data. It is initialized inside + * prldap_init_tpd(). + */ +static PRUintn prldap_tpdindex = 0; + +/* + * The prldap_callonce_init_tpd structure is used by NSPR to ensure + * that prldap_init_tpd() is called at most once. + */ +static PRCallOnceType prldap_callonce_init_tpd = { 0, 0, 0 }; + + +/* + * Private function prototypes: + */ +static void prldap_set_ld_error( int err, char *matched, char *errmsg, + void *errorarg ); +static int prldap_get_ld_error( char **matchedp, char **errmsgp, + void *errorarg ); +#endif +static void *prldap_mutex_alloc( void ); +static void prldap_mutex_free( void *mutex ); +static int prldap_mutex_lock( void *mutex ); +static int prldap_mutex_unlock( void *mutex ); +static void *prldap_get_thread_id( void ); +#ifndef _SOLARIS_SDK +static PRStatus prldap_init_tpd( void ); +static PRLDAP_TPDMap *prldap_allocate_map( LDAP *ld ); +static void prldap_return_map( PRLDAP_TPDMap *map ); +static PRUintn prldap_new_tpdindex( void ); +static int prldap_set_thread_private( PRInt32 tpdindex, void *priv ); +static void *prldap_get_thread_private( PRInt32 tpdindex ); +static PRLDAP_TPDHeader *prldap_tsd_realloc( PRLDAP_TPDHeader *tsdhdr, + int maxindex ); +static void prldap_tsd_destroy( void *priv ); +#endif + + +/* + * Install NSPR thread functions into ld (if ld is NULL, they are installed + * as the default functions for new LDAP * handles). + * + * Returns 0 if all goes well and -1 if not. + */ +int +prldap_install_thread_functions( LDAP *ld, int shared ) +{ + struct ldap_thread_fns tfns; + struct ldap_extra_thread_fns xtfns; + +#ifndef _SOLARIS_SDK + if ( PR_CallOnce( &prldap_callonce_init_tpd, prldap_init_tpd ) + != PR_SUCCESS ) { + ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL ); + return( -1 ); + } +#endif /* _SOLARIS_SDK */ + + /* set thread function pointers */ + memset( &tfns, '\0', sizeof(struct ldap_thread_fns) ); + tfns.ltf_get_errno = prldap_get_system_errno; + tfns.ltf_set_errno = prldap_set_system_errno; + if ( shared ) { + tfns.ltf_mutex_alloc = prldap_mutex_alloc; + tfns.ltf_mutex_free = prldap_mutex_free; + tfns.ltf_mutex_lock = prldap_mutex_lock; + tfns.ltf_mutex_unlock = prldap_mutex_unlock; +#ifdef _SOLARIS_SDK + tfns.ltf_get_lderrno = NULL; + tfns.ltf_set_lderrno = NULL; +#else + tfns.ltf_get_lderrno = prldap_get_ld_error; + tfns.ltf_set_lderrno = prldap_set_ld_error; + if ( ld != NULL ) { + /* + * If this is a real ld (i.e., we are not setting the global + * defaults) allocate thread private data for error information. + * If ld is NULL we do not do this here but it is done in + * prldap_thread_new_handle(). + */ + if (( tfns.ltf_lderrno_arg = (void *)prldap_allocate_map( ld )) + == NULL ) { + return( -1 ); + } + } +#endif + } + + if ( ldap_set_option( ld, LDAP_OPT_THREAD_FN_PTRS, + (void *)&tfns ) != 0 ) { +#ifndef _SOLARIS_SDK + prldap_return_map( (PRLDAP_TPDMap *)tfns.ltf_lderrno_arg ); +#endif + return( -1 ); + } + + /* set extended thread function pointers */ + memset( &xtfns, '\0', sizeof(struct ldap_extra_thread_fns) ); + xtfns.ltf_threadid_fn = prldap_get_thread_id; + if ( ldap_set_option( ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS, + (void *)&xtfns ) != 0 ) { + return( -1 ); + } + + return( 0 ); +} + + +static void * +prldap_mutex_alloc( void ) +{ + return( (void *)PR_NewLock()); +} + + +static void +prldap_mutex_free( void *mutex ) +{ + PR_DestroyLock( (PRLock *)mutex ); +} + + +static int +prldap_mutex_lock( void *mutex ) +{ + PR_Lock( (PRLock *)mutex ); + return( 0 ); +} + + +static int +prldap_mutex_unlock( void *mutex ) +{ + if ( PR_Unlock( (PRLock *)mutex ) == PR_FAILURE ) { + return( -1 ); + } + + return( 0 ); +} + + +static void * +prldap_get_thread_id( void ) +{ +#ifdef _SOLARIS_SDK + return ((void *)thr_self()); +#else + return( (void *)PR_GetCurrentThread()); +#endif +} + +#ifndef _SOLARIS_SDK +static int +prldap_get_ld_error( char **matchedp, char **errmsgp, void *errorarg ) +{ + PRLDAP_TPDMap *map; + PRLDAP_ErrorInfo *eip; + + if (( map = (PRLDAP_TPDMap *)errorarg ) != NULL && ( eip = + (PRLDAP_ErrorInfo *)prldap_get_thread_private( + map->prtm_index )) != NULL ) { + if ( matchedp != NULL ) { + *matchedp = eip->plei_matched; + } + if ( errmsgp != NULL ) { + *errmsgp = eip->plei_errmsg; + } + return( eip->plei_lderrno ); + } else { + if ( matchedp != NULL ) { + *matchedp = NULL; + } + if ( errmsgp != NULL ) { + *errmsgp = NULL; + } + return( LDAP_LOCAL_ERROR ); /* punt */ + } +} + + +static void +prldap_set_ld_error( int err, char *matched, char *errmsg, void *errorarg ) +{ + PRLDAP_TPDMap *map; + PRLDAP_ErrorInfo *eip; + + if (( map = (PRLDAP_TPDMap *)errorarg ) != NULL ) { + if (( eip = (PRLDAP_ErrorInfo *)prldap_get_thread_private( + map->prtm_index )) == NULL ) { + /* + * Error info. has not yet been allocated for this thread. + * Do so now. Note that we free this memory only for the + * thread that calls prldap_thread_dispose_handle(), which + * should be the one that called ldap_unbind() -- see + * prldap_return_map(). Not freeing the memory used by + * other threads is deemed acceptable since it will be + * recycled and used by other LDAP sessions. All of the + * thread-private memory is freed when a thread exits + * (inside the prldap_tsd_destroy() function). + */ + eip = (PRLDAP_ErrorInfo *)PR_Calloc( 1, + sizeof( PRLDAP_ErrorInfo )); + if ( eip == NULL ) { + return; /* punt */ + } + (void)prldap_set_thread_private( map->prtm_index, eip ); + } + + eip->plei_lderrno = err; + if ( eip->plei_matched != NULL ) { + ldap_memfree( eip->plei_matched ); + } + eip->plei_matched = matched; + if ( eip->plei_errmsg != NULL ) { + ldap_memfree( eip->plei_errmsg ); + } + eip->plei_errmsg = errmsg; + } +} +#endif + + +/* + * Called when a new LDAP * session handle is allocated. + * Allocate thread-private data for error information, but only if + * it has not already been allocated and the get_ld_error callback has + * been installed. If ld is not NULL when prldap_install_thread_functions() + * is called, we will have already allocated the thread-private data there. + */ +int +prldap_thread_new_handle( LDAP *ld, void *sessionarg ) +{ + struct ldap_thread_fns tfns; + +#ifndef _SOLARIS_SDK + if ( ldap_get_option( ld, LDAP_OPT_THREAD_FN_PTRS, (void *)&tfns ) != 0 ) { + return( LDAP_LOCAL_ERROR ); + } + + if ( tfns.ltf_lderrno_arg == NULL && tfns.ltf_get_lderrno != NULL ) { + if (( tfns.ltf_lderrno_arg = (void *)prldap_allocate_map( ld )) == NULL + || ldap_set_option( ld, LDAP_OPT_THREAD_FN_PTRS, + (void *)&tfns ) != 0 ) { + return( LDAP_LOCAL_ERROR ); + } + } +#endif + + return( LDAP_SUCCESS ); +} + + +/* + * Called when an LDAP * session handle is being destroyed. + * Clean up our thread private data map. + */ +void +prldap_thread_dispose_handle( LDAP *ld, void *sessionarg ) +{ +#ifndef _SOLARIS_SDK + struct ldap_thread_fns tfns; + + if ( ldap_get_option( ld, LDAP_OPT_THREAD_FN_PTRS, + (void *)&tfns ) == 0 && + tfns.ltf_lderrno_arg != NULL ) { + prldap_return_map( (PRLDAP_TPDMap *)tfns.ltf_lderrno_arg ); + } +#endif +} + + +#ifndef _SOLARIS_SDK +static PRStatus +prldap_init_tpd( void ) +{ + if (( prldap_map_mutex = PR_NewLock()) == NULL || PR_NewThreadPrivateIndex( + &prldap_tpdindex, prldap_tsd_destroy ) != PR_SUCCESS ) { + return( PR_FAILURE ); + } + + prldap_map_list = NULL; + + return( PR_SUCCESS ); +} + + +/* + * Function: prldap_allocate_map() + * Description: allocate a thread-private data map to use for a new + * LDAP session handle. + * Returns: a pointer to the TPD map or NULL if none available. + */ +static PRLDAP_TPDMap * +prldap_allocate_map( LDAP *ld ) +{ + PRLDAP_TPDMap *map, *prevmap; + + PR_Lock( prldap_map_mutex ); + + /* + * first look for a map that is already allocated but free to be re-used + */ + prevmap = NULL; + for ( map = prldap_map_list; map != NULL; map = map->prtm_next ) { + if ( map->prtm_ld == NULL ) { + break; + } + prevmap = map; + } + + /* + * if none we found (map == NULL), try to allocate a new one and add it + * to the end of our global list. + */ + if ( map == NULL ) { + PRUintn tpdindex; + + tpdindex = prldap_new_tpdindex(); + map = (PRLDAP_TPDMap *)PR_Malloc( sizeof( PRLDAP_TPDMap )); + if ( map != NULL ) { + map->prtm_index = tpdindex; + map->prtm_next = NULL; + if ( prevmap == NULL ) { + prldap_map_list = map; + } else { + prevmap->prtm_next = map; + } + } + } + + if ( map != NULL ) { + map->prtm_ld = ld; /* now marked as "in use" */ + /* since we are reusing...reset */ + /* to initial state */ + (void)prldap_set_thread_private( map->prtm_index, NULL ); + } + + PR_Unlock( prldap_map_mutex ); + + return( map ); +} + + +/* + * Function: prldap_return_map() + * Description: return a thread-private data map to the pool of ones + * available for re-use. + */ +static void +prldap_return_map( PRLDAP_TPDMap *map ) +{ + PRLDAP_ErrorInfo *eip; + + PR_Lock( prldap_map_mutex ); + + /* + * Dispose of thread-private LDAP error information. Note that this + * only disposes of the memory consumed on THIS thread, but that is + * okay. See the comment in prldap_set_ld_error() for the reason why. + */ + if (( eip = (PRLDAP_ErrorInfo *)prldap_get_thread_private( + map->prtm_index )) != NULL && + prldap_set_thread_private( map->prtm_index, NULL ) == 0 ) { + if ( eip->plei_matched != NULL ) { + ldap_memfree( eip->plei_matched ); + } + if ( eip->plei_errmsg != NULL ) { + ldap_memfree( eip->plei_errmsg ); + } + + PR_Free( eip ); + } + + /* mark map as available for re-use */ + map->prtm_ld = NULL; + + PR_Unlock( prldap_map_mutex ); +} + + +/* + * Function: prldap_new_tpdindex() + * Description: allocate a thread-private data index. + * Returns: the new index. + */ +static PRUintn +prldap_new_tpdindex( void ) +{ + PRUintn tpdindex; + + tpdindex = (PRUintn)PR_AtomicIncrement( &prldap_tpd_maxindex ); + return( tpdindex ); +} + + +/* + * Function: prldap_set_thread_private() + * Description: store a piece of thread-private data. + * Returns: 0 if successful and -1 if not. + */ +static int +prldap_set_thread_private( PRInt32 tpdindex, void *priv ) +{ + PRLDAP_TPDHeader *tsdhdr; + + if ( tpdindex > prldap_tpd_maxindex ) { + return( -1 ); /* bad index */ + } + + tsdhdr = (PRLDAP_TPDHeader *)PR_GetThreadPrivate( prldap_tpdindex ); + if ( tsdhdr == NULL || tpdindex >= tsdhdr->ptpdh_tpd_count ) { + tsdhdr = prldap_tsd_realloc( tsdhdr, tpdindex ); + if ( tsdhdr == NULL ) { + return( -1 ); /* realloc failed */ + } + } + + tsdhdr->ptpdh_dataitems[ tpdindex ] = priv; + return( 0 ); +} + + +/* + * Function: prldap_get_thread_private() + * Description: retrieve a piece of thread-private data. If not set, + * NULL is returned. + * Returns: 0 if successful and -1 if not. + */ +static void * +prldap_get_thread_private( PRInt32 tpdindex ) +{ + PRLDAP_TPDHeader *tsdhdr; + + tsdhdr = (PRLDAP_TPDHeader *)PR_GetThreadPrivate( prldap_tpdindex ); + if ( tsdhdr == NULL ) { + return( NULL ); /* no thread private data */ + } + + if ( tpdindex >= tsdhdr->ptpdh_tpd_count + || tsdhdr->ptpdh_dataitems == NULL ) { + return( NULL ); /* fewer data items than requested index */ + } + + return( tsdhdr->ptpdh_dataitems[ tpdindex ] ); +} + + +/* + * Function: prldap_tsd_realloc() + * Description: enlarge the thread-private data array. + * Returns: the new PRLDAP_TPDHeader value (non-NULL if successful). + * Note: tsdhdr can be NULL (allocates a new PRLDAP_TPDHeader). + */ +static PRLDAP_TPDHeader * +prldap_tsd_realloc( PRLDAP_TPDHeader *tsdhdr, int maxindex ) +{ + void *newdataitems = NULL; + int count; + + if ( tsdhdr == NULL ) { + /* allocate a new thread private data header */ + if (( tsdhdr = PR_Calloc( 1, sizeof( PRLDAP_TPDHeader ))) == NULL ) { + return( NULL ); + } + (void)PR_SetThreadPrivate( prldap_tpdindex, tsdhdr ); + } + + /* + * Make the size of the new array the next highest multiple of + * the array increment value that is greater than maxindex. + */ + count = PRLDAP_TPD_ARRAY_INCREMENT * + ( 1 + ( maxindex / PRLDAP_TPD_ARRAY_INCREMENT )); + + /* increase the size of the data item array if necessary */ + if ( count > tsdhdr->ptpdh_tpd_count ) { + newdataitems = (PRLDAP_ErrorInfo *)PR_Calloc( count, sizeof( void * )); + if ( newdataitems == NULL ) { + return( NULL ); + } + if ( tsdhdr->ptpdh_dataitems != NULL ) { /* preserve old data */ + memcpy( newdataitems, tsdhdr->ptpdh_dataitems, + tsdhdr->ptpdh_tpd_count * sizeof( void * )); + PR_Free( tsdhdr->ptpdh_dataitems ); + } + + tsdhdr->ptpdh_tpd_count = count; + tsdhdr->ptpdh_dataitems = newdataitems; + } + + return( tsdhdr ); +} + + +/* + * Function: prldap_tsd_destroy() + * Description: Free a thread-private data array. Installed as an NSPR TPD + * destructor function + * Returns: nothing. + * Note: this function assumes that each TPD item installed at the PRLDAP + * level can be freed with a call to PR_Free(). + */ +static void +prldap_tsd_destroy( void *priv ) +{ + PRLDAP_TPDHeader *tsdhdr; + int i; + + tsdhdr = (PRLDAP_TPDHeader *)priv; + if ( tsdhdr != NULL ) { + if ( tsdhdr->ptpdh_dataitems != NULL ) { + for ( i = 0; i < tsdhdr->ptpdh_tpd_count; ++i ) { + if ( tsdhdr->ptpdh_dataitems[ i ] != NULL ) { + PR_Free( tsdhdr->ptpdh_dataitems[ i ] ); + tsdhdr->ptpdh_dataitems[ i ] = NULL; + } + } + PR_Free( tsdhdr->ptpdh_dataitems ); + tsdhdr->ptpdh_dataitems = NULL; + } + PR_Free( tsdhdr ); + } +} +#endif + +#ifdef _SOLARIS_SDK +#pragma init(prldap_nspr_init) +static mutex_t nspr_init_lock = DEFAULTMUTEX; +static mutex_t nspr_idle_lock = DEFAULTMUTEX; +static cond_t nspr_idle_cond = DEFAULTCV; +static int nspr_pr_init_is_done = 0; +static int nspr_initialized = 0; + +void * +prldap_nspr_idle_primordial_thread(void *arg) { + /* + * Make sure PR_Init finishes before any other thread can continue + */ + (void) mutex_lock(&nspr_idle_lock); + if (PR_Initialized() == PR_FALSE) + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + nspr_pr_init_is_done = 1; + (void) cond_signal(&nspr_idle_cond); + (void) mutex_unlock(&nspr_idle_lock); + + /* Debug only */ + syslog(LOG_DEBUG, "NSPR is initialized by the" + "idle primordial thread tid %ld created by thread " + "tid %ld", thr_self(), (long)arg); + pause(); + +} + +/* + * Initialize NSPR once + * + * Ideally this should be done in .init of NSPR. + * This is a workaround so only main thread can initialize + * NSPR but main() does not need to call PR_Init(). + * The future direction is NSPR free so we don't want programs + * to call PR_Init(). + * + * For most of cases, programs link libldap (-lldap) + * and .init is executed before the control is transfered to + * main(). + * But for programs linking libnsl (-lnsl), libldap is loaded + * via dlopen("nss_ldap.so.1", RTLD_LAZY) so the thread loads + * libldap is not necessary a main or a primordial + * thread. In the latter case, an idle primordial thread is created + * to initialize NSPR so NSPR won't be initialized by non-primordial + * threads. + * libldap is built with "-z nodelete" so libldap and libnspr4.so + * are persistent in the address space. + */ +void +prldap_nspr_init(void) { + struct sigaction action; + + /* + * For performance reason, test it here first + */ + if (nspr_initialized != 0) + return; + + (void) mutex_lock(&nspr_init_lock); + /* Make sure PR_Init() is executed only once */ + if (nspr_initialized == 0) { + /* + * PR_Init changes the signal handler of SIGPIPE to SIG_IGN. + * Save the original and restore it after PR_Init. + */ + (void) sigaction(SIGPIPE, NULL, &action); + + if (thr_self() == 1) { + /* main thread */ + if (PR_Initialized() == PR_FALSE) + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + nspr_initialized = 1; + } else { + if (thr_create(NULL, NULL, + prldap_nspr_idle_primordial_thread, + (void *)thr_self(), THR_DETACHED, NULL) != 0) { + syslog(LOG_ERR, + "libldap:.init: Can't create thread. " + "%s", strerror(errno)); + } else { + /* + * Make sure PR_Init finishes before any other thread + * can continue. + * It's unlikely, but not impossible that this thread + * finishes dlopen and starts to call + * LDAP API when the idle thread still has not + * finished PR_Init() yet. + */ + (void) mutex_lock(&nspr_idle_lock); + while (nspr_pr_init_is_done == 0) { + (void) cond_wait(&nspr_idle_cond, + &nspr_idle_lock); + + } + (void) mutex_unlock(&nspr_idle_lock); + nspr_initialized = 1; + } + } + /* + * Restore signal handling attributes of SIGPIPE + */ + (void) sigaction(SIGPIPE, &action, NULL); + + } + (void) mutex_unlock(&nspr_init_lock); +} +#endif |