cache.c revision 12788:c71b0e8f856c
11590Srgrimes/* 21590Srgrimes * CDDL HEADER START 31590Srgrimes * 41590Srgrimes * The contents of this file are subject to the terms of the 51590Srgrimes * Common Development and Distribution License (the "License"). 61590Srgrimes * You may not use this file except in compliance with the License. 71590Srgrimes * 81590Srgrimes * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91590Srgrimes * or http://www.opensolaris.org/os/licensing. 101590Srgrimes * See the License for the specific language governing permissions 111590Srgrimes * and limitations under the License. 121590Srgrimes * 131590Srgrimes * When distributing Covered Code, include this CDDL HEADER in each 141590Srgrimes * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151590Srgrimes * If applicable, add the following below this CDDL HEADER, with the 161590Srgrimes * fields enclosed by brackets "[]" replaced with your own identifying 171590Srgrimes * information: Portions Copyright [yyyy] [name of copyright owner] 181590Srgrimes * 191590Srgrimes * CDDL HEADER END 201590Srgrimes */ 211590Srgrimes/* 221590Srgrimes * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 231590Srgrimes */ 241590Srgrimes 251590Srgrimes/* 261590Srgrimes * Cache routines for nscd 271590Srgrimes */ 281590Srgrimes#include <assert.h> 291590Srgrimes#include <errno.h> 301590Srgrimes#include <memory.h> 311590Srgrimes#include <signal.h> 321590Srgrimes#include <stdlib.h> 331590Srgrimes#include <stddef.h> 341590Srgrimes#include <stdio.h> 351590Srgrimes#include <string.h> 361590Srgrimes#include <sys/stat.h> 371590Srgrimes#include <sys/time.h> 3827625Scharnier#include <sys/types.h> 391590Srgrimes#include <sys/wait.h> 4027625Scharnier#include <unistd.h> 4127625Scharnier#include <ucred.h> 4250477Speter#include <nss_common.h> 431590Srgrimes#include <locale.h> 441590Srgrimes#include <ctype.h> 451590Srgrimes#include <strings.h> 461590Srgrimes#include <string.h> 471590Srgrimes#include <umem.h> 481590Srgrimes#include <fcntl.h> 491590Srgrimes#include "cache.h" 501590Srgrimes#include "nscd_door.h" 511590Srgrimes#include "nscd_log.h" 5227625Scharnier#include "nscd_config.h" 531590Srgrimes#include "nscd_frontend.h" 541590Srgrimes#include "nscd_switch.h" 551590Srgrimes 5627625Scharnier#define SUCCESS 0 571590Srgrimes#define NOTFOUND -1 581590Srgrimes#define SERVERERROR -2 591590Srgrimes#define NOSERVER -3 601590Srgrimes#define CONTINUE -4 611590Srgrimes 621590Srgrimesstatic nsc_db_t *nsc_get_db(nsc_ctx_t *, int); 631590Srgrimesstatic nscd_rc_t lookup_cache(nsc_lookup_args_t *, nscd_cfg_cache_t *, 641590Srgrimes nss_XbyY_args_t *, char *, nsc_entry_t **); 651590Srgrimesstatic uint_t reap_cache(nsc_ctx_t *, uint_t, uint_t); 661590Srgrimesstatic void delete_entry(nsc_db_t *, nsc_ctx_t *, nsc_entry_t *); 671590Srgrimesstatic void print_stats(nscd_cfg_stat_cache_t *); 681590Srgrimesstatic void print_cfg(nscd_cfg_cache_t *); 691590Srgrimesstatic int lookup_int(nsc_lookup_args_t *, int); 701590Srgrimes 711590Srgrimes#ifdef NSCD_DEBUG 721590Srgrimesstatic void print_entry(nsc_db_t *, time_t, nsc_entry_t *); 731590Srgrimesstatic void avl_dump(nsc_db_t *, time_t); 741590Srgrimesstatic void hash_dump(nsc_db_t *, time_t); 751590Srgrimes#endif /* NSCD_DEBUG */ 761590Srgrimesstatic nsc_entry_t *hash_find(nsc_db_t *, nsc_entry_t *, uint_t *, nscd_bool_t); 771590Srgrimes 781590Srgrimesstatic void queue_adjust(nsc_db_t *, nsc_entry_t *); 791590Srgrimesstatic void queue_remove(nsc_db_t *, nsc_entry_t *); 801590Srgrimes#ifdef NSCD_DEBUG 811590Srgrimesstatic void queue_dump(nsc_db_t *, time_t); 821590Srgrimes#endif /* NSCD_DEBUG */ 831590Srgrimes 841590Srgrimesstatic int launch_update(nsc_lookup_args_t *); 851590Srgrimesstatic void do_update(nsc_lookup_args_t *); 861590Srgrimesstatic void getxy_keepalive(nsc_ctx_t *, nsc_db_t *, int, int); 871590Srgrimes 881590Srgrimesstatic void ctx_info(nsc_ctx_t *); 891590Srgrimesstatic void ctx_info_nolock(nsc_ctx_t *); 901590Srgrimesstatic void ctx_invalidate(nsc_ctx_t *); 911590Srgrimes 921590Srgrimesstatic void nsc_db_str_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); 931590Srgrimesstatic void nsc_db_int_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); 941590Srgrimesstatic void nsc_db_any_key_getlogstr(char *, char *, size_t, nss_XbyY_args_t *); 951590Srgrimes 961590Srgrimesstatic int nsc_db_cis_key_compar(const void *, const void *); 971590Srgrimesstatic int nsc_db_ces_key_compar(const void *, const void *); 981590Srgrimesstatic int nsc_db_int_key_compar(const void *, const void *); 991590Srgrimes 1001590Srgrimesstatic uint_t nsc_db_cis_key_gethash(nss_XbyY_key_t *, int); 1011590Srgrimesstatic uint_t nsc_db_ces_key_gethash(nss_XbyY_key_t *, int); 1021590Srgrimesstatic uint_t nsc_db_int_key_gethash(nss_XbyY_key_t *, int); 1031590Srgrimes 1041590Srgrimesstatic umem_cache_t *nsc_entry_cache; 1051590Srgrimes 1061590Srgrimesstatic nsc_ctx_t *init_cache_ctx(int); 1071590Srgrimesstatic void reaper(nsc_ctx_t *); 1081590Srgrimesstatic void revalidate(nsc_ctx_t *); 1091590Srgrimes 1101590Srgrimesstatic nss_status_t 1111590Srgrimesdup_packed_buffer(void *src, void *dst) { 1121590Srgrimes nsc_lookup_args_t *s = (nsc_lookup_args_t *)src; 1131590Srgrimes nsc_entry_t *d = (nsc_entry_t *)dst; 1141590Srgrimes nss_pheader_t *sphdr = (nss_pheader_t *)s->buffer; 1151590Srgrimes nss_pheader_t *dphdr = (nss_pheader_t *)d->buffer; 1161590Srgrimes int slen, new_pbufsiz = 0; 1171590Srgrimes 1181590Srgrimes if (NSCD_GET_STATUS(sphdr) != NSS_SUCCESS) { 1191590Srgrimes 1201590Srgrimes /* no result, copy header only (status, errno, etc) */ 1211590Srgrimes slen = sphdr->data_off; 1221590Srgrimes } else { 1231590Srgrimes /* 1241590Srgrimes * lookup result returned, data to copy is the packed 1251590Srgrimes * header plus result (add 1 for the terminating NULL 1261590Srgrimes * just in case) 1271590Srgrimes */ 1281590Srgrimes slen = sphdr->data_off + sphdr->data_len + 1; 1291590Srgrimes } 1301590Srgrimes 1311590Srgrimes /* allocate cache packed buffer */ 1321590Srgrimes if (dphdr != NULL && d->bufsize <= slen && d->bufsize != 0) { 1331590Srgrimes /* old buffer too small, free it */ 1341590Srgrimes free(dphdr); 1351590Srgrimes d->buffer = NULL; 1361590Srgrimes d->bufsize = 0; 1371590Srgrimes dphdr = NULL; 1381590Srgrimes } 1391590Srgrimes if (dphdr == NULL) { 1401590Srgrimes /* get new buffer */ 1411590Srgrimes dphdr = calloc(1, slen + 1); 1421590Srgrimes if (dphdr == NULL) 1431590Srgrimes return (NSS_ERROR); 1441590Srgrimes d->buffer = dphdr; 1451590Srgrimes d->bufsize = slen + 1; 1461590Srgrimes new_pbufsiz = slen + 1; 1471590Srgrimes } 1481590Srgrimes 1491590Srgrimes (void) memcpy(dphdr, sphdr, slen); 1501590Srgrimes if (new_pbufsiz != 0) 1511590Srgrimes dphdr->pbufsiz = new_pbufsiz; 1521590Srgrimes 1531590Srgrimes return (NSS_SUCCESS); 1541590Srgrimes} 1551590Srgrimes 1561590Srgrimeschar *cache_name[CACHE_CTX_COUNT] = { 1571590Srgrimes NSS_DBNAM_PASSWD, 1581590Srgrimes NSS_DBNAM_GROUP, 1591590Srgrimes NSS_DBNAM_HOSTS, 1601590Srgrimes NSS_DBNAM_IPNODES, 1611590Srgrimes NSS_DBNAM_EXECATTR, 1621590Srgrimes NSS_DBNAM_PROFATTR, 1631590Srgrimes NSS_DBNAM_USERATTR, 1641590Srgrimes NSS_DBNAM_ETHERS, 1651590Srgrimes NSS_DBNAM_RPC, 1661590Srgrimes NSS_DBNAM_PROTOCOLS, 1671590Srgrimes NSS_DBNAM_NETWORKS, 1681590Srgrimes NSS_DBNAM_BOOTPARAMS, 1691590Srgrimes NSS_DBNAM_AUTHATTR, 1701590Srgrimes NSS_DBNAM_SERVICES, 1711590Srgrimes NSS_DBNAM_NETMASKS, 1721590Srgrimes NSS_DBNAM_PRINTERS, 1731590Srgrimes NSS_DBNAM_PROJECT, 1741590Srgrimes NSS_DBNAM_TSOL_TP, 1751590Srgrimes NSS_DBNAM_TSOL_RH 1761590Srgrimes}; 1778874Srgrimes 1788874Srgrimestypedef void (*cache_init_ctx_t)(nsc_ctx_t *); 1798874Srgrimesstatic cache_init_ctx_t cache_init_ctx[CACHE_CTX_COUNT] = { 1807004Sache passwd_init_ctx, 1811590Srgrimes group_init_ctx, 1821590Srgrimes host_init_ctx, 1831590Srgrimes ipnode_init_ctx, 1841590Srgrimes exec_init_ctx, 1851590Srgrimes prof_init_ctx, 1861590Srgrimes user_init_ctx, 1871590Srgrimes ether_init_ctx, 1881590Srgrimes rpc_init_ctx, 1898874Srgrimes proto_init_ctx, 1901590Srgrimes net_init_ctx, 1911590Srgrimes bootp_init_ctx, 1921590Srgrimes auth_init_ctx, 1931590Srgrimes serv_init_ctx, 1941590Srgrimes netmask_init_ctx, 1951590Srgrimes printer_init_ctx, 1961590Srgrimes project_init_ctx, 19727625Scharnier tnrhtp_init_ctx, 1981590Srgrimes tnrhdb_init_ctx 1991590Srgrimes}; 2001590Srgrimes 2011590Srgrimesnsc_ctx_t *cache_ctx_p[CACHE_CTX_COUNT] = { 0 }; 2021590Srgrimesstatic nscd_cfg_stat_cache_t null_stats = { 0 }; 2031590Srgrimesstatic nscd_cfg_global_cache_t global_cfg; 2041590Srgrimes 2051590Srgrimes/* 2061590Srgrimes * Given database name 'dbname' find cache index 2071590Srgrimes */ 20827625Scharnierint 2091590Srgrimesget_cache_idx(char *dbname) { 2101590Srgrimes int i; 2111590Srgrimes char *nsc_name; 2121590Srgrimes 2131590Srgrimes for (i = 0; i < CACHE_CTX_COUNT; i++) { 2141590Srgrimes nsc_name = cache_name[i]; 2151590Srgrimes if (strcmp(nsc_name, dbname) == 0) 2161590Srgrimes return (i); 2171590Srgrimes } 2181590Srgrimes return (-1); 2191590Srgrimes} 2201590Srgrimes 2211590Srgrimes/* 2221590Srgrimes * Given database name 'dbname' retrieve cache context, 2231590Srgrimes * if not created yet, allocate and initialize it. 2241590Srgrimes */ 2251590Srgrimesstatic nscd_rc_t 2261590Srgrimesget_cache_ctx(char *dbname, nsc_ctx_t **ctx) { 2278874Srgrimes int i; 2281590Srgrimes 2291590Srgrimes *ctx = NULL; 2301590Srgrimes 2311590Srgrimes i = get_cache_idx(dbname); 2321590Srgrimes if (i == -1) 2331590Srgrimes return (NSCD_INVALID_ARGUMENT); 2341590Srgrimes if ((*ctx = cache_ctx_p[i]) == NULL) { 2351590Srgrimes *ctx = init_cache_ctx(i); 2361590Srgrimes if (*ctx == NULL) 2371590Srgrimes return (NSCD_NO_MEMORY); 2381590Srgrimes } 2391590Srgrimes 2401590Srgrimes return (NSCD_SUCCESS); 2411590Srgrimes} 2421590Srgrimes 2431590Srgrimes/* 2441590Srgrimes * Generate a log string to identify backend operation in debug logs 2451590Srgrimes */ 2461590Srgrimesstatic void 2471590Srgrimesnsc_db_str_key_getlogstr(char *name, char *whoami, size_t len, 2481590Srgrimes nss_XbyY_args_t *argp) { 2491590Srgrimes (void) snprintf(whoami, len, "%s [key=%s]", name, argp->key.name); 2501590Srgrimes} 2511590Srgrimes 2521590Srgrimes 2531590Srgrimesstatic void 2541590Srgrimesnsc_db_int_key_getlogstr(char *name, char *whoami, size_t len, 2551590Srgrimes nss_XbyY_args_t *argp) { 2561590Srgrimes (void) snprintf(whoami, len, "%s [key=%d]", name, argp->key.number); 2571590Srgrimes} 2581590Srgrimes 2591590Srgrimes/*ARGSUSED*/ 2601590Srgrimesstatic void 2611590Srgrimesnsc_db_any_key_getlogstr(char *name, char *whoami, size_t len, 2621590Srgrimes nss_XbyY_args_t *argp) { 2631590Srgrimes (void) snprintf(whoami, len, "%s", name); 2641590Srgrimes} 2651590Srgrimes 2661590Srgrimes 2671590Srgrimes/* 2681590Srgrimes * Returns cache based on dbop 2691590Srgrimes */ 2701590Srgrimesstatic nsc_db_t * 2711590Srgrimesnsc_get_db(nsc_ctx_t *ctx, int dbop) { 2721590Srgrimes int i; 2731590Srgrimes 2741590Srgrimes for (i = 0; i < ctx->db_count; i++) { 2751590Srgrimes if (ctx->nsc_db[i] && dbop == ctx->nsc_db[i]->dbop) 2761590Srgrimes return (ctx->nsc_db[i]); 2771590Srgrimes } 2781590Srgrimes return (NULL); 2791590Srgrimes} 2801590Srgrimes 2811590Srgrimes 2821590Srgrimes/* 2831590Srgrimes * integer compare routine for _NSC_DB_INT_KEY 2841590Srgrimes */ 2851590Srgrimesstatic int 2861590Srgrimesnsc_db_int_key_compar(const void *n1, const void *n2) { 2871590Srgrimes nsc_entry_t *e1, *e2; 2881590Srgrimes 2891590Srgrimes e1 = (nsc_entry_t *)n1; 2901590Srgrimes e2 = (nsc_entry_t *)n2; 2911590Srgrimes return (_NSC_INT_KEY_CMP(e1->key.number, e2->key.number)); 2921590Srgrimes} 2931590Srgrimes 2941590Srgrimes 2951590Srgrimes/* 2961590Srgrimes * case sensitive name compare routine for _NSC_DB_CES_KEY 2971590Srgrimes */ 2981590Srgrimesstatic int 2991590Srgrimesnsc_db_ces_key_compar(const void *n1, const void *n2) { 3001590Srgrimes nsc_entry_t *e1, *e2; 3011590Srgrimes int res, l1, l2; 3021590Srgrimes 3031590Srgrimes e1 = (nsc_entry_t *)n1; 3041590Srgrimes e2 = (nsc_entry_t *)n2; 3051590Srgrimes l1 = strlen(e1->key.name); 3061590Srgrimes l2 = strlen(e2->key.name); 3071590Srgrimes res = strncmp(e1->key.name, e2->key.name, (l1 > l2)?l1:l2); 3081590Srgrimes return (_NSC_INT_KEY_CMP(res, 0)); 3091590Srgrimes} 31028386Sjlemon 3111590Srgrimes 3121590Srgrimes/* 3131590Srgrimes * case insensitive name compare routine _NSC_DB_CIS_KEY 3141590Srgrimes */ 3151590Srgrimesstatic int 3161590Srgrimesnsc_db_cis_key_compar(const void *n1, const void *n2) { 3171590Srgrimes nsc_entry_t *e1, *e2; 3181590Srgrimes int res, l1, l2; 3191590Srgrimes 3201590Srgrimes e1 = (nsc_entry_t *)n1; 3211590Srgrimes e2 = (nsc_entry_t *)n2; 3221590Srgrimes l1 = strlen(e1->key.name); 3231590Srgrimes l2 = strlen(e2->key.name); 3241590Srgrimes res = strncasecmp(e1->key.name, e2->key.name, (l1 > l2)?l1:l2); 3251590Srgrimes return (_NSC_INT_KEY_CMP(res, 0)); 3261590Srgrimes} 3271590Srgrimes 3281590Srgrimes/* 3291590Srgrimes * macro used to generate elf hashes for strings 3301590Srgrimes */ 3311590Srgrimes#define _NSC_ELF_STR_GETHASH(func, str, htsize, hval) \ 3321590Srgrimes hval = 0; \ 3331590Srgrimes while (*str) { \ 3341590Srgrimes uint_t g; \ 3351590Srgrimes hval = (hval << 4) + func(*str++); \ 3361590Srgrimes if ((g = (hval & 0xf0000000)) != 0) \ 3371590Srgrimes hval ^= g >> 24; \ 3381590Srgrimes hval &= ~g; \ 3391590Srgrimes } \ 3401590Srgrimes hval %= htsize; 3411590Srgrimes 3421590Srgrimes 3431590Srgrimes/* 3441590Srgrimes * cis hash function 3451590Srgrimes */ 3461590Srgrimesuint_t 3471590Srgrimescis_gethash(const char *key, int htsize) { 3481590Srgrimes uint_t hval; 3491590Srgrimes if (key == NULL) 3501590Srgrimes return (0); 3511590Srgrimes _NSC_ELF_STR_GETHASH(tolower, key, htsize, hval); 3521590Srgrimes return (hval); 3531590Srgrimes} 3541590Srgrimes 3551590Srgrimes 3561590Srgrimes/* 3571590Srgrimes * ces hash function 3581590Srgrimes */ 3591590Srgrimesuint_t 3601590Srgrimesces_gethash(const char *key, int htsize) { 3611590Srgrimes uint_t hval; 3621590Srgrimes if (key == NULL) 3637896Sache return (0); 3641590Srgrimes _NSC_ELF_STR_GETHASH(, key, htsize, hval); 3651590Srgrimes return (hval); 3661590Srgrimes} 3671590Srgrimes 3681590Srgrimes 3691590Srgrimes/* 3701590Srgrimes * one-at-a-time hash function 3711590Srgrimes */ 3721590Srgrimesuint_t 37338926Sstevedb_gethash(const void *key, int len, int htsize) { 37438926Ssteve uint_t hval, i; 37538926Ssteve const char *str = key; 37638926Ssteve 3771590Srgrimes if (str == NULL) 37827625Scharnier return (0); 3791590Srgrimes 3801590Srgrimes for (hval = 0, i = 0; i < len; i++) { 3811590Srgrimes hval += str[i]; 3821590Srgrimes hval += (hval << 10); 38369246Skris hval ^= (hval >> 6); 3841590Srgrimes } 3851590Srgrimes hval += (hval << 3); 3861590Srgrimes hval ^= (hval >> 11); 3871590Srgrimes hval += (hval << 15); 3881590Srgrimes return (hval % htsize); 3891590Srgrimes} 3901590Srgrimes 3911590Srgrimes 3921590Srgrimes/* 3935165Sache * case insensitive name gethash routine _NSC_DB_CIS_KEY 3945165Sache */ 3951590Srgrimesstatic uint_t 3961590Srgrimesnsc_db_cis_key_gethash(nss_XbyY_key_t *key, int htsize) { 3971590Srgrimes return (cis_gethash(key->name, htsize)); 3981590Srgrimes} 3991590Srgrimes 4001590Srgrimes 4011590Srgrimes/* 4021590Srgrimes * case sensitive name gethash routine _NSC_DB_CES_KEY 4031590Srgrimes */ 4041590Srgrimesstatic uint_t 4051590Srgrimesnsc_db_ces_key_gethash(nss_XbyY_key_t *key, int htsize) { 4061590Srgrimes return (ces_gethash(key->name, htsize)); 4071590Srgrimes} 4081590Srgrimes 4091590Srgrimes 4101590Srgrimes/* 4111590Srgrimes * integer gethash routine _NSC_DB_INT_KEY 4121590Srgrimes */ 4131590Srgrimesstatic uint_t 4141590Srgrimesnsc_db_int_key_gethash(nss_XbyY_key_t *key, int htsize) { 4151590Srgrimes return (db_gethash(&key->number, sizeof (key->number), htsize)); 4161590Srgrimes} 4171590Srgrimes 4181590Srgrimes 4191590Srgrimes/* 4201590Srgrimes * Find entry in the hash table 4211590Srgrimes * if cmp == nscd_true) 4221590Srgrimes * return entry only if the keys match 4231590Srgrimes * else 4241590Srgrimes * return entry in the hash location without checking the keys 4251590Srgrimes * 4261590Srgrimes */ 4271590Srgrimesstatic nsc_entry_t * 4281590Srgrimeshash_find(nsc_db_t *nscdb, nsc_entry_t *entry, uint_t *hash, 4291590Srgrimes nscd_bool_t cmp) { 4301590Srgrimes 4311590Srgrimes nsc_entry_t *hashentry; 43224901Sjoerg 43324901Sjoerg if (nscdb->gethash) 43424901Sjoerg *hash = nscdb->gethash(&entry->key, nscdb->htsize); 43524901Sjoerg else 43624901Sjoerg return (NULL); 43724901Sjoerg 43824901Sjoerg hashentry = nscdb->htable[*hash]; 43924901Sjoerg if (cmp == nscd_false || hashentry == NULL) 44024901Sjoerg return (hashentry); 44124901Sjoerg if (nscdb->compar) { 4421590Srgrimes if (nscdb->compar(entry, hashentry) == 0) 4431590Srgrimes return (hashentry); 4441590Srgrimes } 4451590Srgrimes return (NULL); 4461590Srgrimes} 4471590Srgrimes 4481590Srgrimes 4491590Srgrimes#define HASH_REMOVE(nscdb, entry, hash, cmp) \ 4501590Srgrimes if (nscdb->htable) { \ 4511590Srgrimes if (entry == hash_find(nscdb, entry, &hash, cmp)) \ 4521590Srgrimes nscdb->htable[hash] = NULL; \ 4531590Srgrimes } 4541590Srgrimes 4551590Srgrimes 4561590Srgrimes#define HASH_INSERT(nscdb, entry, hash, cmp) \ 4571590Srgrimes if (nscdb->htable) { \ 4581590Srgrimes (void) hash_find(nscdb, entry, &hash, cmp); \ 4591590Srgrimes nscdb->htable[hash] = entry; \ 4601590Srgrimes } 4611590Srgrimes 4621590Srgrimes 4631590Srgrimes#ifdef NSCD_DEBUG 4641590Srgrimesstatic void 4651590Srgrimesprint_entry(nsc_db_t *nscdb, time_t now, nsc_entry_t *entry) { 46627625Scharnier nss_XbyY_args_t args; 4671590Srgrimes char whoami[512]; 46827625Scharnier 4691590Srgrimes switch (entry->stats.status) { 4701590Srgrimes case ST_NEW_ENTRY: 4711590Srgrimes (void) fprintf(stdout, gettext("\t status: new entry\n")); 4721590Srgrimes return; 4731590Srgrimes case ST_UPDATE_PENDING: 4741590Srgrimes (void) fprintf(stdout, gettext("\t status: update pending\n")); 4751590Srgrimes return; 4761590Srgrimes case ST_LOOKUP_PENDING: 4771590Srgrimes (void) fprintf(stdout, gettext("\t status: lookup pending\n")); 4781590Srgrimes return; 4791590Srgrimes case ST_DISCARD: 4801590Srgrimes (void) fprintf(stdout, gettext("\t status: discarded entry\n")); 4811590Srgrimes return; 4821590Srgrimes default: 4831590Srgrimes if (entry->stats.timestamp < now) 4841590Srgrimes (void) fprintf(stdout, 4851590Srgrimes gettext("\t status: expired (%d seconds ago)\n"), 4861590Srgrimes now - entry->stats.timestamp); 4871590Srgrimes else 4881590Srgrimes (void) fprintf(stdout, 4891590Srgrimes gettext("\t status: valid (expiry in %d seconds)\n"), 4901590Srgrimes entry->stats.timestamp - now); 4911590Srgrimes break; 4921590Srgrimes } 4931590Srgrimes (void) fprintf(stdout, gettext("\t hits: %u\n"), entry->stats.hits); 4941590Srgrimes args.key = entry->key; 4951590Srgrimes (void) nscdb->getlogstr(nscdb->name, whoami, sizeof (whoami), &args); 4961590Srgrimes (void) fprintf(stdout, "\t %s\n", whoami); 4971590Srgrimes} 4981590Srgrimes#endif /* NSCD_DEBUG */ 4991590Srgrimes 5001590Srgrimesstatic void 5011590Srgrimesprint_stats(nscd_cfg_stat_cache_t *statsp) { 5021590Srgrimes 5031590Srgrimes (void) fprintf(stdout, gettext("\n\t STATISTICS:\n")); 5041590Srgrimes (void) fprintf(stdout, gettext("\t positive hits: %lu\n"), 5051590Srgrimes statsp->pos_hits); 5061590Srgrimes (void) fprintf(stdout, gettext("\t negative hits: %lu\n"), 5071590Srgrimes statsp->neg_hits); 5081590Srgrimes (void) fprintf(stdout, gettext("\t positive misses: %lu\n"), 5091590Srgrimes statsp->pos_misses); 5101590Srgrimes (void) fprintf(stdout, gettext("\t negative misses: %lu\n"), 5111590Srgrimes statsp->neg_misses); 51227625Scharnier (void) fprintf(stdout, gettext("\t total entries: %lu\n"), 5131590Srgrimes statsp->entries); 51427625Scharnier (void) fprintf(stdout, gettext("\t queries queued: %lu\n"), 5151590Srgrimes statsp->wait_count); 5161590Srgrimes (void) fprintf(stdout, gettext("\t queries dropped: %lu\n"), 5171590Srgrimes statsp->drop_count); 5181590Srgrimes (void) fprintf(stdout, gettext("\t cache invalidations: %lu\n"), 5191590Srgrimes statsp->invalidate_count); 5201590Srgrimes 5211590Srgrimes _NSC_GET_HITRATE(statsp); 5221590Srgrimes (void) fprintf(stdout, gettext("\t cache hit rate: %10.1f\n"), 5231590Srgrimes statsp->hitrate); 5241590Srgrimes} 5251590Srgrimes 5261590Srgrimes 5271590Srgrimesstatic void 5281590Srgrimesprint_cfg(nscd_cfg_cache_t *cfgp) { 5291590Srgrimes (void) fprintf(stdout, gettext("\n\t CONFIG:\n")); 5301590Srgrimes (void) fprintf(stdout, gettext("\t enabled: %s\n"), 5311590Srgrimes yes_no(cfgp->enable)); 5321590Srgrimes (void) fprintf(stdout, gettext("\t per user cache: %s\n"), 5331590Srgrimes yes_no(cfgp->per_user)); 5341590Srgrimes (void) fprintf(stdout, gettext("\t avoid name service: %s\n"), 5351590Srgrimes yes_no(cfgp->avoid_ns)); 5361590Srgrimes (void) fprintf(stdout, gettext("\t check file: %s\n"), 5371590Srgrimes yes_no(cfgp->check_files)); 5381590Srgrimes (void) fprintf(stdout, gettext("\t check file interval: %d\n"), 5391590Srgrimes cfgp->check_interval); 5401590Srgrimes (void) fprintf(stdout, gettext("\t positive ttl: %d\n"), 5411590Srgrimes cfgp->pos_ttl); 5421590Srgrimes (void) fprintf(stdout, gettext("\t negative ttl: %d\n"), 5431590Srgrimes cfgp->neg_ttl); 5441590Srgrimes (void) fprintf(stdout, gettext("\t keep hot count: %d\n"), 5451590Srgrimes cfgp->keephot); 5461590Srgrimes (void) fprintf(stdout, gettext("\t hint size: %d\n"), 5471590Srgrimes cfgp->hint_size); 5481590Srgrimes (void) fprintf(stdout, gettext("\t max entries: %lu%s"), 5491590Srgrimes cfgp->maxentries, 5501590Srgrimes cfgp->maxentries?"\n":" (unlimited)\n"); 5511590Srgrimes} 5521590Srgrimes 5531590Srgrimes 5541590Srgrimes#ifdef NSCD_DEBUG 5551590Srgrimesstatic void 5561590Srgrimeshash_dump(nsc_db_t *nscdb, time_t now) { 5571590Srgrimes nsc_entry_t *entry; 5581590Srgrimes int i; 5591590Srgrimes 5601590Srgrimes (void) fprintf(stdout, gettext("\n\nHASH TABLE:\n")); 5611590Srgrimes for (i = 0; i < nscdb->htsize; i++) { 5621590Srgrimes if ((entry = nscdb->htable[i]) != NULL) { 5631590Srgrimes (void) fprintf(stdout, "hash[%d]:\n", i); 5641590Srgrimes print_entry(nscdb, now, entry); 5651590Srgrimes } 5661590Srgrimes } 5671590Srgrimes} 5681590Srgrimes#endif /* NSCD_DEBUG */ 5691590Srgrimes 5701590Srgrimes 5711590Srgrimes#ifdef NSCD_DEBUG 5721590Srgrimesstatic void 5731590Srgrimesavl_dump(nsc_db_t *nscdb, time_t now) { 5741590Srgrimes nsc_entry_t *entry; 5751590Srgrimes int i; 5761590Srgrimes 5771590Srgrimes (void) fprintf(stdout, gettext("\n\nAVL TREE:\n")); 5781590Srgrimes for (entry = avl_first(&nscdb->tree), i = 0; entry != NULL; 5791590Srgrimes entry = avl_walk(&nscdb->tree, entry, AVL_AFTER)) { 58027625Scharnier (void) fprintf(stdout, "avl node[%d]:\n", i++); 5811590Srgrimes print_entry(nscdb, now, entry); 5821590Srgrimes } 5831590Srgrimes} 5841590Srgrimes#endif /* NSCD_DEBUG */ 5851590Srgrimes 5861590Srgrimes 5871590Srgrimes#ifdef NSCD_DEBUG 5881590Srgrimesstatic void 5891590Srgrimesqueue_dump(nsc_db_t *nscdb, time_t now) { 5901590Srgrimes nsc_entry_t *entry; 5911590Srgrimes int i; 5921590Srgrimes 5931590Srgrimes (void) fprintf(stdout, 5941590Srgrimes gettext("\n\nCACHE [name=%s, nodes=%lu]:\n"), 5951590Srgrimes nscdb->name, avl_numnodes(&nscdb->tree)); 5961590Srgrimes 5971590Srgrimes (void) fprintf(stdout, 5981590Srgrimes gettext("Starting with the most recently accessed:\n")); 5991590Srgrimes 6001590Srgrimes for (entry = nscdb->qtail, i = 0; entry; entry = entry->qnext) { 6011590Srgrimes (void) fprintf(stdout, "entry[%d]:\n", i++); 6021590Srgrimes print_entry(nscdb, now, entry); 6031590Srgrimes } 6041590Srgrimes} 6051590Srgrimes#endif /* NSCD_DEBUG */ 6061590Srgrimes 6071590Srgrimesstatic void 6081590Srgrimesqueue_remove(nsc_db_t *nscdb, nsc_entry_t *entry) { 6091590Srgrimes 6101590Srgrimes if (nscdb->qtail == entry) 6111590Srgrimes nscdb->qtail = entry->qnext; 6121590Srgrimes else 6131590Srgrimes entry->qprev->qnext = entry->qnext; 6141590Srgrimes 6151590Srgrimes if (nscdb->qhead == entry) 6161590Srgrimes nscdb->qhead = entry->qprev; 6171590Srgrimes else 6181590Srgrimes entry->qnext->qprev = entry->qprev; 6191590Srgrimes 6201590Srgrimes if (nscdb->reap_node == entry) 6211590Srgrimes nscdb->reap_node = entry->qnext; 6221590Srgrimes entry->qnext = entry->qprev = NULL; 6231590Srgrimes} 62428386Sjlemon 62528386Sjlemon 6261590Srgrimesstatic void 6271590Srgrimesqueue_adjust(nsc_db_t *nscdb, nsc_entry_t *entry) { 6281590Srgrimes 62928386Sjlemon#ifdef NSCD_DEBUG 63028386Sjlemon assert(nscdb->qtail || entry->qnext == NULL && 6311590Srgrimes entry->qprev == NULL); 6321590Srgrimes 6331590Srgrimes assert(nscdb->qtail && nscdb->qhead || 6341590Srgrimes nscdb->qtail == NULL && nscdb->qhead == NULL); 6351590Srgrimes 6361590Srgrimes assert(entry->qprev || entry->qnext == NULL || 6371590Srgrimes nscdb->qtail == entry); 6381590Srgrimes#endif /* NSCD_DEBUG */ 6391590Srgrimes 6401590Srgrimes /* already in the desired position */ 6411590Srgrimes if (nscdb->qtail == entry) 6421590Srgrimes return; 6431590Srgrimes 6441590Srgrimes /* new queue */ 6451590Srgrimes if (nscdb->qtail == NULL) { 6461590Srgrimes nscdb->qhead = nscdb->qtail = entry; 6471590Srgrimes return; 6481590Srgrimes } 6491590Srgrimes 6501590Srgrimes /* new entry (prev == NULL AND tail != entry) */ 6511590Srgrimes if (entry->qprev == NULL) { 6521590Srgrimes nscdb->qtail->qprev = entry; 6531590Srgrimes entry->qnext = nscdb->qtail; 6541590Srgrimes nscdb->qtail = entry; 6551590Srgrimes return; 6561590Srgrimes } 6571590Srgrimes 6581590Srgrimes /* existing entry */ 6591590Srgrimes if (nscdb->reap_node == entry) 6601590Srgrimes nscdb->reap_node = entry->qnext; 6611590Srgrimes if (nscdb->qhead == entry) 6621590Srgrimes nscdb->qhead = entry->qprev; 6631590Srgrimes else 6641590Srgrimes entry->qnext->qprev = entry->qprev; 6651590Srgrimes entry->qprev->qnext = entry->qnext; 6661590Srgrimes entry->qprev = NULL; 6671590Srgrimes entry->qnext = nscdb->qtail; 6681590Srgrimes nscdb->qtail->qprev = entry; 6691590Srgrimes nscdb->qtail = entry; 6701590Srgrimes} 6711590Srgrimes 67228386Sjlemon 6731590Srgrimes/* 6741590Srgrimes * Init cache 6751590Srgrimes */ 6761590Srgrimesnscd_rc_t 6771590Srgrimesinit_cache(int debug_level) { 67827625Scharnier int cflags; 6791590Srgrimes 6801590Srgrimes cflags = (debug_level > 0)?0:UMC_NODEBUG; 6811590Srgrimes nsc_entry_cache = umem_cache_create("nsc_entry_cache", 6821590Srgrimes sizeof (nsc_entry_t), 0, NULL, NULL, NULL, 6831590Srgrimes NULL, NULL, cflags); 6841590Srgrimes if (nsc_entry_cache == NULL) 6851590Srgrimes return (NSCD_NO_MEMORY); 6861590Srgrimes return (NSCD_SUCCESS); 6871590Srgrimes} 6881590Srgrimes 6891590Srgrimes 6901590Srgrimes/* 6911590Srgrimes * Create cache 6921590Srgrimes */ 6931590Srgrimesnsc_db_t * 6941590Srgrimesmake_cache(enum db_type dbtype, int dbop, char *name, 6951590Srgrimes int (*compar) (const void *, const void *), 6961590Srgrimes void (*getlogstr)(char *, char *, size_t, nss_XbyY_args_t *), 6971590Srgrimes uint_t (*gethash)(nss_XbyY_key_t *, int), 6981590Srgrimes enum hash_type httype, int htsize) { 6991590Srgrimes 7001590Srgrimes nsc_db_t *nscdb; 7011590Srgrimes char *me = "make_cache"; 7021590Srgrimes 7031590Srgrimes nscdb = (nsc_db_t *)malloc(sizeof (*nscdb)); 7041590Srgrimes if (nscdb == NULL) { 7051590Srgrimes _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 7061590Srgrimes (me, "%s: memory allocation failure\n", name); 7071590Srgrimes goto out; 7081590Srgrimes } 7091590Srgrimes (void) memset(nscdb, 0, sizeof (*nscdb)); 7101590Srgrimes 7111590Srgrimes nscdb->dbop = dbop; 7121590Srgrimes nscdb->name = name; 7131590Srgrimes nscdb->db_type = dbtype; 7141590Srgrimes 7151590Srgrimes /* Assign compare routine */ 7161590Srgrimes if (compar == NULL) { 7175165Sache if (_NSC_DB_CES_KEY(nscdb)) 7181590Srgrimes nscdb->compar = nsc_db_ces_key_compar; 7191590Srgrimes else if (_NSC_DB_CIS_KEY(nscdb)) 72076822Sgshapiro nscdb->compar = nsc_db_cis_key_compar; 72176822Sgshapiro else if (_NSC_DB_INT_KEY(nscdb)) 72276822Sgshapiro nscdb->compar = nsc_db_int_key_compar; 72376822Sgshapiro else 72476822Sgshapiro assert(0); 72576822Sgshapiro } else { 7261590Srgrimes nscdb->compar = compar; 72776822Sgshapiro } 7281590Srgrimes 7291590Srgrimes /* The cache is an AVL tree */ 7301590Srgrimes avl_create(&nscdb->tree, nscdb->compar, sizeof (nsc_entry_t), 7311590Srgrimes offsetof(nsc_entry_t, avl_link)); 7321590Srgrimes 7331590Srgrimes /* Assign log routine */ 7341590Srgrimes if (getlogstr == NULL) { 73576822Sgshapiro if (_NSC_DB_STR_KEY(nscdb)) 7361590Srgrimes nscdb->getlogstr = nsc_db_str_key_getlogstr; 7371590Srgrimes else if (_NSC_DB_INT_KEY(nscdb)) 7381590Srgrimes nscdb->getlogstr = nsc_db_int_key_getlogstr; 7391590Srgrimes else 7401590Srgrimes nscdb->getlogstr = nsc_db_any_key_getlogstr; 7411590Srgrimes } else { 7421590Srgrimes nscdb->getlogstr = getlogstr; 7431590Srgrimes } 7441590Srgrimes 7451590Srgrimes /* The AVL tree based cache uses a hash table for quick access */ 7461590Srgrimes if (htsize != 0) { 7471590Srgrimes /* Determine hash table size based on type */ 7481590Srgrimes nscdb->hash_type = httype; 7491590Srgrimes if (htsize < 0) { 7501590Srgrimes switch (httype) { 7511590Srgrimes case nsc_ht_power2: 7521590Srgrimes htsize = _NSC_INIT_HTSIZE_POWER2; 7531590Srgrimes break; 7541590Srgrimes case nsc_ht_prime: 7551590Srgrimes case nsc_ht_default: 7561590Srgrimes default: 7571590Srgrimes htsize = _NSC_INIT_HTSIZE_PRIME; 7581590Srgrimes } 7591590Srgrimes } 7601590Srgrimes nscdb->htsize = htsize; 7611590Srgrimes 7621590Srgrimes /* Create the hash table */ 7631590Srgrimes nscdb->htable = calloc(htsize, sizeof (*(nscdb->htable))); 7641590Srgrimes if (nscdb->htable == NULL) { 7651590Srgrimes _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 7661590Srgrimes (me, "%s: memory allocation failure\n", name); 7671590Srgrimes goto out; 7681590Srgrimes } 7691590Srgrimes 7701590Srgrimes /* Assign gethash routine */ 7711590Srgrimes if (gethash == NULL) { 7721590Srgrimes if (_NSC_DB_CES_KEY(nscdb)) 7731590Srgrimes nscdb->gethash = nsc_db_ces_key_gethash; 7741590Srgrimes else if (_NSC_DB_CIS_KEY(nscdb)) 7751590Srgrimes nscdb->gethash = nsc_db_cis_key_gethash; 7761590Srgrimes else if (_NSC_DB_INT_KEY(nscdb)) 7771590Srgrimes nscdb->gethash = nsc_db_int_key_gethash; 7781590Srgrimes else 7791590Srgrimes assert(0); 7801590Srgrimes } else { 7811590Srgrimes nscdb->gethash = gethash; 7821590Srgrimes } 7831590Srgrimes } 7841590Srgrimes 7851590Srgrimes (void) mutex_init(&nscdb->db_mutex, USYNC_THREAD, NULL); 7861590Srgrimes return (nscdb); 7871590Srgrimes 7881590Srgrimesout: 7891590Srgrimes if (nscdb->htable) 7901590Srgrimes free(nscdb->htable); 7911590Srgrimes if (nscdb) 7921590Srgrimes free(nscdb); 7931590Srgrimes return (NULL); 7941590Srgrimes} 7951590Srgrimes 7961590Srgrimes 7971590Srgrimes/* 7981590Srgrimes * verify 7991590Srgrimes */ 8001590Srgrimes/* ARGSUSED */ 8011590Srgrimesnscd_rc_t 8021590Srgrimes_nscd_cfg_cache_verify( 8031590Srgrimes void *data, 8041590Srgrimes struct nscd_cfg_param_desc *pdesc, 8051590Srgrimes nscd_cfg_id_t *nswdb, 8061590Srgrimes nscd_cfg_flag_t dflag, 8071590Srgrimes nscd_cfg_error_t **errorp, 8081590Srgrimes void **cookie) 8091590Srgrimes{ 8101590Srgrimes 8111590Srgrimes return (NSCD_SUCCESS); 8121590Srgrimes} 8131590Srgrimes 8141590Srgrimes/* 8151590Srgrimes * notify 816 */ 817/* ARGSUSED */ 818nscd_rc_t 819_nscd_cfg_cache_notify( 820 void *data, 821 struct nscd_cfg_param_desc *pdesc, 822 nscd_cfg_id_t *nswdb, 823 nscd_cfg_flag_t dflag, 824 nscd_cfg_error_t **errorp, 825 void **cookie) 826{ 827 nsc_ctx_t *ctx; 828 void *dp; 829 int i; 830 831 /* group data */ 832 if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) { 833 if (_nscd_cfg_flag_is_set(pdesc->pflag, 834 NSCD_CFG_PFLAG_GLOBAL)) { 835 /* global config */ 836 global_cfg = *(nscd_cfg_global_cache_t *)data; 837 } else if (_nscd_cfg_flag_is_set(dflag, 838 NSCD_CFG_DFLAG_SET_ALL_DB)) { 839 /* non-global config for all dbs */ 840 for (i = 0; i < CACHE_CTX_COUNT; i++) { 841 ctx = cache_ctx_p[i]; 842 if (ctx == NULL) 843 return (NSCD_CTX_NOT_FOUND); 844 (void) rw_wrlock(&ctx->cfg_rwlp); 845 ctx->cfg = *(nscd_cfg_cache_t *)data; 846 ctx->cfg_mtime = time(NULL); 847 (void) rw_unlock(&ctx->cfg_rwlp); 848 } 849 } else { 850 /* non-global config for a specific db */ 851 852 /* ignore non-caching databases */ 853 if (get_cache_ctx(nswdb->name, &ctx) != NSCD_SUCCESS) 854 return (NSCD_SUCCESS); 855 (void) rw_wrlock(&ctx->cfg_rwlp); 856 ctx->cfg = *(nscd_cfg_cache_t *)data; 857 ctx->cfg_mtime = time(NULL); 858 (void) rw_unlock(&ctx->cfg_rwlp); 859 } 860 return (NSCD_SUCCESS); 861 } 862 863 /* individual data */ 864 if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL)) { 865 /* global config */ 866 dp = (char *)&global_cfg + pdesc->p_offset; 867 (void) memcpy(dp, data, pdesc->p_size); 868 } else if (_nscd_cfg_flag_is_set(dflag, 869 NSCD_CFG_DFLAG_SET_ALL_DB)) { 870 /* non-global config for all dbs */ 871 for (i = 0; i < CACHE_CTX_COUNT; i++) { 872 ctx = cache_ctx_p[i]; 873 if (ctx == NULL) 874 return (NSCD_CTX_NOT_FOUND); 875 dp = (char *)&ctx->cfg + pdesc->p_offset; 876 (void) rw_wrlock(&ctx->cfg_rwlp); 877 (void) memcpy(dp, data, pdesc->p_size); 878 ctx->cfg_mtime = time(NULL); 879 (void) rw_unlock(&ctx->cfg_rwlp); 880 } 881 } else { 882 /* non-global config for a specific db */ 883 884 /* ignore non-caching databases */ 885 if (get_cache_ctx(nswdb->name, &ctx) != NSCD_SUCCESS) 886 return (NSCD_SUCCESS); 887 dp = (char *)&ctx->cfg + pdesc->p_offset; 888 (void) rw_wrlock(&ctx->cfg_rwlp); 889 (void) memcpy(dp, data, pdesc->p_size); 890 ctx->cfg_mtime = time(NULL); 891 (void) rw_unlock(&ctx->cfg_rwlp); 892 } 893 return (NSCD_SUCCESS); 894} 895 896 897/* 898 * get stat 899 */ 900/* ARGSUSED */ 901nscd_rc_t 902_nscd_cfg_cache_get_stat( 903 void **stat, 904 struct nscd_cfg_stat_desc *sdesc, 905 nscd_cfg_id_t *nswdb, 906 nscd_cfg_flag_t *dflag, 907 void (**free_stat)(void *stat), 908 nscd_cfg_error_t **errorp) 909{ 910 nscd_cfg_stat_cache_t *statsp, stats; 911 nsc_ctx_t *ctx; 912 int i; 913 nscd_rc_t rc; 914 915 statsp = calloc(1, sizeof (*statsp)); 916 if (statsp == NULL) 917 return (NSCD_NO_MEMORY); 918 919 if (_nscd_cfg_flag_is_set(sdesc->sflag, NSCD_CFG_SFLAG_GLOBAL)) { 920 for (i = 0; i < CACHE_CTX_COUNT; i++) { 921 if (cache_ctx_p[i] == NULL) 922 stats = null_stats; 923 else { 924 (void) mutex_lock(&cache_ctx_p[i]->stats_mutex); 925 stats = cache_ctx_p[i]->stats; 926 (void) mutex_unlock( 927 &cache_ctx_p[i]->stats_mutex); 928 } 929 statsp->pos_hits += stats.pos_hits; 930 statsp->neg_hits += stats.neg_hits; 931 statsp->pos_misses += stats.pos_misses; 932 statsp->neg_misses += stats.neg_misses; 933 statsp->entries += stats.entries; 934 statsp->drop_count += stats.drop_count; 935 statsp->wait_count += stats.wait_count; 936 statsp->invalidate_count += 937 stats.invalidate_count; 938 } 939 } else { 940 if ((rc = get_cache_ctx(nswdb->name, &ctx)) != NSCD_SUCCESS) { 941 free(statsp); 942 return (rc); 943 } 944 (void) mutex_lock(&ctx->stats_mutex); 945 *statsp = ctx->stats; 946 (void) mutex_unlock(&ctx->stats_mutex); 947 } 948 949 _NSC_GET_HITRATE(statsp); 950 *stat = statsp; 951 return (NSCD_SUCCESS); 952} 953 954/* 955 * This function should only be called when nscd is 956 * not a daemon. 957 */ 958void 959nsc_info(nsc_ctx_t *ctx, char *dbname, nscd_cfg_cache_t cfg[], 960 nscd_cfg_stat_cache_t stats[]) 961{ 962 int i; 963 char *me = "nsc_info"; 964 nsc_ctx_t *ctx1; 965 nsc_ctx_t ctx2; 966 nscd_rc_t rc; 967 968 if (ctx) { 969 ctx_info(ctx); 970 return; 971 } 972 973 if (dbname) { 974 rc = get_cache_ctx(dbname, &ctx1); 975 if (rc == NSCD_INVALID_ARGUMENT) { 976 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) 977 (me, "%s: no cache context found\n", dbname); 978 return; 979 } else if (rc == NSCD_NO_MEMORY) { 980 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) 981 (me, "%s: unable to create cache context - no memory\n", 982 dbname); 983 return; 984 } 985 ctx_info(ctx1); 986 return; 987 } 988 989 if (cfg == NULL || stats == NULL) 990 return; 991 992 for (i = 0; i < CACHE_CTX_COUNT; i++) { 993 994 ctx2.dbname = cache_name[i]; 995 ctx2.cfg = cfg[i]; 996 ctx2.stats = stats[i]; 997 ctx_info_nolock(&ctx2); 998 } 999} 1000 1001static void 1002ctx_info_nolock(nsc_ctx_t *ctx) { 1003 nscd_cfg_cache_t cfg; 1004 nscd_cfg_stat_cache_t stats; 1005 1006 cfg = ctx->cfg; 1007 (void) fprintf(stdout, gettext("\n\nCACHE: %s\n"), ctx->dbname); 1008 (void) print_cfg(&cfg); 1009 1010 if (cfg.enable == nscd_false) 1011 return; 1012 1013 stats = ctx->stats; 1014 (void) print_stats(&stats); 1015} 1016 1017static void 1018ctx_info(nsc_ctx_t *ctx) { 1019 nscd_cfg_cache_t cfg; 1020 nscd_cfg_stat_cache_t stats; 1021 1022 (void) rw_rdlock(&ctx->cfg_rwlp); 1023 cfg = ctx->cfg; 1024 (void) rw_unlock(&ctx->cfg_rwlp); 1025 (void) fprintf(stdout, gettext("\n\nCACHE: %s\n"), ctx->dbname); 1026 (void) print_cfg(&cfg); 1027 1028 if (cfg.enable == nscd_false) 1029 return; 1030 1031 (void) mutex_lock(&ctx->stats_mutex); 1032 stats = ctx->stats; 1033 (void) mutex_unlock(&ctx->stats_mutex); 1034 (void) print_stats(&stats); 1035} 1036 1037#ifdef NSCD_DEBUG 1038/* 1039 * This function should only be called when nscd is 1040 * not a daemon. 1041 */ 1042int 1043nsc_dump(char *dbname, int dbop) { 1044 nsc_ctx_t *ctx; 1045 nsc_db_t *nscdb; 1046 nscd_bool_t enabled; 1047 time_t now; 1048 char *me = "nsc_dump"; 1049 int i; 1050 1051 if ((i = get_cache_idx(dbname)) == -1) { 1052 (void) fprintf(stdout, gettext("invalid cache name\n")); 1053 1054 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) 1055 (me, "%s: invalid cache name\n", dbname); 1056 return (NSCD_CACHE_INVALID_CACHE_NAME); 1057 } 1058 1059 if ((ctx = cache_ctx_p[i]) == NULL) { 1060 (void) fprintf(stdout, gettext("no cache context\n")); 1061 1062 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) 1063 (me, "%s: no cache context\n", dbname); 1064 return (NSCD_CACHE_NO_CACHE_CTX); 1065 } 1066 1067 now = time(NULL); 1068 (void) rw_rdlock(&ctx->cfg_rwlp); 1069 enabled = ctx->cfg.enable; 1070 (void) rw_unlock(&ctx->cfg_rwlp); 1071 1072 if (enabled == nscd_false) 1073 return (NSCD_CACHE_DISABLED); 1074 1075 nscdb = nsc_get_db(ctx, dbop); 1076 if (nscdb == NULL) { 1077 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) 1078 (me, "%s:%d: no cache found\n", dbname, dbop); 1079 return (NSCD_CACHE_NO_CACHE_FOUND); 1080 } 1081 1082 (void) mutex_lock(&nscdb->db_mutex); 1083 (void) queue_dump(nscdb, now); 1084 (void) hash_dump(nscdb, now); 1085 (void) avl_dump(nscdb, now); 1086 (void) mutex_unlock(&nscdb->db_mutex); 1087 return (NSCD_SUCCESS); 1088} 1089#endif /* NSCD_DEBUG */ 1090 1091/* 1092 * These macros are for exclusive use of nsc_lookup 1093 */ 1094#define NSC_LOOKUP_RETURN(retcode, loglevel, fmt) \ 1095 (void) mutex_unlock(&nscdb->db_mutex); \ 1096 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_##loglevel) \ 1097 (me, fmt, whoami); \ 1098 return (retcode); 1099 1100#define NSC_LOOKUP_NO_CACHE(str) \ 1101 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) \ 1102 (me, "%s: name service lookup (bypassing cache\n", \ 1103 str); \ 1104 nss_psearch(largs->buffer, largs->bufsize); \ 1105 status = NSCD_GET_STATUS(largs->buffer); \ 1106 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) \ 1107 (me, "%s: name service lookup status = %d\n", \ 1108 str, status); \ 1109 if (status == NSS_SUCCESS) { \ 1110 return (SUCCESS); \ 1111 } else if (status == NSS_NOTFOUND) \ 1112 return (NOTFOUND); \ 1113 else \ 1114 return (SERVERERROR); 1115 1116/* 1117 * This function starts the revalidation and reaper threads 1118 * for a cache 1119 */ 1120static void 1121start_threads(nsc_ctx_t *ctx) { 1122 1123 int errnum; 1124 char *me = "start_threads"; 1125 1126 /* 1127 * kick off the revalidate thread (if necessary) 1128 */ 1129 if (ctx->revalidate_on != nscd_true) { 1130 if (thr_create(NULL, NULL, (void *(*)(void *))revalidate, 1131 ctx, 0, NULL) != 0) { 1132 errnum = errno; 1133 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1134 (me, "thr_create (revalidate thread for %s): %s\n", 1135 ctx->dbname, strerror(errnum)); 1136 exit(1); 1137 } 1138 ctx->revalidate_on = nscd_true; 1139 } 1140 1141 /* 1142 * kick off the reaper thread (if necessary) 1143 */ 1144 if (ctx->reaper_on != nscd_true) { 1145 if (thr_create(NULL, NULL, (void *(*)(void *))reaper, 1146 ctx, 0, NULL) != 0) { 1147 errnum = errno; 1148 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1149 (me, "thr_create (reaper thread for %s): %s\n", 1150 ctx->dbname, strerror(errnum)); 1151 exit(1); 1152 } 1153 ctx->reaper_on = nscd_true; 1154 } 1155} 1156 1157/* 1158 * Examine the packed buffer, see if the front-end parameters 1159 * indicate that the caller specified nsswitch config should be 1160 * used for the lookup. Return 1 if yes, otherwise 0. 1161 */ 1162static int 1163nsw_config_in_phdr(void *buf) 1164{ 1165 nss_pheader_t *pbuf = (nss_pheader_t *)buf; 1166 nssuint_t off; 1167 nss_dbd_t *pdbd; 1168 char *me = "nsw_config_in_phdr"; 1169 1170 off = pbuf->dbd_off; 1171 if (off == 0) 1172 return (0); 1173 pdbd = (nss_dbd_t *)((void *)((char *)pbuf + off)); 1174 if (pdbd->o_default_config == 0) 1175 return (0); 1176 1177 if ((enum nss_dbp_flags)pdbd->flags & NSS_USE_DEFAULT_CONFIG) { 1178 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1179 (me, "use caller specified nsswitch config\n"); 1180 return (1); 1181 } else 1182 return (0); 1183} 1184 1185static nss_status_t 1186copy_result(void *rbuf, void *cbuf) 1187{ 1188 nss_pheader_t *rphdr = (nss_pheader_t *)rbuf; 1189 nss_pheader_t *cphdr = (nss_pheader_t *)cbuf; 1190 char *me = "copy_result"; 1191 1192 /* return NSS_ERROR if not enough room to copy result */ 1193 if (cphdr->data_len + 1 > rphdr->data_len) { 1194 NSCD_SET_STATUS(rphdr, NSS_ERROR, ERANGE); 1195 return (NSS_ERROR); 1196 } else { 1197 char *dst; 1198 1199 if (cphdr->data_len == 0) 1200 return (NSS_SUCCESS); 1201 1202 dst = (char *)rphdr + rphdr->data_off; 1203 (void) memcpy(dst, (char *)cphdr + cphdr->data_off, 1204 cphdr->data_len); 1205 rphdr->data_len = cphdr->data_len; 1206 /* some frontend code expects a terminating NULL char */ 1207 *(dst + rphdr->data_len) = '\0'; 1208 1209 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1210 (me, "cache data (len = %lld): >>%s<<\n", 1211 cphdr->data_len, (char *)cphdr + cphdr->data_off); 1212 1213 return (NSS_SUCCESS); 1214 } 1215} 1216 1217static int 1218get_dns_ttl(void *pbuf, char *dbname) 1219{ 1220 nss_pheader_t *phdr = (nss_pheader_t *)pbuf; 1221 int ttl; 1222 char *me = "get_dns_ttl"; 1223 1224 /* if returned, dns ttl is stored in the extended data area */ 1225 if (phdr->ext_off == 0) 1226 return (-1); 1227 1228 if (strcmp(dbname, NSS_DBNAM_HOSTS) != 0 && 1229 strcmp(dbname, NSS_DBNAM_IPNODES) != 0) 1230 return (-1); 1231 1232 ttl = *(nssuint_t *)((void *)((char *)pbuf + phdr->ext_off)); 1233 1234 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1235 (me, "dns ttl is %d seconds\n", ttl); 1236 1237 return (ttl); 1238} 1239 1240static int 1241check_config(nsc_lookup_args_t *largs, nscd_cfg_cache_t *cfgp, 1242 char *whoami, int flag) 1243{ 1244 nsc_db_t *nscdb; 1245 nsc_ctx_t *ctx; 1246 nss_status_t status; 1247 char *me = "check_config"; 1248 1249 ctx = largs->ctx; 1250 nscdb = largs->nscdb; 1251 1252 /* see if the cached config needs update */ 1253 if (nscdb->cfg_mtime != ctx->cfg_mtime) { 1254 (void) rw_rdlock(&ctx->cfg_rwlp); 1255 nscdb->cfg = ctx->cfg; 1256 nscdb->cfg_mtime = ctx->cfg_mtime; 1257 (void) rw_unlock(&ctx->cfg_rwlp); 1258 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1259 (me, "config for context %s, database %s updated\n", 1260 ctx->dbname, nscdb->name); 1261 } 1262 *cfgp = nscdb->cfg; 1263 1264 if (cfgp->enable == nscd_false) { 1265 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1266 (me, "%s: cache disabled\n", ctx->dbname); 1267 1268 if (UPDATEBIT & flag) 1269 return (NOTFOUND); 1270 else { 1271 NSC_LOOKUP_NO_CACHE(whoami); 1272 } 1273 } 1274 1275 /* 1276 * if caller requests lookup using his 1277 * own nsswitch config, bypass cache 1278 */ 1279 if (nsw_config_in_phdr(largs->buffer)) { 1280 NSC_LOOKUP_NO_CACHE(whoami); 1281 } 1282 1283 /* no need of cache if we are dealing with 0 ttls */ 1284 if (cfgp->pos_ttl <= 0 && cfgp->neg_ttl <= 0) { 1285 if (flag & UPDATEBIT) 1286 return (NOTFOUND); 1287 else if (cfgp->avoid_ns == nscd_true) 1288 return (SERVERERROR); 1289 NSC_LOOKUP_NO_CACHE(whoami); 1290 } 1291 1292 return (CONTINUE); 1293} 1294 1295/* 1296 * Invalidate cache if database file has been modified. 1297 * See check_files config param for details. 1298 */ 1299static void 1300check_db_file(nsc_ctx_t *ctx, nscd_cfg_cache_t cfg, 1301 char *whoami, time_t now) 1302{ 1303 struct stat buf; 1304 nscd_bool_t file_modified = nscd_false; 1305 char *me = "check_db_file"; 1306 1307 if (cfg.check_interval != 0 && 1308 (now - ctx->file_chktime) < cfg.check_interval) 1309 return; 1310 1311 ctx->file_chktime = now; 1312 if (stat(ctx->file_name, &buf) == 0) { 1313 if (ctx->file_mtime == 0) { 1314 (void) mutex_lock(&ctx->file_mutex); 1315 if (ctx->file_mtime == 0) { 1316 ctx->file_mtime = buf.st_mtime; 1317 ctx->file_size = buf.st_size; 1318 ctx->file_ino = buf.st_ino; 1319 } 1320 (void) mutex_unlock(&ctx->file_mutex); 1321 } else if (ctx->file_mtime < buf.st_mtime || 1322 ctx->file_size != buf.st_size || 1323 ctx->file_ino != buf.st_ino) { 1324 (void) mutex_lock(&ctx->file_mutex); 1325 if (ctx->file_mtime < buf.st_mtime || 1326 ctx->file_size != buf.st_size || 1327 ctx->file_ino != buf.st_ino) { 1328 file_modified = nscd_true; 1329 ctx->file_mtime = buf.st_mtime; 1330 ctx->file_size = buf.st_size; 1331 ctx->file_ino = buf.st_ino; 1332 } 1333 (void) mutex_unlock(&ctx->file_mutex); 1334 } 1335 } 1336 1337 if (file_modified == nscd_true) { 1338 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1339 (me, "%s: file %s has been modified - invalidating cache\n", 1340 whoami, ctx->file_name); 1341 ctx_invalidate(ctx); 1342 } 1343} 1344 1345static int 1346lookup_int(nsc_lookup_args_t *largs, int flag) { 1347 1348 nsc_ctx_t *ctx; 1349 nsc_db_t *nscdb; 1350 nscd_cfg_cache_t cfg; 1351 nsc_entry_t *this_entry; 1352 nsc_entry_stat_t *this_stats; 1353 nsc_action_t next_action; 1354 nss_status_t status; 1355 nscd_bool_t delete; 1356 nscd_rc_t rc; 1357 char *dbname; 1358 int dbop, errnum; 1359 int cfg_rc; 1360 nss_XbyY_args_t args; 1361 char whoami[128]; 1362 time_t now = time(NULL); /* current time */ 1363 char *me = "lookup_int"; 1364 1365 /* extract dbop, dbname, key and cred */ 1366 status = nss_packed_getkey(largs->buffer, largs->bufsize, &dbname, 1367 &dbop, &args); 1368 if (status != NSS_SUCCESS) { 1369 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1370 (me, "nss_packed_getkey failure (%d)\n", status); 1371 return (SERVERERROR); 1372 } 1373 1374 /* get the cache context */ 1375 if (largs->ctx == NULL) { 1376 if (get_cache_ctx(dbname, &largs->ctx) != NSCD_SUCCESS) { 1377 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) 1378 (me, "%s: no cache context found\n", dbname); 1379 1380 if (UPDATEBIT & flag) 1381 return (NOTFOUND); 1382 else { 1383 NSC_LOOKUP_NO_CACHE(dbname); 1384 } 1385 } 1386 } 1387 ctx = largs->ctx; 1388 1389 if (largs->nscdb == NULL) { 1390 if ((largs->nscdb = nsc_get_db(ctx, dbop)) == NULL) { 1391 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) 1392 (me, "%s:%d: no cache found\n", 1393 dbname, dbop); 1394 1395 if (UPDATEBIT & flag) 1396 return (NOTFOUND); 1397 else { 1398 NSC_LOOKUP_NO_CACHE(dbname); 1399 } 1400 } 1401 } 1402 1403 nscdb = largs->nscdb; 1404 1405 _NSCD_LOG_IF(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ALL) { 1406 (void) nscdb->getlogstr(nscdb->name, whoami, 1407 sizeof (whoami), &args); 1408 } 1409 1410 if (UPDATEBIT & flag) { 1411 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1412 (me, "%s: refresh start\n", whoami); 1413 } else { 1414 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1415 (me, "%s: lookup start\n", whoami); 1416 } 1417 1418 cfg_rc = check_config(largs, &cfg, whoami, flag); 1419 if (cfg_rc != CONTINUE) 1420 return (cfg_rc); 1421 1422 /* 1423 * Invalidate cache if file has been modified. 1424 */ 1425 if (cfg.check_files == nscd_true) 1426 check_db_file(ctx, cfg, whoami, now); 1427 1428 (void) mutex_lock(&nscdb->db_mutex); 1429 1430 /* Lookup the cache table */ 1431 for (;;) { 1432 delete = nscd_false; 1433 rc = lookup_cache(largs, &cfg, &args, whoami, &this_entry); 1434 if (rc != NSCD_SUCCESS) { 1435 (void) mutex_unlock(&nscdb->db_mutex); 1436 1437 /* Either no entry and avoid name service */ 1438 if (rc == NSCD_DB_ENTRY_NOT_FOUND || 1439 rc == NSCD_INVALID_ARGUMENT) 1440 return (NOTFOUND); 1441 1442 /* OR memory error */ 1443 return (SERVERERROR); 1444 } 1445 1446 /* get the stats from the entry */ 1447 this_stats = &this_entry->stats; 1448 1449 /* 1450 * What should we do next ? 1451 */ 1452 switch (this_stats->status) { 1453 case ST_NEW_ENTRY: 1454 delete = nscd_true; 1455 next_action = _NSC_NSLOOKUP; 1456 break; 1457 case ST_UPDATE_PENDING: 1458 if (flag & UPDATEBIT) { 1459 (void) mutex_unlock(&nscdb->db_mutex); 1460 return (NOTFOUND); 1461 } else if (this_stats->timestamp < now) 1462 next_action = _NSC_WAIT; 1463 else 1464 next_action = _NSC_USECACHED; 1465 break; 1466 case ST_LOOKUP_PENDING: 1467 if (flag & UPDATEBIT) { 1468 (void) mutex_unlock(&nscdb->db_mutex); 1469 return (NOTFOUND); 1470 } 1471 next_action = _NSC_WAIT; 1472 break; 1473 case ST_DISCARD: 1474 if (cfg.avoid_ns == nscd_true) { 1475 (void) mutex_unlock(&nscdb->db_mutex); 1476 return (NOTFOUND); 1477 } 1478 /* otherwise reuse the entry */ 1479 (void) memset(this_stats, 0, sizeof (*this_stats)); 1480 next_action = _NSC_NSLOOKUP; 1481 break; 1482 default: 1483 if (cfg.avoid_ns == nscd_true) 1484 next_action = _NSC_USECACHED; 1485 else if ((flag & UPDATEBIT) || 1486 (this_stats->timestamp < now)) { 1487 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1488 (me, "%s: cached entry needs to be updated\n", 1489 whoami); 1490 next_action = _NSC_NSLOOKUP; 1491 } else 1492 next_action = _NSC_USECACHED; 1493 break; 1494 } 1495 1496 if (next_action == _NSC_WAIT) { 1497 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1498 (me, "%s: need to wait\n", whoami); 1499 1500 /* do we have clearance ? */ 1501 if (_nscd_get_clearance(&ctx->throttle_sema) != 0) { 1502 /* nope. quit */ 1503 (void) mutex_lock(&ctx->stats_mutex); 1504 ctx->stats.drop_count++; 1505 (void) mutex_unlock(&ctx->stats_mutex); 1506 _NSCD_LOG(NSCD_LOG_CACHE, 1507 NSCD_LOG_LEVEL_DEBUG_6) 1508 (me, "%s: throttling load\n", whoami); 1509 NSC_LOOKUP_RETURN(NOSERVER, WARNING, 1510 "%s: no clearance to wait\n"); 1511 } 1512 /* yes can wait */ 1513 (void) nscd_wait(ctx, nscdb, this_entry); 1514 (void) _nscd_release_clearance(&ctx->throttle_sema); 1515 continue; 1516 } 1517 1518 break; 1519 } 1520 1521 1522 if (!(UPDATEBIT & flag)) 1523 this_stats->hits++; /* update hit count */ 1524 1525 if (next_action == _NSC_NSLOOKUP) { 1526 1527 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1528 (me, "%s: name service lookup required\n", whoami); 1529 1530 if (_nscd_get_clearance(&ctx->throttle_sema) != 0) { 1531 if (delete == nscd_true) 1532 delete_entry(nscdb, ctx, this_entry); 1533 else 1534 this_stats->status = ST_DISCARD; 1535 (void) mutex_lock(&ctx->stats_mutex); 1536 ctx->stats.drop_count++; 1537 (void) mutex_unlock(&ctx->stats_mutex); 1538 NSC_LOOKUP_RETURN(NOSERVER, WARNING, 1539 "%s: no clearance for lookup\n"); 1540 } 1541 1542 /* block any threads accessing this entry */ 1543 this_stats->status = (flag & UPDATEBIT)? 1544 ST_UPDATE_PENDING:ST_LOOKUP_PENDING; 1545 1546 /* release lock and do name service lookup */ 1547 (void) mutex_unlock(&nscdb->db_mutex); 1548 nss_psearch(largs->buffer, largs->bufsize); 1549 status = NSCD_GET_STATUS(largs->buffer); 1550 (void) mutex_lock(&nscdb->db_mutex); 1551 this_stats->status = 0; 1552 (void) _nscd_release_clearance(&ctx->throttle_sema); 1553 1554 /* signal waiting threads */ 1555 (void) nscd_signal(ctx, nscdb, this_entry); 1556 1557 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1558 (me, "%s: name service lookup status = %d\n", 1559 whoami, status); 1560 1561 if (status == NSS_SUCCESS) { 1562 int ttl; 1563 1564 /* 1565 * data found in name service 1566 * update cache 1567 */ 1568 status = dup_packed_buffer(largs, this_entry); 1569 if (status != NSS_SUCCESS) { 1570 delete_entry(nscdb, ctx, this_entry); 1571 NSC_LOOKUP_RETURN(SERVERERROR, ERROR, 1572 "%s: failed to update cache\n"); 1573 } 1574 1575 /* 1576 * store unpacked key in cache 1577 */ 1578 status = nss_packed_getkey(this_entry->buffer, 1579 this_entry->bufsize, 1580 &dbname, &dbop, &args); 1581 if (status != NSS_SUCCESS) { 1582 delete_entry(nscdb, ctx, this_entry); 1583 NSC_LOOKUP_RETURN(SERVERERROR, ERROR, 1584 "%s: failed to extract key\n"); 1585 } 1586 this_entry->key = args.key; /* struct copy */ 1587 1588 /* update +ve miss count */ 1589 if (!(UPDATEBIT & flag)) { 1590 (void) mutex_lock(&ctx->stats_mutex); 1591 ctx->stats.pos_misses++; 1592 (void) mutex_unlock(&ctx->stats_mutex); 1593 } 1594 1595 /* update +ve ttl */ 1596 ttl = get_dns_ttl(largs->buffer, dbname); 1597 /* honor the dns ttl less than postive ttl */ 1598 if (ttl < 0 || ttl > cfg.pos_ttl) 1599 ttl = cfg.pos_ttl; 1600 this_stats->timestamp = time(NULL) + ttl; 1601 1602 /* 1603 * start the revalidation and reaper threads 1604 * if not already started 1605 */ 1606 start_threads(ctx); 1607 1608 NSC_LOOKUP_RETURN(SUCCESS, DEBUG, 1609 "%s: cache updated with positive entry\n"); 1610 } else if (status == NSS_NOTFOUND) { 1611 /* 1612 * data not found in name service 1613 * update cache 1614 */ 1615 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG_6) 1616 (me, "%s: name service lookup failed\n", whoami); 1617 1618 if (NSCD_GET_ERRNO(largs->buffer) == ERANGE) { 1619 delete_entry(nscdb, ctx, this_entry); 1620 NSC_LOOKUP_RETURN(NOTFOUND, DEBUG, 1621 "%s: ERANGE, cache not updated with negative entry\n"); 1622 } 1623 1624 status = dup_packed_buffer(largs, this_entry); 1625 if (status != NSS_SUCCESS) { 1626 delete_entry(nscdb, ctx, this_entry); 1627 NSC_LOOKUP_RETURN(SERVERERROR, ERROR, 1628 "%s: failed to update cache\n"); 1629 } 1630 1631 /* store unpacked key in cache */ 1632 status = nss_packed_getkey(this_entry->buffer, 1633 this_entry->bufsize, 1634 &dbname, &dbop, &args); 1635 if (status != NSS_SUCCESS) { 1636 delete_entry(nscdb, ctx, this_entry); 1637 NSC_LOOKUP_RETURN(SERVERERROR, ERROR, 1638 "%s: failed to extract key\n"); 1639 } 1640 this_entry->key = args.key; /* struct copy */ 1641 1642 /* update -ve ttl */ 1643 this_stats->timestamp = time(NULL) + cfg.neg_ttl; 1644 1645 /* update -ve miss count */ 1646 if (!(UPDATEBIT & flag)) { 1647 (void) mutex_lock(&ctx->stats_mutex); 1648 ctx->stats.neg_misses++; 1649 (void) mutex_unlock(&ctx->stats_mutex); 1650 } 1651 1652 /* 1653 * start the revalidation and reaper threads 1654 * if not already started 1655 */ 1656 start_threads(ctx); 1657 1658 NSC_LOOKUP_RETURN(NOTFOUND, DEBUG, 1659 "%s: cache updated with negative entry\n"); 1660 } else { 1661 /* 1662 * name service lookup failed 1663 */ 1664 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG_6) 1665 (me, "%s: name service lookup failed\n", whoami); 1666 1667 errnum = NSCD_GET_ERRNO(largs->buffer); 1668 if (delete == nscd_true) 1669 delete_entry(nscdb, ctx, this_entry); 1670 else 1671 this_stats->status = ST_DISCARD; 1672 1673 (void) mutex_unlock(&nscdb->db_mutex); 1674 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) 1675 (me, "%s: name service lookup failed (status=%d, errno=%d)\n", 1676 whoami, status, errnum); 1677 1678 return (SERVERERROR); 1679 } 1680 } else if (next_action == _NSC_USECACHED) { 1681 /* 1682 * found entry in cache 1683 */ 1684 if (UPDATEBIT & flag) { 1685 NSC_LOOKUP_RETURN(SUCCESS, DEBUG, 1686 "%s: no need to update\n"); 1687 } 1688 1689 if (NSCD_GET_STATUS((nss_pheader_t *)this_entry->buffer) == 1690 NSS_SUCCESS) { 1691 /* positive hit */ 1692 (void) mutex_lock(&ctx->stats_mutex); 1693 ctx->stats.pos_hits++; 1694 (void) mutex_unlock(&ctx->stats_mutex); 1695 1696 /* update response buffer */ 1697 if (copy_result(largs->buffer, 1698 this_entry->buffer) != NSS_SUCCESS) { 1699 NSC_LOOKUP_RETURN(SERVERERROR, ERROR, 1700 "%s: response buffer insufficient\n"); 1701 } 1702 1703 NSC_LOOKUP_RETURN(SUCCESS, DEBUG, 1704 "%s: positive entry in cache\n"); 1705 } else { 1706 /* negative hit */ 1707 (void) mutex_lock(&ctx->stats_mutex); 1708 ctx->stats.neg_hits++; 1709 (void) mutex_unlock(&ctx->stats_mutex); 1710 1711 NSCD_SET_STATUS((nss_pheader_t *)largs->buffer, 1712 NSCD_GET_STATUS(this_entry->buffer), 1713 NSCD_GET_ERRNO(this_entry->buffer)); 1714 NSCD_SET_HERRNO((nss_pheader_t *)largs->buffer, 1715 NSCD_GET_HERRNO(this_entry->buffer)); 1716 1717 NSC_LOOKUP_RETURN(NOTFOUND, DEBUG, 1718 "%s: negative entry in cache\n"); 1719 } 1720 } 1721 1722 NSC_LOOKUP_RETURN(SERVERERROR, ERROR, 1723 "%s: cache backend failure\n"); 1724} 1725 1726/* 1727 * NSCD cache backend lookup function 1728 */ 1729/*ARGSUSED*/ 1730void 1731nsc_lookup(nsc_lookup_args_t *largs, int flag) { 1732 1733 nss_pheader_t *phdr = (nss_pheader_t *)largs->buffer; 1734 int rc; 1735 1736 rc = lookup_int(largs, 0); 1737 1738 if (NSCD_GET_STATUS(phdr) == NSS_TRYLOCAL) 1739 return; 1740 1741 switch (rc) { 1742 1743 case SUCCESS: 1744 NSCD_RETURN_STATUS(phdr, NSS_SUCCESS, 0); 1745 break; 1746 1747 case NOTFOUND: 1748 NSCD_RETURN_STATUS(phdr, NSS_NOTFOUND, -1); 1749 break; 1750 1751 case SERVERERROR: 1752 /* 1753 * status and errno should have been set in the phdr, 1754 * if not, set status to NSS_ERROR 1755 */ 1756 if (NSCD_STATUS_IS_OK(phdr)) { 1757 NSCD_SET_STATUS(phdr, NSS_ERROR, 0); 1758 } 1759 break; 1760 1761 case NOSERVER: 1762 NSCD_RETURN_STATUS(phdr, NSS_TRYLOCAL, -1); 1763 break; 1764 } 1765} 1766 1767 1768static nsc_ctx_t * 1769init_cache_ctx(int i) { 1770 nsc_ctx_t *ctx; 1771 1772 ctx = calloc(1, sizeof (nsc_ctx_t)); 1773 if (ctx == NULL) 1774 return (NULL); 1775 1776 /* init locks and semaphores */ 1777 (void) mutex_init(&ctx->file_mutex, USYNC_THREAD, NULL); 1778 (void) rwlock_init(&ctx->cfg_rwlp, USYNC_THREAD, NULL); 1779 (void) mutex_init(&ctx->stats_mutex, USYNC_THREAD, NULL); 1780 (void) _nscd_init_cache_sema(&ctx->throttle_sema, cache_name[i]); 1781 cache_init_ctx[i](ctx); 1782 cache_ctx_p[i] = ctx; 1783 1784 return (ctx); 1785} 1786 1787 1788static void 1789revalidate(nsc_ctx_t *ctx) 1790{ 1791 for (;;) { 1792 int i, slp, interval, count; 1793 1794 (void) rw_rdlock(&ctx->cfg_rwlp); 1795 slp = ctx->cfg.pos_ttl; 1796 count = ctx->cfg.keephot; 1797 (void) rw_unlock(&ctx->cfg_rwlp); 1798 1799 if (slp < 60) 1800 slp = 60; 1801 if (count != 0) { 1802 interval = (slp/2)/count; 1803 if (interval == 0) 1804 interval = 1; 1805 (void) sleep(slp*2/3); 1806 for (i = 0; i < ctx->db_count; i++) { 1807 getxy_keepalive(ctx, ctx->nsc_db[i], 1808 count, interval); 1809 } 1810 } else { 1811 (void) sleep(slp); 1812 } 1813 } 1814} 1815 1816 1817static void 1818getxy_keepalive(nsc_ctx_t *ctx, nsc_db_t *nscdb, int keep, int interval) 1819{ 1820 nsc_keephot_t *table; 1821 nsc_entry_t *entry, *ptr; 1822 int i; 1823 nsc_lookup_args_t *largs; 1824 nss_pheader_t *phdr; 1825 int bufsiz; 1826 char *me = "getxy_keepalive"; 1827 1828 /* we won't be here if keep == 0 so need to check that */ 1829 1830 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1831 (me, "%s: keep alive\n", nscdb->name); 1832 1833 if ((table = maken(keep)) == NULL) { 1834 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1835 (me, "memory allocation failure\n"); 1836 exit(1); 1837 } 1838 1839 (void) mutex_lock(&nscdb->db_mutex); 1840 entry = nscdb->qtail; 1841 while (entry != NULL) { 1842 /* leave pending calls alone */ 1843 if (!(entry->stats.status & ST_PENDING)) { 1844 /* do_revalidate */ 1845 (void) insertn(table, entry->stats.hits, entry); 1846 } 1847 entry = entry->qnext; 1848 } 1849 for (i = 1; i <= keep; i++) { 1850 if (table[i].ptr == NULL) 1851 continue; 1852 ptr = (nsc_entry_t *)table[i].ptr; 1853 phdr = (nss_pheader_t *)ptr->buffer; 1854 if (NSCD_GET_STATUS(phdr) == NSS_SUCCESS) 1855 /* 1856 * for positive cache, in addition to the packed 1857 * header size, allocate twice the size of the 1858 * existing result (in case the result grows 1859 * larger) plus 2K (for the file/compat backend to 1860 * process a possible large entry in the /etc files) 1861 */ 1862 bufsiz = phdr->data_off + 2 * phdr->data_len + 2048; 1863 else 1864 /* 1865 * for negative cache, allocate 8K buffer to 1866 * hold result in case the next lookup may 1867 * return something (in addition to the 1868 * packed header size) 1869 */ 1870 bufsiz = phdr->data_off + 8096; 1871 table[i].ptr = malloc(bufsiz); 1872 if (table[i].ptr == NULL) { 1873 (void) mutex_unlock(&nscdb->db_mutex); 1874 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1875 (me, "memory allocation failure\n"); 1876 exit(1); 1877 } 1878 (void) memcpy(table[i].ptr, ptr->buffer, ptr->bufsize); 1879 ((nss_pheader_t *)table[i].ptr)->pbufsiz = bufsiz; 1880 table[i].num = bufsiz; 1881 } 1882 (void) mutex_unlock(&nscdb->db_mutex); 1883 1884 /* launch update thread for each keep hot entry */ 1885 for (i = keep; i > 0; i--) { 1886 if (table[i].ptr == NULL) 1887 continue; /* unused slot in table */ 1888 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1889 (me, "%s: launching update\n", nscdb->name); 1890 largs = (nsc_lookup_args_t *)malloc(sizeof (*largs)); 1891 if (largs == NULL) { 1892 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1893 (me, "memory allocation failure\n"); 1894 exit(1); 1895 } 1896 largs->buffer = table[i].ptr; 1897 largs->bufsize = table[i].num; 1898 largs->ctx = ctx; 1899 largs->nscdb = nscdb; 1900 if (launch_update(largs) < 0) 1901 exit(1); 1902 (void) sleep(interval); 1903 } 1904 1905 /* 1906 * The update thread will handle freeing of buffer and largs. 1907 * Free the table here. 1908 */ 1909 free(table); 1910} 1911 1912 1913static int 1914launch_update(nsc_lookup_args_t *in) 1915{ 1916 char *me = "launch_update"; 1917 int errnum; 1918 1919 errnum = thr_create(NULL, NULL, (void *(*)(void*))do_update, 1920 in, 0|THR_DETACHED, NULL); 1921 if (errnum != 0) { 1922 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1923 (me, "%s: thread creation failure (%d)\n", 1924 in->nscdb->name, errnum); 1925 return (-1); 1926 } 1927 return (0); 1928} 1929 1930 1931static void 1932do_update(nsc_lookup_args_t *in) { 1933 nss_pheader_t *phdr = (nss_pheader_t *)in->buffer; 1934 1935 /* update the length of the data buffer */ 1936 phdr->data_len = phdr->pbufsiz - phdr->data_off; 1937 1938 (void) lookup_int(in, UPDATEBIT); 1939 if (in->buffer) 1940 free(in->buffer); 1941 free(in); 1942} 1943 1944 1945/* 1946 * Invalidate cache 1947 */ 1948void 1949nsc_invalidate(nsc_ctx_t *ctx, char *dbname, nsc_ctx_t **ctxs) { 1950 int i; 1951 char *me = "nsc_invalidate"; 1952 1953 if (ctx) { 1954 ctx_invalidate(ctx); 1955 return; 1956 } 1957 1958 if (dbname) { 1959 if ((i = get_cache_idx(dbname)) == -1) { 1960 _NSCD_LOG(NSCD_LOG_CACHE, 1961 NSCD_LOG_LEVEL_WARNING) 1962 (me, "%s: invalid cache name\n", dbname); 1963 return; 1964 } 1965 if ((ctx = cache_ctx_p[i]) == NULL) { 1966 _NSCD_LOG(NSCD_LOG_CACHE, 1967 NSCD_LOG_LEVEL_WARNING) 1968 (me, "%s: no cache context found\n", 1969 dbname); 1970 return; 1971 } 1972 ctx_invalidate(ctx); 1973 return; 1974 } 1975 1976 if (ctxs == NULL) 1977 ctxs = cache_ctx_p; 1978 1979 for (i = 0; i < CACHE_CTX_COUNT; i++) { 1980 if (ctxs[i] != NULL) 1981 ctx_invalidate(ctxs[i]); 1982 } 1983} 1984 1985 1986/* 1987 * Invalidate cache by context 1988 */ 1989static void 1990ctx_invalidate(nsc_ctx_t *ctx) { 1991 int i; 1992 nsc_entry_t *entry; 1993 char *me = "ctx_invalidate"; 1994 1995 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1996 (me, "%s: invalidate cache\n", ctx->dbname); 1997 1998 for (i = 0; i < ctx->db_count; i++) { 1999 if (ctx->nsc_db[i] == NULL) 2000 continue; 2001 (void) mutex_lock(&ctx->nsc_db[i]->db_mutex); 2002 entry = ctx->nsc_db[i]->qtail; 2003 while (entry != NULL) { 2004 /* leave pending calls alone */ 2005 if (!(entry->stats.status & ST_PENDING)) 2006 entry->stats.status = ST_DISCARD; 2007 entry = entry->qnext; 2008 } 2009 (void) mutex_unlock(&ctx->nsc_db[i]->db_mutex); 2010 } 2011 2012 (void) mutex_lock(&ctx->stats_mutex); 2013 ctx->stats.invalidate_count++; 2014 (void) mutex_unlock(&ctx->stats_mutex); 2015} 2016 2017 2018/* 2019 * Free nsc_entry_t 2020 * 2021 * Pre-reqs: 2022 * nscdb->db_mutex lock must be held before calling this function 2023 */ 2024static void 2025delete_entry(nsc_db_t *nscdb, nsc_ctx_t *ctx, nsc_entry_t *entry) { 2026 uint_t hash; 2027 2028 avl_remove(&nscdb->tree, entry); 2029 HASH_REMOVE(nscdb, entry, hash, nscd_false); 2030 queue_remove(nscdb, entry); 2031 if (entry->buffer != NULL) { 2032 free(entry->buffer); 2033 entry->buffer = NULL; 2034 } 2035 umem_cache_free(nsc_entry_cache, entry); 2036 (void) mutex_lock(&ctx->stats_mutex); 2037 ctx->stats.entries--; 2038 (void) mutex_unlock(&ctx->stats_mutex); 2039} 2040 2041 2042static nscd_rc_t 2043lookup_cache(nsc_lookup_args_t *largs, nscd_cfg_cache_t *cfgp, 2044 nss_XbyY_args_t *argp, char *whoami, nsc_entry_t **entry) { 2045 2046 nsc_db_t *nscdb; 2047 nsc_ctx_t *ctx; 2048 uint_t hash; 2049 avl_index_t pos; 2050 ulong_t nentries; 2051 nsc_entry_t find_entry, *node; 2052 char *me = "lookup_cache"; 2053 2054 ctx = largs->ctx; 2055 nscdb = largs->nscdb; 2056 2057 /* set the search key */ 2058 find_entry.key = argp->key; /* struct copy (not deep) */ 2059 2060 /* lookup the hash table ==> O(1) */ 2061 if (nscdb->htable) { 2062 *entry = hash_find(nscdb, &find_entry, &hash, nscd_true); 2063 if (*entry != NULL) { 2064 (void) queue_adjust(nscdb, *entry); 2065 return (NSCD_SUCCESS); 2066 } 2067 } 2068 2069 /* if not found, lookup the AVL tree ==> O(log n) */ 2070 *entry = (nsc_entry_t *)avl_find(&nscdb->tree, &find_entry, &pos); 2071 if (*entry != NULL) { 2072 (void) queue_adjust(nscdb, *entry); 2073 /* move it to the hash table */ 2074 if (nscdb->htable) { 2075 if (nscdb->htable[hash] == NULL || 2076 (*entry)->stats.hits >= 2077 nscdb->htable[hash]->stats.hits) { 2078 nscdb->htable[hash] = *entry; 2079 } 2080 } 2081 return (NSCD_SUCCESS); 2082 } 2083 2084 /* entry not found in the cache */ 2085 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2086 (me, "%s: cache miss\n", whoami); 2087 2088 if (cfgp->avoid_ns == nscd_true) { 2089 _NSCD_LOG(NSCD_LOG_CACHE, 2090 NSCD_LOG_LEVEL_DEBUG) 2091 (me, "%s: avoid name service\n", whoami); 2092 return (NSCD_DB_ENTRY_NOT_FOUND); 2093 } 2094 2095 /* allocate memory for new entry (stub) */ 2096 *entry = (nsc_entry_t *)umem_cache_alloc(nsc_entry_cache, 2097 UMEM_DEFAULT); 2098 if (*entry == NULL) { 2099 _NSCD_LOG(NSCD_LOG_CACHE, 2100 NSCD_LOG_LEVEL_ERROR) 2101 (me, "%s: memory allocation failure\n", whoami); 2102 return (NSCD_NO_MEMORY); 2103 } 2104 (void) memset(*entry, 0, sizeof (**entry)); 2105 2106 /* 2107 * Note that the actual data for the key is stored within 2108 * the largs->buffer (input buffer to nsc_lookup). 2109 * find_entry.key only contains pointers to this data. 2110 * 2111 * If largs->buffer will be re-allocated by nss_psearch 2112 * then (*entry)->key will have dangling pointers. 2113 * In such case, the following assignment needs to be 2114 * replaced by code that duplicates the key. 2115 */ 2116 (*entry)->key = find_entry.key; 2117 2118 /* 2119 * Add the entry to the cache. 2120 */ 2121 avl_insert(&nscdb->tree, *entry, pos); /* O(log n) */ 2122 (void) queue_adjust(nscdb, *entry); /* constant */ 2123 if (nscdb->htable) /* constant */ 2124 nscdb->htable[hash] = *entry; 2125 (*entry)->stats.status = ST_NEW_ENTRY; 2126 2127 (void) mutex_lock(&ctx->stats_mutex); 2128 nentries = ++(ctx->stats.entries); 2129 (void) mutex_unlock(&ctx->stats_mutex); 2130 2131 /* Have we exceeded max entries ? */ 2132 if (cfgp->maxentries > 0 && nentries > cfgp->maxentries) { 2133 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2134 (me, "%s: maximum entries exceeded -- " 2135 "deleting least recently used entry\n", 2136 whoami); 2137 2138 node = nscdb->qhead; 2139 while (node != NULL && node != *entry) { 2140 if (node->stats.status == ST_DISCARD || 2141 !(node->stats.status & ST_PENDING)) { 2142 delete_entry(nscdb, ctx, node); 2143 break; 2144 } 2145 node = node->qprev; 2146 } 2147 2148 /* 2149 * It's okay if we were not able to find one to delete. 2150 * The reaper (when invoked) will return the cache to a 2151 * safe level. 2152 */ 2153 } 2154 2155 return (NSCD_SUCCESS); 2156} 2157 2158static void 2159reaper(nsc_ctx_t *ctx) { 2160 uint_t ttl, extra_sleep, total_sleep, intervals; 2161 uint_t nodes_per_interval, seconds_per_interval; 2162 ulong_t nsc_entries; 2163 char *me = "reaper"; 2164 2165 for (;;) { 2166 (void) mutex_lock(&ctx->stats_mutex); 2167 nsc_entries = ctx->stats.entries; 2168 (void) mutex_unlock(&ctx->stats_mutex); 2169 2170 (void) rw_rdlock(&ctx->cfg_rwlp); 2171 ttl = ctx->cfg.pos_ttl; 2172 (void) rw_unlock(&ctx->cfg_rwlp); 2173 2174 if (nsc_entries == 0) { 2175 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2176 (me, "%s: nothing to reap\n", ctx->dbname); 2177 2178 /* sleep for atleast 60 seconds */ 2179 if (ttl < 60) 2180 ttl = 60; 2181 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2182 (me, "%s: sleep %d\n", ctx->dbname, ttl); 2183 (void) sleep(ttl); 2184 continue; 2185 } 2186 2187 if (ttl < 32) ttl = 32; 2188 if (ttl > (1<<28)) ttl = 1<<28; 2189 2190 /* 2191 * minimum nodes_per_interval = 256 or 1<<8 2192 * maximum nodes_per_interval = nsc_entries 2193 * minimum seconds_per_interval = 32 or 1<<5 2194 * maximum_seconds_per_interval = ttl 2195 */ 2196 if (nsc_entries <= ttl) { 2197 intervals = (nsc_entries >> 8) + 1; 2198 seconds_per_interval = ttl / intervals; 2199 nodes_per_interval = 256; 2200 } else { 2201 intervals = (ttl >> 5) + 1; 2202 seconds_per_interval = 32; 2203 nodes_per_interval = nsc_entries / intervals; 2204 if (nodes_per_interval < 256) 2205 nodes_per_interval = 256; 2206 } 2207 2208 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2209 (me, "%s: total entries = %d, " 2210 "seconds per interval = %d, " 2211 "nodes per interval = %d\n", 2212 ctx->dbname, nsc_entries, seconds_per_interval, 2213 nodes_per_interval); 2214 total_sleep = reap_cache(ctx, nodes_per_interval, 2215 seconds_per_interval); 2216 extra_sleep = 1 + ttl - total_sleep; 2217 if (extra_sleep > 0) 2218 (void) sleep(extra_sleep); 2219 } 2220} 2221 2222 2223static uint_t 2224reap_cache(nsc_ctx_t *ctx, uint_t nodes_per_interval, 2225 uint_t seconds_per_interval) { 2226 uint_t nodes_togo, total_sleep; 2227 time_t now; 2228 nsc_entry_t *node, *next_node; 2229 nsc_db_t *nscdb; 2230 uint_t primes[] = {_NSC_HTSIZE_PRIMES}; 2231 ulong_t count, nentries, maxentries; 2232 int i, slot, value, newhtsize; 2233 char *me = "reap_cache"; 2234 2235 count = 0; 2236 total_sleep = 0; 2237 nodes_togo = nodes_per_interval; 2238 now = time(NULL); 2239 2240 for (i = 0; i < ctx->db_count; i++) { 2241 nscdb = ctx->nsc_db[i]; 2242 (void) mutex_lock(&nscdb->db_mutex); 2243 nscdb->reap_node = nscdb->qtail; 2244 while (nscdb->reap_node != NULL) { 2245 if (nodes_togo == 0) { 2246 (void) mutex_unlock(&nscdb->db_mutex); 2247 (void) sleep(seconds_per_interval); 2248 total_sleep += seconds_per_interval; 2249 nodes_togo = nodes_per_interval; 2250 now = time(NULL); 2251 (void) mutex_lock(&nscdb->db_mutex); 2252 } 2253 /* delete ST_DISCARD and expired nodes */ 2254 if ((node = nscdb->reap_node) == NULL) 2255 break; 2256 if (node->stats.status == ST_DISCARD || 2257 (!(node->stats.status & ST_PENDING) && 2258 node->stats.timestamp < now)) { 2259 /* 2260 * Delete entry if its discard flag is 2261 * set OR if it has expired. Entries 2262 * with pending updates are not 2263 * deleted. 2264 * nscdb->reap_node will be adjusted 2265 * by delete_entry() 2266 */ 2267 delete_entry(nscdb, ctx, node); 2268 count++; 2269 } else { 2270 nscdb->reap_node = node->qnext; 2271 } 2272 nodes_togo--; 2273 } 2274 2275 if (nscdb->htsize == 0) { 2276 (void) mutex_unlock(&nscdb->db_mutex); 2277 continue; 2278 } 2279 2280 /* 2281 * Dynamic adjustment of hash table size. 2282 * 2283 * Hash table size is roughly 1/8th of the 2284 * total entries. However the size is changed 2285 * only when the number of entries double or 2286 * reduced by half 2287 */ 2288 nentries = avl_numnodes(&nscdb->tree); 2289 for (slot = 0, value = _NSC_INIT_HTSIZE_SLOT_VALUE; 2290 slot < _NSC_HTSIZE_NUM_SLOTS && nentries > value; 2291 value = (value << 1) + 1, slot++); 2292 if (nscdb->hash_type == nsc_ht_power2) 2293 newhtsize = _NSC_INIT_HTSIZE_POWER2 << slot; 2294 else 2295 newhtsize = primes[slot]; 2296 2297 /* Recommended size is same as the current size. Done */ 2298 if (nscdb->htsize == newhtsize) { 2299 (void) mutex_unlock(&nscdb->db_mutex); 2300 continue; 2301 } 2302 2303 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2304 (me, "%s: resizing hash table from %d to %d\n", 2305 nscdb->name, nscdb->htsize, newhtsize); 2306 2307 /* 2308 * Dump old hashes because it would be time 2309 * consuming to rehash them. 2310 */ 2311 (void) free(nscdb->htable); 2312 nscdb->htable = calloc(newhtsize, sizeof (*(nscdb->htable))); 2313 if (nscdb->htable == NULL) { 2314 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 2315 (me, 2316 "%s: memory allocation failure\n", 2317 nscdb->name); 2318 /* -1 to try later */ 2319 nscdb->htsize = -1; 2320 } else { 2321 nscdb->htsize = newhtsize; 2322 } 2323 (void) mutex_unlock(&nscdb->db_mutex); 2324 } 2325 2326 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2327 (me, "%s: reaped %lu entries\n", ctx->dbname, count); 2328 2329 /* 2330 * if cache is almost full then reduce it to a safe level by 2331 * evicting LRU entries 2332 */ 2333 2334 (void) rw_rdlock(&ctx->cfg_rwlp); 2335 maxentries = ctx->cfg.maxentries; 2336 (void) rw_unlock(&ctx->cfg_rwlp); 2337 2338 /* No limit on number of entries. Done */ 2339 if (maxentries == 0) 2340 goto out; 2341 2342 (void) mutex_lock(&ctx->stats_mutex); 2343 nentries = ctx->stats.entries; 2344 (void) mutex_unlock(&ctx->stats_mutex); 2345 2346 /* what is the percentage of cache used ? */ 2347 value = (nentries * 100) / maxentries; 2348 if (value < _NSC_EVICTION_START_LEVEL) 2349 goto out; 2350 2351 /* 2352 * cache needs to be reduced to a safe level 2353 */ 2354 value -= _NSC_EVICTION_SAFE_LEVEL; 2355 for (i = 0, count = 0; i < ctx->db_count; i++) { 2356 /* 2357 * Reduce each subcache by 'value' percent 2358 */ 2359 nscdb = ctx->nsc_db[i]; 2360 (void) mutex_lock(&nscdb->db_mutex); 2361 nodes_togo = (value * avl_numnodes(&nscdb->tree)) / 100; 2362 2363 /* Start from LRU entry i.e queue head */ 2364 next_node = nscdb->qhead; 2365 while (nodes_togo > 0 && next_node != NULL) { 2366 node = next_node; 2367 next_node = next_node->qprev; 2368 if (node->stats.status == ST_DISCARD || 2369 !(node->stats.status & ST_PENDING)) { 2370 /* Leave nodes with pending updates alone */ 2371 delete_entry(nscdb, ctx, node); 2372 count++; 2373 nodes_togo--; 2374 } 2375 } 2376 (void) mutex_unlock(&nscdb->db_mutex); 2377 } 2378 2379 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2380 (me, "%s: evicted %lu LRU entries\n", ctx->dbname, count); 2381 2382out: 2383 return (total_sleep); 2384} 2385