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 }