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