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