xref: /illumos-gate/usr/src/cmd/nscd/nscd_getentctx.c (revision baf2c95cb7af3bdff76a3e6693e566e0b8b75bc4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdlib.h>
29 #include <assert.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include "nscd_db.h"
34 #include "nscd_log.h"
35 #include "nscd_switch.h"
36 #include "nscd_door.h"
37 
38 extern int		_whoami;
39 static mutex_t		getent_monitor_mutex = DEFAULTMUTEX;
40 static int		getent_monitor_started = 0;
41 
42 static rwlock_t		getent_ctxDB_rwlock = DEFAULTRWLOCK;
43 static nscd_db_t	*getent_ctxDB = NULL;
44 
45 /*
46  * internal structure representing a nscd getent context
47  */
48 typedef struct nscd_getent_ctx {
49 	int			to_delete; /* this ctx no longer valid */
50 	nscd_getent_context_t	*ptr;
51 	nscd_cookie_num_t	cookie_num;
52 } nscd_getent_ctx_t;
53 
54 /*
55  * nscd_getent_context_t list for each nss database. Protected
56  * by the readers/writer lock nscd_getent_ctx_lock.
57  */
58 nscd_getent_ctx_base_t **nscd_getent_ctx_base;
59 static rwlock_t nscd_getent_ctx_base_lock = DEFAULTRWLOCK;
60 
61 extern nscd_db_entry_t *_nscd_walk_db(nscd_db_t *db, void **cookie);
62 
63 static nscd_rc_t _nscd_init_getent_ctx_monitor();
64 
65 /*
66  * FUNCTION: _nscd_create_getent_ctxDB
67  *
68  * Create the internal getent context database to keep track of the
69  * getent contexts currently being used.
70  */
71 nscd_db_t *
72 _nscd_create_getent_ctxDB()
73 {
74 
75 	nscd_db_t	*ret;
76 
77 	(void) rw_wrlock(&getent_ctxDB_rwlock);
78 
79 	if (getent_ctxDB != NULL) {
80 		(void) rw_unlock(&getent_ctxDB_rwlock);
81 		return (getent_ctxDB);
82 	}
83 
84 	ret = _nscd_alloc_db(NSCD_DB_SIZE_LARGE);
85 
86 	if (ret != NULL)
87 		getent_ctxDB = ret;
88 
89 	(void) rw_unlock(&getent_ctxDB_rwlock);
90 
91 	return (ret);
92 }
93 
94 /*
95  * FUNCTION: _nscd_add_getent_ctx
96  *
97  * Add a getent context to the internal context database.
98  */
99 static nscd_rc_t
100 _nscd_add_getent_ctx(
101 	nscd_getent_context_t	*ptr,
102 	nscd_cookie_num_t	cookie_num)
103 {
104 	int			size;
105 	char			buf[32];
106 	nscd_db_entry_t		*db_entry;
107 	nscd_getent_ctx_t	*gnctx;
108 
109 	if (ptr == NULL)
110 		return (NSCD_INVALID_ARGUMENT);
111 
112 	(void) snprintf(buf, sizeof (buf), "%lld", cookie_num);
113 
114 	size = sizeof (*gnctx);
115 
116 	db_entry = _nscd_alloc_db_entry(NSCD_DATA_CTX_ADDR,
117 	    (const char *)buf, size, 1, 1);
118 	if (db_entry == NULL)
119 		return (NSCD_NO_MEMORY);
120 
121 	gnctx = (nscd_getent_ctx_t *)*(db_entry->data_array);
122 	gnctx->ptr = ptr;
123 	gnctx->cookie_num = cookie_num;
124 
125 	(void) rw_wrlock(&getent_ctxDB_rwlock);
126 	(void) _nscd_add_db_entry(getent_ctxDB, buf, db_entry,
127 	    NSCD_ADD_DB_ENTRY_FIRST);
128 	(void) rw_unlock(&getent_ctxDB_rwlock);
129 
130 	return (NSCD_SUCCESS);
131 }
132 
133 /*
134  * FUNCTION: _nscd_is_getent_ctx
135  *
136  * Check to see if a getent context can be found in the internal
137  * getent context database.
138  */
139 nscd_getent_context_t *
140 _nscd_is_getent_ctx(
141 	nscd_cookie_num_t	cookie_num)
142 {
143 	char			ptrstr[32];
144 	const nscd_db_entry_t	*db_entry;
145 	nscd_getent_context_t	*ret = NULL;
146 
147 	(void) snprintf(ptrstr, sizeof (ptrstr), "%lld", cookie_num);
148 
149 	(void) rw_rdlock(&getent_ctxDB_rwlock);
150 
151 	db_entry = _nscd_get_db_entry(getent_ctxDB, NSCD_DATA_CTX_ADDR,
152 	    (const char *)ptrstr, NSCD_GET_FIRST_DB_ENTRY, 0);
153 
154 	if (db_entry != NULL) {
155 		nscd_getent_ctx_t *gnctx;
156 
157 		gnctx = (nscd_getent_ctx_t *)*(db_entry->data_array);
158 
159 		/*
160 		 * If the ctx is not to be deleted and the cookie number
161 		 * match, return the ctx if not aborted and not in use,
162 		 * Otherwise return NULL.
163 		 */
164 		if (gnctx->to_delete == 0 && gnctx->cookie_num == cookie_num) {
165 			ret = gnctx->ptr;
166 			(void) mutex_lock(&gnctx->ptr->getent_mutex);
167 			if (ret->aborted == 1 || ret->in_use == 1)
168 				ret = NULL;
169 			else
170 				ret->in_use = 1;
171 			(void) mutex_unlock(&gnctx->ptr->getent_mutex);
172 		}
173 	}
174 
175 	(void) rw_unlock(&getent_ctxDB_rwlock);
176 
177 	return (ret);
178 }
179 
180 /*
181  * FUNCTION: _nscd_free_ctx_if_aborted
182  *
183  * Check to see if the getent session associated with a getent context had
184  * been aborted. If so, return the getent context back to the pool.
185  */
186 void
187 _nscd_free_ctx_if_aborted(
188 	nscd_getent_context_t	*ctx)
189 {
190 	int	aborted;
191 	char	*me = "_nscd_free_ctx_if_aborted";
192 
193 	if (ctx->in_use != 1)
194 		return;
195 
196 	(void) mutex_lock(&ctx->getent_mutex);
197 	aborted = ctx->aborted;
198 	(void) mutex_unlock(&ctx->getent_mutex);
199 
200 	if (aborted == 1) {
201 		_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
202 		(me, "getent session aborted, return the getent context\n");
203 		_nscd_put_getent_ctx(ctx);
204 	}
205 	ctx->in_use = 0;
206 }
207 
208 /*
209  * FUNCTION: _nscd_del_getent_ctx
210  *
211  * Delete a getent context from the internal getent context database.
212  */
213 static void
214 _nscd_del_getent_ctx(
215 	nscd_getent_context_t	*ptr,
216 	nscd_cookie_num_t	cookie_num)
217 {
218 	char			ptrstr[32];
219 	nscd_getent_ctx_t	*gnctx;
220 	const nscd_db_entry_t	*db_entry;
221 
222 	if (ptr == NULL)
223 		return;
224 
225 	(void) snprintf(ptrstr, sizeof (ptrstr), "%lld", cookie_num);
226 
227 	(void) rw_rdlock(&getent_ctxDB_rwlock);
228 	/*
229 	 * first find the db entry and make sure the
230 	 * sequence number matched, then delete it from
231 	 * the database.
232 	 */
233 	db_entry = _nscd_get_db_entry(getent_ctxDB,
234 	    NSCD_DATA_CTX_ADDR,
235 	    (const char *)ptrstr,
236 	    NSCD_GET_FIRST_DB_ENTRY, 0);
237 	if (db_entry != NULL) {
238 		gnctx = (nscd_getent_ctx_t *)*(db_entry->data_array);
239 		if (gnctx->ptr == ptr && gnctx->cookie_num  == cookie_num) {
240 
241 			(void) rw_unlock(&getent_ctxDB_rwlock);
242 			(void) rw_wrlock(&getent_ctxDB_rwlock);
243 
244 			(void) _nscd_delete_db_entry(getent_ctxDB,
245 			    NSCD_DATA_CTX_ADDR,
246 			    (const char *)ptrstr,
247 			    NSCD_DEL_FIRST_DB_ENTRY, 0);
248 		}
249 	}
250 	(void) rw_unlock(&getent_ctxDB_rwlock);
251 }
252 
253 static void
254 _nscd_free_getent_ctx(
255 	nscd_getent_context_t	*gnctx)
256 {
257 
258 	char			*me = "_nscd_free_getent_ctx";
259 
260 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
261 	(me, "getent context %p\n", gnctx);
262 
263 	_nscd_put_nsw_state(gnctx->nsw_state);
264 	_nscd_del_getent_ctx(gnctx, gnctx->cookie_num);
265 	free(gnctx);
266 }
267 
268 
269 static void
270 _nscd_free_getent_ctx_base(
271 	nscd_acc_data_t		*data)
272 {
273 	nscd_getent_ctx_base_t	*base = (nscd_getent_ctx_base_t *)data;
274 	nscd_getent_context_t	*c, *tc;
275 	char			*me = "_nscd_free_getent_ctx_base";
276 
277 	_NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
278 	(me, "getent context base %p\n", base);
279 
280 	if (base == NULL)
281 		return;
282 
283 	c = base->first;
284 	while (c != NULL) {
285 		tc = c->next;
286 		_nscd_free_getent_ctx(c);
287 		c = tc;
288 	}
289 }
290 
291 void
292 _nscd_free_all_getent_ctx_base()
293 {
294 	nscd_getent_ctx_base_t	*base;
295 	int			i;
296 	char			*me = "_nscd_free_all_getent_ctx_base";
297 
298 	_NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
299 	(me, "entering ..\n");
300 
301 	(void) rw_wrlock(&nscd_getent_ctx_base_lock);
302 
303 	for (i = 0; i < NSCD_NUM_DB; i++) {
304 
305 		base = nscd_getent_ctx_base[i];
306 		if (base == NULL)
307 			continue;
308 
309 		nscd_getent_ctx_base[i] = (nscd_getent_ctx_base_t *)
310 		    _nscd_set((nscd_acc_data_t *)base, NULL);
311 	}
312 	(void) rw_unlock(&nscd_getent_ctx_base_lock);
313 }
314 
315 static nscd_getent_context_t *
316 _nscd_create_getent_ctx(
317 	nscd_nsw_params_t	*params)
318 {
319 	nscd_getent_context_t	*gnctx;
320 	nss_db_root_t		db_root;
321 	char			*me = "_nscd_create_getent_ctx";
322 
323 	gnctx = calloc(1, sizeof (nscd_getent_context_t));
324 	if (gnctx == NULL)
325 		return (NULL);
326 	else {
327 		_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
328 		(me, "getent context allocated %p\n", gnctx);
329 	}
330 
331 	gnctx->dbi = params->dbi;
332 	gnctx->cookie_num = _nscd_get_cookie_num();
333 	gnctx->pid = -1;
334 	(void) mutex_init(&gnctx->getent_mutex, USYNC_THREAD, NULL);
335 
336 	if (_nscd_get_nsw_state(&db_root, params) != NSCD_SUCCESS) {
337 		free(gnctx);
338 		return (NULL);
339 	}
340 	gnctx->nsw_state = (nscd_nsw_state_t *)db_root.s;
341 	/* this is a nsw_state used for getent processing */
342 	gnctx->nsw_state->getent = 1;
343 
344 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
345 	(me, "got nsw_state %p\n", gnctx->nsw_state);
346 
347 	return (gnctx);
348 }
349 
350 
351 nscd_rc_t
352 _nscd_get_getent_ctx(
353 	nss_getent_t		*contextpp,
354 	nscd_nsw_params_t	*params)
355 {
356 
357 	nscd_getent_context_t	*c;
358 	nscd_getent_ctx_base_t	*base, *tmp;
359 	nscd_rc_t		rc;
360 	char			*me = "_nscd_get_getent_ctx";
361 
362 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
363 	(me, "entering ...\n");
364 
365 	(void) rw_rdlock(&nscd_getent_ctx_base_lock);
366 	base = nscd_getent_ctx_base[params->dbi];
367 	(void) rw_unlock(&nscd_getent_ctx_base_lock);
368 	assert(base != NULL);
369 
370 	/*
371 	 * If the context list is not empty, return the first one
372 	 * on the list. Otherwise, create and return a new one if
373 	 * limit is not reached. if reacehed, wait for the 'one is
374 	 * available' signal.
375 	 */
376 	tmp = (nscd_getent_ctx_base_t *)_nscd_mutex_lock(
377 	    (nscd_acc_data_t *)base);
378 	assert(base == tmp);
379 	if (base->first == NULL) {
380 		if (base->num_getent_ctx == base->max_getent_ctx) {
381 			base->num_waiter++;
382 			while (base->first == NULL) {
383 
384 				_NSCD_LOG(NSCD_LOG_GETENT_CTX,
385 				    NSCD_LOG_LEVEL_DEBUG)
386 				(me, "waiting for signal\n");
387 
388 				_nscd_cond_wait((nscd_acc_data_t *)base, NULL);
389 
390 				_NSCD_LOG(NSCD_LOG_GETENT_CTX,
391 				    NSCD_LOG_LEVEL_DEBUG)
392 				(me, "woke up\n");
393 			}
394 			base->num_waiter--;
395 		} else {
396 			base->first = _nscd_create_getent_ctx(params);
397 			if (base->first != NULL) {
398 				base->first->base = base;
399 				base->num_getent_ctx++;
400 			} else {
401 				/* not able to create an getent ctx */
402 
403 				_NSCD_LOG(NSCD_LOG_GETENT_CTX,
404 				    NSCD_LOG_LEVEL_ERROR)
405 				(me, "create getent ctx failed\n");
406 
407 				_nscd_mutex_unlock((nscd_acc_data_t *)base);
408 				return (NSCD_CREATE_GETENT_CTX_FAILED);
409 			}
410 
411 			_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
412 			(me, "got a new getent ctx %p\n", base->first);
413 		}
414 	}
415 
416 	assert(base->first != NULL);
417 
418 	c = base->first;
419 	base->first = c->next;
420 	c->next = NULL;
421 	c->seq_num = 1;
422 	c->in_use = 1;
423 
424 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
425 	(me, "got a getent ctx %p\n", c);
426 
427 	_nscd_mutex_unlock((nscd_acc_data_t *)base);
428 
429 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
430 	(me, "adding new ctx %p, cookie # = %lld\n", c, c->cookie_num);
431 
432 	if ((rc = _nscd_add_getent_ctx(c, c->cookie_num)) != NSCD_SUCCESS) {
433 		_nscd_put_getent_ctx(c);
434 		return (rc);
435 	}
436 	contextpp->ctx = (struct nss_getent_context *)c;
437 
438 	/* start monitor and reclaim orphan getent context */
439 	if (getent_monitor_started == 0) {
440 		(void) mutex_lock(&getent_monitor_mutex);
441 		if (getent_monitor_started == 0) {
442 			getent_monitor_started = 1;
443 			(void) _nscd_init_getent_ctx_monitor();
444 		}
445 		(void) mutex_unlock(&getent_monitor_mutex);
446 	}
447 
448 	return (NSCD_SUCCESS);
449 }
450 
451 void
452 _nscd_put_getent_ctx(
453 	nscd_getent_context_t	*gnctx)
454 {
455 
456 	nscd_getent_ctx_base_t	*base;
457 	char			*me = "_nscd_put_getent_ctx";
458 
459 	base = gnctx->base;
460 	gnctx->seq_num = 0;
461 
462 	/* if context base is gone, so should this context */
463 	if ((_nscd_mutex_lock((nscd_acc_data_t *)base)) == NULL) {
464 		_nscd_free_getent_ctx(gnctx);
465 		return;
466 	}
467 
468 	if (base->first != NULL) {
469 		gnctx->next = base->first;
470 		base->first = gnctx;
471 	} else
472 		base->first = gnctx;
473 
474 	/* put back the db state */
475 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
476 	(me, "putting back nsw state %p\n", gnctx->nsw_state);
477 
478 	/* this nsw_state is no longer used for getent processing */
479 	if (gnctx->nsw_state != NULL)
480 		gnctx->nsw_state->getent = 0;
481 	_nscd_put_nsw_state(gnctx->nsw_state);
482 	gnctx->nsw_state = NULL;
483 
484 	gnctx->aborted = 0;
485 	gnctx->in_use = 0;
486 	_nscd_del_getent_ctx(gnctx, gnctx->cookie_num);
487 
488 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
489 	(me, "ctx (%p, cookie # = %lld) removed from getent ctx DB\n",
490 	    gnctx, gnctx->cookie_num);
491 
492 	if (base->num_waiter > 0) {
493 		_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
494 		(me, "signaling (waiter = %d)\n", base->num_waiter);
495 
496 		_nscd_cond_signal((nscd_acc_data_t *)base);
497 	}
498 
499 	_nscd_mutex_unlock((nscd_acc_data_t *)base);
500 }
501 
502 nscd_rc_t
503 _nscd_init_getent_ctx_base(
504 	int			dbi,
505 	int			lock)
506 {
507 	nscd_getent_ctx_base_t	*base = NULL;
508 	char			*me = "_nscd_init_getent_ctx_base";
509 
510 	if (lock)
511 		(void) rw_rdlock(&nscd_getent_ctx_base_lock);
512 
513 	base = (nscd_getent_ctx_base_t *)_nscd_alloc(
514 	    NSCD_DATA_GETENT_CTX_BASE,
515 	    sizeof (nscd_getent_ctx_base_t),
516 	    _nscd_free_getent_ctx_base,
517 	    NSCD_ALLOC_MUTEX | NSCD_ALLOC_COND);
518 
519 	if (base == NULL) {
520 		if (lock)
521 			(void) rw_unlock(&nscd_getent_ctx_base_lock);
522 		return (NSCD_NO_MEMORY);
523 	}
524 	_NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
525 	(me, "base %p allocated\n", base);
526 
527 	/*
528 	 * initialize and activate the new getent_ctx base
529 	 */
530 	base->dbi = dbi;
531 	base->max_getent_ctx = NSCD_SW_CFG(dbi).max_getent_ctx_per_db;
532 	nscd_getent_ctx_base[dbi] =
533 	    (nscd_getent_ctx_base_t *)_nscd_set(
534 	    (nscd_acc_data_t *)nscd_getent_ctx_base[dbi],
535 	    (nscd_acc_data_t *)base);
536 
537 	if (lock)
538 		(void) rw_unlock(&nscd_getent_ctx_base_lock);
539 
540 	return (NSCD_SUCCESS);
541 }
542 
543 nscd_rc_t
544 _nscd_init_all_getent_ctx_base()
545 {
546 	int			i;
547 	nscd_rc_t		rc;
548 	char			*me = "_nscd_init_all_getent_ctx_base";
549 
550 	(void) rw_wrlock(&nscd_getent_ctx_base_lock);
551 
552 	for (i = 0; i < NSCD_NUM_DB; i++) {
553 
554 		rc = _nscd_init_getent_ctx_base(i, 0);
555 
556 		if (rc != NSCD_SUCCESS) {
557 			(void) rw_unlock(&nscd_getent_ctx_base_lock);
558 			return (rc);
559 		}
560 	}
561 
562 	_NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
563 	(me, "all getent context base initialized\n");
564 
565 	(void) rw_unlock(&nscd_getent_ctx_base_lock);
566 
567 	return (NSCD_SUCCESS);
568 }
569 nscd_rc_t
570 _nscd_alloc_getent_ctx_base()
571 {
572 
573 	(void) rw_wrlock(&nscd_getent_ctx_base_lock);
574 
575 	nscd_getent_ctx_base = calloc(NSCD_NUM_DB,
576 	    sizeof (nscd_getent_ctx_base_t *));
577 	if (nscd_getent_ctx_base == NULL) {
578 		(void) rw_unlock(&nscd_getent_ctx_base_lock);
579 		return (NSCD_NO_MEMORY);
580 	}
581 
582 	(void) rw_unlock(&nscd_getent_ctx_base_lock);
583 
584 	return (NSCD_SUCCESS);
585 }
586 
587 static int
588 process_exited(pid_t pid)
589 {
590 	char	pname[PATH_MAX];
591 	int	fd;
592 
593 	(void) snprintf(pname, sizeof (pname), "/proc/%d/psinfo", pid);
594 	if ((fd = open(pname, O_RDONLY)) == -1)
595 		return (1);
596 	else {
597 		(void) close(fd);
598 		return (0);
599 	}
600 }
601 
602 /*
603  * FUNCTION: reclaim_getent_ctx
604  */
605 /*ARGSUSED*/
606 static void *
607 reclaim_getent_ctx(void *arg)
608 {
609 	void			*cookie = NULL;
610 	nscd_db_entry_t		*ep;
611 	nscd_getent_ctx_t	*ctx;
612 	nscd_getent_context_t	*gctx, *c;
613 	nscd_getent_context_t	*first = NULL, *last = NULL;
614 	char			*me = "reclaim_getent_ctx";
615 
616 	/*CONSTCOND*/
617 	while (1) {
618 
619 		(void) sleep(60);
620 
621 		(void) rw_rdlock(&getent_ctxDB_rwlock);
622 
623 		for (ep = _nscd_walk_db(getent_ctxDB, &cookie); ep != NULL;
624 		    ep = _nscd_walk_db(getent_ctxDB, &cookie)) {
625 
626 			ctx = (nscd_getent_ctx_t *)*(ep->data_array);
627 
628 			gctx = ctx->ptr;
629 
630 			/*
631 			 * if the client process, which did the setent,
632 			 * exited, add the context to the orphan list
633 			 */
634 			if (gctx->pid != -1 && process_exited(gctx->pid)) {
635 
636 				_NSCD_LOG(NSCD_LOG_GETENT_CTX,
637 				    NSCD_LOG_LEVEL_DEBUG)
638 				(me, "process  %d exited, "
639 				    "getent context = %p, "
640 				    "db index = %d, cookie # = %lld, "
641 				    "sequence # = %lld\n",
642 				    gctx->pid, gctx, gctx->dbi,
643 				    gctx->cookie_num, gctx->seq_num);
644 
645 				if (first != NULL) {
646 					last->next = gctx;
647 					last = gctx;
648 				} else {
649 					first = gctx;
650 					last = gctx;
651 				}
652 			}
653 		}
654 
655 		(void) rw_unlock(&getent_ctxDB_rwlock);
656 
657 
658 		/*
659 		 * return all the orphan getent contexts to the pool if not
660 		 * in use
661 		 */
662 		for (gctx = first; gctx; ) {
663 			int in_use;
664 			c = gctx->next;
665 			gctx->aborted = 1;
666 			(void) mutex_lock(&gctx->getent_mutex);
667 			in_use = gctx->in_use;
668 			(void) mutex_unlock(&gctx->getent_mutex);
669 			if (in_use != 1) {
670 				gctx->next = NULL;
671 				_nscd_put_getent_ctx(gctx);
672 			}
673 			gctx = c;
674 		}
675 		first = last = NULL;
676 	}
677 	/*NOTREACHED*/
678 	/*LINTED E_FUNC_HAS_NO_RETURN_STMT*/
679 }
680 
681 static nscd_rc_t
682 _nscd_init_getent_ctx_monitor() {
683 
684 	int	errnum;
685 	char	*me = "_nscd_init_getent_ctx_monitor";
686 
687 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
688 	(me, "initializing the getent context monitor\n");
689 
690 	/*
691 	 * the forker nscd does not process getent requests
692 	 * so no need to monitor orphan getent contexts
693 	 */
694 	if (_whoami == NSCD_FORKER)
695 		return (NSCD_SUCCESS);
696 
697 	/*
698 	 * start a thread to reclaim unused getent contexts
699 	 */
700 	if (thr_create(NULL, NULL, reclaim_getent_ctx,
701 		NULL, THR_DETACHED, NULL) != 0) {
702 		errnum = errno;
703 		_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_ERROR)
704 		(me, "thr_create: %s\n", strerror(errnum));
705 		return (NSCD_THREAD_CREATE_ERROR);
706 	}
707 
708 	return (NSCD_SUCCESS);
709 }
710