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 }