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