8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / nscd / cache.c
blob2ffb95c30dd83305054dba149fa145c755d8069f
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
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.
28 * Cache routines for nscd
30 #include <assert.h>
31 #include <errno.h>
32 #include <memory.h>
33 #include <signal.h>
34 #include <stdlib.h>
35 #include <stddef.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <sys/time.h>
40 #include <sys/types.h>
41 #include <sys/wait.h>
42 #include <unistd.h>
43 #include <ucred.h>
44 #include <nss_common.h>
45 #include <locale.h>
46 #include <ctype.h>
47 #include <strings.h>
48 #include <string.h>
49 #include <umem.h>
50 #include <fcntl.h>
51 #include "cache.h"
52 #include "nscd_door.h"
53 #include "nscd_log.h"
54 #include "nscd_config.h"
55 #include "nscd_frontend.h"
56 #include "nscd_switch.h"
58 #define SUCCESS 0
59 #define NOTFOUND -1
60 #define SERVERERROR -2
61 #define NOSERVER -3
62 #define CONTINUE -4
64 static nsc_db_t *nsc_get_db(nsc_ctx_t *, int);
65 static nscd_rc_t lookup_cache(nsc_lookup_args_t *, nscd_cfg_cache_t *,
66 nss_XbyY_args_t *, char *, nsc_entry_t **);
67 static uint_t reap_cache(nsc_ctx_t *, uint_t, uint_t);
68 static void delete_entry(nsc_db_t *, nsc_ctx_t *, nsc_entry_t *);
69 static void print_stats(nscd_cfg_stat_cache_t *);
70 static void print_cfg(nscd_cfg_cache_t *);
71 static int lookup_int(nsc_lookup_args_t *, int);
73 #ifdef NSCD_DEBUG
74 static void print_entry(nsc_db_t *, time_t, nsc_entry_t *);
75 static void avl_dump(nsc_db_t *, time_t);
76 static void hash_dump(nsc_db_t *, time_t);
77 #endif /* NSCD_DEBUG */
78 static nsc_entry_t *hash_find(nsc_db_t *, nsc_entry_t *, uint_t *, nscd_bool_t);
80 static void queue_adjust(nsc_db_t *, nsc_entry_t *);
81 static void queue_remove(nsc_db_t *, nsc_entry_t *);
82 #ifdef NSCD_DEBUG
83 static void queue_dump(nsc_db_t *, time_t);
84 #endif /* NSCD_DEBUG */
86 static int launch_update(nsc_lookup_args_t *);
87 static void do_update(nsc_lookup_args_t *);
88 static void getxy_keepalive(nsc_ctx_t *, nsc_db_t *, int, int);
90 static void ctx_info(nsc_ctx_t *);
91 static void ctx_info_nolock(nsc_ctx_t *);
92 static void ctx_invalidate(nsc_ctx_t *);
94 static void nsc_db_str_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t *);
95 static void nsc_db_int_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t *);
96 static void nsc_db_any_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t *);
98 static int nsc_db_cis_key_compar(const void *, const void *);
99 static int nsc_db_ces_key_compar(const void *, const void *);
100 static int nsc_db_int_key_compar(const void *, const void *);
102 static uint_t nsc_db_cis_key_gethash(nss_XbyY_key_t *, int);
103 static uint_t nsc_db_ces_key_gethash(nss_XbyY_key_t *, int);
104 static uint_t nsc_db_int_key_gethash(nss_XbyY_key_t *, int);
106 static umem_cache_t *nsc_entry_cache;
108 static nsc_ctx_t *init_cache_ctx(int);
109 static void reaper(nsc_ctx_t *);
110 static void revalidate(nsc_ctx_t *);
112 static nss_status_t
113 dup_packed_buffer(void *src, void *dst) {
114 nsc_lookup_args_t *s = (nsc_lookup_args_t *)src;
115 nsc_entry_t *d = (nsc_entry_t *)dst;
116 nss_pheader_t *sphdr = (nss_pheader_t *)s->buffer;
117 nss_pheader_t *dphdr = (nss_pheader_t *)d->buffer;
118 int slen, new_pbufsiz = 0;
120 if (NSCD_GET_STATUS(sphdr) != NSS_SUCCESS) {
122 /* no result, copy header only (status, errno, etc) */
123 slen = sphdr->data_off;
124 } else {
126 * lookup result returned, data to copy is the packed
127 * header plus result (add 1 for the terminating NULL
128 * just in case)
130 slen = sphdr->data_off + sphdr->data_len + 1;
133 /* allocate cache packed buffer */
134 if (dphdr != NULL && d->bufsize <= slen && d->bufsize != 0) {
135 /* old buffer too small, free it */
136 free(dphdr);
137 d->buffer = NULL;
138 d->bufsize = 0;
139 dphdr = NULL;
141 if (dphdr == NULL) {
142 /* get new buffer */
143 dphdr = calloc(1, slen + 1);
144 if (dphdr == NULL)
145 return (NSS_ERROR);
146 d->buffer = dphdr;
147 d->bufsize = slen + 1;
148 new_pbufsiz = slen + 1;
151 (void) memcpy(dphdr, sphdr, slen);
152 if (new_pbufsiz != 0)
153 dphdr->pbufsiz = new_pbufsiz;
155 return (NSS_SUCCESS);
158 char *cache_name[CACHE_CTX_COUNT] = {
159 NSS_DBNAM_PASSWD,
160 NSS_DBNAM_GROUP,
161 NSS_DBNAM_HOSTS,
162 NSS_DBNAM_IPNODES,
163 NSS_DBNAM_EXECATTR,
164 NSS_DBNAM_PROFATTR,
165 NSS_DBNAM_USERATTR,
166 NSS_DBNAM_ETHERS,
167 NSS_DBNAM_RPC,
168 NSS_DBNAM_PROTOCOLS,
169 NSS_DBNAM_NETWORKS,
170 NSS_DBNAM_BOOTPARAMS,
171 NSS_DBNAM_AUTHATTR,
172 NSS_DBNAM_SERVICES,
173 NSS_DBNAM_NETMASKS,
174 NSS_DBNAM_PRINTERS,
175 NSS_DBNAM_PROJECT,
176 NSS_DBNAM_TSOL_TP,
177 NSS_DBNAM_TSOL_RH
180 typedef void (*cache_init_ctx_t)(nsc_ctx_t *);
181 static cache_init_ctx_t cache_init_ctx[CACHE_CTX_COUNT] = {
182 passwd_init_ctx,
183 group_init_ctx,
184 host_init_ctx,
185 ipnode_init_ctx,
186 exec_init_ctx,
187 prof_init_ctx,
188 user_init_ctx,
189 ether_init_ctx,
190 rpc_init_ctx,
191 proto_init_ctx,
192 net_init_ctx,
193 bootp_init_ctx,
194 auth_init_ctx,
195 serv_init_ctx,
196 netmask_init_ctx,
197 printer_init_ctx,
198 project_init_ctx,
199 tnrhtp_init_ctx,
200 tnrhdb_init_ctx
203 nsc_ctx_t *cache_ctx_p[CACHE_CTX_COUNT] = { 0 };
204 static nscd_cfg_stat_cache_t null_stats = { 0 };
205 static nscd_cfg_global_cache_t global_cfg;
208 * Given database name 'dbname' find cache index
211 get_cache_idx(char *dbname) {
212 int i;
213 char *nsc_name;
215 for (i = 0; i < CACHE_CTX_COUNT; i++) {
216 nsc_name = cache_name[i];
217 if (strcmp(nsc_name, dbname) == 0)
218 return (i);
220 return (-1);
224 * Given database name 'dbname' retrieve cache context,
225 * if not created yet, allocate and initialize it.
227 static nscd_rc_t
228 get_cache_ctx(char *dbname, nsc_ctx_t **ctx) {
229 int i;
231 *ctx = NULL;
233 i = get_cache_idx(dbname);
234 if (i == -1)
235 return (NSCD_INVALID_ARGUMENT);
236 if ((*ctx = cache_ctx_p[i]) == NULL) {
237 *ctx = init_cache_ctx(i);
238 if (*ctx == NULL)
239 return (NSCD_NO_MEMORY);
242 return (NSCD_SUCCESS);
246 * Generate a log string to identify backend operation in debug logs
248 static void
249 nsc_db_str_key_getlogstr(char *name, char *whoami, size_t len,
250 nss_XbyY_args_t *argp) {
251 (void) snprintf(whoami, len, "%s [key=%s]", name, argp->key.name);
255 static void
256 nsc_db_int_key_getlogstr(char *name, char *whoami, size_t len,
257 nss_XbyY_args_t *argp) {
258 (void) snprintf(whoami, len, "%s [key=%d]", name, argp->key.number);
261 /*ARGSUSED*/
262 static void
263 nsc_db_any_key_getlogstr(char *name, char *whoami, size_t len,
264 nss_XbyY_args_t *argp) {
265 (void) snprintf(whoami, len, "%s", name);
270 * Returns cache based on dbop
272 static nsc_db_t *
273 nsc_get_db(nsc_ctx_t *ctx, int dbop) {
274 int i;
276 for (i = 0; i < ctx->db_count; i++) {
277 if (ctx->nsc_db[i] && dbop == ctx->nsc_db[i]->dbop)
278 return (ctx->nsc_db[i]);
280 return (NULL);
285 * integer compare routine for _NSC_DB_INT_KEY
287 static int
288 nsc_db_int_key_compar(const void *n1, const void *n2) {
289 nsc_entry_t *e1, *e2;
291 e1 = (nsc_entry_t *)n1;
292 e2 = (nsc_entry_t *)n2;
293 return (_NSC_INT_KEY_CMP(e1->key.number, e2->key.number));
298 * case sensitive name compare routine for _NSC_DB_CES_KEY
300 static int
301 nsc_db_ces_key_compar(const void *n1, const void *n2) {
302 nsc_entry_t *e1, *e2;
303 int res, l1, l2;
305 e1 = (nsc_entry_t *)n1;
306 e2 = (nsc_entry_t *)n2;
307 l1 = strlen(e1->key.name);
308 l2 = strlen(e2->key.name);
309 res = strncmp(e1->key.name, e2->key.name, (l1 > l2)?l1:l2);
310 return (_NSC_INT_KEY_CMP(res, 0));
315 * case insensitive name compare routine _NSC_DB_CIS_KEY
317 static int
318 nsc_db_cis_key_compar(const void *n1, const void *n2) {
319 nsc_entry_t *e1, *e2;
320 int res, l1, l2;
322 e1 = (nsc_entry_t *)n1;
323 e2 = (nsc_entry_t *)n2;
324 l1 = strlen(e1->key.name);
325 l2 = strlen(e2->key.name);
326 res = strncasecmp(e1->key.name, e2->key.name, (l1 > l2)?l1:l2);
327 return (_NSC_INT_KEY_CMP(res, 0));
331 * macro used to generate elf hashes for strings
333 #define _NSC_ELF_STR_GETHASH(func, str, htsize, hval) \
334 hval = 0; \
335 while (*str) { \
336 uint_t g; \
337 hval = (hval << 4) + func(*str++); \
338 if ((g = (hval & 0xf0000000)) != 0) \
339 hval ^= g >> 24; \
340 hval &= ~g; \
342 hval %= htsize;
346 * cis hash function
348 uint_t
349 cis_gethash(const char *key, int htsize) {
350 uint_t hval;
351 if (key == NULL)
352 return (0);
353 _NSC_ELF_STR_GETHASH(tolower, key, htsize, hval);
354 return (hval);
359 * ces hash function
361 uint_t
362 ces_gethash(const char *key, int htsize) {
363 uint_t hval;
364 if (key == NULL)
365 return (0);
366 _NSC_ELF_STR_GETHASH(, key, htsize, hval);
367 return (hval);
372 * one-at-a-time hash function
374 uint_t
375 db_gethash(const void *key, int len, int htsize) {
376 uint_t hval, i;
377 const char *str = key;
379 if (str == NULL)
380 return (0);
382 for (hval = 0, i = 0; i < len; i++) {
383 hval += str[i];
384 hval += (hval << 10);
385 hval ^= (hval >> 6);
387 hval += (hval << 3);
388 hval ^= (hval >> 11);
389 hval += (hval << 15);
390 return (hval % htsize);
395 * case insensitive name gethash routine _NSC_DB_CIS_KEY
397 static uint_t
398 nsc_db_cis_key_gethash(nss_XbyY_key_t *key, int htsize) {
399 return (cis_gethash(key->name, htsize));
404 * case sensitive name gethash routine _NSC_DB_CES_KEY
406 static uint_t
407 nsc_db_ces_key_gethash(nss_XbyY_key_t *key, int htsize) {
408 return (ces_gethash(key->name, htsize));
413 * integer gethash routine _NSC_DB_INT_KEY
415 static uint_t
416 nsc_db_int_key_gethash(nss_XbyY_key_t *key, int htsize) {
417 return (db_gethash(&key->number, sizeof (key->number), htsize));
422 * Find entry in the hash table
423 * if cmp == nscd_true)
424 * return entry only if the keys match
425 * else
426 * return entry in the hash location without checking the keys
429 static nsc_entry_t *
430 hash_find(nsc_db_t *nscdb, nsc_entry_t *entry, uint_t *hash,
431 nscd_bool_t cmp) {
433 nsc_entry_t *hashentry;
435 if (nscdb->gethash)
436 *hash = nscdb->gethash(&entry->key, nscdb->htsize);
437 else
438 return (NULL);
440 hashentry = nscdb->htable[*hash];
441 if (cmp == nscd_false || hashentry == NULL)
442 return (hashentry);
443 if (nscdb->compar) {
444 if (nscdb->compar(entry, hashentry) == 0)
445 return (hashentry);
447 return (NULL);
451 #define HASH_REMOVE(nscdb, entry, hash, cmp) \
452 if (nscdb->htable) { \
453 if (entry == hash_find(nscdb, entry, &hash, cmp)) \
454 nscdb->htable[hash] = NULL; \
458 #define HASH_INSERT(nscdb, entry, hash, cmp) \
459 if (nscdb->htable) { \
460 (void) hash_find(nscdb, entry, &hash, cmp); \
461 nscdb->htable[hash] = entry; \
465 #ifdef NSCD_DEBUG
466 static void
467 print_entry(nsc_db_t *nscdb, time_t now, nsc_entry_t *entry) {
468 nss_XbyY_args_t args;
469 char whoami[512];
471 switch (entry->stats.status) {
472 case ST_NEW_ENTRY:
473 (void) fprintf(stdout, gettext("\t status: new entry\n"));
474 return;
475 case ST_UPDATE_PENDING:
476 (void) fprintf(stdout, gettext("\t status: update pending\n"));
477 return;
478 case ST_LOOKUP_PENDING:
479 (void) fprintf(stdout, gettext("\t status: lookup pending\n"));
480 return;
481 case ST_DISCARD:
482 (void) fprintf(stdout, gettext("\t status: discarded entry\n"));
483 return;
484 default:
485 if (entry->stats.timestamp < now)
486 (void) fprintf(stdout,
487 gettext("\t status: expired (%d seconds ago)\n"),
488 now - entry->stats.timestamp);
489 else
490 (void) fprintf(stdout,
491 gettext("\t status: valid (expiry in %d seconds)\n"),
492 entry->stats.timestamp - now);
493 break;
495 (void) fprintf(stdout, gettext("\t hits: %u\n"), entry->stats.hits);
496 args.key = entry->key;
497 (void) nscdb->getlogstr(nscdb->name, whoami, sizeof (whoami), &args);
498 (void) fprintf(stdout, "\t %s\n", whoami);
500 #endif /* NSCD_DEBUG */
502 static void
503 print_stats(nscd_cfg_stat_cache_t *statsp) {
505 (void) fprintf(stdout, gettext("\n\t STATISTICS:\n"));
506 (void) fprintf(stdout, gettext("\t positive hits: %lu\n"),
507 statsp->pos_hits);
508 (void) fprintf(stdout, gettext("\t negative hits: %lu\n"),
509 statsp->neg_hits);
510 (void) fprintf(stdout, gettext("\t positive misses: %lu\n"),
511 statsp->pos_misses);
512 (void) fprintf(stdout, gettext("\t negative misses: %lu\n"),
513 statsp->neg_misses);
514 (void) fprintf(stdout, gettext("\t total entries: %lu\n"),
515 statsp->entries);
516 (void) fprintf(stdout, gettext("\t queries queued: %lu\n"),
517 statsp->wait_count);
518 (void) fprintf(stdout, gettext("\t queries dropped: %lu\n"),
519 statsp->drop_count);
520 (void) fprintf(stdout, gettext("\t cache invalidations: %lu\n"),
521 statsp->invalidate_count);
523 _NSC_GET_HITRATE(statsp);
524 (void) fprintf(stdout, gettext("\t cache hit rate: %10.1f\n"),
525 statsp->hitrate);
529 static void
530 print_cfg(nscd_cfg_cache_t *cfgp) {
531 (void) fprintf(stdout, gettext("\n\t CONFIG:\n"));
532 (void) fprintf(stdout, gettext("\t enabled: %s\n"),
533 yes_no(cfgp->enable));
534 (void) fprintf(stdout, gettext("\t per user cache: %s\n"),
535 yes_no(cfgp->per_user));
536 (void) fprintf(stdout, gettext("\t avoid name service: %s\n"),
537 yes_no(cfgp->avoid_ns));
538 (void) fprintf(stdout, gettext("\t check file: %s\n"),
539 yes_no(cfgp->check_files));
540 (void) fprintf(stdout, gettext("\t check file interval: %d\n"),
541 cfgp->check_interval);
542 (void) fprintf(stdout, gettext("\t positive ttl: %d\n"),
543 cfgp->pos_ttl);
544 (void) fprintf(stdout, gettext("\t negative ttl: %d\n"),
545 cfgp->neg_ttl);
546 (void) fprintf(stdout, gettext("\t keep hot count: %d\n"),
547 cfgp->keephot);
548 (void) fprintf(stdout, gettext("\t hint size: %d\n"),
549 cfgp->hint_size);
550 (void) fprintf(stdout, gettext("\t max entries: %lu%s"),
551 cfgp->maxentries,
552 cfgp->maxentries?"\n":" (unlimited)\n");
556 #ifdef NSCD_DEBUG
557 static void
558 hash_dump(nsc_db_t *nscdb, time_t now) {
559 nsc_entry_t *entry;
560 int i;
562 (void) fprintf(stdout, gettext("\n\nHASH TABLE:\n"));
563 for (i = 0; i < nscdb->htsize; i++) {
564 if ((entry = nscdb->htable[i]) != NULL) {
565 (void) fprintf(stdout, "hash[%d]:\n", i);
566 print_entry(nscdb, now, entry);
570 #endif /* NSCD_DEBUG */
573 #ifdef NSCD_DEBUG
574 static void
575 avl_dump(nsc_db_t *nscdb, time_t now) {
576 nsc_entry_t *entry;
577 int i;
579 (void) fprintf(stdout, gettext("\n\nAVL TREE:\n"));
580 for (entry = avl_first(&nscdb->tree), i = 0; entry != NULL;
581 entry = avl_walk(&nscdb->tree, entry, AVL_AFTER)) {
582 (void) fprintf(stdout, "avl node[%d]:\n", i++);
583 print_entry(nscdb, now, entry);
586 #endif /* NSCD_DEBUG */
589 #ifdef NSCD_DEBUG
590 static void
591 queue_dump(nsc_db_t *nscdb, time_t now) {
592 nsc_entry_t *entry;
593 int i;
595 (void) fprintf(stdout,
596 gettext("\n\nCACHE [name=%s, nodes=%lu]:\n"),
597 nscdb->name, avl_numnodes(&nscdb->tree));
599 (void) fprintf(stdout,
600 gettext("Starting with the most recently accessed:\n"));
602 for (entry = nscdb->qtail, i = 0; entry; entry = entry->qnext) {
603 (void) fprintf(stdout, "entry[%d]:\n", i++);
604 print_entry(nscdb, now, entry);
607 #endif /* NSCD_DEBUG */
609 static void
610 queue_remove(nsc_db_t *nscdb, nsc_entry_t *entry) {
612 if (nscdb->qtail == entry)
613 nscdb->qtail = entry->qnext;
614 else
615 entry->qprev->qnext = entry->qnext;
617 if (nscdb->qhead == entry)
618 nscdb->qhead = entry->qprev;
619 else
620 entry->qnext->qprev = entry->qprev;
622 if (nscdb->reap_node == entry)
623 nscdb->reap_node = entry->qnext;
624 entry->qnext = entry->qprev = NULL;
628 static void
629 queue_adjust(nsc_db_t *nscdb, nsc_entry_t *entry) {
631 #ifdef NSCD_DEBUG
632 assert(nscdb->qtail || entry->qnext == NULL &&
633 entry->qprev == NULL);
635 assert(nscdb->qtail && nscdb->qhead ||
636 nscdb->qtail == NULL && nscdb->qhead == NULL);
638 assert(entry->qprev || entry->qnext == NULL ||
639 nscdb->qtail == entry);
640 #endif /* NSCD_DEBUG */
642 /* already in the desired position */
643 if (nscdb->qtail == entry)
644 return;
646 /* new queue */
647 if (nscdb->qtail == NULL) {
648 nscdb->qhead = nscdb->qtail = entry;
649 return;
652 /* new entry (prev == NULL AND tail != entry) */
653 if (entry->qprev == NULL) {
654 nscdb->qtail->qprev = entry;
655 entry->qnext = nscdb->qtail;
656 nscdb->qtail = entry;
657 return;
660 /* existing entry */
661 if (nscdb->reap_node == entry)
662 nscdb->reap_node = entry->qnext;
663 if (nscdb->qhead == entry)
664 nscdb->qhead = entry->qprev;
665 else
666 entry->qnext->qprev = entry->qprev;
667 entry->qprev->qnext = entry->qnext;
668 entry->qprev = NULL;
669 entry->qnext = nscdb->qtail;
670 nscdb->qtail->qprev = entry;
671 nscdb->qtail = entry;
676 * Init cache
678 nscd_rc_t
679 init_cache(int debug_level) {
680 int cflags;
682 cflags = (debug_level > 0)?0:UMC_NODEBUG;
683 nsc_entry_cache = umem_cache_create("nsc_entry_cache",
684 sizeof (nsc_entry_t), 0, NULL, NULL, NULL,
685 NULL, NULL, cflags);
686 if (nsc_entry_cache == NULL)
687 return (NSCD_NO_MEMORY);
688 return (NSCD_SUCCESS);
693 * Create cache
695 nsc_db_t *
696 make_cache(enum db_type dbtype, int dbop, char *name,
697 int (*compar) (const void *, const void *),
698 void (*getlogstr)(char *, char *, size_t, nss_XbyY_args_t *),
699 uint_t (*gethash)(nss_XbyY_key_t *, int),
700 enum hash_type httype, int htsize)
702 nsc_db_t *nscdb;
703 char *me = "make_cache";
705 nscdb = (nsc_db_t *)malloc(sizeof (*nscdb));
706 if (nscdb == NULL) {
707 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
708 (me, "%s: memory allocation failure\n", name);
709 goto out;
711 (void) memset(nscdb, 0, sizeof (*nscdb));
713 nscdb->dbop = dbop;
714 nscdb->name = name;
715 nscdb->db_type = dbtype;
717 /* Assign compare routine */
718 if (compar == NULL) {
719 if (_NSC_DB_CES_KEY(nscdb))
720 nscdb->compar = nsc_db_ces_key_compar;
721 else if (_NSC_DB_CIS_KEY(nscdb))
722 nscdb->compar = nsc_db_cis_key_compar;
723 else if (_NSC_DB_INT_KEY(nscdb))
724 nscdb->compar = nsc_db_int_key_compar;
725 else
726 assert(0);
727 } else {
728 nscdb->compar = compar;
731 /* The cache is an AVL tree */
732 avl_create(&nscdb->tree, nscdb->compar, sizeof (nsc_entry_t),
733 offsetof(nsc_entry_t, avl_link));
735 /* Assign log routine */
736 if (getlogstr == NULL) {
737 if (_NSC_DB_STR_KEY(nscdb))
738 nscdb->getlogstr = nsc_db_str_key_getlogstr;
739 else if (_NSC_DB_INT_KEY(nscdb))
740 nscdb->getlogstr = nsc_db_int_key_getlogstr;
741 else
742 nscdb->getlogstr = nsc_db_any_key_getlogstr;
743 } else {
744 nscdb->getlogstr = getlogstr;
747 /* The AVL tree based cache uses a hash table for quick access */
748 if (htsize != 0) {
749 /* Determine hash table size based on type */
750 nscdb->hash_type = httype;
751 if (htsize < 0) {
752 switch (httype) {
753 case nsc_ht_power2:
754 htsize = _NSC_INIT_HTSIZE_POWER2;
755 break;
756 case nsc_ht_prime:
757 case nsc_ht_default:
758 default:
759 htsize = _NSC_INIT_HTSIZE_PRIME;
762 nscdb->htsize = htsize;
764 /* Create the hash table */
765 nscdb->htable = calloc(htsize, sizeof (*(nscdb->htable)));
766 if (nscdb->htable == NULL) {
767 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
768 (me, "%s: memory allocation failure\n", name);
769 goto out;
772 /* Assign gethash routine */
773 if (gethash == NULL) {
774 if (_NSC_DB_CES_KEY(nscdb))
775 nscdb->gethash = nsc_db_ces_key_gethash;
776 else if (_NSC_DB_CIS_KEY(nscdb))
777 nscdb->gethash = nsc_db_cis_key_gethash;
778 else if (_NSC_DB_INT_KEY(nscdb))
779 nscdb->gethash = nsc_db_int_key_gethash;
780 else
781 assert(0);
782 } else {
783 nscdb->gethash = gethash;
787 (void) mutex_init(&nscdb->db_mutex, USYNC_THREAD, NULL);
788 return (nscdb);
790 out:
791 if (nscdb->htable)
792 free(nscdb->htable);
793 if (nscdb)
794 free(nscdb);
795 return (NULL);
800 * verify
802 /* ARGSUSED */
803 nscd_rc_t
804 _nscd_cfg_cache_verify(
805 void *data,
806 struct nscd_cfg_param_desc *pdesc,
807 nscd_cfg_id_t *nswdb,
808 nscd_cfg_flag_t dflag,
809 nscd_cfg_error_t **errorp,
810 void **cookie)
813 return (NSCD_SUCCESS);
817 * notify
819 /* ARGSUSED */
820 nscd_rc_t
821 _nscd_cfg_cache_notify(
822 void *data,
823 struct nscd_cfg_param_desc *pdesc,
824 nscd_cfg_id_t *nswdb,
825 nscd_cfg_flag_t dflag,
826 nscd_cfg_error_t **errorp,
827 void **cookie)
829 nsc_ctx_t *ctx;
830 void *dp;
831 int i;
833 /* group data */
834 if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) {
835 if (_nscd_cfg_flag_is_set(pdesc->pflag,
836 NSCD_CFG_PFLAG_GLOBAL)) {
837 /* global config */
838 global_cfg = *(nscd_cfg_global_cache_t *)data;
839 } else if (_nscd_cfg_flag_is_set(dflag,
840 NSCD_CFG_DFLAG_SET_ALL_DB)) {
841 /* non-global config for all dbs */
842 for (i = 0; i < CACHE_CTX_COUNT; i++) {
843 ctx = cache_ctx_p[i];
844 if (ctx == NULL)
845 return (NSCD_CTX_NOT_FOUND);
846 (void) rw_wrlock(&ctx->cfg_rwlp);
847 ctx->cfg = *(nscd_cfg_cache_t *)data;
848 ctx->cfg_mtime = time(NULL);
849 (void) rw_unlock(&ctx->cfg_rwlp);
851 } else {
852 /* non-global config for a specific db */
854 /* ignore non-caching databases */
855 if (get_cache_ctx(nswdb->name, &ctx) != NSCD_SUCCESS)
856 return (NSCD_SUCCESS);
857 (void) rw_wrlock(&ctx->cfg_rwlp);
858 ctx->cfg = *(nscd_cfg_cache_t *)data;
859 ctx->cfg_mtime = time(NULL);
860 (void) rw_unlock(&ctx->cfg_rwlp);
862 return (NSCD_SUCCESS);
865 /* individual data */
866 if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL)) {
867 /* global config */
868 dp = (char *)&global_cfg + pdesc->p_offset;
869 (void) memcpy(dp, data, pdesc->p_size);
870 } else if (_nscd_cfg_flag_is_set(dflag,
871 NSCD_CFG_DFLAG_SET_ALL_DB)) {
872 /* non-global config for all dbs */
873 for (i = 0; i < CACHE_CTX_COUNT; i++) {
874 ctx = cache_ctx_p[i];
875 if (ctx == NULL)
876 return (NSCD_CTX_NOT_FOUND);
877 dp = (char *)&ctx->cfg + pdesc->p_offset;
878 (void) rw_wrlock(&ctx->cfg_rwlp);
879 (void) memcpy(dp, data, pdesc->p_size);
880 ctx->cfg_mtime = time(NULL);
881 (void) rw_unlock(&ctx->cfg_rwlp);
883 } else {
884 /* non-global config for a specific db */
886 /* ignore non-caching databases */
887 if (get_cache_ctx(nswdb->name, &ctx) != NSCD_SUCCESS)
888 return (NSCD_SUCCESS);
889 dp = (char *)&ctx->cfg + pdesc->p_offset;
890 (void) rw_wrlock(&ctx->cfg_rwlp);
891 (void) memcpy(dp, data, pdesc->p_size);
892 ctx->cfg_mtime = time(NULL);
893 (void) rw_unlock(&ctx->cfg_rwlp);
895 return (NSCD_SUCCESS);
900 * get stat
902 /* ARGSUSED */
903 nscd_rc_t
904 _nscd_cfg_cache_get_stat(
905 void **stat,
906 struct nscd_cfg_stat_desc *sdesc,
907 nscd_cfg_id_t *nswdb,
908 nscd_cfg_flag_t *dflag,
909 void (**free_stat)(void *stat),
910 nscd_cfg_error_t **errorp)
912 nscd_cfg_stat_cache_t *statsp, stats;
913 nsc_ctx_t *ctx;
914 int i;
915 nscd_rc_t rc;
917 statsp = calloc(1, sizeof (*statsp));
918 if (statsp == NULL)
919 return (NSCD_NO_MEMORY);
921 if (_nscd_cfg_flag_is_set(sdesc->sflag, NSCD_CFG_SFLAG_GLOBAL)) {
922 for (i = 0; i < CACHE_CTX_COUNT; i++) {
923 if (cache_ctx_p[i] == NULL)
924 stats = null_stats;
925 else {
926 (void) mutex_lock(&cache_ctx_p[i]->stats_mutex);
927 stats = cache_ctx_p[i]->stats;
928 (void) mutex_unlock(
929 &cache_ctx_p[i]->stats_mutex);
931 statsp->pos_hits += stats.pos_hits;
932 statsp->neg_hits += stats.neg_hits;
933 statsp->pos_misses += stats.pos_misses;
934 statsp->neg_misses += stats.neg_misses;
935 statsp->entries += stats.entries;
936 statsp->drop_count += stats.drop_count;
937 statsp->wait_count += stats.wait_count;
938 statsp->invalidate_count +=
939 stats.invalidate_count;
941 } else {
942 if ((rc = get_cache_ctx(nswdb->name, &ctx)) != NSCD_SUCCESS) {
943 free(statsp);
944 return (rc);
946 (void) mutex_lock(&ctx->stats_mutex);
947 *statsp = ctx->stats;
948 (void) mutex_unlock(&ctx->stats_mutex);
951 _NSC_GET_HITRATE(statsp);
952 *stat = statsp;
953 return (NSCD_SUCCESS);
957 * This function should only be called when nscd is
958 * not a daemon.
960 void
961 nsc_info(nsc_ctx_t *ctx, char *dbname, nscd_cfg_cache_t cfg[],
962 nscd_cfg_stat_cache_t stats[])
964 int i;
965 char *me = "nsc_info";
966 nsc_ctx_t *ctx1;
967 nsc_ctx_t ctx2;
968 nscd_rc_t rc;
970 if (ctx) {
971 ctx_info(ctx);
972 return;
975 if (dbname) {
976 rc = get_cache_ctx(dbname, &ctx1);
977 if (rc == NSCD_INVALID_ARGUMENT) {
978 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
979 (me, "%s: no cache context found\n", dbname);
980 return;
981 } else if (rc == NSCD_NO_MEMORY) {
982 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
983 (me, "%s: unable to create cache context - no memory\n",
984 dbname);
985 return;
987 ctx_info(ctx1);
988 return;
991 if (cfg == NULL || stats == NULL)
992 return;
994 for (i = 0; i < CACHE_CTX_COUNT; i++) {
996 ctx2.dbname = cache_name[i];
997 ctx2.cfg = cfg[i];
998 ctx2.stats = stats[i];
999 ctx_info_nolock(&ctx2);
1003 static void
1004 ctx_info_nolock(nsc_ctx_t *ctx) {
1005 nscd_cfg_cache_t cfg;
1006 nscd_cfg_stat_cache_t stats;
1008 cfg = ctx->cfg;
1009 (void) fprintf(stdout, gettext("\n\nCACHE: %s\n"), ctx->dbname);
1010 (void) print_cfg(&cfg);
1012 if (cfg.enable == nscd_false)
1013 return;
1015 stats = ctx->stats;
1016 (void) print_stats(&stats);
1019 static void
1020 ctx_info(nsc_ctx_t *ctx) {
1021 nscd_cfg_cache_t cfg;
1022 nscd_cfg_stat_cache_t stats;
1024 (void) rw_rdlock(&ctx->cfg_rwlp);
1025 cfg = ctx->cfg;
1026 (void) rw_unlock(&ctx->cfg_rwlp);
1027 (void) fprintf(stdout, gettext("\n\nCACHE: %s\n"), ctx->dbname);
1028 (void) print_cfg(&cfg);
1030 if (cfg.enable == nscd_false)
1031 return;
1033 (void) mutex_lock(&ctx->stats_mutex);
1034 stats = ctx->stats;
1035 (void) mutex_unlock(&ctx->stats_mutex);
1036 (void) print_stats(&stats);
1039 #ifdef NSCD_DEBUG
1041 * This function should only be called when nscd is
1042 * not a daemon.
1045 nsc_dump(char *dbname, int dbop)
1047 nsc_ctx_t *ctx;
1048 nsc_db_t *nscdb;
1049 nscd_bool_t enabled;
1050 time_t now;
1051 char *me = "nsc_dump";
1052 int i;
1054 if ((i = get_cache_idx(dbname)) == -1) {
1055 (void) fprintf(stdout, gettext("invalid cache name\n"));
1057 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
1058 (me, "%s: invalid cache name\n", dbname);
1059 return (NSCD_CACHE_INVALID_CACHE_NAME);
1062 if ((ctx = cache_ctx_p[i]) == NULL) {
1063 (void) fprintf(stdout, gettext("no cache context\n"));
1065 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
1066 (me, "%s: no cache context\n", dbname);
1067 return (NSCD_CACHE_NO_CACHE_CTX);
1070 now = time(NULL);
1071 (void) rw_rdlock(&ctx->cfg_rwlp);
1072 enabled = ctx->cfg.enable;
1073 (void) rw_unlock(&ctx->cfg_rwlp);
1075 if (enabled == nscd_false)
1076 return (NSCD_CACHE_DISABLED);
1078 nscdb = nsc_get_db(ctx, dbop);
1079 if (nscdb == NULL) {
1080 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
1081 (me, "%s:%d: no cache found\n", dbname, dbop);
1082 return (NSCD_CACHE_NO_CACHE_FOUND);
1085 (void) mutex_lock(&nscdb->db_mutex);
1086 (void) queue_dump(nscdb, now);
1087 (void) hash_dump(nscdb, now);
1088 (void) avl_dump(nscdb, now);
1089 (void) mutex_unlock(&nscdb->db_mutex);
1090 return (NSCD_SUCCESS);
1092 #endif /* NSCD_DEBUG */
1095 * These macros are for exclusive use of nsc_lookup
1097 #define NSC_LOOKUP_LOG(loglevel, fmt) \
1098 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_##loglevel) \
1099 (me, fmt, whoami);
1101 static int
1102 nsc_lookup_no_cache(nsc_lookup_args_t *largs, const char *str)
1104 char *me = "nsc_lookup_no_cache";
1105 nss_status_t status;
1107 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1108 (me, "%s: name service lookup (bypassing cache)\n", str);
1109 nss_psearch(largs->buffer, largs->bufsize);
1110 status = NSCD_GET_STATUS(largs->buffer);
1111 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1112 (me, "%s: name service lookup status = %d\n", str, status);
1113 if (status == NSS_SUCCESS) {
1114 return (SUCCESS);
1115 } else if (status == NSS_NOTFOUND) {
1116 return (NOTFOUND);
1117 } else {
1118 return (SERVERERROR);
1123 * This function starts the revalidation and reaper threads
1124 * for a cache
1126 static void
1127 start_threads(nsc_ctx_t *ctx)
1129 int errnum;
1130 char *me = "start_threads";
1133 * kick off the revalidate thread (if necessary)
1135 if (ctx->revalidate_on != nscd_true) {
1136 if (thr_create(NULL, NULL, (void *(*)(void *))revalidate,
1137 ctx, 0, NULL) != 0) {
1138 errnum = errno;
1139 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
1140 (me, "thr_create (revalidate thread for %s): %s\n",
1141 ctx->dbname, strerror(errnum));
1142 exit(1);
1144 ctx->revalidate_on = nscd_true;
1148 * kick off the reaper thread (if necessary)
1150 if (ctx->reaper_on != nscd_true) {
1151 if (thr_create(NULL, NULL, (void *(*)(void *))reaper,
1152 ctx, 0, NULL) != 0) {
1153 errnum = errno;
1154 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
1155 (me, "thr_create (reaper thread for %s): %s\n",
1156 ctx->dbname, strerror(errnum));
1157 exit(1);
1159 ctx->reaper_on = nscd_true;
1164 * Examine the packed buffer, see if the front-end parameters
1165 * indicate that the caller specified nsswitch config should be
1166 * used for the lookup. Return 1 if yes, otherwise 0.
1168 static int
1169 nsw_config_in_phdr(void *buf)
1171 nss_pheader_t *pbuf = (nss_pheader_t *)buf;
1172 nssuint_t off;
1173 nss_dbd_t *pdbd;
1174 char *me = "nsw_config_in_phdr";
1176 off = pbuf->dbd_off;
1177 if (off == 0)
1178 return (0);
1179 pdbd = (nss_dbd_t *)((void *)((char *)pbuf + off));
1180 if (pdbd->o_default_config == 0)
1181 return (0);
1183 if ((enum nss_dbp_flags)pdbd->flags & NSS_USE_DEFAULT_CONFIG) {
1184 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1185 (me, "use caller specified nsswitch config\n");
1186 return (1);
1187 } else
1188 return (0);
1191 static nss_status_t
1192 copy_result(void *rbuf, void *cbuf)
1194 nss_pheader_t *rphdr = (nss_pheader_t *)rbuf;
1195 nss_pheader_t *cphdr = (nss_pheader_t *)cbuf;
1196 char *me = "copy_result";
1198 /* return NSS_ERROR if not enough room to copy result */
1199 if (cphdr->data_len + 1 > rphdr->data_len) {
1200 NSCD_SET_STATUS(rphdr, NSS_ERROR, ERANGE);
1201 return (NSS_ERROR);
1202 } else {
1203 char *dst;
1205 if (cphdr->data_len == 0)
1206 return (NSS_SUCCESS);
1208 dst = (char *)rphdr + rphdr->data_off;
1209 (void) memcpy(dst, (char *)cphdr + cphdr->data_off,
1210 cphdr->data_len);
1211 rphdr->data_len = cphdr->data_len;
1212 /* some frontend code expects a terminating NULL char */
1213 *(dst + rphdr->data_len) = '\0';
1215 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1216 (me, "cache data (len = %lld): >>%s<<\n",
1217 cphdr->data_len, (char *)cphdr + cphdr->data_off);
1219 return (NSS_SUCCESS);
1223 static int
1224 get_dns_ttl(void *pbuf, char *dbname)
1226 nss_pheader_t *phdr = (nss_pheader_t *)pbuf;
1227 int ttl;
1228 char *me = "get_dns_ttl";
1230 /* if returned, dns ttl is stored in the extended data area */
1231 if (phdr->ext_off == 0)
1232 return (-1);
1234 if (strcmp(dbname, NSS_DBNAM_HOSTS) != 0 &&
1235 strcmp(dbname, NSS_DBNAM_IPNODES) != 0)
1236 return (-1);
1238 ttl = *(nssuint_t *)((void *)((char *)pbuf + phdr->ext_off));
1240 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1241 (me, "dns ttl is %d seconds\n", ttl);
1243 return (ttl);
1246 static int
1247 check_config(nsc_lookup_args_t *largs, nscd_cfg_cache_t *cfgp,
1248 char *whoami, int flag)
1250 nsc_db_t *nscdb;
1251 nsc_ctx_t *ctx;
1252 char *me = "check_config";
1254 ctx = largs->ctx;
1255 nscdb = largs->nscdb;
1257 /* see if the cached config needs update */
1258 if (nscdb->cfg_mtime != ctx->cfg_mtime) {
1259 (void) rw_rdlock(&ctx->cfg_rwlp);
1260 nscdb->cfg = ctx->cfg;
1261 nscdb->cfg_mtime = ctx->cfg_mtime;
1262 (void) rw_unlock(&ctx->cfg_rwlp);
1263 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1264 (me, "config for context %s, database %s updated\n",
1265 ctx->dbname, nscdb->name);
1267 *cfgp = nscdb->cfg;
1269 if (cfgp->enable == nscd_false) {
1270 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1271 (me, "%s: cache disabled\n", ctx->dbname);
1273 if (UPDATEBIT & flag)
1274 return (NOTFOUND);
1275 else
1276 return (nsc_lookup_no_cache(largs, whoami));
1280 * if caller requests lookup using its
1281 * own nsswitch config, bypass cache
1283 if (nsw_config_in_phdr(largs->buffer))
1284 return (nsc_lookup_no_cache(largs, whoami));
1286 /* no need of cache if we are dealing with 0 ttls */
1287 if (cfgp->pos_ttl <= 0 && cfgp->neg_ttl <= 0) {
1288 if (flag & UPDATEBIT)
1289 return (NOTFOUND);
1290 else if (cfgp->avoid_ns == nscd_true)
1291 return (SERVERERROR);
1292 return (nsc_lookup_no_cache(largs, whoami));
1295 return (CONTINUE);
1299 * Invalidate cache if database file has been modified.
1300 * See check_files config param for details.
1302 static void
1303 check_db_file(nsc_ctx_t *ctx, nscd_cfg_cache_t cfg,
1304 char *whoami, time_t now)
1306 struct stat buf;
1307 nscd_bool_t file_modified = nscd_false;
1308 char *me = "check_db_file";
1310 if (cfg.check_interval != 0 &&
1311 (now - ctx->file_chktime) < cfg.check_interval)
1312 return;
1314 ctx->file_chktime = now;
1315 if (stat(ctx->file_name, &buf) == 0) {
1316 if (ctx->file_mtime == 0) {
1317 (void) mutex_lock(&ctx->file_mutex);
1318 if (ctx->file_mtime == 0) {
1319 ctx->file_mtime = buf.st_mtime;
1320 ctx->file_size = buf.st_size;
1321 ctx->file_ino = buf.st_ino;
1323 (void) mutex_unlock(&ctx->file_mutex);
1324 } else if (ctx->file_mtime < buf.st_mtime ||
1325 ctx->file_size != buf.st_size ||
1326 ctx->file_ino != buf.st_ino) {
1327 (void) mutex_lock(&ctx->file_mutex);
1328 if (ctx->file_mtime < buf.st_mtime ||
1329 ctx->file_size != buf.st_size ||
1330 ctx->file_ino != buf.st_ino) {
1331 file_modified = nscd_true;
1332 ctx->file_mtime = buf.st_mtime;
1333 ctx->file_size = buf.st_size;
1334 ctx->file_ino = buf.st_ino;
1336 (void) mutex_unlock(&ctx->file_mutex);
1340 if (file_modified == nscd_true) {
1341 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1342 (me, "%s: file %s has been modified - invalidating cache\n",
1343 whoami, ctx->file_name);
1344 ctx_invalidate(ctx);
1348 static int
1349 lookup_int(nsc_lookup_args_t *largs, int flag)
1351 nsc_ctx_t *ctx;
1352 nsc_db_t *nscdb;
1353 nscd_cfg_cache_t cfg;
1354 nsc_entry_t *this_entry;
1355 nsc_entry_stat_t *this_stats;
1356 nsc_action_t next_action;
1357 nss_status_t status;
1358 nscd_bool_t delete;
1359 nscd_rc_t rc;
1360 char *dbname;
1361 int dbop, errnum;
1362 int cfg_rc;
1363 nss_XbyY_args_t args;
1364 char whoami[128];
1365 time_t now = time(NULL); /* current time */
1366 char *me = "lookup_int";
1368 /* extract dbop, dbname, key and cred */
1369 status = nss_packed_getkey(largs->buffer, largs->bufsize, &dbname,
1370 &dbop, &args);
1371 if (status != NSS_SUCCESS) {
1372 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
1373 (me, "nss_packed_getkey failure (%d)\n", status);
1374 return (SERVERERROR);
1377 /* get the cache context */
1378 if (largs->ctx == NULL) {
1379 if (get_cache_ctx(dbname, &largs->ctx) != NSCD_SUCCESS) {
1380 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
1381 (me, "%s: no cache context found\n", dbname);
1383 if (UPDATEBIT & flag)
1384 return (NOTFOUND);
1385 else
1386 return (nsc_lookup_no_cache(largs, dbname));
1389 ctx = largs->ctx;
1391 if (largs->nscdb == NULL) {
1392 if ((largs->nscdb = nsc_get_db(ctx, dbop)) == NULL) {
1393 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
1394 (me, "%s:%d: no cache found\n",
1395 dbname, dbop);
1397 if (UPDATEBIT & flag)
1398 return (NOTFOUND);
1399 else
1400 return (nsc_lookup_no_cache(largs, dbname));
1404 nscdb = largs->nscdb;
1406 _NSCD_LOG_IF(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ALL) {
1407 (void) nscdb->getlogstr(nscdb->name, whoami,
1408 sizeof (whoami), &args);
1411 if (UPDATEBIT & flag) {
1412 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1413 (me, "%s: refresh start\n", whoami);
1414 } else {
1415 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1416 (me, "%s: lookup start\n", whoami);
1419 cfg_rc = check_config(largs, &cfg, whoami, flag);
1420 if (cfg_rc != CONTINUE)
1421 return (cfg_rc);
1424 * Invalidate cache if file has been modified.
1426 if (cfg.check_files == nscd_true)
1427 check_db_file(ctx, cfg, whoami, now);
1429 (void) mutex_lock(&nscdb->db_mutex);
1431 /* Lookup the cache table */
1432 for (;;) {
1433 delete = nscd_false;
1434 rc = lookup_cache(largs, &cfg, &args, whoami, &this_entry);
1435 if (rc != NSCD_SUCCESS) {
1436 (void) mutex_unlock(&nscdb->db_mutex);
1438 /* Either no entry and avoid name service */
1439 if (rc == NSCD_DB_ENTRY_NOT_FOUND ||
1440 rc == NSCD_INVALID_ARGUMENT)
1441 return (NOTFOUND);
1443 /* OR memory error */
1444 return (SERVERERROR);
1447 /* get the stats from the entry */
1448 this_stats = &this_entry->stats;
1451 * What should we do next ?
1453 switch (this_stats->status) {
1454 case ST_NEW_ENTRY:
1455 delete = nscd_true;
1456 next_action = _NSC_NSLOOKUP;
1457 break;
1458 case ST_UPDATE_PENDING:
1459 if (flag & UPDATEBIT) {
1460 (void) mutex_unlock(&nscdb->db_mutex);
1461 return (NOTFOUND);
1462 } else if (this_stats->timestamp < now)
1463 next_action = _NSC_WAIT;
1464 else
1465 next_action = _NSC_USECACHED;
1466 break;
1467 case ST_LOOKUP_PENDING:
1468 if (flag & UPDATEBIT) {
1469 (void) mutex_unlock(&nscdb->db_mutex);
1470 return (NOTFOUND);
1472 next_action = _NSC_WAIT;
1473 break;
1474 case ST_DISCARD:
1475 if (cfg.avoid_ns == nscd_true) {
1476 (void) mutex_unlock(&nscdb->db_mutex);
1477 return (NOTFOUND);
1479 /* otherwise reuse the entry */
1480 (void) memset(this_stats, 0, sizeof (*this_stats));
1481 next_action = _NSC_NSLOOKUP;
1482 break;
1483 default:
1484 if (cfg.avoid_ns == nscd_true)
1485 next_action = _NSC_USECACHED;
1486 else if ((flag & UPDATEBIT) ||
1487 (this_stats->timestamp < now)) {
1488 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1489 (me, "%s: cached entry needs to be updated\n",
1490 whoami);
1491 next_action = _NSC_NSLOOKUP;
1492 } else
1493 next_action = _NSC_USECACHED;
1494 break;
1497 if (next_action == _NSC_WAIT) {
1498 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1499 (me, "%s: need to wait\n", whoami);
1501 /* do we have clearance ? */
1502 if (_nscd_get_clearance(&ctx->throttle_sema) != 0) {
1503 /* nope. quit */
1504 (void) mutex_lock(&ctx->stats_mutex);
1505 ctx->stats.drop_count++;
1506 (void) mutex_unlock(&ctx->stats_mutex);
1507 _NSCD_LOG(NSCD_LOG_CACHE,
1508 NSCD_LOG_LEVEL_DEBUG_6)
1509 (me, "%s: throttling load\n", whoami);
1510 (void) mutex_unlock(&nscdb->db_mutex);
1511 NSC_LOOKUP_LOG(WARNING,
1512 "%s: no clearance to wait\n");
1513 return (NOSERVER);
1515 /* yes can wait */
1516 (void) nscd_wait(ctx, nscdb, this_entry);
1517 (void) _nscd_release_clearance(&ctx->throttle_sema);
1518 continue;
1521 break;
1525 if (!(UPDATEBIT & flag))
1526 this_stats->hits++; /* update hit count */
1528 if (next_action == _NSC_NSLOOKUP) {
1530 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1531 (me, "%s: name service lookup required\n", whoami);
1533 if (_nscd_get_clearance(&ctx->throttle_sema) != 0) {
1534 if (delete == nscd_true)
1535 delete_entry(nscdb, ctx, this_entry);
1536 else
1537 this_stats->status = ST_DISCARD;
1538 (void) mutex_lock(&ctx->stats_mutex);
1539 ctx->stats.drop_count++;
1540 (void) mutex_unlock(&ctx->stats_mutex);
1541 (void) mutex_unlock(&nscdb->db_mutex);
1542 NSC_LOOKUP_LOG(WARNING,
1543 "%s: no clearance for lookup\n");
1544 return (NOSERVER);
1547 /* block any threads accessing this entry */
1548 this_stats->status = (flag & UPDATEBIT) ?
1549 ST_UPDATE_PENDING : ST_LOOKUP_PENDING;
1551 /* release lock and do name service lookup */
1552 (void) mutex_unlock(&nscdb->db_mutex);
1553 nss_psearch(largs->buffer, largs->bufsize);
1554 status = NSCD_GET_STATUS(largs->buffer);
1555 (void) mutex_lock(&nscdb->db_mutex);
1556 this_stats->status = 0;
1557 (void) _nscd_release_clearance(&ctx->throttle_sema);
1559 /* signal waiting threads */
1560 (void) nscd_signal(ctx, nscdb, this_entry);
1562 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1563 (me, "%s: name service lookup status = %d\n",
1564 whoami, status);
1566 if (status == NSS_SUCCESS) {
1567 int ttl;
1570 * data found in name service
1571 * update cache
1573 status = dup_packed_buffer(largs, this_entry);
1574 if (status != NSS_SUCCESS) {
1575 delete_entry(nscdb, ctx, this_entry);
1576 (void) mutex_unlock(&nscdb->db_mutex);
1577 NSC_LOOKUP_LOG(ERROR,
1578 "%s: failed to update cache\n");
1579 return (SERVERERROR);
1583 * store unpacked key in cache
1585 status = nss_packed_getkey(this_entry->buffer,
1586 this_entry->bufsize,
1587 &dbname, &dbop, &args);
1588 if (status != NSS_SUCCESS) {
1589 delete_entry(nscdb, ctx, this_entry);
1590 (void) mutex_unlock(&nscdb->db_mutex);
1591 NSC_LOOKUP_LOG(ERROR,
1592 "%s: failed to extract key\n");
1593 return (SERVERERROR);
1595 this_entry->key = args.key; /* struct copy */
1597 /* update +ve miss count */
1598 if (!(UPDATEBIT & flag)) {
1599 (void) mutex_lock(&ctx->stats_mutex);
1600 ctx->stats.pos_misses++;
1601 (void) mutex_unlock(&ctx->stats_mutex);
1604 /* update +ve ttl */
1605 ttl = get_dns_ttl(largs->buffer, dbname);
1606 /* honor the dns ttl less than postive ttl */
1607 if (ttl < 0 || ttl > cfg.pos_ttl)
1608 ttl = cfg.pos_ttl;
1609 this_stats->timestamp = time(NULL) + ttl;
1612 * start the revalidation and reaper threads
1613 * if not already started
1615 start_threads(ctx);
1617 (void) mutex_unlock(&nscdb->db_mutex);
1618 NSC_LOOKUP_LOG(DEBUG,
1619 "%s: cache updated with positive entry\n");
1620 return (SUCCESS);
1621 } else if (status == NSS_NOTFOUND) {
1623 * data not found in name service
1624 * update cache
1626 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG_6)
1627 (me, "%s: name service lookup failed\n", whoami);
1629 if (NSCD_GET_ERRNO(largs->buffer) == ERANGE) {
1630 delete_entry(nscdb, ctx, this_entry);
1631 (void) mutex_unlock(&nscdb->db_mutex);
1632 NSC_LOOKUP_LOG(DEBUG,
1633 "%s: ERANGE, cache not updated "
1634 "with negative entry\n");
1635 return (NOTFOUND);
1638 status = dup_packed_buffer(largs, this_entry);
1639 if (status != NSS_SUCCESS) {
1640 delete_entry(nscdb, ctx, this_entry);
1641 (void) mutex_unlock(&nscdb->db_mutex);
1642 NSC_LOOKUP_LOG(ERROR,
1643 "%s: failed to update cache\n");
1644 return (SERVERERROR);
1647 /* store unpacked key in cache */
1648 status = nss_packed_getkey(this_entry->buffer,
1649 this_entry->bufsize,
1650 &dbname, &dbop, &args);
1651 if (status != NSS_SUCCESS) {
1652 delete_entry(nscdb, ctx, this_entry);
1653 (void) mutex_unlock(&nscdb->db_mutex);
1654 NSC_LOOKUP_LOG(ERROR,
1655 "%s: failed to extract key\n");
1656 return (SERVERERROR);
1658 this_entry->key = args.key; /* struct copy */
1660 /* update -ve ttl */
1661 this_stats->timestamp = time(NULL) + cfg.neg_ttl;
1663 /* update -ve miss count */
1664 if (!(UPDATEBIT & flag)) {
1665 (void) mutex_lock(&ctx->stats_mutex);
1666 ctx->stats.neg_misses++;
1667 (void) mutex_unlock(&ctx->stats_mutex);
1671 * start the revalidation and reaper threads
1672 * if not already started
1674 start_threads(ctx);
1676 (void) mutex_unlock(&nscdb->db_mutex);
1677 NSC_LOOKUP_LOG(DEBUG,
1678 "%s: cache updated with negative entry\n");
1679 return (NOTFOUND);
1680 } else {
1682 * name service lookup failed
1684 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG_6)
1685 (me, "%s: name service lookup failed\n", whoami);
1687 errnum = NSCD_GET_ERRNO(largs->buffer);
1688 if (delete == nscd_true)
1689 delete_entry(nscdb, ctx, this_entry);
1690 else
1691 this_stats->status = ST_DISCARD;
1693 (void) mutex_unlock(&nscdb->db_mutex);
1694 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
1695 (me, "%s: name service lookup failed "
1696 "(status=%d, errno=%d)\n",
1697 whoami, status, errnum);
1699 return (SERVERERROR);
1701 } else if (next_action == _NSC_USECACHED) {
1703 * found entry in cache
1705 if (UPDATEBIT & flag) {
1706 (void) mutex_unlock(&nscdb->db_mutex);
1707 NSC_LOOKUP_LOG(DEBUG, "%s: no need to update\n");
1708 return (SUCCESS);
1711 if (NSCD_GET_STATUS((nss_pheader_t *)this_entry->buffer) ==
1712 NSS_SUCCESS) {
1713 /* positive hit */
1714 (void) mutex_lock(&ctx->stats_mutex);
1715 ctx->stats.pos_hits++;
1716 (void) mutex_unlock(&ctx->stats_mutex);
1718 /* update response buffer */
1719 if (copy_result(largs->buffer,
1720 this_entry->buffer) != NSS_SUCCESS) {
1721 (void) mutex_unlock(&nscdb->db_mutex);
1722 NSC_LOOKUP_LOG(ERROR,
1723 "%s: response buffer insufficient\n");
1724 return (SERVERERROR);
1727 (void) mutex_unlock(&nscdb->db_mutex);
1728 NSC_LOOKUP_LOG(DEBUG,
1729 "%s: positive entry in cache\n");
1730 return (SUCCESS);
1731 } else {
1732 /* negative hit */
1733 (void) mutex_lock(&ctx->stats_mutex);
1734 ctx->stats.neg_hits++;
1735 (void) mutex_unlock(&ctx->stats_mutex);
1737 NSCD_SET_STATUS((nss_pheader_t *)largs->buffer,
1738 NSCD_GET_STATUS(this_entry->buffer),
1739 NSCD_GET_ERRNO(this_entry->buffer));
1740 NSCD_SET_HERRNO((nss_pheader_t *)largs->buffer,
1741 NSCD_GET_HERRNO(this_entry->buffer));
1743 (void) mutex_unlock(&nscdb->db_mutex);
1744 NSC_LOOKUP_LOG(DEBUG,
1745 "%s: negative entry in cache\n");
1746 return (NOTFOUND);
1750 (void) mutex_unlock(&nscdb->db_mutex);
1751 NSC_LOOKUP_LOG(ERROR, "%s: cache backend failure\n");
1752 return (SERVERERROR);
1756 * NSCD cache backend lookup function
1758 /*ARGSUSED*/
1759 void
1760 nsc_lookup(nsc_lookup_args_t *largs, int flag) {
1762 nss_pheader_t *phdr = (nss_pheader_t *)largs->buffer;
1763 int rc;
1765 rc = lookup_int(largs, 0);
1767 if (NSCD_GET_STATUS(phdr) == NSS_TRYLOCAL)
1768 return;
1770 switch (rc) {
1772 case SUCCESS:
1773 NSCD_SET_STATUS(phdr, NSS_SUCCESS, 0);
1774 break;
1776 case NOTFOUND:
1777 NSCD_SET_STATUS(phdr, NSS_NOTFOUND, -1);
1778 break;
1780 case SERVERERROR:
1782 * status and errno should have been set in the phdr,
1783 * if not, set status to NSS_ERROR
1785 if (NSCD_STATUS_IS_OK(phdr)) {
1786 NSCD_SET_STATUS(phdr, NSS_ERROR, 0);
1788 break;
1790 case NOSERVER:
1791 NSCD_SET_STATUS(phdr, NSS_TRYLOCAL, -1);
1792 break;
1797 static nsc_ctx_t *
1798 init_cache_ctx(int i) {
1799 nsc_ctx_t *ctx;
1801 ctx = calloc(1, sizeof (nsc_ctx_t));
1802 if (ctx == NULL)
1803 return (NULL);
1805 /* init locks and semaphores */
1806 (void) mutex_init(&ctx->file_mutex, USYNC_THREAD, NULL);
1807 (void) rwlock_init(&ctx->cfg_rwlp, USYNC_THREAD, NULL);
1808 (void) mutex_init(&ctx->stats_mutex, USYNC_THREAD, NULL);
1809 (void) _nscd_init_cache_sema(&ctx->throttle_sema, cache_name[i]);
1810 cache_init_ctx[i](ctx);
1811 cache_ctx_p[i] = ctx;
1813 return (ctx);
1817 static void
1818 revalidate(nsc_ctx_t *ctx)
1820 for (;;) {
1821 int i, slp, interval, count;
1823 (void) rw_rdlock(&ctx->cfg_rwlp);
1824 slp = ctx->cfg.pos_ttl;
1825 count = ctx->cfg.keephot;
1826 (void) rw_unlock(&ctx->cfg_rwlp);
1828 if (slp < 60)
1829 slp = 60;
1830 if (count != 0) {
1831 interval = (slp/2)/count;
1832 if (interval == 0)
1833 interval = 1;
1834 (void) sleep(slp*2/3);
1835 for (i = 0; i < ctx->db_count; i++) {
1836 getxy_keepalive(ctx, ctx->nsc_db[i],
1837 count, interval);
1839 } else {
1840 (void) sleep(slp);
1846 static void
1847 getxy_keepalive(nsc_ctx_t *ctx, nsc_db_t *nscdb, int keep, int interval)
1849 nsc_keephot_t *table;
1850 nsc_entry_t *entry, *ptr;
1851 int i;
1852 nsc_lookup_args_t *largs;
1853 nss_pheader_t *phdr;
1854 int bufsiz;
1855 char *me = "getxy_keepalive";
1857 /* we won't be here if keep == 0 so need to check that */
1859 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1860 (me, "%s: keep alive\n", nscdb->name);
1862 if ((table = maken(keep)) == NULL) {
1863 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
1864 (me, "memory allocation failure\n");
1865 exit(1);
1868 (void) mutex_lock(&nscdb->db_mutex);
1869 entry = nscdb->qtail;
1870 while (entry != NULL) {
1871 /* leave pending calls alone */
1872 if (!(entry->stats.status & ST_PENDING)) {
1873 /* do_revalidate */
1874 (void) insertn(table, entry->stats.hits, entry);
1876 entry = entry->qnext;
1878 for (i = 1; i <= keep; i++) {
1879 if (table[i].ptr == NULL)
1880 continue;
1881 ptr = (nsc_entry_t *)table[i].ptr;
1882 phdr = (nss_pheader_t *)ptr->buffer;
1883 if (NSCD_GET_STATUS(phdr) == NSS_SUCCESS)
1885 * for positive cache, in addition to the packed
1886 * header size, allocate twice the size of the
1887 * existing result (in case the result grows
1888 * larger) plus 2K (for the file/compat backend to
1889 * process a possible large entry in the /etc files)
1891 bufsiz = phdr->data_off + 2 * phdr->data_len + 2048;
1892 else
1894 * for negative cache, allocate 8K buffer to
1895 * hold result in case the next lookup may
1896 * return something (in addition to the
1897 * packed header size)
1899 bufsiz = phdr->data_off + 8096;
1900 table[i].ptr = malloc(bufsiz);
1901 if (table[i].ptr == NULL) {
1902 (void) mutex_unlock(&nscdb->db_mutex);
1903 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
1904 (me, "memory allocation failure\n");
1905 exit(1);
1907 (void) memcpy(table[i].ptr, ptr->buffer, ptr->bufsize);
1908 ((nss_pheader_t *)table[i].ptr)->pbufsiz = bufsiz;
1909 table[i].num = bufsiz;
1911 (void) mutex_unlock(&nscdb->db_mutex);
1913 /* launch update thread for each keep hot entry */
1914 for (i = keep; i > 0; i--) {
1915 if (table[i].ptr == NULL)
1916 continue; /* unused slot in table */
1917 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1918 (me, "%s: launching update\n", nscdb->name);
1919 largs = (nsc_lookup_args_t *)malloc(sizeof (*largs));
1920 if (largs == NULL) {
1921 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
1922 (me, "memory allocation failure\n");
1923 exit(1);
1925 largs->buffer = table[i].ptr;
1926 largs->bufsize = table[i].num;
1927 largs->ctx = ctx;
1928 largs->nscdb = nscdb;
1929 if (launch_update(largs) < 0)
1930 exit(1);
1931 (void) sleep(interval);
1935 * The update thread will handle freeing of buffer and largs.
1936 * Free the table here.
1938 free(table);
1942 static int
1943 launch_update(nsc_lookup_args_t *in)
1945 char *me = "launch_update";
1946 int errnum;
1948 errnum = thr_create(NULL, NULL, (void *(*)(void*))do_update,
1949 in, 0|THR_DETACHED, NULL);
1950 if (errnum != 0) {
1951 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
1952 (me, "%s: thread creation failure (%d)\n",
1953 in->nscdb->name, errnum);
1954 return (-1);
1956 return (0);
1960 static void
1961 do_update(nsc_lookup_args_t *in) {
1962 nss_pheader_t *phdr = (nss_pheader_t *)in->buffer;
1964 /* update the length of the data buffer */
1965 phdr->data_len = phdr->pbufsiz - phdr->data_off;
1967 (void) lookup_int(in, UPDATEBIT);
1968 if (in->buffer)
1969 free(in->buffer);
1970 free(in);
1975 * Invalidate cache
1977 void
1978 nsc_invalidate(nsc_ctx_t *ctx, char *dbname, nsc_ctx_t **ctxs)
1980 int i;
1981 char *me = "nsc_invalidate";
1983 if (ctx) {
1984 ctx_invalidate(ctx);
1985 return;
1988 if (dbname) {
1989 if ((i = get_cache_idx(dbname)) == -1) {
1990 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
1991 (me, "%s: invalid cache name\n", dbname);
1992 return;
1994 if ((ctx = cache_ctx_p[i]) == NULL) {
1995 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING)
1996 (me, "%s: no cache context found\n",
1997 dbname);
1998 return;
2000 ctx_invalidate(ctx);
2001 return;
2004 if (ctxs == NULL)
2005 ctxs = cache_ctx_p;
2007 for (i = 0; i < CACHE_CTX_COUNT; i++) {
2008 if (ctxs[i] != NULL)
2009 ctx_invalidate(ctxs[i]);
2015 * Invalidate cache by context
2017 static void
2018 ctx_invalidate(nsc_ctx_t *ctx)
2020 int i;
2021 nsc_entry_t *entry;
2022 char *me = "ctx_invalidate";
2024 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2025 (me, "%s: invalidate cache\n", ctx->dbname);
2027 for (i = 0; i < ctx->db_count; i++) {
2028 if (ctx->nsc_db[i] == NULL)
2029 continue;
2030 (void) mutex_lock(&ctx->nsc_db[i]->db_mutex);
2031 entry = ctx->nsc_db[i]->qtail;
2032 while (entry != NULL) {
2033 /* leave pending calls alone */
2034 if (!(entry->stats.status & ST_PENDING))
2035 entry->stats.status = ST_DISCARD;
2036 entry = entry->qnext;
2038 (void) mutex_unlock(&ctx->nsc_db[i]->db_mutex);
2041 (void) mutex_lock(&ctx->stats_mutex);
2042 ctx->stats.invalidate_count++;
2043 (void) mutex_unlock(&ctx->stats_mutex);
2048 * Free nsc_entry_t
2050 * Pre-reqs:
2051 * nscdb->db_mutex lock must be held before calling this function
2053 static void
2054 delete_entry(nsc_db_t *nscdb, nsc_ctx_t *ctx, nsc_entry_t *entry) {
2055 uint_t hash;
2057 avl_remove(&nscdb->tree, entry);
2058 HASH_REMOVE(nscdb, entry, hash, nscd_false);
2059 queue_remove(nscdb, entry);
2060 if (entry->buffer != NULL) {
2061 free(entry->buffer);
2062 entry->buffer = NULL;
2064 umem_cache_free(nsc_entry_cache, entry);
2065 (void) mutex_lock(&ctx->stats_mutex);
2066 ctx->stats.entries--;
2067 (void) mutex_unlock(&ctx->stats_mutex);
2071 static nscd_rc_t
2072 lookup_cache(nsc_lookup_args_t *largs, nscd_cfg_cache_t *cfgp,
2073 nss_XbyY_args_t *argp, char *whoami, nsc_entry_t **entry)
2075 nsc_db_t *nscdb;
2076 nsc_ctx_t *ctx;
2077 uint_t hash;
2078 avl_index_t pos;
2079 ulong_t nentries;
2080 nsc_entry_t find_entry, *node;
2081 char *me = "lookup_cache";
2083 ctx = largs->ctx;
2084 nscdb = largs->nscdb;
2086 /* set the search key */
2087 find_entry.key = argp->key; /* struct copy (not deep) */
2089 /* lookup the hash table ==> O(1) */
2090 if (nscdb->htable) {
2091 *entry = hash_find(nscdb, &find_entry, &hash, nscd_true);
2092 if (*entry != NULL) {
2093 (void) queue_adjust(nscdb, *entry);
2094 return (NSCD_SUCCESS);
2098 /* if not found, lookup the AVL tree ==> O(log n) */
2099 *entry = (nsc_entry_t *)avl_find(&nscdb->tree, &find_entry, &pos);
2100 if (*entry != NULL) {
2101 (void) queue_adjust(nscdb, *entry);
2102 /* move it to the hash table */
2103 if (nscdb->htable) {
2104 if (nscdb->htable[hash] == NULL ||
2105 (*entry)->stats.hits >=
2106 nscdb->htable[hash]->stats.hits) {
2107 nscdb->htable[hash] = *entry;
2110 return (NSCD_SUCCESS);
2113 /* entry not found in the cache */
2114 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2115 (me, "%s: cache miss\n", whoami);
2117 if (cfgp->avoid_ns == nscd_true) {
2118 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2119 (me, "%s: avoid name service\n", whoami);
2120 return (NSCD_DB_ENTRY_NOT_FOUND);
2123 /* allocate memory for new entry (stub) */
2124 *entry = (nsc_entry_t *)umem_cache_alloc(nsc_entry_cache,
2125 UMEM_DEFAULT);
2126 if (*entry == NULL) {
2127 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
2128 (me, "%s: memory allocation failure\n", whoami);
2129 return (NSCD_NO_MEMORY);
2131 (void) memset(*entry, 0, sizeof (**entry));
2134 * Note that the actual data for the key is stored within
2135 * the largs->buffer (input buffer to nsc_lookup).
2136 * find_entry.key only contains pointers to this data.
2138 * If largs->buffer will be re-allocated by nss_psearch
2139 * then (*entry)->key will have dangling pointers.
2140 * In such case, the following assignment needs to be
2141 * replaced by code that duplicates the key.
2143 (*entry)->key = find_entry.key;
2146 * Add the entry to the cache.
2148 avl_insert(&nscdb->tree, *entry, pos); /* O(log n) */
2149 (void) queue_adjust(nscdb, *entry); /* constant */
2150 if (nscdb->htable) /* constant */
2151 nscdb->htable[hash] = *entry;
2152 (*entry)->stats.status = ST_NEW_ENTRY;
2154 (void) mutex_lock(&ctx->stats_mutex);
2155 nentries = ++(ctx->stats.entries);
2156 (void) mutex_unlock(&ctx->stats_mutex);
2158 /* Have we exceeded max entries ? */
2159 if (cfgp->maxentries > 0 && nentries > cfgp->maxentries) {
2160 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2161 (me, "%s: maximum entries exceeded -- "
2162 "deleting least recently used entry\n",
2163 whoami);
2165 node = nscdb->qhead;
2166 while (node != NULL && node != *entry) {
2167 if (node->stats.status == ST_DISCARD ||
2168 !(node->stats.status & ST_PENDING)) {
2169 delete_entry(nscdb, ctx, node);
2170 break;
2172 node = node->qprev;
2176 * It's okay if we were not able to find one to delete.
2177 * The reaper (when invoked) will return the cache to a
2178 * safe level.
2182 return (NSCD_SUCCESS);
2185 static void
2186 reaper(nsc_ctx_t *ctx)
2188 uint_t ttl, extra_sleep, total_sleep, intervals;
2189 uint_t nodes_per_interval, seconds_per_interval;
2190 ulong_t nsc_entries;
2191 char *me = "reaper";
2193 for (;;) {
2194 (void) mutex_lock(&ctx->stats_mutex);
2195 nsc_entries = ctx->stats.entries;
2196 (void) mutex_unlock(&ctx->stats_mutex);
2198 (void) rw_rdlock(&ctx->cfg_rwlp);
2199 ttl = ctx->cfg.pos_ttl;
2200 (void) rw_unlock(&ctx->cfg_rwlp);
2202 if (nsc_entries == 0) {
2203 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2204 (me, "%s: nothing to reap\n", ctx->dbname);
2206 /* sleep for atleast 60 seconds */
2207 if (ttl < 60)
2208 ttl = 60;
2209 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2210 (me, "%s: sleep %d\n", ctx->dbname, ttl);
2211 (void) sleep(ttl);
2212 continue;
2215 if (ttl < 32) ttl = 32;
2216 if (ttl > (1<<28)) ttl = 1<<28;
2219 * minimum nodes_per_interval = 256 or 1<<8
2220 * maximum nodes_per_interval = nsc_entries
2221 * minimum seconds_per_interval = 32 or 1<<5
2222 * maximum_seconds_per_interval = ttl
2224 if (nsc_entries <= ttl) {
2225 intervals = (nsc_entries >> 8) + 1;
2226 seconds_per_interval = ttl / intervals;
2227 nodes_per_interval = 256;
2228 } else {
2229 intervals = (ttl >> 5) + 1;
2230 seconds_per_interval = 32;
2231 nodes_per_interval = nsc_entries / intervals;
2232 if (nodes_per_interval < 256)
2233 nodes_per_interval = 256;
2236 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2237 (me, "%s: total entries = %d, "
2238 "seconds per interval = %d, "
2239 "nodes per interval = %d\n",
2240 ctx->dbname, nsc_entries, seconds_per_interval,
2241 nodes_per_interval);
2242 total_sleep = reap_cache(ctx, nodes_per_interval,
2243 seconds_per_interval);
2244 extra_sleep = 1 + ttl - total_sleep;
2245 if (extra_sleep > 0)
2246 (void) sleep(extra_sleep);
2251 static uint_t
2252 reap_cache(nsc_ctx_t *ctx, uint_t nodes_per_interval,
2253 uint_t seconds_per_interval)
2255 uint_t nodes_togo, total_sleep;
2256 time_t now;
2257 nsc_entry_t *node, *next_node;
2258 nsc_db_t *nscdb;
2259 uint_t primes[] = {_NSC_HTSIZE_PRIMES};
2260 ulong_t count, nentries, maxentries;
2261 int i, slot, value, newhtsize;
2262 char *me = "reap_cache";
2264 count = 0;
2265 total_sleep = 0;
2266 nodes_togo = nodes_per_interval;
2267 now = time(NULL);
2269 for (i = 0; i < ctx->db_count; i++) {
2270 nscdb = ctx->nsc_db[i];
2271 (void) mutex_lock(&nscdb->db_mutex);
2272 nscdb->reap_node = nscdb->qtail;
2273 while (nscdb->reap_node != NULL) {
2274 if (nodes_togo == 0) {
2275 (void) mutex_unlock(&nscdb->db_mutex);
2276 (void) sleep(seconds_per_interval);
2277 total_sleep += seconds_per_interval;
2278 nodes_togo = nodes_per_interval;
2279 now = time(NULL);
2280 (void) mutex_lock(&nscdb->db_mutex);
2282 /* delete ST_DISCARD and expired nodes */
2283 if ((node = nscdb->reap_node) == NULL)
2284 break;
2285 if (node->stats.status == ST_DISCARD ||
2286 (!(node->stats.status & ST_PENDING) &&
2287 node->stats.timestamp < now)) {
2289 * Delete entry if its discard flag is
2290 * set OR if it has expired. Entries
2291 * with pending updates are not
2292 * deleted.
2293 * nscdb->reap_node will be adjusted
2294 * by delete_entry()
2296 delete_entry(nscdb, ctx, node);
2297 count++;
2298 } else {
2299 nscdb->reap_node = node->qnext;
2301 nodes_togo--;
2304 if (nscdb->htsize == 0) {
2305 (void) mutex_unlock(&nscdb->db_mutex);
2306 continue;
2310 * Dynamic adjustment of hash table size.
2312 * Hash table size is roughly 1/8th of the
2313 * total entries. However the size is changed
2314 * only when the number of entries double or
2315 * reduced by half
2317 nentries = avl_numnodes(&nscdb->tree);
2318 for (slot = 0, value = _NSC_INIT_HTSIZE_SLOT_VALUE;
2319 slot < _NSC_HTSIZE_NUM_SLOTS && nentries > value;
2320 value = (value << 1) + 1, slot++)
2322 if (nscdb->hash_type == nsc_ht_power2)
2323 newhtsize = _NSC_INIT_HTSIZE_POWER2 << slot;
2324 else
2325 newhtsize = primes[slot];
2327 /* Recommended size is same as the current size. Done */
2328 if (nscdb->htsize == newhtsize) {
2329 (void) mutex_unlock(&nscdb->db_mutex);
2330 continue;
2333 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2334 (me, "%s: resizing hash table from %d to %d\n",
2335 nscdb->name, nscdb->htsize, newhtsize);
2338 * Dump old hashes because it would be time
2339 * consuming to rehash them.
2341 (void) free(nscdb->htable);
2342 nscdb->htable = calloc(newhtsize, sizeof (*(nscdb->htable)));
2343 if (nscdb->htable == NULL) {
2344 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR)
2345 (me, "%s: memory allocation failure\n",
2346 nscdb->name);
2347 /* -1 to try later */
2348 nscdb->htsize = -1;
2349 } else {
2350 nscdb->htsize = newhtsize;
2352 (void) mutex_unlock(&nscdb->db_mutex);
2355 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2356 (me, "%s: reaped %lu entries\n", ctx->dbname, count);
2359 * if cache is almost full then reduce it to a safe level by
2360 * evicting LRU entries
2363 (void) rw_rdlock(&ctx->cfg_rwlp);
2364 maxentries = ctx->cfg.maxentries;
2365 (void) rw_unlock(&ctx->cfg_rwlp);
2367 /* No limit on number of entries. Done */
2368 if (maxentries == 0)
2369 goto out;
2371 (void) mutex_lock(&ctx->stats_mutex);
2372 nentries = ctx->stats.entries;
2373 (void) mutex_unlock(&ctx->stats_mutex);
2375 /* what is the percentage of cache used ? */
2376 value = (nentries * 100) / maxentries;
2377 if (value < _NSC_EVICTION_START_LEVEL)
2378 goto out;
2381 * cache needs to be reduced to a safe level
2383 value -= _NSC_EVICTION_SAFE_LEVEL;
2384 for (i = 0, count = 0; i < ctx->db_count; i++) {
2386 * Reduce each subcache by 'value' percent
2388 nscdb = ctx->nsc_db[i];
2389 (void) mutex_lock(&nscdb->db_mutex);
2390 nodes_togo = (value * avl_numnodes(&nscdb->tree)) / 100;
2392 /* Start from LRU entry i.e queue head */
2393 next_node = nscdb->qhead;
2394 while (nodes_togo > 0 && next_node != NULL) {
2395 node = next_node;
2396 next_node = next_node->qprev;
2397 if (node->stats.status == ST_DISCARD ||
2398 !(node->stats.status & ST_PENDING)) {
2399 /* Leave nodes with pending updates alone */
2400 delete_entry(nscdb, ctx, node);
2401 count++;
2402 nodes_togo--;
2405 (void) mutex_unlock(&nscdb->db_mutex);
2408 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2409 (me, "%s: evicted %lu LRU entries\n", ctx->dbname, count);
2411 out:
2412 return (total_sleep);