8158 Want named threads API
[unleashed.git] / usr / src / cmd / nscd / cache.c
blob288c6e767e42df4e166ae8156c6c407cfecfb101
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.
25 * Copyright 2018 Joyent, Inc.
29 * Cache routines for nscd
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"
59 #define SUCCESS 0
60 #define NOTFOUND -1
61 #define SERVERERROR -2
62 #define NOSERVER -3
63 #define CONTINUE -4
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);
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);
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 */
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);
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 *);
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 *);
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 *);
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);
107 static umem_cache_t *nsc_entry_cache;
109 static nsc_ctx_t *init_cache_ctx(int);
110 static void reaper(nsc_ctx_t *);
111 static void revalidate(nsc_ctx_t *);
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;
121 if (NSCD_GET_STATUS(sphdr) != NSS_SUCCESS) {
123 /* no result, copy header only (status, errno, etc) */
124 slen = sphdr->data_off;
125 } else {
127 * lookup result returned, data to copy is the packed
128 * header plus result (add 1 for the terminating NULL
129 * just in case)
131 slen = sphdr->data_off + sphdr->data_len + 1;
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;
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;
152 (void) memcpy(dphdr, sphdr, slen);
153 if (new_pbufsiz != 0)
154 dphdr->pbufsiz = new_pbufsiz;
156 return (NSS_SUCCESS);
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
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
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;
209 * Given database name 'dbname' find cache index
212 get_cache_idx(char *dbname) {
213 int i;
214 char *nsc_name;
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);
221 return (-1);
225 * Given database name 'dbname' retrieve cache context,
226 * if not created yet, allocate and initialize it.
228 static nscd_rc_t
229 get_cache_ctx(char *dbname, nsc_ctx_t **ctx) {
230 int i;
232 *ctx = NULL;
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);
243 return (NSCD_SUCCESS);
247 * Generate a log string to identify backend operation in debug logs
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);
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);
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);
271 * Returns cache based on dbop
273 static nsc_db_t *
274 nsc_get_db(nsc_ctx_t *ctx, int dbop) {
275 int i;
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]);
281 return (NULL);
286 * integer compare routine for _NSC_DB_INT_KEY
288 static int
289 nsc_db_int_key_compar(const void *n1, const void *n2) {
290 nsc_entry_t *e1, *e2;
292 e1 = (nsc_entry_t *)n1;
293 e2 = (nsc_entry_t *)n2;
294 return (_NSC_INT_KEY_CMP(e1->key.number, e2->key.number));
299 * case sensitive name compare routine for _NSC_DB_CES_KEY
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;
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));
316 * case insensitive name compare routine _NSC_DB_CIS_KEY
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;
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));
332 * macro used to generate elf hashes for strings
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; \
343 hval %= htsize;
347 * cis hash function
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);
360 * ces hash function
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);
373 * one-at-a-time hash function
375 uint_t
376 db_gethash(const void *key, int len, int htsize) {
377 uint_t hval, i;
378 const char *str = key;
380 if (str == NULL)
381 return (0);
383 for (hval = 0, i = 0; i < len; i++) {
384 hval += str[i];
385 hval += (hval << 10);
386 hval ^= (hval >> 6);
388 hval += (hval << 3);
389 hval ^= (hval >> 11);
390 hval += (hval << 15);
391 return (hval % htsize);
396 * case insensitive name gethash routine _NSC_DB_CIS_KEY
398 static uint_t
399 nsc_db_cis_key_gethash(nss_XbyY_key_t *key, int htsize) {
400 return (cis_gethash(key->name, htsize));
405 * case sensitive name gethash routine _NSC_DB_CES_KEY
407 static uint_t
408 nsc_db_ces_key_gethash(nss_XbyY_key_t *key, int htsize) {
409 return (ces_gethash(key->name, htsize));
414 * integer gethash routine _NSC_DB_INT_KEY
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));
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
430 static nsc_entry_t *
431 hash_find(nsc_db_t *nscdb, nsc_entry_t *entry, uint_t *hash,
432 nscd_bool_t cmp) {
434 nsc_entry_t *hashentry;
436 if (nscdb->gethash)
437 *hash = nscdb->gethash(&entry->key, nscdb->htsize);
438 else
439 return (NULL);
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);
448 return (NULL);
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; \
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; \
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];
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;
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);
501 #endif /* NSCD_DEBUG */
503 static void
504 print_stats(nscd_cfg_stat_cache_t *statsp) {
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);
524 _NSC_GET_HITRATE(statsp);
525 (void) fprintf(stdout, gettext("\t cache hit rate: %10.1f\n"),
526 statsp->hitrate);
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");
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;
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);
571 #endif /* NSCD_DEBUG */
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;
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);
587 #endif /* NSCD_DEBUG */
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;
596 (void) fprintf(stdout,
597 gettext("\n\nCACHE [name=%s, nodes=%lu]:\n"),
598 nscdb->name, avl_numnodes(&nscdb->tree));
600 (void) fprintf(stdout,
601 gettext("Starting with the most recently accessed:\n"));
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);
608 #endif /* NSCD_DEBUG */
610 static void
611 queue_remove(nsc_db_t *nscdb, nsc_entry_t *entry) {
613 if (nscdb->qtail == entry)
614 nscdb->qtail = entry->qnext;
615 else
616 entry->qprev->qnext = entry->qnext;
618 if (nscdb->qhead == entry)
619 nscdb->qhead = entry->qprev;
620 else
621 entry->qnext->qprev = entry->qprev;
623 if (nscdb->reap_node == entry)
624 nscdb->reap_node = entry->qnext;
625 entry->qnext = entry->qprev = NULL;
629 static void
630 queue_adjust(nsc_db_t *nscdb, nsc_entry_t *entry) {
632 #ifdef NSCD_DEBUG
633 assert(nscdb->qtail || entry->qnext == NULL &&
634 entry->qprev == NULL);
636 assert(nscdb->qtail && nscdb->qhead ||
637 nscdb->qtail == NULL && nscdb->qhead == NULL);
639 assert(entry->qprev || entry->qnext == NULL ||
640 nscdb->qtail == entry);
641 #endif /* NSCD_DEBUG */
643 /* already in the desired position */
644 if (nscdb->qtail == entry)
645 return;
647 /* new queue */
648 if (nscdb->qtail == NULL) {
649 nscdb->qhead = nscdb->qtail = entry;
650 return;
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;
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;
677 * Init cache
679 nscd_rc_t
680 init_cache(int debug_level) {
681 int cflags;
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);
694 * Create cache
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)
703 nsc_db_t *nscdb;
704 char *me = "make_cache";
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;
712 (void) memset(nscdb, 0, sizeof (*nscdb));
714 nscdb->dbop = dbop;
715 nscdb->name = name;
716 nscdb->db_type = dbtype;
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;
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));
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;
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;
763 nscdb->htsize = htsize;
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;
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;
788 (void) mutex_init(&nscdb->db_mutex, USYNC_THREAD, NULL);
789 return (nscdb);
791 out:
792 if (nscdb->htable)
793 free(nscdb->htable);
794 if (nscdb)
795 free(nscdb);
796 return (NULL);
801 * verify
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)
814 return (NSCD_SUCCESS);
818 * notify
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)
830 nsc_ctx_t *ctx;
831 void *dp;
832 int i;
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);
852 } else {
853 /* non-global config for a specific db */
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);
863 return (NSCD_SUCCESS);
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);
884 } else {
885 /* non-global config for a specific db */
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);
896 return (NSCD_SUCCESS);
901 * get stat
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)
913 nscd_cfg_stat_cache_t *statsp, stats;
914 nsc_ctx_t *ctx;
915 int i;
916 nscd_rc_t rc;
918 statsp = calloc(1, sizeof (*statsp));
919 if (statsp == NULL)
920 return (NSCD_NO_MEMORY);
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);
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;
942 } else {
943 if ((rc = get_cache_ctx(nswdb->name, &ctx)) != NSCD_SUCCESS) {
944 free(statsp);
945 return (rc);
947 (void) mutex_lock(&ctx->stats_mutex);
948 *statsp = ctx->stats;
949 (void) mutex_unlock(&ctx->stats_mutex);
952 _NSC_GET_HITRATE(statsp);
953 *stat = statsp;
954 return (NSCD_SUCCESS);
958 * This function should only be called when nscd is
959 * not a daemon.
961 void
962 nsc_info(nsc_ctx_t *ctx, char *dbname, nscd_cfg_cache_t cfg[],
963 nscd_cfg_stat_cache_t stats[])
965 int i;
966 char *me = "nsc_info";
967 nsc_ctx_t *ctx1;
968 nsc_ctx_t ctx2;
969 nscd_rc_t rc;
971 if (ctx) {
972 ctx_info(ctx);
973 return;
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;
988 ctx_info(ctx1);
989 return;
992 if (cfg == NULL || stats == NULL)
993 return;
995 for (i = 0; i < CACHE_CTX_COUNT; i++) {
997 ctx2.dbname = cache_name[i];
998 ctx2.cfg = cfg[i];
999 ctx2.stats = stats[i];
1000 ctx_info_nolock(&ctx2);
1004 static void
1005 ctx_info_nolock(nsc_ctx_t *ctx) {
1006 nscd_cfg_cache_t cfg;
1007 nscd_cfg_stat_cache_t stats;
1009 cfg = ctx->cfg;
1010 (void) fprintf(stdout, gettext("\n\nCACHE: %s\n"), ctx->dbname);
1011 (void) print_cfg(&cfg);
1013 if (cfg.enable == nscd_false)
1014 return;
1016 stats = ctx->stats;
1017 (void) print_stats(&stats);
1020 static void
1021 ctx_info(nsc_ctx_t *ctx) {
1022 nscd_cfg_cache_t cfg;
1023 nscd_cfg_stat_cache_t stats;
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);
1031 if (cfg.enable == nscd_false)
1032 return;
1034 (void) mutex_lock(&ctx->stats_mutex);
1035 stats = ctx->stats;
1036 (void) mutex_unlock(&ctx->stats_mutex);
1037 (void) print_stats(&stats);
1040 #ifdef NSCD_DEBUG
1042 * This function should only be called when nscd is
1043 * not a daemon.
1046 nsc_dump(char *dbname, int dbop)
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;
1055 if ((i = get_cache_idx(dbname)) == -1) {
1056 (void) fprintf(stdout, gettext("invalid cache name\n"));
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);
1063 if ((ctx = cache_ctx_p[i]) == NULL) {
1064 (void) fprintf(stdout, gettext("no cache context\n"));
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);
1071 now = time(NULL);
1072 (void) rw_rdlock(&ctx->cfg_rwlp);
1073 enabled = ctx->cfg.enable;
1074 (void) rw_unlock(&ctx->cfg_rwlp);
1076 if (enabled == nscd_false)
1077 return (NSCD_CACHE_DISABLED);
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);
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);
1093 #endif /* NSCD_DEBUG */
1096 * These macros are for exclusive use of nsc_lookup
1098 #define NSC_LOOKUP_LOG(loglevel, fmt) \
1099 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_##loglevel) \
1100 (me, fmt, whoami);
1102 static int
1103 nsc_lookup_no_cache(nsc_lookup_args_t *largs, const char *str)
1105 char *me = "nsc_lookup_no_cache";
1106 nss_status_t status;
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);
1124 * This function starts the revalidation and reaper threads
1125 * for a cache
1127 static void
1128 start_threads(nsc_ctx_t *ctx)
1130 int errnum;
1131 char *me = "start_threads";
1134 * kick off the revalidate thread (if necessary)
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);
1145 ctx->revalidate_on = nscd_true;
1149 * kick off the reaper thread (if necessary)
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);
1160 ctx->reaper_on = nscd_true;
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.
1169 static int
1170 nsw_config_in_phdr(void *buf)
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";
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);
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);
1192 static nss_status_t
1193 copy_result(void *rbuf, void *cbuf)
1195 nss_pheader_t *rphdr = (nss_pheader_t *)rbuf;
1196 nss_pheader_t *cphdr = (nss_pheader_t *)cbuf;
1197 char *me = "copy_result";
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;
1206 if (cphdr->data_len == 0)
1207 return (NSS_SUCCESS);
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';
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);
1220 return (NSS_SUCCESS);
1224 static int
1225 get_dns_ttl(void *pbuf, char *dbname)
1227 nss_pheader_t *phdr = (nss_pheader_t *)pbuf;
1228 int ttl;
1229 char *me = "get_dns_ttl";
1231 /* if returned, dns ttl is stored in the extended data area */
1232 if (phdr->ext_off == 0)
1233 return (-1);
1235 if (strcmp(dbname, NSS_DBNAM_HOSTS) != 0 &&
1236 strcmp(dbname, NSS_DBNAM_IPNODES) != 0)
1237 return (-1);
1239 ttl = *(nssuint_t *)((void *)((char *)pbuf + phdr->ext_off));
1241 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1242 (me, "dns ttl is %d seconds\n", ttl);
1244 return (ttl);
1247 static int
1248 check_config(nsc_lookup_args_t *largs, nscd_cfg_cache_t *cfgp,
1249 char *whoami, int flag)
1251 nsc_db_t *nscdb;
1252 nsc_ctx_t *ctx;
1253 char *me = "check_config";
1255 ctx = largs->ctx;
1256 nscdb = largs->nscdb;
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);
1268 *cfgp = nscdb->cfg;
1270 if (cfgp->enable == nscd_false) {
1271 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1272 (me, "%s: cache disabled\n", ctx->dbname);
1274 if (UPDATEBIT & flag)
1275 return (NOTFOUND);
1276 else
1277 return (nsc_lookup_no_cache(largs, whoami));
1281 * if caller requests lookup using its
1282 * own nsswitch config, bypass cache
1284 if (nsw_config_in_phdr(largs->buffer))
1285 return (nsc_lookup_no_cache(largs, whoami));
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));
1296 return (CONTINUE);
1300 * Invalidate cache if database file has been modified.
1301 * See check_files config param for details.
1303 static void
1304 check_db_file(nsc_ctx_t *ctx, nscd_cfg_cache_t cfg,
1305 char *whoami, time_t now)
1307 struct stat buf;
1308 nscd_bool_t file_modified = nscd_false;
1309 char *me = "check_db_file";
1311 if (cfg.check_interval != 0 &&
1312 (now - ctx->file_chktime) < cfg.check_interval)
1313 return;
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;
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;
1337 (void) mutex_unlock(&ctx->file_mutex);
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);
1349 static int
1350 lookup_int(nsc_lookup_args_t *largs, int flag)
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";
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);
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);
1384 if (UPDATEBIT & flag)
1385 return (NOTFOUND);
1386 else
1387 return (nsc_lookup_no_cache(largs, dbname));
1390 ctx = largs->ctx;
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);
1398 if (UPDATEBIT & flag)
1399 return (NOTFOUND);
1400 else
1401 return (nsc_lookup_no_cache(largs, dbname));
1405 nscdb = largs->nscdb;
1407 _NSCD_LOG_IF(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ALL) {
1408 (void) nscdb->getlogstr(nscdb->name, whoami,
1409 sizeof (whoami), &args);
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);
1420 cfg_rc = check_config(largs, &cfg, whoami, flag);
1421 if (cfg_rc != CONTINUE)
1422 return (cfg_rc);
1425 * Invalidate cache if file has been modified.
1427 if (cfg.check_files == nscd_true)
1428 check_db_file(ctx, cfg, whoami, now);
1430 (void) mutex_lock(&nscdb->db_mutex);
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);
1439 /* Either no entry and avoid name service */
1440 if (rc == NSCD_DB_ENTRY_NOT_FOUND ||
1441 rc == NSCD_INVALID_ARGUMENT)
1442 return (NOTFOUND);
1444 /* OR memory error */
1445 return (SERVERERROR);
1448 /* get the stats from the entry */
1449 this_stats = &this_entry->stats;
1452 * What should we do next ?
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);
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);
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;
1498 if (next_action == _NSC_WAIT) {
1499 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1500 (me, "%s: need to wait\n", whoami);
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);
1516 /* yes can wait */
1517 (void) nscd_wait(ctx, nscdb, this_entry);
1518 (void) _nscd_release_clearance(&ctx->throttle_sema);
1519 continue;
1522 break;
1526 if (!(UPDATEBIT & flag))
1527 this_stats->hits++; /* update hit count */
1529 if (next_action == _NSC_NSLOOKUP) {
1531 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1532 (me, "%s: name service lookup required\n", whoami);
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);
1548 /* block any threads accessing this entry */
1549 this_stats->status = (flag & UPDATEBIT) ?
1550 ST_UPDATE_PENDING : ST_LOOKUP_PENDING;
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);
1560 /* signal waiting threads */
1561 (void) nscd_signal(ctx, nscdb, this_entry);
1563 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1564 (me, "%s: name service lookup status = %d\n",
1565 whoami, status);
1567 if (status == NSS_SUCCESS) {
1568 int ttl;
1571 * data found in name service
1572 * update cache
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);
1584 * store unpacked key in cache
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);
1596 this_entry->key = args.key; /* struct copy */
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);
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;
1613 * start the revalidation and reaper threads
1614 * if not already started
1616 start_threads(ctx);
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) {
1624 * data not found in name service
1625 * update cache
1627 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG_6)
1628 (me, "%s: name service lookup failed\n", whoami);
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);
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);
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);
1659 this_entry->key = args.key; /* struct copy */
1661 /* update -ve ttl */
1662 this_stats->timestamp = time(NULL) + cfg.neg_ttl;
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);
1672 * start the revalidation and reaper threads
1673 * if not already started
1675 start_threads(ctx);
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 {
1683 * name service lookup failed
1685 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG_6)
1686 (me, "%s: name service lookup failed\n", whoami);
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;
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);
1700 return (SERVERERROR);
1702 } else if (next_action == _NSC_USECACHED) {
1704 * found entry in cache
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);
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);
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);
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);
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));
1744 (void) mutex_unlock(&nscdb->db_mutex);
1745 NSC_LOOKUP_LOG(DEBUG,
1746 "%s: negative entry in cache\n");
1747 return (NOTFOUND);
1751 (void) mutex_unlock(&nscdb->db_mutex);
1752 NSC_LOOKUP_LOG(ERROR, "%s: cache backend failure\n");
1753 return (SERVERERROR);
1757 * NSCD cache backend lookup function
1759 /*ARGSUSED*/
1760 void
1761 nsc_lookup(nsc_lookup_args_t *largs, int flag) {
1763 nss_pheader_t *phdr = (nss_pheader_t *)largs->buffer;
1764 int rc;
1766 rc = lookup_int(largs, 0);
1768 if (NSCD_GET_STATUS(phdr) == NSS_TRYLOCAL)
1769 return;
1771 switch (rc) {
1773 case SUCCESS:
1774 NSCD_SET_STATUS(phdr, NSS_SUCCESS, 0);
1775 break;
1777 case NOTFOUND:
1778 NSCD_SET_STATUS(phdr, NSS_NOTFOUND, -1);
1779 break;
1781 case SERVERERROR:
1783 * status and errno should have been set in the phdr,
1784 * if not, set status to NSS_ERROR
1786 if (NSCD_STATUS_IS_OK(phdr)) {
1787 NSCD_SET_STATUS(phdr, NSS_ERROR, 0);
1789 break;
1791 case NOSERVER:
1792 NSCD_SET_STATUS(phdr, NSS_TRYLOCAL, -1);
1793 break;
1798 static nsc_ctx_t *
1799 init_cache_ctx(int i) {
1800 nsc_ctx_t *ctx;
1802 ctx = calloc(1, sizeof (nsc_ctx_t));
1803 if (ctx == NULL)
1804 return (NULL);
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;
1814 return (ctx);
1818 static void
1819 revalidate(nsc_ctx_t *ctx)
1821 (void) thr_setname(thr_self(), "revalidate");
1823 for (;;) {
1824 int i, slp, interval, count;
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);
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);
1842 } else {
1843 (void) sleep(slp);
1849 static void
1850 getxy_keepalive(nsc_ctx_t *ctx, nsc_db_t *nscdb, int keep, int interval)
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";
1860 /* we won't be here if keep == 0 so need to check that */
1862 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
1863 (me, "%s: keep alive\n", nscdb->name);
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);
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);
1879 entry = entry->qnext;
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)
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)
1894 bufsiz = phdr->data_off + 2 * phdr->data_len + 2048;
1895 else
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)
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);
1910 (void) memcpy(table[i].ptr, ptr->buffer, ptr->bufsize);
1911 ((nss_pheader_t *)table[i].ptr)->pbufsiz = bufsiz;
1912 table[i].num = bufsiz;
1914 (void) mutex_unlock(&nscdb->db_mutex);
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);
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);
1938 * The update thread will handle freeing of buffer and largs.
1939 * Free the table here.
1941 free(table);
1945 static int
1946 launch_update(nsc_lookup_args_t *in)
1948 char *me = "launch_update";
1949 int errnum;
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);
1959 return (0);
1963 static void
1964 do_update(nsc_lookup_args_t *in) {
1965 nss_pheader_t *phdr = (nss_pheader_t *)in->buffer;
1967 (void) thr_setname(thr_self(), "do_update");
1969 /* update the length of the data buffer */
1970 phdr->data_len = phdr->pbufsiz - phdr->data_off;
1972 (void) lookup_int(in, UPDATEBIT);
1973 if (in->buffer)
1974 free(in->buffer);
1975 free(in);
1980 * Invalidate cache
1982 void
1983 nsc_invalidate(nsc_ctx_t *ctx, char *dbname, nsc_ctx_t **ctxs)
1985 int i;
1986 char *me = "nsc_invalidate";
1988 if (ctx) {
1989 ctx_invalidate(ctx);
1990 return;
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;
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;
2005 ctx_invalidate(ctx);
2006 return;
2009 if (ctxs == NULL)
2010 ctxs = cache_ctx_p;
2012 for (i = 0; i < CACHE_CTX_COUNT; i++) {
2013 if (ctxs[i] != NULL)
2014 ctx_invalidate(ctxs[i]);
2020 * Invalidate cache by context
2022 static void
2023 ctx_invalidate(nsc_ctx_t *ctx)
2025 int i;
2026 nsc_entry_t *entry;
2027 char *me = "ctx_invalidate";
2029 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2030 (me, "%s: invalidate cache\n", ctx->dbname);
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;
2043 (void) mutex_unlock(&ctx->nsc_db[i]->db_mutex);
2046 (void) mutex_lock(&ctx->stats_mutex);
2047 ctx->stats.invalidate_count++;
2048 (void) mutex_unlock(&ctx->stats_mutex);
2053 * Free nsc_entry_t
2055 * Pre-reqs:
2056 * nscdb->db_mutex lock must be held before calling this function
2058 static void
2059 delete_entry(nsc_db_t *nscdb, nsc_ctx_t *ctx, nsc_entry_t *entry) {
2060 uint_t hash;
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;
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);
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)
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";
2088 ctx = largs->ctx;
2089 nscdb = largs->nscdb;
2091 /* set the search key */
2092 find_entry.key = argp->key; /* struct copy (not deep) */
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);
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;
2115 return (NSCD_SUCCESS);
2118 /* entry not found in the cache */
2119 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2120 (me, "%s: cache miss\n", whoami);
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);
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);
2136 (void) memset(*entry, 0, sizeof (**entry));
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.
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.
2148 (*entry)->key = find_entry.key;
2151 * Add the entry to the cache.
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;
2159 (void) mutex_lock(&ctx->stats_mutex);
2160 nentries = ++(ctx->stats.entries);
2161 (void) mutex_unlock(&ctx->stats_mutex);
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);
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;
2177 node = node->qprev;
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.
2187 return (NSCD_SUCCESS);
2190 static void
2191 reaper(nsc_ctx_t *ctx)
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";
2198 (void) thr_setname(thr_self(), me);
2200 for (;;) {
2201 (void) mutex_lock(&ctx->stats_mutex);
2202 nsc_entries = ctx->stats.entries;
2203 (void) mutex_unlock(&ctx->stats_mutex);
2205 (void) rw_rdlock(&ctx->cfg_rwlp);
2206 ttl = ctx->cfg.pos_ttl;
2207 (void) rw_unlock(&ctx->cfg_rwlp);
2209 if (nsc_entries == 0) {
2210 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2211 (me, "%s: nothing to reap\n", ctx->dbname);
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;
2222 if (ttl < 32) ttl = 32;
2223 if (ttl > (1<<28)) ttl = 1<<28;
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
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;
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);
2258 static uint_t
2259 reap_cache(nsc_ctx_t *ctx, uint_t nodes_per_interval,
2260 uint_t seconds_per_interval)
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";
2271 count = 0;
2272 total_sleep = 0;
2273 nodes_togo = nodes_per_interval;
2274 now = time(NULL);
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);
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)) {
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()
2303 delete_entry(nscdb, ctx, node);
2304 count++;
2305 } else {
2306 nscdb->reap_node = node->qnext;
2308 nodes_togo--;
2311 if (nscdb->htsize == 0) {
2312 (void) mutex_unlock(&nscdb->db_mutex);
2313 continue;
2317 * Dynamic adjustment of hash table size.
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
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++)
2329 if (nscdb->hash_type == nsc_ht_power2)
2330 newhtsize = _NSC_INIT_HTSIZE_POWER2 << slot;
2331 else
2332 newhtsize = primes[slot];
2334 /* Recommended size is same as the current size. Done */
2335 if (nscdb->htsize == newhtsize) {
2336 (void) mutex_unlock(&nscdb->db_mutex);
2337 continue;
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);
2345 * Dump old hashes because it would be time
2346 * consuming to rehash them.
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;
2359 (void) mutex_unlock(&nscdb->db_mutex);
2362 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2363 (me, "%s: reaped %lu entries\n", ctx->dbname, count);
2366 * if cache is almost full then reduce it to a safe level by
2367 * evicting LRU entries
2370 (void) rw_rdlock(&ctx->cfg_rwlp);
2371 maxentries = ctx->cfg.maxentries;
2372 (void) rw_unlock(&ctx->cfg_rwlp);
2374 /* No limit on number of entries. Done */
2375 if (maxentries == 0)
2376 goto out;
2378 (void) mutex_lock(&ctx->stats_mutex);
2379 nentries = ctx->stats.entries;
2380 (void) mutex_unlock(&ctx->stats_mutex);
2382 /* what is the percentage of cache used ? */
2383 value = (nentries * 100) / maxentries;
2384 if (value < _NSC_EVICTION_START_LEVEL)
2385 goto out;
2388 * cache needs to be reduced to a safe level
2390 value -= _NSC_EVICTION_SAFE_LEVEL;
2391 for (i = 0, count = 0; i < ctx->db_count; i++) {
2393 * Reduce each subcache by 'value' percent
2395 nscdb = ctx->nsc_db[i];
2396 (void) mutex_lock(&nscdb->db_mutex);
2397 nodes_togo = (value * avl_numnodes(&nscdb->tree)) / 100;
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--;
2412 (void) mutex_unlock(&nscdb->db_mutex);
2415 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG)
2416 (me, "%s: evicted %lu LRU entries\n", ctx->dbname, count);
2418 out:
2419 return (total_sleep);