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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/ccompile.h>
36 #include "nscd_switch.h"
37 #include "nscd_door.h"
40 static mutex_t getent_monitor_mutex
= DEFAULTMUTEX
;
41 static int getent_monitor_started
= 0;
43 static rwlock_t getent_ctxDB_rwlock
= DEFAULTRWLOCK
;
44 static nscd_db_t
*getent_ctxDB
= NULL
;
47 * internal structure representing a nscd getent context
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
;
56 * nscd_getent_context_t list for each nss database. Protected
57 * by the readers/writer lock nscd_getent_ctx_lock.
59 nscd_getent_ctx_base_t
**nscd_getent_ctx_base
;
60 static rwlock_t nscd_getent_ctx_base_lock
= DEFAULTRWLOCK
;
62 extern nscd_db_entry_t
*_nscd_walk_db(nscd_db_t
*db
, void **cookie
);
64 static nscd_rc_t
_nscd_init_getent_ctx_monitor();
67 * FUNCTION: _nscd_create_getent_ctxDB
69 * Create the internal getent context database to keep track of the
70 * getent contexts currently being used.
73 _nscd_create_getent_ctxDB()
78 (void) rw_wrlock(&getent_ctxDB_rwlock
);
80 if (getent_ctxDB
!= NULL
) {
81 (void) rw_unlock(&getent_ctxDB_rwlock
);
82 return (getent_ctxDB
);
85 ret
= _nscd_alloc_db(NSCD_DB_SIZE_LARGE
);
90 (void) rw_unlock(&getent_ctxDB_rwlock
);
96 * FUNCTION: _nscd_add_getent_ctx
98 * Add a getent context to the internal context database.
101 _nscd_add_getent_ctx(
102 nscd_getent_context_t
*ptr
,
103 nscd_cookie_num_t cookie_num
)
107 nscd_db_entry_t
*db_entry
;
108 nscd_getent_ctx_t
*gnctx
;
111 return (NSCD_INVALID_ARGUMENT
);
113 (void) snprintf(buf
, sizeof (buf
), "%lld", cookie_num
);
115 size
= sizeof (*gnctx
);
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
);
122 gnctx
= (nscd_getent_ctx_t
*)*(db_entry
->data_array
);
124 gnctx
->cookie_num
= cookie_num
;
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
);
131 return (NSCD_SUCCESS
);
135 * FUNCTION: _nscd_is_getent_ctx
137 * Check to see if a getent context can be found in the internal
138 * getent context database.
140 nscd_getent_context_t
*
142 nscd_cookie_num_t cookie_num
)
145 const nscd_db_entry_t
*db_entry
;
146 nscd_getent_context_t
*ret
= NULL
;
147 char *me
= "_nscd_is_getent_ctx";
149 (void) snprintf(ptrstr
, sizeof (ptrstr
), "%lld", cookie_num
);
151 (void) rw_rdlock(&getent_ctxDB_rwlock
);
153 db_entry
= _nscd_get_db_entry(getent_ctxDB
, NSCD_DATA_CTX_ADDR
,
154 (const char *)ptrstr
, NSCD_GET_FIRST_DB_ENTRY
, 0);
156 if (db_entry
!= NULL
) {
157 nscd_getent_ctx_t
*gnctx
;
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
);
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.
169 if (gnctx
->to_delete
== 0 && gnctx
->cookie_num
== cookie_num
) {
171 (void) mutex_lock(&gnctx
->ptr
->getent_mutex
);
172 if (ret
->aborted
== 1 || ret
->in_use
== 1)
176 (void) mutex_unlock(&gnctx
->ptr
->getent_mutex
);
180 (void) rw_unlock(&getent_ctxDB_rwlock
);
186 _nscd_is_getent_ctx_in_use(
187 nscd_getent_context_t
*ctx
)
190 char *me
= "_nscd_getent_ctx_in_use";
192 (void) mutex_lock(&ctx
->getent_mutex
);
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());
198 in_use
= ctx
->in_use
;
199 if (in_use
== 1 && ctx
->thr_id
== thr_self())
201 (void) mutex_unlock(&ctx
->getent_mutex
);
206 * FUNCTION: _nscd_free_ctx_if_aborted
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.
212 _nscd_free_ctx_if_aborted(
213 nscd_getent_context_t
*ctx
)
216 char *me
= "_nscd_free_ctx_if_aborted";
218 (void) mutex_lock(&ctx
->getent_mutex
);
220 _NSCD_LOG(NSCD_LOG_GETENT_CTX
, NSCD_LOG_LEVEL_DEBUG
)
221 (me
, "in_use = %d, aborted = %d\n", ctx
->in_use
, ctx
->aborted
);
223 if (ctx
->in_use
!= 1) {
224 (void) mutex_unlock(&ctx
->getent_mutex
);
227 aborted
= ctx
->aborted
;
229 (void) mutex_unlock(&ctx
->getent_mutex
);
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
);
239 * FUNCTION: _nscd_del_getent_ctx
241 * Delete a getent context from the internal getent context database.
244 _nscd_del_getent_ctx(
245 nscd_getent_context_t
*ptr
,
246 nscd_cookie_num_t cookie_num
)
249 nscd_getent_ctx_t
*gnctx
;
250 const nscd_db_entry_t
*db_entry
;
255 (void) snprintf(ptrstr
, sizeof (ptrstr
), "%lld", cookie_num
);
257 (void) rw_rdlock(&getent_ctxDB_rwlock
);
259 * first find the db entry and make sure the
260 * sequence number matched, then delete it from
263 db_entry
= _nscd_get_db_entry(getent_ctxDB
,
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
) {
271 (void) rw_unlock(&getent_ctxDB_rwlock
);
272 (void) rw_wrlock(&getent_ctxDB_rwlock
);
274 (void) _nscd_delete_db_entry(getent_ctxDB
,
276 (const char *)ptrstr
,
277 NSCD_DEL_FIRST_DB_ENTRY
, 0);
280 (void) rw_unlock(&getent_ctxDB_rwlock
);
284 _nscd_free_getent_ctx(
285 nscd_getent_context_t
*gnctx
)
288 char *me
= "_nscd_free_getent_ctx";
290 _NSCD_LOG(NSCD_LOG_GETENT_CTX
, NSCD_LOG_LEVEL_DEBUG
)
291 (me
, "getent context %p\n", gnctx
);
293 _nscd_put_nsw_state(gnctx
->nsw_state
);
295 if (gnctx
->base
!= NULL
) {
296 /* remove reference to the getent context base */
297 _nscd_release((nscd_acc_data_t
*)gnctx
->base
);
301 _nscd_del_getent_ctx(gnctx
, gnctx
->cookie_num
);
307 _nscd_free_getent_ctx_base(
308 nscd_acc_data_t
*data
)
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";
314 _NSCD_LOG(NSCD_LOG_GETENT_CTX
| NSCD_LOG_CONFIG
, NSCD_LOG_LEVEL_DEBUG
)
315 (me
, "getent context base %p\n", base
);
323 _nscd_free_getent_ctx(c
);
329 _nscd_free_all_getent_ctx_base()
331 nscd_getent_ctx_base_t
*base
;
333 char *me
= "_nscd_free_all_getent_ctx_base";
335 _NSCD_LOG(NSCD_LOG_GETENT_CTX
| NSCD_LOG_CONFIG
, NSCD_LOG_LEVEL_DEBUG
)
336 (me
, "entering ..\n");
338 (void) rw_wrlock(&nscd_getent_ctx_base_lock
);
340 for (i
= 0; i
< NSCD_NUM_DB
; i
++) {
342 base
= nscd_getent_ctx_base
[i
];
346 nscd_getent_ctx_base
[i
] = (nscd_getent_ctx_base_t
*)
347 _nscd_set((nscd_acc_data_t
*)base
, NULL
);
349 (void) rw_unlock(&nscd_getent_ctx_base_lock
);
352 static nscd_getent_context_t
*
353 _nscd_create_getent_ctx(
354 nscd_nsw_params_t
*params
)
356 nscd_getent_context_t
*gnctx
;
357 nss_db_root_t db_root
;
358 char *me
= "_nscd_create_getent_ctx";
360 gnctx
= calloc(1, sizeof (nscd_getent_context_t
));
364 _NSCD_LOG(NSCD_LOG_GETENT_CTX
, NSCD_LOG_LEVEL_DEBUG
)
365 (me
, "getent context allocated %p\n", gnctx
);
368 gnctx
->dbi
= params
->dbi
;
369 gnctx
->cookie_num
= _nscd_get_cookie_num();
371 (void) mutex_init(&gnctx
->getent_mutex
, USYNC_THREAD
, NULL
);
373 if (_nscd_get_nsw_state(&db_root
, params
) != NSCD_SUCCESS
) {
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;
381 _NSCD_LOG(NSCD_LOG_GETENT_CTX
, NSCD_LOG_LEVEL_DEBUG
)
382 (me
, "got nsw_state %p\n", gnctx
->nsw_state
);
389 _nscd_get_getent_ctx(
390 nss_getent_t
*contextpp
,
391 nscd_nsw_params_t
*params
)
394 nscd_getent_context_t
*c
;
395 nscd_getent_ctx_base_t
*base
, *tmp
;
397 char *me
= "_nscd_get_getent_ctx";
399 _NSCD_LOG(NSCD_LOG_GETENT_CTX
, NSCD_LOG_LEVEL_DEBUG
)
400 (me
, "entering ...\n");
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
);
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.
413 tmp
= (nscd_getent_ctx_base_t
*)_nscd_mutex_lock(
414 (nscd_acc_data_t
*)base
);
416 if (base
->first
== NULL
) {
417 if (base
->num_getent_ctx
>= base
->max_getent_ctx
) {
418 /* run out of contexts */
420 _NSCD_LOG(NSCD_LOG_GETENT_CTX
,
421 NSCD_LOG_LEVEL_DEBUG
)
422 (me
, "run out of getent ctxs\n");
424 _nscd_mutex_unlock((nscd_acc_data_t
*)base
);
425 return (NSCD_CREATE_GETENT_CTX_FAILED
);
427 base
->first
= _nscd_create_getent_ctx(params
);
428 if (base
->first
!= NULL
)
429 base
->num_getent_ctx
++;
431 /* not able to create a getent ctx */
433 _NSCD_LOG(NSCD_LOG_GETENT_CTX
,
434 NSCD_LOG_LEVEL_ERROR
)
435 (me
, "create getent ctx failed\n");
437 _nscd_mutex_unlock((nscd_acc_data_t
*)base
);
438 return (NSCD_CREATE_GETENT_CTX_FAILED
);
441 _NSCD_LOG(NSCD_LOG_GETENT_CTX
, NSCD_LOG_LEVEL_DEBUG
)
442 (me
, "got a new getent ctx %p\n", base
->first
);
446 assert(base
->first
!= NULL
);
449 base
->first
= c
->next
;
452 c
->cookie_num
= _nscd_get_cookie_num();
454 c
->thr_id
= thr_self();
456 _NSCD_LOG(NSCD_LOG_GETENT_CTX
, NSCD_LOG_LEVEL_DEBUG
)
457 (me
, "got a getent ctx %p\n", c
);
460 * reference count the getent context base bfore handing out
463 c
->base
= (nscd_getent_ctx_base_t
*)
464 _nscd_get((nscd_acc_data_t
*)base
);
466 _nscd_mutex_unlock((nscd_acc_data_t
*)base
);
468 _NSCD_LOG(NSCD_LOG_GETENT_CTX
, NSCD_LOG_LEVEL_DEBUG
)
469 (me
, "adding new ctx %p, cookie # = %lld\n", c
, c
->cookie_num
);
471 if ((rc
= _nscd_add_getent_ctx(c
, c
->cookie_num
)) != NSCD_SUCCESS
) {
472 _nscd_put_getent_ctx(c
);
475 contextpp
->ctx
= (struct nss_getent_context
*)c
;
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();
484 (void) mutex_unlock(&getent_monitor_mutex
);
487 return (NSCD_SUCCESS
);
491 _nscd_put_getent_ctx(
492 nscd_getent_context_t
*gnctx
)
495 nscd_getent_ctx_base_t
*base
;
496 char *me
= "_nscd_put_getent_ctx";
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
);
506 if (base
->first
!= NULL
) {
507 gnctx
->next
= base
->first
;
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
);
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
;
525 gnctx
->thr_id
= (thread_t
)-1;
526 _nscd_del_getent_ctx(gnctx
, gnctx
->cookie_num
);
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
);
533 gnctx
->cookie_num
= 0;
535 gnctx
->thr_id
= (thread_t
)-1;
539 /* remove reference to the getent context base */
540 _nscd_release((nscd_acc_data_t
*)base
);
543 _nscd_mutex_unlock((nscd_acc_data_t
*)base
);
547 _nscd_init_getent_ctx_base(
551 nscd_getent_ctx_base_t
*base
= NULL
;
552 char *me
= "_nscd_init_getent_ctx_base";
555 (void) rw_rdlock(&nscd_getent_ctx_base_lock
);
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
);
565 (void) rw_unlock(&nscd_getent_ctx_base_lock
);
566 return (NSCD_NO_MEMORY
);
568 _NSCD_LOG(NSCD_LOG_GETENT_CTX
| NSCD_LOG_CONFIG
, NSCD_LOG_LEVEL_DEBUG
)
569 (me
, "base %p allocated\n", base
);
572 * initialize and activate the new getent_ctx base
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
);
582 (void) rw_unlock(&nscd_getent_ctx_base_lock
);
584 return (NSCD_SUCCESS
);
588 _nscd_init_all_getent_ctx_base()
592 char *me
= "_nscd_init_all_getent_ctx_base";
594 (void) rw_wrlock(&nscd_getent_ctx_base_lock
);
596 for (i
= 0; i
< NSCD_NUM_DB
; i
++) {
598 rc
= _nscd_init_getent_ctx_base(i
, 0);
600 if (rc
!= NSCD_SUCCESS
) {
601 (void) rw_unlock(&nscd_getent_ctx_base_lock
);
606 _NSCD_LOG(NSCD_LOG_GETENT_CTX
| NSCD_LOG_CONFIG
, NSCD_LOG_LEVEL_DEBUG
)
607 (me
, "all getent context base initialized\n");
609 (void) rw_unlock(&nscd_getent_ctx_base_lock
);
611 return (NSCD_SUCCESS
);
614 _nscd_alloc_getent_ctx_base()
617 (void) rw_wrlock(&nscd_getent_ctx_base_lock
);
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
);
626 (void) rw_unlock(&nscd_getent_ctx_base_lock
);
628 return (NSCD_SUCCESS
);
632 process_exited(pid_t pid
)
634 char pname
[PATH_MAX
];
637 (void) snprintf(pname
, sizeof (pname
), "/proc/%d/psinfo", pid
);
638 if ((fd
= open(pname
, O_RDONLY
)) == -1)
647 * FUNCTION: reclaim_getent_ctx
650 static void * __NORETURN
651 reclaim_getent_ctx(void *arg
)
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";
666 (void) rw_rdlock(&getent_ctxDB_rwlock
);
668 for (ep
= _nscd_walk_db(getent_ctxDB
, &cookie
); ep
!= NULL
;
669 ep
= _nscd_walk_db(getent_ctxDB
, &cookie
)) {
671 ctx
= (nscd_getent_ctx_t
*)*(ep
->data_array
);
676 * if the client process, which did the setent,
677 * exited, add the context to the orphan list
679 if (gctx
->pid
!= -1 && process_exited(gctx
->pid
)) {
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
);
691 /* add to list if not in already */
692 for (c
= first
; c
!= NULL
;
693 c
= c
->next_to_reclaim
) {
698 last
->next_to_reclaim
= gctx
;
708 (void) rw_unlock(&getent_ctxDB_rwlock
);
712 * return all the orphan getent contexts to the pool if not
715 for (gctx
= first
; gctx
; ) {
716 int in_use
, num_reclaim_check
;
718 c
= gctx
->next_to_reclaim
;
719 gctx
->next_to_reclaim
= NULL
;
722 (void) mutex_lock(&gctx
->getent_mutex
);
723 num_reclaim_check
= gctx
->num_reclaim_check
++;
724 if (num_reclaim_check
> 1)
726 in_use
= gctx
->in_use
;
727 (void) mutex_unlock(&gctx
->getent_mutex
);
730 _NSCD_LOG(NSCD_LOG_GETENT_CTX
,
731 NSCD_LOG_LEVEL_DEBUG
)
732 (me
, "process %d exited, "
733 "freeing getent context = %p\n",
735 nssctx
.ctx
= (struct nss_getent_context
*)gctx
;
736 nss_endent(NULL
, NULL
, &nssctx
);
743 /*LINTED E_FUNC_HAS_NO_RETURN_STMT*/
747 _nscd_init_getent_ctx_monitor() {
750 char *me
= "_nscd_init_getent_ctx_monitor";
752 _NSCD_LOG(NSCD_LOG_GETENT_CTX
, NSCD_LOG_LEVEL_DEBUG
)
753 (me
, "initializing the getent context monitor\n");
756 * the forker nscd does not process getent requests
757 * so no need to monitor orphan getent contexts
759 if (_whoami
== NSCD_FORKER
)
760 return (NSCD_SUCCESS
);
763 * start a thread to reclaim unused getent contexts
765 if (thr_create(NULL
, NULL
, reclaim_getent_ctx
,
766 NULL
, THR_DETACHED
, NULL
) != 0) {
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
);
773 return (NSCD_SUCCESS
);