xref: /titanic_50/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-threads.c (revision fe62dec3a38f1f79ffe68417df75dbbb58135bb7)
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