xref: /illumos-gate/usr/src/cmd/nscd/nscd_getentctx.c (revision 0a44ef6d9afbfe052a7e975f55ea0d2954b62a82)
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 2006 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[2 * sizeof (cookie_num) + 1];
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[1 + 2 * sizeof (cookie_num)];
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
161 		 * the cookie numbers match, return the ctx.
162 		 * Otherwise return NULL.
163 		 */
164 		if (gnctx->to_delete == 0 && gnctx->cookie_num == cookie_num)
165 			ret = gnctx->ptr;
166 	}
167 
168 	(void) rw_unlock(&getent_ctxDB_rwlock);
169 
170 	return (ret);
171 }
172 
173 /*
174  * FUNCTION: _nscd_del_getent_ctx
175  *
176  * Delete a getent context from the internal getent context database.
177  */
178 static void
179 _nscd_del_getent_ctx(
180 	nscd_getent_context_t	*ptr,
181 	nscd_cookie_num_t	cookie_num)
182 {
183 	char			ptrstr[1 + 2 * sizeof (cookie_num)];
184 	nscd_getent_ctx_t	*gnctx;
185 	const nscd_db_entry_t	*db_entry;
186 
187 	if (ptr == NULL)
188 		return;
189 
190 	(void) snprintf(ptrstr, sizeof (ptrstr), "%lld", cookie_num);
191 
192 	(void) rw_rdlock(&getent_ctxDB_rwlock);
193 	/*
194 	 * first find the db entry and make sure the
195 	 * sequence number matched, then delete it from
196 	 * the database.
197 	 */
198 	db_entry = _nscd_get_db_entry(getent_ctxDB,
199 		NSCD_DATA_CTX_ADDR,
200 		(const char *)ptrstr,
201 		NSCD_GET_FIRST_DB_ENTRY, 0);
202 	if (db_entry != NULL) {
203 		gnctx = (nscd_getent_ctx_t *)*(db_entry->data_array);
204 		if (gnctx->ptr == ptr && gnctx->cookie_num  == cookie_num) {
205 
206 			(void) rw_unlock(&getent_ctxDB_rwlock);
207 			(void) rw_wrlock(&getent_ctxDB_rwlock);
208 
209 			(void) _nscd_delete_db_entry(getent_ctxDB,
210 				NSCD_DATA_CTX_ADDR,
211 				(const char *)ptrstr,
212 				NSCD_DEL_FIRST_DB_ENTRY, 0);
213 		}
214 	}
215 	(void) rw_unlock(&getent_ctxDB_rwlock);
216 }
217 
218 static void
219 _nscd_free_getent_ctx(
220 	nscd_getent_context_t	*gnctx)
221 {
222 
223 	char			*me = "_nscd_free_getent_ctx";
224 
225 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
226 	(me, "getent context %p\n", gnctx);
227 
228 	_nscd_put_nsw_state(gnctx->nsw_state);
229 	_nscd_del_getent_ctx(gnctx, gnctx->cookie_num);
230 	free(gnctx);
231 }
232 
233 
234 static void
235 _nscd_free_getent_ctx_base(
236 	nscd_acc_data_t		*data)
237 {
238 	nscd_getent_ctx_base_t	*base = (nscd_getent_ctx_base_t *)data;
239 	nscd_getent_context_t	*c, *tc;
240 	char			*me = "_nscd_free_getent_ctx_base";
241 
242 	_NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
243 	(me, "getent context base %p\n", base);
244 
245 	if (base == NULL)
246 		return;
247 
248 	c = base->first;
249 	while (c != NULL) {
250 		tc = c->next;
251 		_nscd_free_getent_ctx(c);
252 		c = tc;
253 	}
254 }
255 
256 void
257 _nscd_free_all_getent_ctx_base()
258 {
259 	nscd_getent_ctx_base_t	*base;
260 	int			i;
261 	char			*me = "_nscd_free_all_getent_ctx_base";
262 
263 	_NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
264 	(me, "entering ..\n");
265 
266 	(void) rw_wrlock(&nscd_getent_ctx_base_lock);
267 
268 	for (i = 0; i < NSCD_NUM_DB; i++) {
269 
270 		base = nscd_getent_ctx_base[i];
271 		if (base == NULL)
272 			continue;
273 
274 		nscd_getent_ctx_base[i] = (nscd_getent_ctx_base_t *)
275 			_nscd_set((nscd_acc_data_t *)base, NULL);
276 	}
277 	(void) rw_unlock(&nscd_getent_ctx_base_lock);
278 }
279 
280 static nscd_getent_context_t *
281 _nscd_create_getent_ctx(
282 	nscd_nsw_params_t	*params)
283 {
284 	nscd_getent_context_t	*gnctx;
285 	nss_db_root_t		db_root;
286 	char			*me = "_nscd_create_getent_ctx";
287 
288 	gnctx = calloc(1, sizeof (nscd_getent_context_t));
289 	if (gnctx == NULL)
290 		return (NULL);
291 	else {
292 		_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
293 		(me, "getent context allocated %p\n", gnctx);
294 	}
295 
296 	gnctx->dbi = params->dbi;
297 	gnctx->cookie_num = _nscd_get_cookie_num();
298 	gnctx->pid = -1;
299 
300 	if (_nscd_get_nsw_state(&db_root, params) != NSCD_SUCCESS) {
301 		free(gnctx);
302 		return (NULL);
303 	}
304 	gnctx->nsw_state = (nscd_nsw_state_t *)db_root.s;
305 	/* this is a nsw_state used for getent processing */
306 	gnctx->nsw_state->getent = 1;
307 
308 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
309 	(me, "got nsw_state %p\n", gnctx->nsw_state);
310 
311 	return (gnctx);
312 }
313 
314 
315 nscd_rc_t
316 _nscd_get_getent_ctx(
317 	nss_getent_t		*contextpp,
318 	nscd_nsw_params_t	*params)
319 {
320 
321 	nscd_getent_context_t	*c;
322 	nscd_getent_ctx_base_t	*base, *tmp;
323 	nscd_rc_t		rc;
324 	char			*me = "_nscd_get_getent_ctx";
325 
326 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
327 	(me, "entering ...\n");
328 
329 	(void) rw_rdlock(&nscd_getent_ctx_base_lock);
330 	base = nscd_getent_ctx_base[params->dbi];
331 	(void) rw_unlock(&nscd_getent_ctx_base_lock);
332 	assert(base != NULL);
333 
334 	/*
335 	 * If the context list is not empty, return the first one
336 	 * on the list. Otherwise, create and return a new one if
337 	 * limit is not reached. if reacehed, wait for the 'one is
338 	 * available' signal.
339 	 */
340 	tmp = (nscd_getent_ctx_base_t *)_nscd_mutex_lock(
341 		(nscd_acc_data_t *)base);
342 	assert(base == tmp);
343 	if (base->first == NULL) {
344 		if (base->num_getent_ctx == base->max_getent_ctx) {
345 			base->num_waiter++;
346 			while (base->first == NULL) {
347 
348 				_NSCD_LOG(NSCD_LOG_GETENT_CTX,
349 					NSCD_LOG_LEVEL_DEBUG)
350 				(me, "waiting for signal\n");
351 
352 				_nscd_cond_wait((nscd_acc_data_t *)base, NULL);
353 
354 				_NSCD_LOG(NSCD_LOG_GETENT_CTX,
355 					NSCD_LOG_LEVEL_DEBUG)
356 				(me, "woke up\n");
357 			}
358 			base->num_waiter--;
359 		} else {
360 			base->first = _nscd_create_getent_ctx(params);
361 			if (base->first != NULL) {
362 				base->first->base = base;
363 				base->num_getent_ctx++;
364 			} else {
365 				/* not able to create an getent ctx */
366 
367 				_NSCD_LOG(NSCD_LOG_GETENT_CTX,
368 					NSCD_LOG_LEVEL_ERROR)
369 				(me, "create getent ctx failed\n");
370 
371 				_nscd_mutex_unlock((nscd_acc_data_t *)base);
372 				return (NSCD_CREATE_GETENT_CTX_FAILED);
373 			}
374 
375 			_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
376 			(me, "got a new getent ctx %p\n", base->first);
377 		}
378 	}
379 
380 	assert(base->first != NULL);
381 
382 	c = base->first;
383 	base->first = c->next;
384 	c->next = NULL;
385 	c->seq_num = 1;
386 
387 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
388 	(me, "got a getent ctx %p\n", c);
389 
390 	_nscd_mutex_unlock((nscd_acc_data_t *)base);
391 
392 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
393 	(me, "adding new ctx %p, cookie # = %lld\n", c, c->cookie_num);
394 
395 	if ((rc = _nscd_add_getent_ctx(c, c->cookie_num)) != NSCD_SUCCESS) {
396 		_nscd_put_getent_ctx(c);
397 		return (rc);
398 	}
399 	contextpp->ctx = (struct nss_getent_context *)c;
400 
401 	/* start monitor and reclaim orphan getent context */
402 	if (getent_monitor_started == 0) {
403 		(void) mutex_lock(&getent_monitor_mutex);
404 		if (getent_monitor_started == 0) {
405 			getent_monitor_started = 1;
406 			(void) _nscd_init_getent_ctx_monitor();
407 		}
408 		(void) mutex_unlock(&getent_monitor_mutex);
409 	}
410 
411 	return (NSCD_SUCCESS);
412 }
413 
414 void
415 _nscd_put_getent_ctx(
416 	nscd_getent_context_t	*gnctx)
417 {
418 
419 	nscd_getent_ctx_base_t	*base;
420 	char			*me = "_nscd_put_getent_ctx";
421 
422 	base = gnctx->base;
423 	gnctx->seq_num = 0;
424 
425 	/* if context base is gone, so should this context */
426 	if ((_nscd_mutex_lock((nscd_acc_data_t *)base)) == NULL) {
427 		_nscd_free_getent_ctx(gnctx);
428 		return;
429 	}
430 
431 	if (base->first != NULL) {
432 		gnctx->next = base->first;
433 		base->first = gnctx;
434 	} else
435 		base->first = gnctx;
436 
437 	/* put back the db state */
438 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
439 	(me, "putting back nsw state %p\n", gnctx->nsw_state);
440 
441 	/* this nsw_state is no longer used for getent processing */
442 	if (gnctx->nsw_state != NULL)
443 		gnctx->nsw_state->getent = 0;
444 	_nscd_put_nsw_state(gnctx->nsw_state);
445 	gnctx->nsw_state = NULL;
446 
447 	_nscd_del_getent_ctx(gnctx, gnctx->cookie_num);
448 
449 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
450 	(me, "ctx (%p, cookie # = %lld) removed from getent ctx DB\n",
451 		gnctx, gnctx->cookie_num);
452 
453 	if (base->num_waiter > 0) {
454 		_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
455 		(me, "signaling (waiter = %d)\n", base->num_waiter);
456 
457 		_nscd_cond_signal((nscd_acc_data_t *)base);
458 	}
459 
460 	_nscd_mutex_unlock((nscd_acc_data_t *)base);
461 }
462 
463 nscd_rc_t
464 _nscd_init_getent_ctx_base(
465 	int			dbi,
466 	int			lock)
467 {
468 	nscd_getent_ctx_base_t	*base = NULL;
469 	char			*me = "_nscd_init_getent_ctx_base";
470 
471 	if (lock)
472 		(void) rw_rdlock(&nscd_getent_ctx_base_lock);
473 
474 	base = (nscd_getent_ctx_base_t *)_nscd_alloc(
475 		NSCD_DATA_GETENT_CTX_BASE,
476 		sizeof (nscd_getent_ctx_base_t),
477 		_nscd_free_getent_ctx_base,
478 		NSCD_ALLOC_MUTEX | NSCD_ALLOC_COND);
479 
480 	if (base == NULL) {
481 		if (lock)
482 			(void) rw_unlock(&nscd_getent_ctx_base_lock);
483 		return (NSCD_NO_MEMORY);
484 	}
485 	_NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
486 	(me, "base %p allocated\n", base);
487 
488 	/*
489 	 * initialize and activate the new getent_ctx base
490 	 */
491 	base->dbi = dbi;
492 	base->max_getent_ctx = NSCD_SW_CFG(dbi).max_getent_ctx_per_db;
493 	nscd_getent_ctx_base[dbi] =
494 		(nscd_getent_ctx_base_t *)_nscd_set(
495 		(nscd_acc_data_t *)nscd_getent_ctx_base[dbi],
496 		(nscd_acc_data_t *)base);
497 
498 	if (lock)
499 		(void) rw_unlock(&nscd_getent_ctx_base_lock);
500 
501 	return (NSCD_SUCCESS);
502 }
503 
504 nscd_rc_t
505 _nscd_init_all_getent_ctx_base()
506 {
507 	int			i;
508 	nscd_rc_t		rc;
509 	char			*me = "_nscd_init_all_getent_ctx_base";
510 
511 	(void) rw_wrlock(&nscd_getent_ctx_base_lock);
512 
513 	for (i = 0; i < NSCD_NUM_DB; i++) {
514 
515 		rc = _nscd_init_getent_ctx_base(i, 0);
516 
517 		if (rc != NSCD_SUCCESS) {
518 			(void) rw_unlock(&nscd_getent_ctx_base_lock);
519 			return (rc);
520 		}
521 	}
522 
523 	_NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
524 	(me, "all getent context base initialized\n");
525 
526 	(void) rw_unlock(&nscd_getent_ctx_base_lock);
527 
528 	return (NSCD_SUCCESS);
529 }
530 nscd_rc_t
531 _nscd_alloc_getent_ctx_base()
532 {
533 
534 	(void) rw_wrlock(&nscd_getent_ctx_base_lock);
535 
536 	nscd_getent_ctx_base = calloc(NSCD_NUM_DB,
537 			sizeof (nscd_getent_ctx_base_t *));
538 	if (nscd_getent_ctx_base == NULL) {
539 		(void) rw_unlock(&nscd_getent_ctx_base_lock);
540 		return (NSCD_NO_MEMORY);
541 	}
542 
543 	(void) rw_unlock(&nscd_getent_ctx_base_lock);
544 
545 	return (NSCD_SUCCESS);
546 }
547 
548 static int
549 process_exited(pid_t pid)
550 {
551 	char	pname[PATH_MAX];
552 	int	fd;
553 
554 	(void) snprintf(pname, sizeof (pname), "/proc/%d/psinfo", pid);
555 	if ((fd = open(pname, O_RDONLY)) == -1)
556 		return (1);
557 	else {
558 		(void) close(fd);
559 		return (0);
560 	}
561 }
562 
563 /*
564  * FUNCTION: reclaim_getent_ctx
565  */
566 /*ARGSUSED*/
567 static void *
568 reclaim_getent_ctx(void *arg)
569 {
570 	void			*cookie = NULL;
571 	nscd_db_entry_t		*ep;
572 	nscd_getent_ctx_t	*ctx;
573 	nscd_getent_context_t	*gctx, *c;
574 	nscd_getent_context_t	*first = NULL, *last = NULL;
575 	char			*me = "reclaim_getent_ctx";
576 
577 	/*CONSTCOND*/
578 	while (1) {
579 
580 		(void) sleep(60);
581 
582 		(void) rw_rdlock(&getent_ctxDB_rwlock);
583 
584 		for (ep = _nscd_walk_db(getent_ctxDB, &cookie); ep != NULL;
585 				ep = _nscd_walk_db(getent_ctxDB, &cookie)) {
586 
587 			ctx = (nscd_getent_ctx_t *)*(ep->data_array);
588 
589 			gctx = ctx->ptr;
590 
591 			/*
592 			 * if the client process, which did the setent,
593 			 * exited, add the context to the orphan list
594 			 */
595 			if (gctx->pid != -1 && process_exited(gctx->pid)) {
596 
597 				_NSCD_LOG(NSCD_LOG_GETENT_CTX,
598 					NSCD_LOG_LEVEL_DEBUG)
599 				(me, "process  %d exited, "
600 				"getent context = %p, "
601 				"db index = %d, cookie # = %lld, "
602 				"sequence # = %lld\n",
603 				gctx->pid, gctx, gctx->dbi,
604 				gctx->cookie_num, gctx->seq_num);
605 
606 				if (first != NULL) {
607 					last->next = gctx;
608 					last = gctx;
609 				} else {
610 					first = gctx;
611 					last = gctx;
612 				}
613 			}
614 		}
615 
616 		(void) rw_unlock(&getent_ctxDB_rwlock);
617 
618 
619 		/*
620 		 * return all the orphan getent contexts to the pool
621 		 */
622 		for (gctx = first; gctx; ) {
623 			c = gctx->next;
624 			gctx->next = NULL;
625 			_nscd_put_getent_ctx(gctx);
626 			gctx = c;
627 		}
628 		first = last = NULL;
629 	}
630 	/*NOTREACHED*/
631 	/*LINTED E_FUNC_HAS_NO_RETURN_STMT*/
632 }
633 
634 static nscd_rc_t
635 _nscd_init_getent_ctx_monitor() {
636 
637 	int	errnum;
638 	char	*me = "_nscd_init_getent_ctx_monitor";
639 
640 	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
641 	(me, "initializing the getent context monitor\n");
642 
643 	/*
644 	 * the forker nscd does not process getent requests
645 	 * so no need to monitor orphan getent contexts
646 	 */
647 	if (_whoami == NSCD_FORKER)
648 		return (NSCD_SUCCESS);
649 
650 	/*
651 	 * start a thread to reclaim unused getent contexts
652 	 */
653 	if (thr_create(NULL, NULL, reclaim_getent_ctx,
654 		NULL, THR_DETACHED, NULL) != 0) {
655 		errnum = errno;
656 		_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_ERROR)
657 		(me, "thr_create: %s\n", strerror(errnum));
658 		return (NSCD_THREAD_CREATE_ERROR);
659 	}
660 
661 	return (NSCD_SUCCESS);
662 }
663