1/*
2 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6#pragma ident	"%Z%%M%	%I%	%E% SMI"
7
8
9/*
10 * The contents of this file are subject to the Netscape Public
11 * License Version 1.1 (the "License"); you may not use this file
12 * except in compliance with the License. You may obtain a copy of
13 * the License at http://www.mozilla.org/NPL/
14 *
15 * Software distributed under the License is distributed on an "AS
16 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
17 * implied. See the License for the specific language governing
18 * rights and limitations under the License.
19 *
20 * The Original Code is Mozilla Communicator client code, released
21 * March 31, 1998.
22 *
23 * The Initial Developer of the Original Code is Netscape
24 * Communications Corporation. Portions created by Netscape are
25 * Copyright (C) 1998-1999 Netscape Communications Corporation. All
26 * Rights Reserved.
27 *
28 * Contributor(s):
29 */
30
31/*
32 * Thread callback functions for libldap that use the NSPR (Netscape
33 * Portable Runtime) thread API.
34 *
35 */
36
37#ifdef _SOLARIS_SDK
38#include <thread.h>
39#include <synch.h>
40#include <prinit.h>
41#include <prthread.h>
42#include <syslog.h>
43#include <string.h>
44#include <sys/types.h>
45#include <signal.h>
46#include <errno.h>
47extern int	errno;
48#endif	/* _SOLARIS_SDK */
49
50#include "ldappr-int.h"
51
52#ifndef _SOLARIS_SDK
53/*
54 * Macros:
55 */
56/*
57 * Grow thread private data arrays 10 elements at a time.
58 */
59#define PRLDAP_TPD_ARRAY_INCREMENT	10
60
61/*
62 * Structures and types:
63 */
64/*
65 * Structure used by libldap thread callbacks to maintain error information.
66 */
67typedef struct prldap_errorinfo {
68    int		plei_lderrno;
69    char	*plei_matched;
70    char	*plei_errmsg;
71} PRLDAP_ErrorInfo;
72
73/*
74 * Structure used to maintain thread-private data. At the present time,
75 * only error info. is thread-private.  One of these structures is allocated
76 * for each thread.
77 */
78typedef struct prldap_tpd_header {
79    int			ptpdh_tpd_count;	/* # of data items allocated */
80    void		**ptpdh_dataitems;	/* array of data items */
81} PRLDAP_TPDHeader;
82
83/*
84 * Structure used by associate a PRLDAP thread-private data index with an
85 * LDAP session handle. One of these exists for each active LDAP session
86 * handle.
87 */
88typedef struct prldap_tpd_map {
89    LDAP			*prtm_ld;	/* non-NULL if in use */
90    PRUintn			prtm_index;	/* index into TPD array */
91    struct prldap_tpd_map	*prtm_next;
92} PRLDAP_TPDMap;
93
94#ifdef _SOLARIS_SDK
95extern  mutex_t         inited_mutex;
96#endif  /* _SOLARIS_SDK */
97
98/*
99 * Static Variables:
100 */
101/*
102 * prldap_map_list points to all of the PRLDAP_TPDMap structures
103 * we have ever allocated.  We recycle them as we open and close LDAP
104 * sessions.
105 */
106static PRLDAP_TPDMap *prldap_map_list = NULL;
107
108
109/*
110 * The prldap_map_mutex is used to protect access to the prldap_map_list.
111 */
112static PRLock	*prldap_map_mutex = NULL;
113
114/*
115 * The prldap_tpd_maxindex value is used to track the largest TPD array
116 * index we have used.
117 */
118static PRInt32	prldap_tpd_maxindex = -1;
119
120/*
121 * prldap_tpdindex is an NSPR thread private data index we use to
122 * maintain our own thread-private data. It is initialized inside
123 * prldap_init_tpd().
124 */
125static PRUintn	prldap_tpdindex = 0;
126
127/*
128 * The prldap_callonce_init_tpd structure is used by NSPR to ensure
129 * that prldap_init_tpd() is called at most once.
130 */
131static PRCallOnceType prldap_callonce_init_tpd = { 0, 0, 0 };
132
133
134/*
135 * Private function prototypes:
136 */
137static void prldap_set_ld_error( int err, char *matched, char *errmsg,
138	void *errorarg );
139static int prldap_get_ld_error( char **matchedp, char **errmsgp,
140	void *errorarg );
141#endif
142static void *prldap_mutex_alloc( void );
143static void prldap_mutex_free( void *mutex );
144static int prldap_mutex_lock( void *mutex );
145static int prldap_mutex_unlock( void *mutex );
146static void *prldap_get_thread_id( void );
147#ifndef _SOLARIS_SDK
148static PRStatus prldap_init_tpd( void );
149static PRLDAP_TPDMap *prldap_allocate_map( LDAP *ld );
150static void prldap_return_map( PRLDAP_TPDMap *map );
151static PRUintn prldap_new_tpdindex( void );
152static int prldap_set_thread_private( PRInt32 tpdindex, void *priv );
153static void *prldap_get_thread_private( PRInt32 tpdindex );
154static PRLDAP_TPDHeader *prldap_tsd_realloc( PRLDAP_TPDHeader *tsdhdr,
155	int maxindex );
156static void prldap_tsd_destroy( void *priv );
157#endif
158
159
160/*
161 * Install NSPR thread functions into ld (if ld is NULL, they are installed
162 * as the default functions for new LDAP * handles).
163 *
164 * Returns 0 if all goes well and -1 if not.
165 */
166int
167prldap_install_thread_functions( LDAP *ld, int shared )
168{
169    struct ldap_thread_fns		tfns;
170    struct ldap_extra_thread_fns	xtfns;
171
172#ifndef _SOLARIS_SDK
173    if ( PR_CallOnce( &prldap_callonce_init_tpd, prldap_init_tpd )
174		!= PR_SUCCESS ) {
175	ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL );
176	return( -1 );
177    }
178#endif	/* _SOLARIS_SDK */
179
180    /* set thread function pointers */
181    memset( &tfns, '\0', sizeof(struct ldap_thread_fns) );
182    tfns.ltf_get_errno = prldap_get_system_errno;
183    tfns.ltf_set_errno = prldap_set_system_errno;
184    if ( shared ) {
185	tfns.ltf_mutex_alloc = prldap_mutex_alloc;
186	tfns.ltf_mutex_free = prldap_mutex_free;
187	tfns.ltf_mutex_lock = prldap_mutex_lock;
188	tfns.ltf_mutex_unlock = prldap_mutex_unlock;
189#ifdef _SOLARIS_SDK
190	tfns.ltf_get_lderrno = NULL;
191	tfns.ltf_set_lderrno = NULL;
192#else
193	tfns.ltf_get_lderrno = prldap_get_ld_error;
194	tfns.ltf_set_lderrno = prldap_set_ld_error;
195	if ( ld != NULL ) {
196	    /*
197	     * If this is a real ld (i.e., we are not setting the global
198	     * defaults) allocate thread private data for error information.
199	     * If ld is NULL we do not do this here but it is done in
200	     * prldap_thread_new_handle().
201	     */
202	    if (( tfns.ltf_lderrno_arg = (void *)prldap_allocate_map( ld ))
203		    == NULL ) {
204		return( -1 );
205	    }
206	}
207#endif
208    }
209
210    if ( ldap_set_option( ld, LDAP_OPT_THREAD_FN_PTRS,
211	    (void *)&tfns ) != 0 ) {
212#ifndef _SOLARIS_SDK
213	prldap_return_map( (PRLDAP_TPDMap *)tfns.ltf_lderrno_arg );
214#endif
215	return( -1 );
216    }
217
218    /* set extended thread function pointers */
219    memset( &xtfns, '\0', sizeof(struct ldap_extra_thread_fns) );
220    xtfns.ltf_threadid_fn = prldap_get_thread_id;
221    if ( ldap_set_option( ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS,
222	    (void *)&xtfns ) != 0 ) {
223	return( -1 );
224    }
225
226    return( 0 );
227}
228
229
230static void *
231prldap_mutex_alloc( void )
232{
233    return( (void *)PR_NewLock());
234}
235
236
237static void
238prldap_mutex_free( void *mutex )
239{
240    PR_DestroyLock( (PRLock *)mutex );
241}
242
243
244static int
245prldap_mutex_lock( void *mutex )
246{
247    PR_Lock( (PRLock *)mutex );
248    return( 0 );
249}
250
251
252static int
253prldap_mutex_unlock( void *mutex )
254{
255    if ( PR_Unlock( (PRLock *)mutex ) == PR_FAILURE ) {
256	return( -1 );
257    }
258
259    return( 0 );
260}
261
262
263static void *
264prldap_get_thread_id( void )
265{
266#ifdef	_SOLARIS_SDK
267	return ((void *)thr_self());
268#else
269    return( (void *)PR_GetCurrentThread());
270#endif
271}
272
273#ifndef	_SOLARIS_SDK
274static int
275prldap_get_ld_error( char **matchedp, char **errmsgp, void *errorarg )
276{
277    PRLDAP_TPDMap	*map;
278    PRLDAP_ErrorInfo	*eip;
279
280    if (( map = (PRLDAP_TPDMap *)errorarg ) != NULL && ( eip =
281	    (PRLDAP_ErrorInfo *)prldap_get_thread_private(
282	    map->prtm_index )) != NULL ) {
283	if ( matchedp != NULL ) {
284	    *matchedp = eip->plei_matched;
285	}
286	if ( errmsgp != NULL ) {
287	    *errmsgp = eip->plei_errmsg;
288	}
289	return( eip->plei_lderrno );
290    } else {
291	if ( matchedp != NULL ) {
292	    *matchedp = NULL;
293	}
294	if ( errmsgp != NULL ) {
295	    *errmsgp = NULL;
296	}
297	return( LDAP_LOCAL_ERROR );	/* punt */
298    }
299}
300
301
302static void
303prldap_set_ld_error( int err, char *matched, char *errmsg, void *errorarg )
304{
305    PRLDAP_TPDMap	*map;
306    PRLDAP_ErrorInfo	*eip;
307
308    if (( map = (PRLDAP_TPDMap *)errorarg ) != NULL ) {
309	if (( eip = (PRLDAP_ErrorInfo *)prldap_get_thread_private(
310		map->prtm_index )) == NULL ) {
311	    /*
312	     * Error info. has not yet been allocated for this thread.
313	     * Do so now.  Note that we free this memory only for the
314	     * thread that calls prldap_thread_dispose_handle(), which
315	     * should be the one that called ldap_unbind() -- see
316	     * prldap_return_map().  Not freeing the memory used by
317	     * other threads is deemed acceptable since it will be
318	     * recycled and used by other LDAP sessions.  All of the
319	     * thread-private memory is freed when a thread exits
320	     * (inside the prldap_tsd_destroy() function).
321	     */
322	    eip = (PRLDAP_ErrorInfo *)PR_Calloc( 1,
323		    sizeof( PRLDAP_ErrorInfo ));
324	    if ( eip == NULL ) {
325		return;	/* punt */
326	    }
327	    (void)prldap_set_thread_private( map->prtm_index, eip );
328	}
329
330	eip->plei_lderrno = err;
331	if ( eip->plei_matched != NULL ) {
332	    ldap_memfree( eip->plei_matched );
333	}
334	eip->plei_matched = matched;
335	if ( eip->plei_errmsg != NULL ) {
336	    ldap_memfree( eip->plei_errmsg );
337	}
338	eip->plei_errmsg = errmsg;
339    }
340}
341#endif
342
343
344/*
345 * Called when a new LDAP * session handle is allocated.
346 * Allocate thread-private data for error information, but only if
347 * it has not already been allocated and the get_ld_error callback has
348 * been installed.  If ld is not NULL when prldap_install_thread_functions()
349 * is called, we will have already allocated the thread-private data there.
350 */
351int
352prldap_thread_new_handle( LDAP *ld, void *sessionarg )
353{
354    struct ldap_thread_fns	tfns;
355
356#ifndef _SOLARIS_SDK
357    if ( ldap_get_option( ld, LDAP_OPT_THREAD_FN_PTRS, (void *)&tfns ) != 0 ) {
358	return( LDAP_LOCAL_ERROR );
359    }
360
361    if ( tfns.ltf_lderrno_arg == NULL && tfns.ltf_get_lderrno != NULL ) {
362	if (( tfns.ltf_lderrno_arg = (void *)prldap_allocate_map( ld )) == NULL
363		|| ldap_set_option( ld, LDAP_OPT_THREAD_FN_PTRS,
364		(void *)&tfns ) != 0 ) {
365	    return( LDAP_LOCAL_ERROR );
366	}
367    }
368#endif
369
370    return( LDAP_SUCCESS );
371}
372
373
374/*
375 * Called when an LDAP * session handle is being destroyed.
376 * Clean up our thread private data map.
377 */
378void
379prldap_thread_dispose_handle( LDAP *ld, void *sessionarg )
380{
381#ifndef _SOLARIS_SDK
382    struct ldap_thread_fns	tfns;
383
384    if ( ldap_get_option( ld, LDAP_OPT_THREAD_FN_PTRS,
385	    (void *)&tfns ) == 0 &&
386	    tfns.ltf_lderrno_arg != NULL ) {
387	prldap_return_map( (PRLDAP_TPDMap *)tfns.ltf_lderrno_arg );
388    }
389#endif
390}
391
392
393#ifndef _SOLARIS_SDK
394static PRStatus
395prldap_init_tpd( void )
396{
397    if (( prldap_map_mutex = PR_NewLock()) == NULL || PR_NewThreadPrivateIndex(
398		&prldap_tpdindex, prldap_tsd_destroy ) != PR_SUCCESS ) {
399	return( PR_FAILURE );
400    }
401
402    prldap_map_list = NULL;
403
404    return( PR_SUCCESS );
405}
406
407
408/*
409 * Function: prldap_allocate_map()
410 * Description: allocate a thread-private data map to use for a new
411 *	LDAP session handle.
412 * Returns: a pointer to the TPD map or NULL if none available.
413 */
414static PRLDAP_TPDMap *
415prldap_allocate_map( LDAP *ld )
416{
417    PRLDAP_TPDMap	*map, *prevmap;
418
419    PR_Lock( prldap_map_mutex );
420
421    /*
422     * first look for a map that is already allocated but free to be re-used
423     */
424    prevmap = NULL;
425    for ( map = prldap_map_list; map != NULL; map = map->prtm_next ) {
426	if ( map->prtm_ld == NULL ) {
427	    break;
428	}
429	prevmap = map;
430    }
431
432    /*
433     * if none we found (map == NULL), try to allocate a new one and add it
434     * to the end of our global list.
435     */
436    if ( map == NULL ) {
437	PRUintn	tpdindex;
438
439	tpdindex = prldap_new_tpdindex();
440	map = (PRLDAP_TPDMap *)PR_Malloc( sizeof( PRLDAP_TPDMap ));
441	if ( map != NULL ) {
442	    map->prtm_index = tpdindex;
443	    map->prtm_next = NULL;
444	    if ( prevmap == NULL ) {
445		prldap_map_list = map;
446	    } else {
447		prevmap->prtm_next = map;
448	    }
449	}
450    }
451
452    if ( map != NULL ) {
453	map->prtm_ld = ld;	/* now marked as "in use" */
454				/* since we are reusing...reset */
455				/* to initial state */
456	(void)prldap_set_thread_private( map->prtm_index, NULL );
457    }
458
459    PR_Unlock( prldap_map_mutex );
460
461    return( map );
462}
463
464
465/*
466 * Function: prldap_return_map()
467 * Description: return a thread-private data map to the pool of ones
468 *	available for re-use.
469 */
470static void
471prldap_return_map( PRLDAP_TPDMap *map )
472{
473    PRLDAP_ErrorInfo	*eip;
474
475    PR_Lock( prldap_map_mutex );
476
477    /*
478     * Dispose of thread-private LDAP error information.  Note that this
479     * only disposes of the memory consumed on THIS thread, but that is
480     * okay.  See the comment in prldap_set_ld_error() for the reason why.
481     */
482    if (( eip = (PRLDAP_ErrorInfo *)prldap_get_thread_private(
483		map->prtm_index )) != NULL &&
484		prldap_set_thread_private( map->prtm_index, NULL ) == 0 ) {
485	if ( eip->plei_matched != NULL ) {
486	    ldap_memfree( eip->plei_matched );
487	}
488	if ( eip->plei_errmsg != NULL ) {
489	    ldap_memfree( eip->plei_errmsg );
490	}
491
492	PR_Free( eip );
493    }
494
495    /* mark map as available for re-use */
496    map->prtm_ld = NULL;
497
498    PR_Unlock( prldap_map_mutex );
499}
500
501
502/*
503 * Function: prldap_new_tpdindex()
504 * Description: allocate a thread-private data index.
505 * Returns: the new index.
506 */
507static PRUintn
508prldap_new_tpdindex( void )
509{
510    PRUintn	tpdindex;
511
512    tpdindex = (PRUintn)PR_AtomicIncrement( &prldap_tpd_maxindex );
513    return( tpdindex );
514}
515
516
517/*
518 * Function: prldap_set_thread_private()
519 * Description: store a piece of thread-private data.
520 * Returns: 0 if successful and -1 if not.
521 */
522static int
523prldap_set_thread_private( PRInt32 tpdindex, void *priv )
524{
525    PRLDAP_TPDHeader	*tsdhdr;
526
527    if ( tpdindex > prldap_tpd_maxindex ) {
528	return( -1 );	/* bad index */
529    }
530
531    tsdhdr = (PRLDAP_TPDHeader *)PR_GetThreadPrivate( prldap_tpdindex );
532    if ( tsdhdr == NULL || tpdindex >= tsdhdr->ptpdh_tpd_count ) {
533	tsdhdr = prldap_tsd_realloc( tsdhdr, tpdindex );
534	if ( tsdhdr == NULL ) {
535	    return( -1 );	/* realloc failed */
536	}
537    }
538
539    tsdhdr->ptpdh_dataitems[ tpdindex ] = priv;
540    return( 0 );
541}
542
543
544/*
545 * Function: prldap_get_thread_private()
546 * Description: retrieve a piece of thread-private data.  If not set,
547 *	NULL is returned.
548 * Returns: 0 if successful and -1 if not.
549 */
550static void *
551prldap_get_thread_private( PRInt32 tpdindex )
552{
553    PRLDAP_TPDHeader	*tsdhdr;
554
555    tsdhdr = (PRLDAP_TPDHeader *)PR_GetThreadPrivate( prldap_tpdindex );
556    if ( tsdhdr == NULL ) {
557	return( NULL );	/* no thread private data */
558    }
559
560    if ( tpdindex >= tsdhdr->ptpdh_tpd_count
561		|| tsdhdr->ptpdh_dataitems == NULL ) {
562	return( NULL );	/* fewer data items than requested index */
563    }
564
565    return( tsdhdr->ptpdh_dataitems[ tpdindex ] );
566}
567
568
569/*
570 * Function: prldap_tsd_realloc()
571 * Description: enlarge the thread-private data array.
572 * Returns: the new PRLDAP_TPDHeader value (non-NULL if successful).
573 * Note: tsdhdr can be NULL (allocates a new PRLDAP_TPDHeader).
574 */
575static PRLDAP_TPDHeader *
576prldap_tsd_realloc( PRLDAP_TPDHeader *tsdhdr, int maxindex )
577{
578    void	*newdataitems = NULL;
579    int		count;
580
581    if ( tsdhdr == NULL ) {
582	/* allocate a new thread private data header */
583	if (( tsdhdr = PR_Calloc( 1, sizeof( PRLDAP_TPDHeader ))) == NULL ) {
584	    return( NULL );
585	}
586	(void)PR_SetThreadPrivate( prldap_tpdindex, tsdhdr );
587    }
588
589    /*
590     * Make the size of the new array the next highest multiple of
591     * the array increment value that is greater than maxindex.
592     */
593    count = PRLDAP_TPD_ARRAY_INCREMENT *
594		( 1 + ( maxindex / PRLDAP_TPD_ARRAY_INCREMENT ));
595
596    /* increase the size of the data item array if necessary */
597    if ( count > tsdhdr->ptpdh_tpd_count  ) {
598	newdataitems = (PRLDAP_ErrorInfo *)PR_Calloc( count, sizeof( void * ));
599	if ( newdataitems == NULL ) {
600	    return( NULL );
601	}
602	if ( tsdhdr->ptpdh_dataitems != NULL ) {	/* preserve old data */
603	    memcpy( newdataitems, tsdhdr->ptpdh_dataitems,
604			tsdhdr->ptpdh_tpd_count * sizeof( void * ));
605	    PR_Free( tsdhdr->ptpdh_dataitems );
606	}
607
608	tsdhdr->ptpdh_tpd_count = count;
609	tsdhdr->ptpdh_dataitems = newdataitems;
610    }
611
612    return( tsdhdr );
613}
614
615
616/*
617 * Function: prldap_tsd_destroy()
618 * Description: Free a thread-private data array. Installed as an NSPR TPD
619 *	destructor function
620 * Returns: nothing.
621 * Note: this function assumes that each TPD item installed at the PRLDAP
622 *	level can be freed with a call to PR_Free().
623 */
624static void
625prldap_tsd_destroy( void *priv )
626{
627    PRLDAP_TPDHeader	*tsdhdr;
628    int			i;
629
630    tsdhdr = (PRLDAP_TPDHeader *)priv;
631    if ( tsdhdr != NULL ) {
632	if ( tsdhdr->ptpdh_dataitems != NULL ) {
633	    for ( i = 0; i < tsdhdr->ptpdh_tpd_count; ++i ) {
634		if ( tsdhdr->ptpdh_dataitems[ i ] != NULL ) {
635		    PR_Free( tsdhdr->ptpdh_dataitems[ i ] );
636		    tsdhdr->ptpdh_dataitems[ i ] = NULL;
637		}
638	    }
639	    PR_Free( tsdhdr->ptpdh_dataitems );
640	    tsdhdr->ptpdh_dataitems = NULL;
641	}
642	PR_Free( tsdhdr );
643    }
644}
645#endif
646
647#ifdef	_SOLARIS_SDK
648#pragma	init(prldap_nspr_init)
649static mutex_t	nspr_init_lock = DEFAULTMUTEX;
650static int	nspr_initialized = 0;
651
652/*
653 * Initialize NSPR once
654 *
655 */
656void
657prldap_nspr_init(void) {
658	struct sigaction	action;
659
660	/*
661	 * For performance reason, test it here first
662	 */
663	if (nspr_initialized != 0)
664		return;
665
666	(void) mutex_lock(&nspr_init_lock);
667	/* Make sure PR_Init() is executed only once */
668	if (nspr_initialized == 0) {
669		/*
670		 * PR_Init changes the signal handler of SIGPIPE to SIG_IGN.
671		 * Save the original and restore it after PR_Init.
672		 */
673		(void) sigaction(SIGPIPE, NULL, &action);
674
675		if (PR_Initialized() == PR_FALSE) {
676			/*
677			 * PR_Init() changes the current thread's
678			 * priority.  Save and restore the priority.
679			 */
680			int priority;
681			(void) thr_getprio(thr_self(), &priority);
682			PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
683			(void) thr_setprio(thr_self(), priority);
684		}
685		nspr_initialized = 1;
686		/*
687		 * Restore signal handling attributes of SIGPIPE
688		 */
689		(void) sigaction(SIGPIPE, &action, NULL);
690	}
691	(void) mutex_unlock(&nspr_init_lock);
692}
693#endif
694