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 }