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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2012 Milan Jurik. All rights reserved.
  24  * Copyright (c) 2016 by Delphix. All rights reserved.
  25  * Copyright 2018 Joyent, Inc.
  26  */
  27 
  28 /*
  29  * Cache routines for nscd
  30  */
  31 #include <assert.h>
  32 #include <errno.h>
  33 #include <memory.h>
  34 #include <signal.h>
  35 #include <stdlib.h>
  36 #include <stddef.h>
  37 #include <stdio.h>
  38 #include <string.h>
  39 #include <sys/stat.h>
  40 #include <sys/time.h>
  41 #include <sys/types.h>
  42 #include <sys/wait.h>
  43 #include <unistd.h>
  44 #include <ucred.h>
  45 #include <nss_common.h>
  46 #include <locale.h>
  47 #include <ctype.h>
  48 #include <strings.h>
  49 #include <string.h>
  50 #include <umem.h>
  51 #include <fcntl.h>
  52 #include "cache.h"
  53 #include "nscd_door.h"
  54 #include "nscd_log.h"
  55 #include "nscd_config.h"
  56 #include "nscd_frontend.h"
  57 #include "nscd_switch.h"
  58 
  59 #define SUCCESS         0
  60 #define NOTFOUND        -1
  61 #define SERVERERROR     -2
  62 #define NOSERVER        -3
  63 #define CONTINUE        -4
  64 
  65 static nsc_db_t *nsc_get_db(nsc_ctx_t *, int);
  66 static nscd_rc_t lookup_cache(nsc_lookup_args_t *, nscd_cfg_cache_t *,
  67                 nss_XbyY_args_t *, char *, nsc_entry_t **);
  68 static uint_t reap_cache(nsc_ctx_t *, uint_t, uint_t);
  69 static void delete_entry(nsc_db_t *, nsc_ctx_t *, nsc_entry_t *);
  70 static void print_stats(nscd_cfg_stat_cache_t *);
  71 static void print_cfg(nscd_cfg_cache_t *);
  72 static int lookup_int(nsc_lookup_args_t *, int);
  73 
  74 #ifdef  NSCD_DEBUG
  75 static void print_entry(nsc_db_t *, time_t, nsc_entry_t *);
  76 static void avl_dump(nsc_db_t *, time_t);
  77 static void hash_dump(nsc_db_t *, time_t);
  78 #endif  /* NSCD_DEBUG */
  79 static nsc_entry_t *hash_find(nsc_db_t *, nsc_entry_t *, uint_t *, nscd_bool_t);
  80 
  81 static void queue_adjust(nsc_db_t *, nsc_entry_t *);
  82 static void queue_remove(nsc_db_t *, nsc_entry_t *);
  83 #ifdef  NSCD_DEBUG
  84 static void queue_dump(nsc_db_t *, time_t);
  85 #endif  /* NSCD_DEBUG */
  86 
  87 static int launch_update(nsc_lookup_args_t *);
  88 static void do_update(nsc_lookup_args_t *);
  89 static void getxy_keepalive(nsc_ctx_t *, nsc_db_t *, int, int);
  90 
  91 static void ctx_info(nsc_ctx_t *);
  92 static void ctx_info_nolock(nsc_ctx_t *);
  93 static void ctx_invalidate(nsc_ctx_t *);
  94 
  95 static void nsc_db_str_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t *);
  96 static void nsc_db_int_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t *);
  97 static void nsc_db_any_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t *);
  98 
  99 static int nsc_db_cis_key_compar(const void *, const void *);
 100 static int nsc_db_ces_key_compar(const void *, const void *);
 101 static int nsc_db_int_key_compar(const void *, const void *);
 102 
 103 static uint_t nsc_db_cis_key_gethash(nss_XbyY_key_t *, int);
 104 static uint_t nsc_db_ces_key_gethash(nss_XbyY_key_t *, int);
 105 static uint_t nsc_db_int_key_gethash(nss_XbyY_key_t *, int);
 106 
 107 static umem_cache_t     *nsc_entry_cache;
 108 
 109 static nsc_ctx_t *init_cache_ctx(int);
 110 static void reaper(nsc_ctx_t *);
 111 static void revalidate(nsc_ctx_t *);
 112 
 113 static nss_status_t
 114 dup_packed_buffer(void *src, void *dst) {
 115         nsc_lookup_args_t       *s = (nsc_lookup_args_t *)src;
 116         nsc_entry_t             *d = (nsc_entry_t *)dst;
 117         nss_pheader_t           *sphdr = (nss_pheader_t *)s->buffer;
 118         nss_pheader_t           *dphdr = (nss_pheader_t *)d->buffer;
 119         int                     slen, new_pbufsiz = 0;
 120 
 121         if (NSCD_GET_STATUS(sphdr) != NSS_SUCCESS) {
 122 
 123                 /* no result, copy header only (status, errno, etc) */
 124                 slen = sphdr->data_off;
 125         } else {
 126                 /*
 127                  * lookup result returned, data to copy is the packed
 128                  * header plus result (add 1 for the terminating NULL
 129                  * just in case)
 130                  */
 131                 slen = sphdr->data_off + sphdr->data_len + 1;
 132         }
 133 
 134         /* allocate cache packed buffer */
 135         if (dphdr != NULL && d->bufsize <= slen && d->bufsize != 0) {
 136                 /* old buffer too small, free it */
 137                 free(dphdr);
 138                 d->buffer = NULL;
 139                 d->bufsize = 0;
 140                 dphdr = NULL;
 141         }
 142         if (dphdr == NULL) {
 143                 /* get new buffer */
 144                 dphdr = calloc(1, slen + 1);
 145                 if (dphdr == NULL)
 146                         return (NSS_ERROR);
 147                 d->buffer = dphdr;
 148                 d->bufsize = slen + 1;
 149                 new_pbufsiz = slen + 1;
 150         }
 151 
 152         (void) memcpy(dphdr, sphdr, slen);
 153         if (new_pbufsiz != 0)
 154                 dphdr->pbufsiz = new_pbufsiz;
 155 
 156         return (NSS_SUCCESS);
 157 }
 158 
 159 char *cache_name[CACHE_CTX_COUNT] = {
 160         NSS_DBNAM_PASSWD,
 161         NSS_DBNAM_GROUP,
 162         NSS_DBNAM_HOSTS,
 163         NSS_DBNAM_IPNODES,
 164         NSS_DBNAM_EXECATTR,
 165         NSS_DBNAM_PROFATTR,
 166         NSS_DBNAM_USERATTR,
 167         NSS_DBNAM_ETHERS,
 168         NSS_DBNAM_RPC,
 169         NSS_DBNAM_PROTOCOLS,
 170         NSS_DBNAM_NETWORKS,
 171         NSS_DBNAM_BOOTPARAMS,
 172         NSS_DBNAM_AUTHATTR,
 173         NSS_DBNAM_SERVICES,
 174         NSS_DBNAM_NETMASKS,
 175         NSS_DBNAM_PRINTERS,
 176         NSS_DBNAM_PROJECT,
 177         NSS_DBNAM_TSOL_TP,
 178         NSS_DBNAM_TSOL_RH
 179 };
 180 
 181 typedef void (*cache_init_ctx_t)(nsc_ctx_t *);
 182 static cache_init_ctx_t cache_init_ctx[CACHE_CTX_COUNT] = {
 183         passwd_init_ctx,
 184         group_init_ctx,
 185         host_init_ctx,
 186         ipnode_init_ctx,
 187         exec_init_ctx,
 188         prof_init_ctx,
 189         user_init_ctx,
 190         ether_init_ctx,
 191         rpc_init_ctx,
 192         proto_init_ctx,
 193         net_init_ctx,
 194         bootp_init_ctx,
 195         auth_init_ctx,
 196         serv_init_ctx,
 197         netmask_init_ctx,
 198         printer_init_ctx,
 199         project_init_ctx,
 200         tnrhtp_init_ctx,
 201         tnrhdb_init_ctx
 202 };
 203 
 204 nsc_ctx_t *cache_ctx_p[CACHE_CTX_COUNT] = { 0 };
 205 static nscd_cfg_stat_cache_t    null_stats = { 0 };
 206 static nscd_cfg_global_cache_t  global_cfg;
 207 
 208 /*
 209  * Given database name 'dbname' find cache index
 210  */
 211 int
 212 get_cache_idx(char *dbname) {
 213         int     i;
 214         char    *nsc_name;
 215 
 216         for (i = 0; i < CACHE_CTX_COUNT; i++) {
 217                 nsc_name = cache_name[i];
 218                 if (strcmp(nsc_name, dbname) == 0)
 219                         return (i);
 220         }
 221         return (-1);
 222 }
 223 
 224 /*
 225  * Given database name 'dbname' retrieve cache context,
 226  * if not created yet, allocate and initialize it.
 227  */
 228 static nscd_rc_t
 229 get_cache_ctx(char *dbname, nsc_ctx_t **ctx) {
 230         int     i;
 231 
 232         *ctx = NULL;
 233 
 234         i = get_cache_idx(dbname);
 235         if (i == -1)
 236                 return (NSCD_INVALID_ARGUMENT);
 237         if ((*ctx = cache_ctx_p[i]) == NULL) {
 238                 *ctx = init_cache_ctx(i);
 239                 if (*ctx == NULL)
 240                         return (NSCD_NO_MEMORY);
 241         }
 242 
 243         return (NSCD_SUCCESS);
 244 }
 245 
 246 /*
 247  * Generate a log string to identify backend operation in debug logs
 248  */
 249 static void
 250 nsc_db_str_key_getlogstr(char *name, char *whoami, size_t len,
 251                 nss_XbyY_args_t *argp) {
 252         (void) snprintf(whoami, len, "%s [key=%s]", name, argp->key.name);
 253 }
 254 
 255 
 256 static void
 257 nsc_db_int_key_getlogstr(char *name, char *whoami, size_t len,
 258                 nss_XbyY_args_t *argp) {
 259         (void) snprintf(whoami, len, "%s [key=%d]", name, argp->key.number);
 260 }
 261 
 262 /*ARGSUSED*/
 263 static void
 264 nsc_db_any_key_getlogstr(char *name, char *whoami, size_t len,
 265                 nss_XbyY_args_t *argp) {
 266         (void) snprintf(whoami, len, "%s", name);
 267 }
 268 
 269 
 270 /*
 271  * Returns cache based on dbop
 272  */
 273 static nsc_db_t *
 274 nsc_get_db(nsc_ctx_t *ctx, int dbop) {
 275         int     i;
 276 
 277         for (i = 0; i < ctx->db_count; i++) {
 278                 if (ctx->nsc_db[i] && dbop == ctx->nsc_db[i]->dbop)
 279                         return (ctx->nsc_db[i]);
 280         }
 281         return (NULL);
 282 }
 283 
 284 
 285 /*
 286  * integer compare routine for _NSC_DB_INT_KEY
 287  */
 288 static int
 289 nsc_db_int_key_compar(const void *n1, const void *n2) {
 290         nsc_entry_t     *e1, *e2;
 291 
 292         e1 = (nsc_entry_t *)n1;
 293         e2 = (nsc_entry_t *)n2;
 294         return (_NSC_INT_KEY_CMP(e1->key.number, e2->key.number));
 295 }
 296 
 297 
 298 /*
 299  * case sensitive name compare routine for _NSC_DB_CES_KEY
 300  */
 301 static int
 302 nsc_db_ces_key_compar(const void *n1, const void *n2) {
 303         nsc_entry_t     *e1, *e2;
 304         int             res, l1, l2;
 305 
 306         e1 = (nsc_entry_t *)n1;
 307         e2 = (nsc_entry_t *)n2;
 308         l1 = strlen(e1->key.name);
 309         l2 = strlen(e2->key.name);
 310         res = strncmp(e1->key.name, e2->key.name, (l1 > l2)?l1:l2);
 311         return (_NSC_INT_KEY_CMP(res, 0));
 312 }
 313 
 314 
 315 /*
 316  * case insensitive name compare routine _NSC_DB_CIS_KEY
 317  */
 318 static int
 319 nsc_db_cis_key_compar(const void *n1, const void *n2) {
 320         nsc_entry_t     *e1, *e2;
 321         int             res, l1, l2;
 322 
 323         e1 = (nsc_entry_t *)n1;
 324         e2 = (nsc_entry_t *)n2;
 325         l1 = strlen(e1->key.name);
 326         l2 = strlen(e2->key.name);
 327         res = strncasecmp(e1->key.name, e2->key.name, (l1 > l2)?l1:l2);
 328         return (_NSC_INT_KEY_CMP(res, 0));
 329 }
 330 
 331 /*
 332  * macro used to generate elf hashes for strings
 333  */
 334 #define _NSC_ELF_STR_GETHASH(func, str, htsize, hval) \
 335         hval = 0; \
 336         while (*str) { \
 337                 uint_t  g; \
 338                 hval = (hval << 4) + func(*str++); \
 339                 if ((g = (hval & 0xf0000000)) != 0) \
 340                         hval ^= g >> 24; \
 341                 hval &= ~g; \
 342         } \
 343         hval %= htsize;
 344 
 345 
 346 /*
 347  * cis hash function
 348  */
 349 uint_t
 350 cis_gethash(const char *key, int htsize) {
 351         uint_t  hval;
 352         if (key == NULL)
 353                 return (0);
 354         _NSC_ELF_STR_GETHASH(tolower, key, htsize, hval);
 355         return (hval);
 356 }
 357 
 358 
 359 /*
 360  * ces hash function
 361  */
 362 uint_t
 363 ces_gethash(const char *key, int htsize) {
 364         uint_t  hval;
 365         if (key == NULL)
 366                 return (0);
 367         _NSC_ELF_STR_GETHASH(, key, htsize, hval);
 368         return (hval);
 369 }
 370 
 371 
 372 /*
 373  * one-at-a-time hash function
 374  */
 375 uint_t
 376 db_gethash(const void *key, int len, int htsize) {
 377         uint_t  hval, i;
 378         const char *str = key;
 379 
 380         if (str == NULL)
 381                 return (0);
 382 
 383         for (hval = 0, i = 0; i < len; i++) {
 384                 hval += str[i];
 385                 hval += (hval << 10);
 386                 hval ^= (hval >> 6);
 387         }
 388         hval += (hval << 3);
 389         hval ^= (hval >> 11);
 390         hval += (hval << 15);
 391         return (hval % htsize);
 392 }
 393 
 394 
 395 /*
 396  * case insensitive name gethash routine _NSC_DB_CIS_KEY
 397  */
 398 static uint_t
 399 nsc_db_cis_key_gethash(nss_XbyY_key_t *key, int htsize) {
 400         return (cis_gethash(key->name, htsize));
 401 }
 402 
 403 
 404 /*
 405  * case sensitive name gethash routine _NSC_DB_CES_KEY
 406  */
 407 static uint_t
 408 nsc_db_ces_key_gethash(nss_XbyY_key_t *key, int htsize) {
 409         return (ces_gethash(key->name, htsize));
 410 }
 411 
 412 
 413 /*
 414  * integer gethash routine _NSC_DB_INT_KEY
 415  */
 416 static uint_t
 417 nsc_db_int_key_gethash(nss_XbyY_key_t *key, int htsize) {
 418         return (db_gethash(&key->number, sizeof (key->number), htsize));
 419 }
 420 
 421 
 422 /*
 423  * Find entry in the hash table
 424  * if cmp == nscd_true)
 425  *      return entry only if the keys match
 426  * else
 427  *      return entry in the hash location without checking the keys
 428  *
 429  */
 430 static nsc_entry_t *
 431 hash_find(nsc_db_t *nscdb, nsc_entry_t *entry, uint_t *hash,
 432                         nscd_bool_t cmp) {
 433 
 434         nsc_entry_t     *hashentry;
 435 
 436         if (nscdb->gethash)
 437                 *hash = nscdb->gethash(&entry->key, nscdb->htsize);
 438         else
 439                 return (NULL);
 440 
 441         hashentry = nscdb->htable[*hash];
 442         if (cmp == nscd_false || hashentry == NULL)
 443                 return (hashentry);
 444         if (nscdb->compar) {
 445                 if (nscdb->compar(entry, hashentry) == 0)
 446                         return (hashentry);
 447         }
 448         return (NULL);
 449 }
 450 
 451 
 452 #define HASH_REMOVE(nscdb, entry, hash, cmp) \
 453         if (nscdb->htable) { \
 454                 if (entry == hash_find(nscdb, entry, &hash, cmp)) \
 455                         nscdb->htable[hash] = NULL; \
 456         }
 457 
 458 
 459 #define HASH_INSERT(nscdb, entry, hash, cmp) \
 460         if (nscdb->htable) { \
 461                 (void) hash_find(nscdb, entry, &hash, cmp); \
 462                 nscdb->htable[hash] = entry; \
 463         }
 464 
 465 
 466 #ifdef  NSCD_DEBUG
 467 static void
 468 print_entry(nsc_db_t *nscdb, time_t now, nsc_entry_t *entry) {
 469         nss_XbyY_args_t args;
 470         char            whoami[512];
 471 
 472         switch (entry->stats.status) {
 473         case ST_NEW_ENTRY:
 474                 (void) fprintf(stdout, gettext("\t status: new entry\n"));
 475                 return;
 476         case ST_UPDATE_PENDING:
 477                 (void) fprintf(stdout, gettext("\t status: update pending\n"));
 478                 return;
 479         case ST_LOOKUP_PENDING:
 480                 (void) fprintf(stdout, gettext("\t status: lookup pending\n"));
 481                 return;
 482         case ST_DISCARD:
 483                 (void) fprintf(stdout, gettext("\t status: discarded entry\n"));
 484                 return;
 485         default:
 486                 if (entry->stats.timestamp < now)
 487                         (void) fprintf(stdout,
 488                         gettext("\t status: expired (%d seconds ago)\n"),
 489                         now - entry->stats.timestamp);
 490                 else
 491                         (void) fprintf(stdout,
 492                         gettext("\t status: valid (expiry in %d seconds)\n"),
 493                         entry->stats.timestamp - now);
 494                 break;
 495         }
 496         (void) fprintf(stdout, gettext("\t hits: %u\n"), entry->stats.hits);
 497         args.key = entry->key;
 498         (void) nscdb->getlogstr(nscdb->name, whoami, sizeof (whoami), &args);
 499         (void) fprintf(stdout, "\t %s\n", whoami);
 500 }
 501 #endif  /* NSCD_DEBUG */
 502 
 503 static void
 504 print_stats(nscd_cfg_stat_cache_t *statsp) {
 505 
 506         (void) fprintf(stdout, gettext("\n\t STATISTICS:\n"));
 507         (void) fprintf(stdout, gettext("\t positive hits: %lu\n"),
 508                         statsp->pos_hits);
 509         (void) fprintf(stdout, gettext("\t negative hits: %lu\n"),
 510                         statsp->neg_hits);
 511         (void) fprintf(stdout, gettext("\t positive misses: %lu\n"),
 512                         statsp->pos_misses);
 513         (void) fprintf(stdout, gettext("\t negative misses: %lu\n"),
 514                         statsp->neg_misses);
 515         (void) fprintf(stdout, gettext("\t total entries: %lu\n"),
 516                         statsp->entries);
 517         (void) fprintf(stdout, gettext("\t queries queued: %lu\n"),
 518                         statsp->wait_count);
 519         (void) fprintf(stdout, gettext("\t queries dropped: %lu\n"),
 520                         statsp->drop_count);
 521         (void) fprintf(stdout, gettext("\t cache invalidations: %lu\n"),
 522                         statsp->invalidate_count);
 523 
 524         _NSC_GET_HITRATE(statsp);
 525         (void) fprintf(stdout, gettext("\t cache hit rate: %10.1f\n"),
 526                         statsp->hitrate);
 527 }
 528 
 529 
 530 static void
 531 print_cfg(nscd_cfg_cache_t *cfgp) {
 532         (void) fprintf(stdout, gettext("\n\t CONFIG:\n"));
 533         (void) fprintf(stdout, gettext("\t enabled: %s\n"),
 534                         yes_no(cfgp->enable));
 535         (void) fprintf(stdout, gettext("\t per user cache: %s\n"),
 536                         yes_no(cfgp->per_user));
 537         (void) fprintf(stdout, gettext("\t avoid name service: %s\n"),
 538                         yes_no(cfgp->avoid_ns));
 539         (void) fprintf(stdout, gettext("\t check file: %s\n"),
 540                         yes_no(cfgp->check_files));
 541         (void) fprintf(stdout, gettext("\t check file interval: %d\n"),
 542                         cfgp->check_interval);
 543         (void) fprintf(stdout, gettext("\t positive ttl: %d\n"),
 544                         cfgp->pos_ttl);
 545         (void) fprintf(stdout, gettext("\t negative ttl: %d\n"),
 546                         cfgp->neg_ttl);
 547         (void) fprintf(stdout, gettext("\t keep hot count: %d\n"),
 548                         cfgp->keephot);
 549         (void) fprintf(stdout, gettext("\t hint size: %d\n"),
 550                         cfgp->hint_size);
 551         (void) fprintf(stdout, gettext("\t max entries: %lu%s"),
 552                         cfgp->maxentries,
 553                         cfgp->maxentries?"\n":" (unlimited)\n");
 554 }
 555 
 556 
 557 #ifdef  NSCD_DEBUG
 558 static void
 559 hash_dump(nsc_db_t *nscdb, time_t now) {
 560         nsc_entry_t     *entry;
 561         int             i;
 562 
 563         (void) fprintf(stdout, gettext("\n\nHASH TABLE:\n"));
 564         for (i = 0; i < nscdb->htsize; i++) {
 565                 if ((entry = nscdb->htable[i]) != NULL) {
 566                         (void) fprintf(stdout, "hash[%d]:\n", i);
 567                         print_entry(nscdb, now, entry);
 568                 }
 569         }
 570 }
 571 #endif  /* NSCD_DEBUG */
 572 
 573 
 574 #ifdef  NSCD_DEBUG
 575 static void
 576 avl_dump(nsc_db_t *nscdb, time_t now) {
 577         nsc_entry_t     *entry;
 578         int             i;
 579 
 580         (void) fprintf(stdout, gettext("\n\nAVL TREE:\n"));
 581         for (entry = avl_first(&nscdb->tree), i = 0; entry != NULL;
 582                         entry = avl_walk(&nscdb->tree, entry, AVL_AFTER)) {
 583                 (void) fprintf(stdout, "avl node[%d]:\n", i++);
 584                 print_entry(nscdb, now, entry);
 585         }
 586 }
 587 #endif  /* NSCD_DEBUG */
 588 
 589 
 590 #ifdef  NSCD_DEBUG
 591 static void
 592 queue_dump(nsc_db_t *nscdb, time_t now) {
 593         nsc_entry_t     *entry;
 594         int             i;
 595 
 596         (void) fprintf(stdout,
 597                 gettext("\n\nCACHE [name=%s, nodes=%lu]:\n"),
 598                 nscdb->name, avl_numnodes(&nscdb->tree));
 599 
 600         (void) fprintf(stdout,
 601                 gettext("Starting with the most recently accessed:\n"));
 602 
 603         for (entry = nscdb->qtail, i = 0; entry; entry = entry->qnext) {
 604                 (void) fprintf(stdout, "entry[%d]:\n", i++);
 605                 print_entry(nscdb, now, entry);
 606         }
 607 }
 608 #endif  /* NSCD_DEBUG */
 609 
 610 static void
 611 queue_remove(nsc_db_t *nscdb, nsc_entry_t *entry) {
 612 
 613         if (nscdb->qtail == entry)
 614                 nscdb->qtail = entry->qnext;
 615         else
 616                 entry->qprev->qnext = entry->qnext;
 617 
 618         if (nscdb->qhead == entry)
 619                 nscdb->qhead = entry->qprev;
 620         else
 621                 entry->qnext->qprev = entry->qprev;
 622 
 623         if (nscdb->reap_node == entry)
 624                 nscdb->reap_node = entry->qnext;
 625         entry->qnext = entry->qprev = NULL;
 626 }
 627 
 628 
 629 static void
 630 queue_adjust(nsc_db_t *nscdb, nsc_entry_t *entry) {
 631 
 632 #ifdef NSCD_DEBUG
 633         assert(nscdb->qtail || entry->qnext == NULL &&
 634                         entry->qprev == NULL);
 635 
 636         assert(nscdb->qtail && nscdb->qhead ||
 637                 nscdb->qtail == NULL && nscdb->qhead == NULL);
 638 
 639         assert(entry->qprev || entry->qnext == NULL ||
 640                 nscdb->qtail == entry);
 641 #endif /* NSCD_DEBUG */
 642 
 643         /* already in the desired position */
 644         if (nscdb->qtail == entry)
 645                 return;
 646 
 647         /* new queue */
 648         if (nscdb->qtail == NULL) {
 649                 nscdb->qhead = nscdb->qtail = entry;
 650                 return;
 651         }
 652 
 653         /* new entry (prev == NULL AND tail != entry) */
 654         if (entry->qprev == NULL) {
 655                 nscdb->qtail->qprev = entry;
 656                 entry->qnext = nscdb->qtail;
 657                 nscdb->qtail = entry;
 658                 return;
 659         }
 660 
 661         /* existing entry */
 662         if (nscdb->reap_node == entry)
 663                 nscdb->reap_node = entry->qnext;
 664         if (nscdb->qhead == entry)
 665                 nscdb->qhead = entry->qprev;
 666         else
 667                 entry->qnext->qprev = entry->qprev;
 668         entry->qprev->qnext = entry->qnext;
 669         entry->qprev = NULL;
 670         entry->qnext = nscdb->qtail;
 671         nscdb->qtail->qprev = entry;
 672         nscdb->qtail = entry;
 673 }
 674 
 675 
 676 /*
 677  * Init cache
 678  */
 679 nscd_rc_t
 680 init_cache(int debug_level) {
 681         int cflags;
 682 
 683         cflags = (debug_level > 0)?0:UMC_NODEBUG;
 684         nsc_entry_cache = umem_cache_create("nsc_entry_cache",
 685                                 sizeof (nsc_entry_t), 0, NULL, NULL, NULL,
 686                                 NULL, NULL, cflags);
 687         if (nsc_entry_cache == NULL)
 688                 return (NSCD_NO_MEMORY);
 689         return (NSCD_SUCCESS);
 690 }
 691 
 692 
 693 /*
 694  * Create cache
 695  */
 696 nsc_db_t *
 697 make_cache(enum db_type dbtype, int dbop, char *name,
 698     int (*compar) (const void *, const void *),
 699     void (*getlogstr)(char *, char *, size_t, nss_XbyY_args_t *),
 700     uint_t (*gethash)(nss_XbyY_key_t *, int),
 701     enum hash_type httype, int htsize)
 702 {
 703         nsc_db_t        *nscdb;
 704         char            *me = "make_cache";
 705 
 706         nscdb = (nsc_db_t *)malloc(sizeof (*nscdb));
 707         if (nscdb == NULL) {
 708                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
 709                 (me, "%s: memory allocation failure\n", name);
 710                 goto out;
 711         }
 712         (void) memset(nscdb, 0, sizeof (*nscdb));
 713 
 714         nscdb->dbop = dbop;
 715         nscdb->name = name;
 716         nscdb->db_type = dbtype;
 717 
 718         /* Assign compare routine */
 719         if (compar == NULL) {
 720                 if (_NSC_DB_CES_KEY(nscdb))
 721                         nscdb->compar = nsc_db_ces_key_compar;
 722                 else if (_NSC_DB_CIS_KEY(nscdb))
 723                         nscdb->compar = nsc_db_cis_key_compar;
 724                 else if (_NSC_DB_INT_KEY(nscdb))
 725                         nscdb->compar = nsc_db_int_key_compar;
 726                 else
 727                         assert(0);
 728         } else {
 729                 nscdb->compar = compar;
 730         }
 731 
 732         /* The cache is an AVL tree */
 733         avl_create(&nscdb->tree, nscdb->compar, sizeof (nsc_entry_t),
 734             offsetof(nsc_entry_t, avl_link));
 735 
 736         /* Assign log routine */
 737         if (getlogstr == NULL) {
 738                 if (_NSC_DB_STR_KEY(nscdb))
 739                         nscdb->getlogstr = nsc_db_str_key_getlogstr;
 740                 else if (_NSC_DB_INT_KEY(nscdb))
 741                         nscdb->getlogstr = nsc_db_int_key_getlogstr;
 742                 else
 743                         nscdb->getlogstr = nsc_db_any_key_getlogstr;
 744         } else {
 745                 nscdb->getlogstr = getlogstr;
 746         }
 747 
 748         /* The AVL tree based cache uses a hash table for quick access */
 749         if (htsize != 0) {
 750                 /* Determine hash table size based on type */
 751                 nscdb->hash_type = httype;
 752                 if (htsize < 0) {
 753                         switch (httype) {
 754                         case nsc_ht_power2:
 755                                 htsize = _NSC_INIT_HTSIZE_POWER2;
 756                                 break;
 757                         case nsc_ht_prime:
 758                         case nsc_ht_default:
 759                         default:
 760                                 htsize = _NSC_INIT_HTSIZE_PRIME;
 761                         }
 762                 }
 763                 nscdb->htsize = htsize;
 764 
 765                 /* Create the hash table */
 766                 nscdb->htable = calloc(htsize, sizeof (*(nscdb->htable)));
 767                 if (nscdb->htable == NULL) {
 768                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
 769                         (me, "%s: memory allocation failure\n", name);
 770                         goto out;
 771                 }
 772 
 773                 /* Assign gethash routine */
 774                 if (gethash == NULL) {
 775                         if (_NSC_DB_CES_KEY(nscdb))
 776                                 nscdb->gethash = nsc_db_ces_key_gethash;
 777                         else if (_NSC_DB_CIS_KEY(nscdb))
 778                                 nscdb->gethash = nsc_db_cis_key_gethash;
 779                         else if (_NSC_DB_INT_KEY(nscdb))
 780                                 nscdb->gethash = nsc_db_int_key_gethash;
 781                         else
 782                                 assert(0);
 783                 } else {
 784                         nscdb->gethash = gethash;
 785                 }
 786         }
 787 
 788         (void) mutex_init(&nscdb->db_mutex, USYNC_THREAD, NULL);
 789         return (nscdb);
 790 
 791 out:
 792         if (nscdb->htable)
 793                 free(nscdb->htable);
 794         if (nscdb)
 795                 free(nscdb);
 796         return (NULL);
 797 }
 798 
 799 
 800 /*
 801  * verify
 802  */
 803 /* ARGSUSED */
 804 nscd_rc_t
 805 _nscd_cfg_cache_verify(
 806         void                            *data,
 807         struct nscd_cfg_param_desc      *pdesc,
 808         nscd_cfg_id_t                   *nswdb,
 809         nscd_cfg_flag_t                 dflag,
 810         nscd_cfg_error_t                **errorp,
 811         void                            **cookie)
 812 {
 813 
 814         return (NSCD_SUCCESS);
 815 }
 816 
 817 /*
 818  * notify
 819  */
 820 /* ARGSUSED */
 821 nscd_rc_t
 822 _nscd_cfg_cache_notify(
 823         void                            *data,
 824         struct nscd_cfg_param_desc      *pdesc,
 825         nscd_cfg_id_t                   *nswdb,
 826         nscd_cfg_flag_t                 dflag,
 827         nscd_cfg_error_t                **errorp,
 828         void                            **cookie)
 829 {
 830         nsc_ctx_t       *ctx;
 831         void            *dp;
 832         int             i;
 833 
 834         /* group data */
 835         if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) {
 836                 if (_nscd_cfg_flag_is_set(pdesc->pflag,
 837                     NSCD_CFG_PFLAG_GLOBAL)) {
 838                         /* global config */
 839                         global_cfg = *(nscd_cfg_global_cache_t *)data;
 840                 } else if (_nscd_cfg_flag_is_set(dflag,
 841                     NSCD_CFG_DFLAG_SET_ALL_DB)) {
 842                         /* non-global config for all dbs */
 843                         for (i = 0; i < CACHE_CTX_COUNT; i++) {
 844                                 ctx = cache_ctx_p[i];
 845                                 if (ctx == NULL)
 846                                         return (NSCD_CTX_NOT_FOUND);
 847                                 (void) rw_wrlock(&ctx->cfg_rwlp);
 848                                 ctx->cfg = *(nscd_cfg_cache_t *)data;
 849                                 ctx->cfg_mtime = time(NULL);
 850                                 (void) rw_unlock(&ctx->cfg_rwlp);
 851                         }
 852                 } else {
 853                         /* non-global config for a specific db */
 854 
 855                         /* ignore non-caching databases */
 856                         if (get_cache_ctx(nswdb->name, &ctx) != NSCD_SUCCESS)
 857                                 return (NSCD_SUCCESS);
 858                         (void) rw_wrlock(&ctx->cfg_rwlp);
 859                         ctx->cfg = *(nscd_cfg_cache_t *)data;
 860                         ctx->cfg_mtime = time(NULL);
 861                         (void) rw_unlock(&ctx->cfg_rwlp);
 862                 }
 863                 return (NSCD_SUCCESS);
 864         }
 865 
 866         /* individual data */
 867         if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL)) {
 868                 /* global config */
 869                 dp = (char *)&global_cfg + pdesc->p_offset;
 870                 (void) memcpy(dp, data, pdesc->p_size);
 871         } else if (_nscd_cfg_flag_is_set(dflag,
 872             NSCD_CFG_DFLAG_SET_ALL_DB)) {
 873                 /* non-global config for all dbs */
 874                 for (i = 0; i < CACHE_CTX_COUNT; i++) {
 875                         ctx = cache_ctx_p[i];
 876                         if (ctx == NULL)
 877                                 return (NSCD_CTX_NOT_FOUND);
 878                         dp = (char *)&ctx->cfg + pdesc->p_offset;
 879                         (void) rw_wrlock(&ctx->cfg_rwlp);
 880                         (void) memcpy(dp, data, pdesc->p_size);
 881                         ctx->cfg_mtime = time(NULL);
 882                         (void) rw_unlock(&ctx->cfg_rwlp);
 883                 }
 884         } else {
 885                 /* non-global config for a specific db */
 886 
 887                 /* ignore non-caching databases */
 888                 if (get_cache_ctx(nswdb->name, &ctx) != NSCD_SUCCESS)
 889                         return (NSCD_SUCCESS);
 890                 dp = (char *)&ctx->cfg + pdesc->p_offset;
 891                 (void) rw_wrlock(&ctx->cfg_rwlp);
 892                 (void) memcpy(dp, data, pdesc->p_size);
 893                 ctx->cfg_mtime = time(NULL);
 894                 (void) rw_unlock(&ctx->cfg_rwlp);
 895         }
 896         return (NSCD_SUCCESS);
 897 }
 898 
 899 
 900 /*
 901  * get stat
 902  */
 903 /* ARGSUSED */
 904 nscd_rc_t
 905 _nscd_cfg_cache_get_stat(
 906         void                            **stat,
 907         struct nscd_cfg_stat_desc       *sdesc,
 908         nscd_cfg_id_t                   *nswdb,
 909         nscd_cfg_flag_t                 *dflag,
 910         void                            (**free_stat)(void *stat),
 911         nscd_cfg_error_t                **errorp)
 912 {
 913         nscd_cfg_stat_cache_t   *statsp, stats;
 914         nsc_ctx_t               *ctx;
 915         int                     i;
 916         nscd_rc_t               rc;
 917 
 918         statsp = calloc(1, sizeof (*statsp));
 919         if (statsp == NULL)
 920                 return (NSCD_NO_MEMORY);
 921 
 922         if (_nscd_cfg_flag_is_set(sdesc->sflag, NSCD_CFG_SFLAG_GLOBAL)) {
 923                 for (i = 0; i < CACHE_CTX_COUNT; i++) {
 924                         if (cache_ctx_p[i] == NULL)
 925                                 stats = null_stats;
 926                         else {
 927                                 (void) mutex_lock(&cache_ctx_p[i]->stats_mutex);
 928                                 stats = cache_ctx_p[i]->stats;
 929                                 (void) mutex_unlock(
 930                                     &cache_ctx_p[i]->stats_mutex);
 931                         }
 932                         statsp->pos_hits += stats.pos_hits;
 933                         statsp->neg_hits += stats.neg_hits;
 934                         statsp->pos_misses += stats.pos_misses;
 935                         statsp->neg_misses += stats.neg_misses;
 936                         statsp->entries += stats.entries;
 937                         statsp->drop_count += stats.drop_count;
 938                         statsp->wait_count += stats.wait_count;
 939                         statsp->invalidate_count +=
 940                             stats.invalidate_count;
 941                 }
 942         } else {
 943                 if ((rc = get_cache_ctx(nswdb->name, &ctx)) != NSCD_SUCCESS) {
 944                         free(statsp);
 945                         return (rc);
 946                 }
 947                 (void) mutex_lock(&ctx->stats_mutex);
 948                 *statsp = ctx->stats;
 949                 (void) mutex_unlock(&ctx->stats_mutex);
 950         }
 951 
 952         _NSC_GET_HITRATE(statsp);
 953         *stat = statsp;
 954         return (NSCD_SUCCESS);
 955 }
 956 
 957 /*
 958  * This function should only be called when nscd is
 959  * not a daemon.
 960  */
 961 void
 962 nsc_info(nsc_ctx_t *ctx, char *dbname, nscd_cfg_cache_t cfg[],
 963     nscd_cfg_stat_cache_t stats[])
 964 {
 965         int             i;
 966         char            *me = "nsc_info";
 967         nsc_ctx_t       *ctx1;
 968         nsc_ctx_t       ctx2;
 969         nscd_rc_t       rc;
 970 
 971         if (ctx) {
 972                 ctx_info(ctx);
 973                 return;
 974         }
 975 
 976         if (dbname) {
 977                 rc = get_cache_ctx(dbname, &ctx1);
 978                 if (rc == NSCD_INVALID_ARGUMENT) {
 979                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
 980                         (me, "%s: no cache context found\n", dbname);
 981                         return;
 982                 } else if (rc == NSCD_NO_MEMORY) {
 983                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
 984         (me, "%s: unable to create cache context - no memory\n",
 985             dbname);
 986                         return;
 987                 }
 988                 ctx_info(ctx1);
 989                 return;
 990         }
 991 
 992         if (cfg == NULL || stats == NULL)
 993                 return;
 994 
 995         for (i = 0; i < CACHE_CTX_COUNT; i++) {
 996 
 997                 ctx2.dbname = cache_name[i];
 998                 ctx2.cfg = cfg[i];
 999                 ctx2.stats = stats[i];
1000                 ctx_info_nolock(&ctx2);
1001         }
1002 }
1003 
1004 static void
1005 ctx_info_nolock(nsc_ctx_t *ctx) {
1006         nscd_cfg_cache_t        cfg;
1007         nscd_cfg_stat_cache_t   stats;
1008 
1009         cfg = ctx->cfg;
1010         (void) fprintf(stdout, gettext("\n\nCACHE: %s\n"), ctx->dbname);
1011         (void) print_cfg(&cfg);
1012 
1013         if (cfg.enable == nscd_false)
1014                 return;
1015 
1016         stats = ctx->stats;
1017         (void) print_stats(&stats);
1018 }
1019 
1020 static void
1021 ctx_info(nsc_ctx_t *ctx) {
1022         nscd_cfg_cache_t        cfg;
1023         nscd_cfg_stat_cache_t   stats;
1024 
1025         (void) rw_rdlock(&ctx->cfg_rwlp);
1026         cfg = ctx->cfg;
1027         (void) rw_unlock(&ctx->cfg_rwlp);
1028         (void) fprintf(stdout, gettext("\n\nCACHE: %s\n"), ctx->dbname);
1029         (void) print_cfg(&cfg);
1030 
1031         if (cfg.enable == nscd_false)
1032                 return;
1033 
1034         (void) mutex_lock(&ctx->stats_mutex);
1035         stats = ctx->stats;
1036         (void) mutex_unlock(&ctx->stats_mutex);
1037         (void) print_stats(&stats);
1038 }
1039 
1040 #ifdef  NSCD_DEBUG
1041 /*
1042  * This function should only be called when nscd is
1043  * not a daemon.
1044  */
1045 int
1046 nsc_dump(char *dbname, int dbop)
1047 {
1048         nsc_ctx_t       *ctx;
1049         nsc_db_t        *nscdb;
1050         nscd_bool_t     enabled;
1051         time_t          now;
1052         char            *me = "nsc_dump";
1053         int             i;
1054 
1055         if ((i = get_cache_idx(dbname)) == -1) {
1056                 (void) fprintf(stdout, gettext("invalid cache name\n"));
1057 
1058                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
1059                 (me, "%s: invalid cache name\n", dbname);
1060                 return (NSCD_CACHE_INVALID_CACHE_NAME);
1061         }
1062 
1063         if ((ctx = cache_ctx_p[i]) == NULL)  {
1064                 (void) fprintf(stdout, gettext("no cache context\n"));
1065 
1066                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
1067                 (me, "%s: no cache context\n", dbname);
1068                 return (NSCD_CACHE_NO_CACHE_CTX);
1069         }
1070 
1071         now = time(NULL);
1072         (void) rw_rdlock(&ctx->cfg_rwlp);
1073         enabled = ctx->cfg.enable;
1074         (void) rw_unlock(&ctx->cfg_rwlp);
1075 
1076         if (enabled == nscd_false)
1077                 return (NSCD_CACHE_DISABLED);
1078 
1079         nscdb = nsc_get_db(ctx, dbop);
1080         if (nscdb == NULL) {
1081                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
1082                 (me, "%s:%d: no cache found\n", dbname, dbop);
1083                 return (NSCD_CACHE_NO_CACHE_FOUND);
1084         }
1085 
1086         (void) mutex_lock(&nscdb->db_mutex);
1087         (void) queue_dump(nscdb, now);
1088         (void) hash_dump(nscdb, now);
1089         (void) avl_dump(nscdb, now);
1090         (void) mutex_unlock(&nscdb->db_mutex);
1091         return (NSCD_SUCCESS);
1092 }
1093 #endif  /* NSCD_DEBUG */
1094 
1095 /*
1096  * These macros are for exclusive use of nsc_lookup
1097  */
1098 #define NSC_LOOKUP_LOG(loglevel, fmt) \
1099         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_##loglevel) \
1100                 (me, fmt, whoami);
1101 
1102 static int
1103 nsc_lookup_no_cache(nsc_lookup_args_t *largs, const char *str)
1104 {
1105         char *me = "nsc_lookup_no_cache";
1106         nss_status_t status;
1107 
1108         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1109                 (me, "%s: name service lookup (bypassing cache)\n", str);
1110         nss_psearch(largs->buffer, largs->bufsize);
1111         status = NSCD_GET_STATUS(largs->buffer);
1112         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1113                 (me, "%s: name service lookup status = %d\n", str, status);
1114         if (status == NSS_SUCCESS) {
1115                 return (SUCCESS);
1116         } else if (status == NSS_NOTFOUND) {
1117                 return (NOTFOUND);
1118         } else {
1119                 return (SERVERERROR);
1120         }
1121 }
1122 
1123 /*
1124  * This function starts the revalidation and reaper threads
1125  * for a cache
1126  */
1127 static void
1128 start_threads(nsc_ctx_t *ctx)
1129 {
1130         int     errnum;
1131         char    *me = "start_threads";
1132 
1133         /*
1134          *  kick off the revalidate thread (if necessary)
1135          */
1136         if (ctx->revalidate_on != nscd_true) {
1137                 if (thr_create(NULL, NULL, (void *(*)(void *))revalidate,
1138                     ctx, 0, NULL) != 0) {
1139                         errnum = errno;
1140                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
1141                         (me, "thr_create (revalidate thread for %s): %s\n",
1142                             ctx->dbname, strerror(errnum));
1143                         exit(1);
1144                 }
1145                 ctx->revalidate_on = nscd_true;
1146         }
1147 
1148         /*
1149          *  kick off the reaper thread (if necessary)
1150          */
1151         if (ctx->reaper_on != nscd_true) {
1152                 if (thr_create(NULL, NULL, (void *(*)(void *))reaper,
1153                     ctx, 0, NULL) != 0) {
1154                         errnum = errno;
1155                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
1156                         (me, "thr_create (reaper thread for %s): %s\n",
1157                             ctx->dbname, strerror(errnum));
1158                         exit(1);
1159                 }
1160                 ctx->reaper_on = nscd_true;
1161         }
1162 }
1163 
1164 /*
1165  * Examine the packed buffer, see if the front-end parameters
1166  * indicate that the caller specified nsswitch config should be
1167  * used for the lookup. Return 1 if yes, otherwise 0.
1168  */
1169 static int
1170 nsw_config_in_phdr(void *buf)
1171 {
1172         nss_pheader_t           *pbuf = (nss_pheader_t *)buf;
1173         nssuint_t               off;
1174         nss_dbd_t               *pdbd;
1175         char                    *me = "nsw_config_in_phdr";
1176 
1177         off = pbuf->dbd_off;
1178         if (off == 0)
1179                 return (0);
1180         pdbd = (nss_dbd_t *)((void *)((char *)pbuf + off));
1181         if (pdbd->o_default_config == 0)
1182                 return (0);
1183 
1184         if ((enum nss_dbp_flags)pdbd->flags & NSS_USE_DEFAULT_CONFIG) {
1185                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1186                 (me, "use caller specified nsswitch config\n");
1187                 return (1);
1188         } else
1189                 return (0);
1190 }
1191 
1192 static nss_status_t
1193 copy_result(void *rbuf, void *cbuf)
1194 {
1195         nss_pheader_t   *rphdr = (nss_pheader_t *)rbuf;
1196         nss_pheader_t   *cphdr = (nss_pheader_t *)cbuf;
1197         char            *me = "copy_result";
1198 
1199         /* return NSS_ERROR if not enough room to copy result */
1200         if (cphdr->data_len + 1 > rphdr->data_len) {
1201                 NSCD_SET_STATUS(rphdr, NSS_ERROR, ERANGE);
1202                 return (NSS_ERROR);
1203         } else {
1204                 char    *dst;
1205 
1206                 if (cphdr->data_len == 0)
1207                         return (NSS_SUCCESS);
1208 
1209                 dst = (char *)rphdr + rphdr->data_off;
1210                 (void) memcpy(dst, (char *)cphdr + cphdr->data_off,
1211                     cphdr->data_len);
1212                 rphdr->data_len = cphdr->data_len;
1213                 /* some frontend code expects a terminating NULL char */
1214                 *(dst + rphdr->data_len) = '\0';
1215 
1216                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1217                 (me, "cache data (len = %lld): >>%s<<\n",
1218                     cphdr->data_len, (char *)cphdr + cphdr->data_off);
1219 
1220                 return (NSS_SUCCESS);
1221         }
1222 }
1223 
1224 static int
1225 get_dns_ttl(void *pbuf, char *dbname)
1226 {
1227         nss_pheader_t   *phdr = (nss_pheader_t *)pbuf;
1228         int             ttl;
1229         char            *me = "get_dns_ttl";
1230 
1231         /* if returned, dns ttl is stored in the extended data area */
1232         if (phdr->ext_off == 0)
1233                 return (-1);
1234 
1235         if (strcmp(dbname, NSS_DBNAM_HOSTS) != 0 &&
1236             strcmp(dbname, NSS_DBNAM_IPNODES) != 0)
1237                 return (-1);
1238 
1239         ttl = *(nssuint_t *)((void *)((char *)pbuf + phdr->ext_off));
1240 
1241         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1242         (me, "dns ttl is %d seconds\n", ttl);
1243 
1244         return (ttl);
1245 }
1246 
1247 static int
1248 check_config(nsc_lookup_args_t *largs, nscd_cfg_cache_t *cfgp,
1249     char *whoami, int flag)
1250 {
1251         nsc_db_t        *nscdb;
1252         nsc_ctx_t       *ctx;
1253         char            *me = "check_config";
1254 
1255         ctx = largs->ctx;
1256         nscdb = largs->nscdb;
1257 
1258         /* see if the cached config needs update */
1259         if (nscdb->cfg_mtime != ctx->cfg_mtime) {
1260                 (void) rw_rdlock(&ctx->cfg_rwlp);
1261                 nscdb->cfg = ctx->cfg;
1262                 nscdb->cfg_mtime = ctx->cfg_mtime;
1263                 (void) rw_unlock(&ctx->cfg_rwlp);
1264                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1265                 (me, "config for context %s, database %s updated\n",
1266                     ctx->dbname, nscdb->name);
1267         }
1268         *cfgp = nscdb->cfg;
1269 
1270         if (cfgp->enable == nscd_false) {
1271                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1272                         (me, "%s: cache disabled\n", ctx->dbname);
1273 
1274                 if (UPDATEBIT & flag)
1275                         return (NOTFOUND);
1276                 else
1277                         return (nsc_lookup_no_cache(largs, whoami));
1278         }
1279 
1280         /*
1281          * if caller requests lookup using its
1282          * own nsswitch config, bypass cache
1283          */
1284         if (nsw_config_in_phdr(largs->buffer))
1285                 return (nsc_lookup_no_cache(largs, whoami));
1286 
1287         /* no need of cache if we are dealing with 0 ttls */
1288         if (cfgp->pos_ttl <= 0 && cfgp->neg_ttl <= 0) {
1289                 if (flag & UPDATEBIT)
1290                         return (NOTFOUND);
1291                 else if (cfgp->avoid_ns == nscd_true)
1292                         return (SERVERERROR);
1293                 return (nsc_lookup_no_cache(largs, whoami));
1294         }
1295 
1296         return (CONTINUE);
1297 }
1298 
1299 /*
1300  * Invalidate cache if database file has been modified.
1301  * See check_files config param for details.
1302  */
1303 static void
1304 check_db_file(nsc_ctx_t *ctx, nscd_cfg_cache_t cfg,
1305     char *whoami, time_t now)
1306 {
1307         struct stat     buf;
1308         nscd_bool_t     file_modified = nscd_false;
1309         char            *me = "check_db_file";
1310 
1311         if (cfg.check_interval != 0 &&
1312             (now - ctx->file_chktime) < cfg.check_interval)
1313                 return;
1314 
1315         ctx->file_chktime = now;
1316         if (stat(ctx->file_name, &buf) == 0) {
1317                 if (ctx->file_mtime == 0) {
1318                         (void) mutex_lock(&ctx->file_mutex);
1319                         if (ctx->file_mtime == 0) {
1320                                 ctx->file_mtime = buf.st_mtime;
1321                                 ctx->file_size = buf.st_size;
1322                                 ctx->file_ino = buf.st_ino;
1323                         }
1324                         (void) mutex_unlock(&ctx->file_mutex);
1325                 } else if (ctx->file_mtime < buf.st_mtime ||
1326                     ctx->file_size != buf.st_size ||
1327                     ctx->file_ino != buf.st_ino) {
1328                         (void) mutex_lock(&ctx->file_mutex);
1329                         if (ctx->file_mtime < buf.st_mtime ||
1330                             ctx->file_size != buf.st_size ||
1331                             ctx->file_ino != buf.st_ino) {
1332                                 file_modified = nscd_true;
1333                                 ctx->file_mtime = buf.st_mtime;
1334                                 ctx->file_size = buf.st_size;
1335                                 ctx->file_ino = buf.st_ino;
1336                         }
1337                         (void) mutex_unlock(&ctx->file_mutex);
1338                 }
1339         }
1340 
1341         if (file_modified == nscd_true) {
1342                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1343                 (me, "%s: file %s has been modified - invalidating cache\n",
1344                     whoami, ctx->file_name);
1345                 ctx_invalidate(ctx);
1346         }
1347 }
1348 
1349 static int
1350 lookup_int(nsc_lookup_args_t *largs, int flag)
1351 {
1352         nsc_ctx_t               *ctx;
1353         nsc_db_t                *nscdb;
1354         nscd_cfg_cache_t        cfg;
1355         nsc_entry_t             *this_entry;
1356         nsc_entry_stat_t        *this_stats;
1357         nsc_action_t            next_action;
1358         nss_status_t            status;
1359         nscd_bool_t             delete;
1360         nscd_rc_t               rc;
1361         char                    *dbname;
1362         int                     dbop, errnum;
1363         int                     cfg_rc;
1364         nss_XbyY_args_t         args;
1365         char                    whoami[128];
1366         time_t                  now = time(NULL); /* current time */
1367         char                    *me = "lookup_int";
1368 
1369         /* extract dbop, dbname, key and cred */
1370         status = nss_packed_getkey(largs->buffer, largs->bufsize, &dbname,
1371             &dbop, &args);
1372         if (status != NSS_SUCCESS) {
1373                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
1374                         (me, "nss_packed_getkey failure (%d)\n", status);
1375                 return (SERVERERROR);
1376         }
1377 
1378         /* get the cache context */
1379         if (largs->ctx == NULL) {
1380                 if (get_cache_ctx(dbname, &largs->ctx) != NSCD_SUCCESS) {
1381                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
1382                                 (me, "%s: no cache context found\n", dbname);
1383 
1384                         if (UPDATEBIT & flag)
1385                                 return (NOTFOUND);
1386                         else
1387                                 return (nsc_lookup_no_cache(largs, dbname));
1388                 }
1389         }
1390         ctx = largs->ctx;
1391 
1392         if (largs->nscdb == NULL) {
1393                 if ((largs->nscdb = nsc_get_db(ctx, dbop)) == NULL) {
1394                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
1395                                 (me, "%s:%d: no cache found\n",
1396                                     dbname, dbop);
1397 
1398                         if (UPDATEBIT & flag)
1399                                 return (NOTFOUND);
1400                         else
1401                                 return (nsc_lookup_no_cache(largs, dbname));
1402                 }
1403         }
1404 
1405         nscdb = largs->nscdb;
1406 
1407         _NSCD_LOG_IF(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ALL) {
1408                 (void) nscdb->getlogstr(nscdb->name, whoami,
1409                     sizeof (whoami), &args);
1410         }
1411 
1412         if (UPDATEBIT & flag) {
1413                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1414                         (me, "%s: refresh start\n", whoami);
1415         } else {
1416                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1417                         (me, "%s: lookup start\n", whoami);
1418         }
1419 
1420         cfg_rc = check_config(largs, &cfg, whoami, flag);
1421         if (cfg_rc != CONTINUE)
1422                 return (cfg_rc);
1423 
1424         /*
1425          * Invalidate cache if file has been modified.
1426          */
1427         if (cfg.check_files == nscd_true)
1428                 check_db_file(ctx, cfg, whoami, now);
1429 
1430         (void) mutex_lock(&nscdb->db_mutex);
1431 
1432         /* Lookup the cache table */
1433         for (;;) {
1434                 delete = nscd_false;
1435                 rc = lookup_cache(largs, &cfg, &args, whoami, &this_entry);
1436                 if (rc != NSCD_SUCCESS) {
1437                         (void) mutex_unlock(&nscdb->db_mutex);
1438 
1439                         /* Either no entry and avoid name service */
1440                         if (rc == NSCD_DB_ENTRY_NOT_FOUND ||
1441                             rc == NSCD_INVALID_ARGUMENT)
1442                                 return (NOTFOUND);
1443 
1444                         /* OR memory error */
1445                         return (SERVERERROR);
1446                 }
1447 
1448                 /* get the stats from the entry */
1449                 this_stats = &this_entry->stats;
1450 
1451                 /*
1452                  * What should we do next ?
1453                  */
1454                 switch (this_stats->status) {
1455                 case ST_NEW_ENTRY:
1456                         delete = nscd_true;
1457                         next_action = _NSC_NSLOOKUP;
1458                         break;
1459                 case ST_UPDATE_PENDING:
1460                         if (flag & UPDATEBIT) {
1461                                 (void) mutex_unlock(&nscdb->db_mutex);
1462                                 return (NOTFOUND);
1463                         } else if (this_stats->timestamp < now)
1464                                 next_action = _NSC_WAIT;
1465                         else
1466                                 next_action = _NSC_USECACHED;
1467                         break;
1468                 case ST_LOOKUP_PENDING:
1469                         if (flag & UPDATEBIT) {
1470                                 (void) mutex_unlock(&nscdb->db_mutex);
1471                                 return (NOTFOUND);
1472                         }
1473                         next_action = _NSC_WAIT;
1474                         break;
1475                 case ST_DISCARD:
1476                         if (cfg.avoid_ns == nscd_true) {
1477                                 (void) mutex_unlock(&nscdb->db_mutex);
1478                                 return (NOTFOUND);
1479                         }
1480                         /* otherwise reuse the entry */
1481                         (void) memset(this_stats, 0, sizeof (*this_stats));
1482                         next_action = _NSC_NSLOOKUP;
1483                         break;
1484                 default:
1485                         if (cfg.avoid_ns == nscd_true)
1486                                 next_action = _NSC_USECACHED;
1487                         else if ((flag & UPDATEBIT) ||
1488                             (this_stats->timestamp < now)) {
1489                                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1490                         (me, "%s: cached entry needs to be updated\n",
1491                             whoami);
1492                                 next_action = _NSC_NSLOOKUP;
1493                         } else
1494                                 next_action = _NSC_USECACHED;
1495                         break;
1496                 }
1497 
1498                 if (next_action == _NSC_WAIT) {
1499                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1500                         (me, "%s: need to wait\n", whoami);
1501 
1502                         /* do we have clearance ? */
1503                         if (_nscd_get_clearance(&ctx->throttle_sema) != 0) {
1504                                 /* nope. quit */
1505                                 (void) mutex_lock(&ctx->stats_mutex);
1506                                 ctx->stats.drop_count++;
1507                                 (void) mutex_unlock(&ctx->stats_mutex);
1508                                 _NSCD_LOG(NSCD_LOG_CACHE,
1509                                     NSCD_LOG_LEVEL_DEBUG_6)
1510                                 (me, "%s: throttling load\n", whoami);
1511                                 (void) mutex_unlock(&nscdb->db_mutex);
1512                                 NSC_LOOKUP_LOG(WARNING,
1513                                     "%s: no clearance to wait\n");
1514                                 return (NOSERVER);
1515                         }
1516                         /* yes can wait */
1517                         (void) nscd_wait(ctx, nscdb, this_entry);
1518                         (void) _nscd_release_clearance(&ctx->throttle_sema);
1519                         continue;
1520                 }
1521 
1522                 break;
1523         }
1524 
1525 
1526         if (!(UPDATEBIT & flag))
1527                 this_stats->hits++;          /* update hit count */
1528 
1529         if (next_action == _NSC_NSLOOKUP) {
1530 
1531                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1532                 (me, "%s: name service lookup required\n", whoami);
1533 
1534                 if (_nscd_get_clearance(&ctx->throttle_sema) != 0) {
1535                         if (delete == nscd_true)
1536                                 delete_entry(nscdb, ctx, this_entry);
1537                         else
1538                                 this_stats->status = ST_DISCARD;
1539                         (void) mutex_lock(&ctx->stats_mutex);
1540                         ctx->stats.drop_count++;
1541                         (void) mutex_unlock(&ctx->stats_mutex);
1542                         (void) mutex_unlock(&nscdb->db_mutex);
1543                         NSC_LOOKUP_LOG(WARNING,
1544                             "%s: no clearance for lookup\n");
1545                         return (NOSERVER);
1546                 }
1547 
1548                 /* block any threads accessing this entry */
1549                 this_stats->status = (flag & UPDATEBIT) ?
1550                     ST_UPDATE_PENDING : ST_LOOKUP_PENDING;
1551 
1552                 /* release lock and do name service lookup */
1553                 (void) mutex_unlock(&nscdb->db_mutex);
1554                 nss_psearch(largs->buffer, largs->bufsize);
1555                 status = NSCD_GET_STATUS(largs->buffer);
1556                 (void) mutex_lock(&nscdb->db_mutex);
1557                 this_stats->status = 0;
1558                 (void) _nscd_release_clearance(&ctx->throttle_sema);
1559 
1560                 /* signal waiting threads */
1561                 (void) nscd_signal(ctx, nscdb, this_entry);
1562 
1563                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1564                 (me, "%s: name service lookup status = %d\n",
1565                     whoami, status);
1566 
1567                 if (status == NSS_SUCCESS) {
1568                         int ttl;
1569 
1570                         /*
1571                          * data found in name service
1572                          * update cache
1573                          */
1574                         status = dup_packed_buffer(largs, this_entry);
1575                         if (status != NSS_SUCCESS) {
1576                                 delete_entry(nscdb, ctx, this_entry);
1577                                 (void) mutex_unlock(&nscdb->db_mutex);
1578                                 NSC_LOOKUP_LOG(ERROR,
1579                                     "%s: failed to update cache\n");
1580                                 return (SERVERERROR);
1581                         }
1582 
1583                         /*
1584                          * store unpacked key in cache
1585                          */
1586                         status = nss_packed_getkey(this_entry->buffer,
1587                             this_entry->bufsize,
1588                             &dbname, &dbop, &args);
1589                         if (status != NSS_SUCCESS) {
1590                                 delete_entry(nscdb, ctx, this_entry);
1591                                 (void) mutex_unlock(&nscdb->db_mutex);
1592                                 NSC_LOOKUP_LOG(ERROR,
1593                                     "%s: failed to extract key\n");
1594                                 return (SERVERERROR);
1595                         }
1596                         this_entry->key = args.key; /* struct copy */
1597 
1598                         /* update +ve miss count */
1599                         if (!(UPDATEBIT & flag)) {
1600                                 (void) mutex_lock(&ctx->stats_mutex);
1601                                 ctx->stats.pos_misses++;
1602                                 (void) mutex_unlock(&ctx->stats_mutex);
1603                         }
1604 
1605                         /* update +ve ttl */
1606                         ttl = get_dns_ttl(largs->buffer, dbname);
1607                         /* honor the dns ttl less than postive ttl */
1608                         if (ttl < 0 || ttl > cfg.pos_ttl)
1609                                 ttl = cfg.pos_ttl;
1610                         this_stats->timestamp = time(NULL) + ttl;
1611 
1612                         /*
1613                          * start the revalidation and reaper threads
1614                          * if not already started
1615                          */
1616                         start_threads(ctx);
1617 
1618                         (void) mutex_unlock(&nscdb->db_mutex);
1619                         NSC_LOOKUP_LOG(DEBUG,
1620                             "%s: cache updated with positive entry\n");
1621                         return (SUCCESS);
1622                 } else if (status == NSS_NOTFOUND) {
1623                         /*
1624                          * data not found in name service
1625                          * update cache
1626                          */
1627                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG_6)
1628                         (me, "%s: name service lookup failed\n", whoami);
1629 
1630                         if (NSCD_GET_ERRNO(largs->buffer) == ERANGE) {
1631                                 delete_entry(nscdb, ctx, this_entry);
1632                                 (void) mutex_unlock(&nscdb->db_mutex);
1633                                 NSC_LOOKUP_LOG(DEBUG,
1634                                     "%s: ERANGE, cache not updated "
1635                                     "with negative entry\n");
1636                                 return (NOTFOUND);
1637                         }
1638 
1639                         status = dup_packed_buffer(largs, this_entry);
1640                         if (status != NSS_SUCCESS) {
1641                                 delete_entry(nscdb, ctx, this_entry);
1642                                 (void) mutex_unlock(&nscdb->db_mutex);
1643                                 NSC_LOOKUP_LOG(ERROR,
1644                                     "%s: failed to update cache\n");
1645                                 return (SERVERERROR);
1646                         }
1647 
1648                         /* store unpacked key in cache */
1649                         status = nss_packed_getkey(this_entry->buffer,
1650                             this_entry->bufsize,
1651                             &dbname, &dbop, &args);
1652                         if (status != NSS_SUCCESS) {
1653                                 delete_entry(nscdb, ctx, this_entry);
1654                                 (void) mutex_unlock(&nscdb->db_mutex);
1655                                 NSC_LOOKUP_LOG(ERROR,
1656                                     "%s: failed to extract key\n");
1657                                 return (SERVERERROR);
1658                         }
1659                         this_entry->key = args.key; /* struct copy */
1660 
1661                         /* update -ve ttl */
1662                         this_stats->timestamp = time(NULL) + cfg.neg_ttl;
1663 
1664                         /* update -ve miss count */
1665                         if (!(UPDATEBIT & flag)) {
1666                                 (void) mutex_lock(&ctx->stats_mutex);
1667                                 ctx->stats.neg_misses++;
1668                                 (void) mutex_unlock(&ctx->stats_mutex);
1669                         }
1670 
1671                         /*
1672                          * start the revalidation and reaper threads
1673                          * if not already started
1674                          */
1675                         start_threads(ctx);
1676 
1677                         (void) mutex_unlock(&nscdb->db_mutex);
1678                         NSC_LOOKUP_LOG(DEBUG,
1679                             "%s: cache updated with negative entry\n");
1680                         return (NOTFOUND);
1681                 } else {
1682                         /*
1683                          * name service lookup failed
1684                          */
1685                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG_6)
1686                         (me, "%s: name service lookup failed\n", whoami);
1687 
1688                         errnum = NSCD_GET_ERRNO(largs->buffer);
1689                         if (delete == nscd_true)
1690                                 delete_entry(nscdb, ctx, this_entry);
1691                         else
1692                                 this_stats->status = ST_DISCARD;
1693 
1694                         (void) mutex_unlock(&nscdb->db_mutex);
1695                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
1696                         (me, "%s: name service lookup failed "
1697                             "(status=%d, errno=%d)\n",
1698                             whoami, status, errnum);
1699 
1700                         return (SERVERERROR);
1701                 }
1702         } else if (next_action == _NSC_USECACHED) {
1703                 /*
1704                  * found entry in cache
1705                  */
1706                 if (UPDATEBIT & flag) {
1707                         (void) mutex_unlock(&nscdb->db_mutex);
1708                         NSC_LOOKUP_LOG(DEBUG, "%s: no need to update\n");
1709                         return (SUCCESS);
1710                 }
1711 
1712                 if (NSCD_GET_STATUS((nss_pheader_t *)this_entry->buffer) ==
1713                     NSS_SUCCESS) {
1714                         /* positive hit */
1715                         (void) mutex_lock(&ctx->stats_mutex);
1716                         ctx->stats.pos_hits++;
1717                         (void) mutex_unlock(&ctx->stats_mutex);
1718 
1719                         /* update response buffer */
1720                         if (copy_result(largs->buffer,
1721                             this_entry->buffer) != NSS_SUCCESS) {
1722                                 (void) mutex_unlock(&nscdb->db_mutex);
1723                                 NSC_LOOKUP_LOG(ERROR,
1724                                     "%s: response buffer insufficient\n");
1725                                 return (SERVERERROR);
1726                         }
1727 
1728                         (void) mutex_unlock(&nscdb->db_mutex);
1729                         NSC_LOOKUP_LOG(DEBUG,
1730                             "%s: positive entry in cache\n");
1731                         return (SUCCESS);
1732                 } else {
1733                         /* negative hit */
1734                         (void) mutex_lock(&ctx->stats_mutex);
1735                         ctx->stats.neg_hits++;
1736                         (void) mutex_unlock(&ctx->stats_mutex);
1737 
1738                         NSCD_SET_STATUS((nss_pheader_t *)largs->buffer,
1739                             NSCD_GET_STATUS(this_entry->buffer),
1740                             NSCD_GET_ERRNO(this_entry->buffer));
1741                         NSCD_SET_HERRNO((nss_pheader_t *)largs->buffer,
1742                             NSCD_GET_HERRNO(this_entry->buffer));
1743 
1744                         (void) mutex_unlock(&nscdb->db_mutex);
1745                         NSC_LOOKUP_LOG(DEBUG,
1746                             "%s: negative entry in cache\n");
1747                         return (NOTFOUND);
1748                 }
1749         }
1750 
1751         (void) mutex_unlock(&nscdb->db_mutex);
1752         NSC_LOOKUP_LOG(ERROR, "%s: cache backend failure\n");
1753         return (SERVERERROR);
1754 }
1755 
1756 /*
1757  * NSCD cache backend lookup function
1758  */
1759 /*ARGSUSED*/
1760 void
1761 nsc_lookup(nsc_lookup_args_t *largs, int flag) {
1762 
1763         nss_pheader_t   *phdr = (nss_pheader_t *)largs->buffer;
1764         int             rc;
1765 
1766         rc = lookup_int(largs, 0);
1767 
1768         if (NSCD_GET_STATUS(phdr) == NSS_TRYLOCAL)
1769                 return;
1770 
1771         switch (rc) {
1772 
1773         case SUCCESS:
1774                 NSCD_SET_STATUS(phdr, NSS_SUCCESS, 0);
1775                 break;
1776 
1777         case NOTFOUND:
1778                 NSCD_SET_STATUS(phdr, NSS_NOTFOUND, -1);
1779                 break;
1780 
1781         case SERVERERROR:
1782                 /*
1783                  * status and errno should have been set in the phdr,
1784                  * if not, set status to NSS_ERROR
1785                  */
1786                 if (NSCD_STATUS_IS_OK(phdr)) {
1787                         NSCD_SET_STATUS(phdr, NSS_ERROR, 0);
1788                 }
1789                 break;
1790 
1791         case NOSERVER:
1792                 NSCD_SET_STATUS(phdr, NSS_TRYLOCAL, -1);
1793                 break;
1794         }
1795 }
1796 
1797 
1798 static nsc_ctx_t *
1799 init_cache_ctx(int i) {
1800         nsc_ctx_t       *ctx;
1801 
1802         ctx = calloc(1, sizeof (nsc_ctx_t));
1803         if (ctx == NULL)
1804                 return (NULL);
1805 
1806         /* init locks and semaphores */
1807         (void) mutex_init(&ctx->file_mutex, USYNC_THREAD, NULL);
1808         (void) rwlock_init(&ctx->cfg_rwlp, USYNC_THREAD, NULL);
1809         (void) mutex_init(&ctx->stats_mutex, USYNC_THREAD, NULL);
1810         (void) _nscd_init_cache_sema(&ctx->throttle_sema, cache_name[i]);
1811         cache_init_ctx[i](ctx);
1812         cache_ctx_p[i] = ctx;
1813 
1814         return (ctx);
1815 }
1816 
1817 
1818 static void
1819 revalidate(nsc_ctx_t *ctx)
1820 {
1821         (void) thr_setname(thr_self(), "revalidate");
1822 
1823         for (;;) {
1824                 int i, slp, interval, count;
1825 
1826                 (void) rw_rdlock(&ctx->cfg_rwlp);
1827                 slp = ctx->cfg.pos_ttl;
1828                 count = ctx->cfg.keephot;
1829                 (void) rw_unlock(&ctx->cfg_rwlp);
1830 
1831                 if (slp < 60)
1832                         slp = 60;
1833                 if (count != 0) {
1834                         interval = (slp/2)/count;
1835                         if (interval == 0)
1836                                 interval = 1;
1837                         (void) sleep(slp*2/3);
1838                         for (i = 0; i < ctx->db_count; i++) {
1839                                 getxy_keepalive(ctx, ctx->nsc_db[i],
1840                                     count, interval);
1841                         }
1842                 } else {
1843                         (void) sleep(slp);
1844                 }
1845         }
1846 }
1847 
1848 
1849 static void
1850 getxy_keepalive(nsc_ctx_t *ctx, nsc_db_t *nscdb, int keep, int interval)
1851 {
1852         nsc_keephot_t           *table;
1853         nsc_entry_t             *entry, *ptr;
1854         int                     i;
1855         nsc_lookup_args_t       *largs;
1856         nss_pheader_t           *phdr;
1857         int                     bufsiz;
1858         char                    *me = "getxy_keepalive";
1859 
1860         /* we won't be here if keep == 0 so need to check that */
1861 
1862         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1863         (me, "%s: keep alive\n", nscdb->name);
1864 
1865         if ((table = maken(keep)) == NULL) {
1866                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
1867                         (me, "memory allocation failure\n");
1868                 exit(1);
1869         }
1870 
1871         (void) mutex_lock(&nscdb->db_mutex);
1872         entry = nscdb->qtail;
1873         while (entry != NULL) {
1874                 /* leave pending calls alone */
1875                 if (!(entry->stats.status & ST_PENDING)) {
1876                         /* do_revalidate */
1877                         (void) insertn(table, entry->stats.hits, entry);
1878                 }
1879                 entry = entry->qnext;
1880         }
1881         for (i = 1; i <= keep; i++) {
1882                 if (table[i].ptr == NULL)
1883                         continue;
1884                 ptr = (nsc_entry_t *)table[i].ptr;
1885                 phdr = (nss_pheader_t *)ptr->buffer;
1886                 if (NSCD_GET_STATUS(phdr) == NSS_SUCCESS)
1887                         /*
1888                          * for positive cache, in addition to the packed
1889                          * header size, allocate twice the size of the
1890                          * existing result (in case the result grows
1891                          * larger) plus 2K (for the file/compat backend to
1892                          * process a possible large entry in the /etc files)
1893                          */
1894                         bufsiz = phdr->data_off + 2 * phdr->data_len + 2048;
1895                 else
1896                         /*
1897                          * for negative cache, allocate 8K buffer to
1898                          * hold result in case the next lookup may
1899                          * return something (in addition to the
1900                          * packed header size)
1901                          */
1902                         bufsiz = phdr->data_off + 8096;
1903                 table[i].ptr = malloc(bufsiz);
1904                 if (table[i].ptr == NULL) {
1905                         (void) mutex_unlock(&nscdb->db_mutex);
1906                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
1907                                 (me, "memory allocation failure\n");
1908                         exit(1);
1909                 }
1910                 (void) memcpy(table[i].ptr, ptr->buffer,  ptr->bufsize);
1911                 ((nss_pheader_t *)table[i].ptr)->pbufsiz = bufsiz;
1912                 table[i].num = bufsiz;
1913         }
1914         (void) mutex_unlock(&nscdb->db_mutex);
1915 
1916         /* launch update thread for each keep hot entry */
1917         for (i = keep; i > 0; i--) {
1918                 if (table[i].ptr == NULL)
1919                         continue; /* unused slot in table */
1920                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1921                 (me, "%s: launching update\n", nscdb->name);
1922                 largs = (nsc_lookup_args_t *)malloc(sizeof (*largs));
1923                 if (largs == NULL) {
1924                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
1925                                 (me, "memory allocation failure\n");
1926                         exit(1);
1927                 }
1928                 largs->buffer = table[i].ptr;
1929                 largs->bufsize = table[i].num;
1930                 largs->ctx = ctx;
1931                 largs->nscdb = nscdb;
1932                 if (launch_update(largs) < 0)
1933                         exit(1);
1934                 (void) sleep(interval);
1935         }
1936 
1937         /*
1938          * The update thread will handle freeing of buffer and largs.
1939          * Free the table here.
1940          */
1941         free(table);
1942 }
1943 
1944 
1945 static int
1946 launch_update(nsc_lookup_args_t *in)
1947 {
1948         char    *me = "launch_update";
1949         int     errnum;
1950 
1951         errnum = thr_create(NULL, NULL, (void *(*)(void*))do_update,
1952             in, 0|THR_DETACHED, NULL);
1953         if (errnum != 0) {
1954                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
1955                 (me, "%s: thread creation failure (%d)\n",
1956                     in->nscdb->name, errnum);
1957                 return (-1);
1958         }
1959         return (0);
1960 }
1961 
1962 
1963 static void
1964 do_update(nsc_lookup_args_t *in) {
1965         nss_pheader_t   *phdr = (nss_pheader_t *)in->buffer;
1966 
1967         (void) thr_setname(thr_self(), "do_update");
1968 
1969         /* update the length of the data buffer */
1970         phdr->data_len = phdr->pbufsiz - phdr->data_off;
1971 
1972         (void) lookup_int(in, UPDATEBIT);
1973         if (in->buffer)
1974                 free(in->buffer);
1975         free(in);
1976 }
1977 
1978 
1979 /*
1980  * Invalidate cache
1981  */
1982 void
1983 nsc_invalidate(nsc_ctx_t *ctx, char *dbname, nsc_ctx_t **ctxs)
1984 {
1985         int     i;
1986         char    *me = "nsc_invalidate";
1987 
1988         if (ctx) {
1989                 ctx_invalidate(ctx);
1990                 return;
1991         }
1992 
1993         if (dbname) {
1994                 if ((i = get_cache_idx(dbname)) == -1) {
1995                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
1996                         (me, "%s: invalid cache name\n", dbname);
1997                         return;
1998                 }
1999                 if ((ctx = cache_ctx_p[i]) == NULL)  {
2000                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
2001                         (me, "%s: no cache context found\n",
2002                             dbname);
2003                         return;
2004                 }
2005                 ctx_invalidate(ctx);
2006                 return;
2007         }
2008 
2009         if (ctxs == NULL)
2010                 ctxs =  cache_ctx_p;
2011 
2012         for (i = 0; i < CACHE_CTX_COUNT; i++) {
2013                 if (ctxs[i] != NULL)
2014                 ctx_invalidate(ctxs[i]);
2015         }
2016 }
2017 
2018 
2019 /*
2020  * Invalidate cache by context
2021  */
2022 static void
2023 ctx_invalidate(nsc_ctx_t *ctx)
2024 {
2025         int             i;
2026         nsc_entry_t     *entry;
2027         char            *me = "ctx_invalidate";
2028 
2029         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2030         (me, "%s: invalidate cache\n", ctx->dbname);
2031 
2032         for (i = 0; i < ctx->db_count; i++) {
2033                 if (ctx->nsc_db[i] == NULL)
2034                         continue;
2035                 (void) mutex_lock(&ctx->nsc_db[i]->db_mutex);
2036                 entry = ctx->nsc_db[i]->qtail;
2037                 while (entry != NULL) {
2038                         /* leave pending calls alone */
2039                         if (!(entry->stats.status & ST_PENDING))
2040                                 entry->stats.status = ST_DISCARD;
2041                         entry = entry->qnext;
2042                 }
2043                 (void) mutex_unlock(&ctx->nsc_db[i]->db_mutex);
2044         }
2045 
2046         (void) mutex_lock(&ctx->stats_mutex);
2047         ctx->stats.invalidate_count++;
2048         (void) mutex_unlock(&ctx->stats_mutex);
2049 }
2050 
2051 
2052 /*
2053  * Free nsc_entry_t
2054  *
2055  * Pre-reqs:
2056  * nscdb->db_mutex lock must be held before calling this function
2057  */
2058 static void
2059 delete_entry(nsc_db_t *nscdb, nsc_ctx_t *ctx, nsc_entry_t *entry) {
2060         uint_t          hash;
2061 
2062         avl_remove(&nscdb->tree, entry);
2063         HASH_REMOVE(nscdb, entry, hash, nscd_false);
2064         queue_remove(nscdb, entry);
2065         if (entry->buffer != NULL) {
2066                 free(entry->buffer);
2067                 entry->buffer = NULL;
2068         }
2069         umem_cache_free(nsc_entry_cache, entry);
2070         (void) mutex_lock(&ctx->stats_mutex);
2071         ctx->stats.entries--;
2072         (void) mutex_unlock(&ctx->stats_mutex);
2073 }
2074 
2075 
2076 static nscd_rc_t
2077 lookup_cache(nsc_lookup_args_t *largs, nscd_cfg_cache_t *cfgp,
2078     nss_XbyY_args_t *argp, char *whoami, nsc_entry_t **entry)
2079 {
2080         nsc_db_t        *nscdb;
2081         nsc_ctx_t       *ctx;
2082         uint_t          hash;
2083         avl_index_t     pos;
2084         ulong_t         nentries;
2085         nsc_entry_t     find_entry, *node;
2086         char            *me = "lookup_cache";
2087 
2088         ctx = largs->ctx;
2089         nscdb = largs->nscdb;
2090 
2091         /* set the search key */
2092         find_entry.key = argp->key;  /* struct copy (not deep) */
2093 
2094         /* lookup the hash table ==> O(1) */
2095         if (nscdb->htable) {
2096                 *entry = hash_find(nscdb, &find_entry, &hash, nscd_true);
2097                 if (*entry != NULL) {
2098                         (void) queue_adjust(nscdb, *entry);
2099                         return (NSCD_SUCCESS);
2100                 }
2101         }
2102 
2103         /* if not found, lookup the AVL tree ==> O(log n) */
2104         *entry = (nsc_entry_t *)avl_find(&nscdb->tree, &find_entry, &pos);
2105         if (*entry != NULL) {
2106                 (void) queue_adjust(nscdb, *entry);
2107                 /* move it to the hash table */
2108                 if (nscdb->htable) {
2109                         if (nscdb->htable[hash] == NULL ||
2110                             (*entry)->stats.hits >=
2111                             nscdb->htable[hash]->stats.hits) {
2112                                 nscdb->htable[hash] = *entry;
2113                         }
2114                 }
2115                 return (NSCD_SUCCESS);
2116         }
2117 
2118         /* entry not found in the cache */
2119         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2120                 (me, "%s: cache miss\n", whoami);
2121 
2122         if (cfgp->avoid_ns == nscd_true) {
2123                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2124                         (me, "%s: avoid name service\n", whoami);
2125                 return (NSCD_DB_ENTRY_NOT_FOUND);
2126         }
2127 
2128         /* allocate memory for new entry (stub) */
2129         *entry = (nsc_entry_t *)umem_cache_alloc(nsc_entry_cache,
2130             UMEM_DEFAULT);
2131         if (*entry == NULL) {
2132                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
2133                         (me, "%s: memory allocation failure\n", whoami);
2134                 return (NSCD_NO_MEMORY);
2135         }
2136         (void) memset(*entry, 0, sizeof (**entry));
2137 
2138         /*
2139          * Note that the actual data for the key is stored within
2140          * the largs->buffer (input buffer to nsc_lookup).
2141          * find_entry.key only contains pointers to this data.
2142          *
2143          * If largs->buffer will be re-allocated by nss_psearch
2144          * then (*entry)->key will have dangling pointers.
2145          * In such case, the following assignment needs to be
2146          * replaced by code that duplicates the key.
2147          */
2148         (*entry)->key = find_entry.key;
2149 
2150         /*
2151          * Add the entry to the cache.
2152          */
2153         avl_insert(&nscdb->tree, *entry, pos);   /* O(log n) */
2154         (void) queue_adjust(nscdb, *entry);     /* constant */
2155         if (nscdb->htable)                   /* constant */
2156                 nscdb->htable[hash] = *entry;
2157         (*entry)->stats.status = ST_NEW_ENTRY;
2158 
2159         (void) mutex_lock(&ctx->stats_mutex);
2160         nentries = ++(ctx->stats.entries);
2161         (void) mutex_unlock(&ctx->stats_mutex);
2162 
2163         /* Have we exceeded max entries ? */
2164         if (cfgp->maxentries > 0 && nentries > cfgp->maxentries) {
2165                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2166                         (me, "%s: maximum entries exceeded -- "
2167                             "deleting least recently used entry\n",
2168                             whoami);
2169 
2170                 node = nscdb->qhead;
2171                 while (node != NULL && node != *entry) {
2172                         if (node->stats.status == ST_DISCARD ||
2173                             !(node->stats.status & ST_PENDING)) {
2174                                 delete_entry(nscdb, ctx, node);
2175                                 break;
2176                         }
2177                         node = node->qprev;
2178                 }
2179 
2180                 /*
2181                  * It's okay if we were not able to find one to delete.
2182                  * The reaper (when invoked) will return the cache to a
2183                  * safe level.
2184                  */
2185         }
2186 
2187         return (NSCD_SUCCESS);
2188 }
2189 
2190 static void
2191 reaper(nsc_ctx_t *ctx)
2192 {
2193         uint_t          ttl, extra_sleep, total_sleep, intervals;
2194         uint_t          nodes_per_interval, seconds_per_interval;
2195         ulong_t         nsc_entries;
2196         char            *me = "reaper";
2197 
2198         (void) thr_setname(thr_self(), me);
2199 
2200         for (;;) {
2201                 (void) mutex_lock(&ctx->stats_mutex);
2202                 nsc_entries = ctx->stats.entries;
2203                 (void) mutex_unlock(&ctx->stats_mutex);
2204 
2205                 (void) rw_rdlock(&ctx->cfg_rwlp);
2206                 ttl = ctx->cfg.pos_ttl;
2207                 (void) rw_unlock(&ctx->cfg_rwlp);
2208 
2209                 if (nsc_entries == 0) {
2210                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2211                                 (me, "%s: nothing to reap\n", ctx->dbname);
2212 
2213                         /* sleep for atleast 60 seconds */
2214                         if (ttl < 60)
2215                                 ttl = 60;
2216                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2217                         (me, "%s: sleep %d\n", ctx->dbname, ttl);
2218                         (void) sleep(ttl);
2219                         continue;
2220                 }
2221 
2222                 if (ttl < 32) ttl = 32;
2223                 if (ttl > (1<<28)) ttl = 1<<28;
2224 
2225                 /*
2226                  * minimum nodes_per_interval = 256 or 1<<8
2227                  * maximum nodes_per_interval = nsc_entries
2228                  * minimum seconds_per_interval = 32 or 1<<5
2229                  * maximum_seconds_per_interval = ttl
2230                  */
2231                 if (nsc_entries <= ttl) {
2232                         intervals = (nsc_entries >> 8) + 1;
2233                         seconds_per_interval = ttl / intervals;
2234                         nodes_per_interval = 256;
2235                 } else {
2236                         intervals = (ttl >> 5) + 1;
2237                         seconds_per_interval = 32;
2238                         nodes_per_interval = nsc_entries / intervals;
2239                         if (nodes_per_interval < 256)
2240                                 nodes_per_interval = 256;
2241                 }
2242 
2243                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2244                         (me, "%s: total entries = %d, "
2245                             "seconds per interval = %d, "
2246                             "nodes per interval = %d\n",
2247                             ctx->dbname, nsc_entries, seconds_per_interval,
2248                             nodes_per_interval);
2249                 total_sleep = reap_cache(ctx, nodes_per_interval,
2250                     seconds_per_interval);
2251                 extra_sleep = 1 + ttl - total_sleep;
2252                 if (extra_sleep > 0)
2253                         (void) sleep(extra_sleep);
2254         }
2255 }
2256 
2257 
2258 static uint_t
2259 reap_cache(nsc_ctx_t *ctx, uint_t nodes_per_interval,
2260     uint_t seconds_per_interval)
2261 {
2262         uint_t          nodes_togo, total_sleep;
2263         time_t          now;
2264         nsc_entry_t     *node, *next_node;
2265         nsc_db_t        *nscdb;
2266         uint_t          primes[] = {_NSC_HTSIZE_PRIMES};
2267         ulong_t         count, nentries, maxentries;
2268         int             i, slot, value, newhtsize;
2269         char            *me = "reap_cache";
2270 
2271         count = 0;
2272         total_sleep = 0;
2273         nodes_togo = nodes_per_interval;
2274         now = time(NULL);
2275 
2276         for (i = 0; i < ctx->db_count; i++) {
2277                 nscdb = ctx->nsc_db[i];
2278                 (void) mutex_lock(&nscdb->db_mutex);
2279                 nscdb->reap_node = nscdb->qtail;
2280                 while (nscdb->reap_node != NULL) {
2281                         if (nodes_togo == 0) {
2282                                 (void) mutex_unlock(&nscdb->db_mutex);
2283                                 (void) sleep(seconds_per_interval);
2284                                 total_sleep += seconds_per_interval;
2285                                 nodes_togo = nodes_per_interval;
2286                                 now = time(NULL);
2287                                 (void) mutex_lock(&nscdb->db_mutex);
2288                         }
2289                         /* delete ST_DISCARD and expired nodes */
2290                         if ((node = nscdb->reap_node) == NULL)
2291                                 break;
2292                         if (node->stats.status == ST_DISCARD ||
2293                             (!(node->stats.status & ST_PENDING) &&
2294                             node->stats.timestamp < now)) {
2295                                 /*
2296                                  * Delete entry if its discard flag is
2297                                  * set OR if it has expired. Entries
2298                                  * with pending updates are not
2299                                  * deleted.
2300                                  * nscdb->reap_node will be adjusted
2301                                  * by delete_entry()
2302                                  */
2303                                 delete_entry(nscdb, ctx, node);
2304                                 count++;
2305                         } else {
2306                                 nscdb->reap_node = node->qnext;
2307                         }
2308                         nodes_togo--;
2309                 }
2310 
2311                 if (nscdb->htsize == 0) {
2312                         (void) mutex_unlock(&nscdb->db_mutex);
2313                         continue;
2314                 }
2315 
2316                 /*
2317                  * Dynamic adjustment of hash table size.
2318                  *
2319                  * Hash table size is roughly 1/8th of the
2320                  * total entries. However the size is changed
2321                  * only when the number of entries double or
2322                  * reduced by half
2323                  */
2324                 nentries = avl_numnodes(&nscdb->tree);
2325                 for (slot = 0, value = _NSC_INIT_HTSIZE_SLOT_VALUE;
2326                     slot < _NSC_HTSIZE_NUM_SLOTS && nentries > value;
2327                     value = (value << 1) + 1, slot++)
2328                         ;
2329                 if (nscdb->hash_type == nsc_ht_power2)
2330                         newhtsize = _NSC_INIT_HTSIZE_POWER2 << slot;
2331                 else
2332                         newhtsize = primes[slot];
2333 
2334                 /* Recommended size is same as the current size. Done */
2335                 if (nscdb->htsize == newhtsize) {
2336                         (void) mutex_unlock(&nscdb->db_mutex);
2337                         continue;
2338                 }
2339 
2340                 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2341                         (me, "%s: resizing hash table from %d to %d\n",
2342                             nscdb->name, nscdb->htsize, newhtsize);
2343 
2344                 /*
2345                  * Dump old hashes because it would be time
2346                  * consuming to rehash them.
2347                  */
2348                 (void) free(nscdb->htable);
2349                 nscdb->htable = calloc(newhtsize, sizeof (*(nscdb->htable)));
2350                 if (nscdb->htable == NULL) {
2351                         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
2352                                 (me, "%s: memory allocation failure\n",
2353                                     nscdb->name);
2354                         /* -1 to try later */
2355                         nscdb->htsize = -1;
2356                 } else {
2357                         nscdb->htsize = newhtsize;
2358                 }
2359                 (void) mutex_unlock(&nscdb->db_mutex);
2360         }
2361 
2362         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2363                 (me, "%s: reaped %lu entries\n", ctx->dbname, count);
2364 
2365         /*
2366          * if cache is almost full then reduce it to a safe level by
2367          * evicting LRU entries
2368          */
2369 
2370         (void) rw_rdlock(&ctx->cfg_rwlp);
2371         maxentries = ctx->cfg.maxentries;
2372         (void) rw_unlock(&ctx->cfg_rwlp);
2373 
2374         /* No limit on number of entries. Done */
2375         if (maxentries == 0)
2376                 goto out;
2377 
2378         (void) mutex_lock(&ctx->stats_mutex);
2379         nentries = ctx->stats.entries;
2380         (void) mutex_unlock(&ctx->stats_mutex);
2381 
2382         /* what is the percentage of cache used ? */
2383         value = (nentries * 100) / maxentries;
2384         if (value < _NSC_EVICTION_START_LEVEL)
2385                 goto out;
2386 
2387         /*
2388          * cache needs to be reduced to a safe level
2389          */
2390         value -= _NSC_EVICTION_SAFE_LEVEL;
2391         for (i = 0, count = 0; i < ctx->db_count; i++) {
2392                 /*
2393                  * Reduce each subcache by 'value' percent
2394                  */
2395                 nscdb = ctx->nsc_db[i];
2396                 (void) mutex_lock(&nscdb->db_mutex);
2397                 nodes_togo = (value * avl_numnodes(&nscdb->tree)) / 100;
2398 
2399                 /* Start from LRU entry i.e queue head */
2400                 next_node = nscdb->qhead;
2401                 while (nodes_togo > 0 && next_node != NULL) {
2402                         node = next_node;
2403                         next_node = next_node->qprev;
2404                         if (node->stats.status == ST_DISCARD ||
2405                             !(node->stats.status & ST_PENDING)) {
2406                                 /* Leave nodes with pending updates alone  */
2407                                 delete_entry(nscdb, ctx, node);
2408                                 count++;
2409                                 nodes_togo--;
2410                         }
2411                 }
2412                 (void) mutex_unlock(&nscdb->db_mutex);
2413         }
2414 
2415         _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2416                 (me, "%s: evicted %lu LRU entries\n", ctx->dbname, count);
2417 
2418 out:
2419         return (total_sleep);
2420 }