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