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