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>
47 extern 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 */
67 typedef 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 */
78 typedef 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 */
88 typedef 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
95 extern 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 */
106 static 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 */
112 static 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 */
118 static 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 */
125 static 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 */
131 static PRCallOnceType prldap_callonce_init_tpd = { 0, 0, 0 };
132
133
134 /*
135 * Private function prototypes:
136 */
137 static void prldap_set_ld_error( int err, char *matched, char *errmsg,
138 void *errorarg );
139 static int prldap_get_ld_error( char **matchedp, char **errmsgp,
140 void *errorarg );
141 #endif
142 static void *prldap_mutex_alloc( void );
143 static void prldap_mutex_free( void *mutex );
144 static int prldap_mutex_lock( void *mutex );
145 static int prldap_mutex_unlock( void *mutex );
146 static void *prldap_get_thread_id( void );
147 #ifndef _SOLARIS_SDK
148 static PRStatus prldap_init_tpd( void );
149 static PRLDAP_TPDMap *prldap_allocate_map( LDAP *ld );
150 static void prldap_return_map( PRLDAP_TPDMap *map );
151 static PRUintn prldap_new_tpdindex( void );
152 static int prldap_set_thread_private( PRInt32 tpdindex, void *priv );
153 static void *prldap_get_thread_private( PRInt32 tpdindex );
154 static PRLDAP_TPDHeader *prldap_tsd_realloc( PRLDAP_TPDHeader *tsdhdr,
155 int maxindex );
156 static 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 */
166 int
prldap_install_thread_functions(LDAP * ld,int shared)167 prldap_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
230 static void *
prldap_mutex_alloc(void)231 prldap_mutex_alloc( void )
232 {
233 return( (void *)PR_NewLock());
234 }
235
236
237 static void
prldap_mutex_free(void * mutex)238 prldap_mutex_free( void *mutex )
239 {
240 PR_DestroyLock( (PRLock *)mutex );
241 }
242
243
244 static int
prldap_mutex_lock(void * mutex)245 prldap_mutex_lock( void *mutex )
246 {
247 PR_Lock( (PRLock *)mutex );
248 return( 0 );
249 }
250
251
252 static int
prldap_mutex_unlock(void * mutex)253 prldap_mutex_unlock( void *mutex )
254 {
255 if ( PR_Unlock( (PRLock *)mutex ) == PR_FAILURE ) {
256 return( -1 );
257 }
258
259 return( 0 );
260 }
261
262
263 static void *
prldap_get_thread_id(void)264 prldap_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
274 static int
prldap_get_ld_error(char ** matchedp,char ** errmsgp,void * errorarg)275 prldap_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
302 static void
prldap_set_ld_error(int err,char * matched,char * errmsg,void * errorarg)303 prldap_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 */
351 int
prldap_thread_new_handle(LDAP * ld,void * sessionarg)352 prldap_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 */
378 void
prldap_thread_dispose_handle(LDAP * ld,void * sessionarg)379 prldap_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
394 static PRStatus
prldap_init_tpd(void)395 prldap_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 */
414 static PRLDAP_TPDMap *
prldap_allocate_map(LDAP * ld)415 prldap_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 */
470 static void
prldap_return_map(PRLDAP_TPDMap * map)471 prldap_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 */
507 static PRUintn
prldap_new_tpdindex(void)508 prldap_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 */
522 static int
prldap_set_thread_private(PRInt32 tpdindex,void * priv)523 prldap_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 */
550 static void *
prldap_get_thread_private(PRInt32 tpdindex)551 prldap_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 */
575 static PRLDAP_TPDHeader *
prldap_tsd_realloc(PRLDAP_TPDHeader * tsdhdr,int maxindex)576 prldap_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 */
624 static void
prldap_tsd_destroy(void * priv)625 prldap_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)
649 static mutex_t nspr_init_lock = DEFAULTMUTEX;
650 static int nspr_initialized = 0;
651
652 /*
653 * Initialize NSPR once
654 *
655 */
656 void
prldap_nspr_init(void)657 prldap_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