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 }