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