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 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 * 228 prldap_mutex_alloc( void ) 229 { 230 return( (void *)PR_NewLock()); 231 } 232 233 234 static void 235 prldap_mutex_free( void *mutex ) 236 { 237 PR_DestroyLock( (PRLock *)mutex ); 238 } 239 240 241 static int 242 prldap_mutex_lock( void *mutex ) 243 { 244 PR_Lock( (PRLock *)mutex ); 245 return( 0 ); 246 } 247 248 249 static int 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 * 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 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 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 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 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 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 * 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 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 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 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 * 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 * 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 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 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