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