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