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