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
dup_packed_buffer(void * src,void * dst)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
get_cache_idx(char * dbname)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
get_cache_ctx(char * dbname,nsc_ctx_t ** ctx)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
nsc_db_str_key_getlogstr(char * name,char * whoami,size_t len,nss_XbyY_args_t * argp)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
nsc_db_int_key_getlogstr(char * name,char * whoami,size_t len,nss_XbyY_args_t * argp)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
nsc_db_any_key_getlogstr(char * name,char * whoami,size_t len,nss_XbyY_args_t * argp)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 *
nsc_get_db(nsc_ctx_t * ctx,int dbop)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
nsc_db_int_key_compar(const void * n1,const void * n2)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
nsc_db_ces_key_compar(const void * n1,const void * n2)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
nsc_db_cis_key_compar(const void * n1,const void * n2)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
cis_gethash(const char * key,int htsize)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
ces_gethash(const char * key,int htsize)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
db_gethash(const void * key,int len,int htsize)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
nsc_db_cis_key_gethash(nss_XbyY_key_t * key,int htsize)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
nsc_db_ces_key_gethash(nss_XbyY_key_t * key,int htsize)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
nsc_db_int_key_gethash(nss_XbyY_key_t * key,int htsize)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 *
hash_find(nsc_db_t * nscdb,nsc_entry_t * entry,uint_t * hash,nscd_bool_t cmp)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
print_entry(nsc_db_t * nscdb,time_t now,nsc_entry_t * entry)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
print_stats(nscd_cfg_stat_cache_t * statsp)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
print_cfg(nscd_cfg_cache_t * cfgp)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
hash_dump(nsc_db_t * nscdb,time_t now)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
avl_dump(nsc_db_t * nscdb,time_t now)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
queue_dump(nsc_db_t * nscdb,time_t now)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
queue_remove(nsc_db_t * nscdb,nsc_entry_t * entry)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
queue_adjust(nsc_db_t * nscdb,nsc_entry_t * entry)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
init_cache(int debug_level)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 *
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)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
_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)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
_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)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
_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)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
nsc_info(nsc_ctx_t * ctx,char * dbname,nscd_cfg_cache_t cfg[],nscd_cfg_stat_cache_t stats[])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
ctx_info_nolock(nsc_ctx_t * ctx)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
ctx_info(nsc_ctx_t * ctx)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
nsc_dump(char * dbname,int dbop)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
start_threads(nsc_ctx_t * ctx)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
nsw_config_in_phdr(void * buf)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
copy_result(void * rbuf,void * cbuf)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
get_dns_ttl(void * pbuf,char * dbname)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
check_config(nsc_lookup_args_t * largs,nscd_cfg_cache_t * cfgp,char * whoami,int flag)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
check_db_file(nsc_ctx_t * ctx,nscd_cfg_cache_t cfg,char * whoami,time_t now)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
lookup_int(nsc_lookup_args_t * largs,int flag)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
nsc_lookup(nsc_lookup_args_t * largs,int flag)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 *
init_cache_ctx(int i)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
revalidate(nsc_ctx_t * ctx)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
getxy_keepalive(nsc_ctx_t * ctx,nsc_db_t * nscdb,int keep,int interval)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
launch_update(nsc_lookup_args_t * in)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
do_update(nsc_lookup_args_t * in)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
nsc_invalidate(nsc_ctx_t * ctx,char * dbname,nsc_ctx_t ** ctxs)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
ctx_invalidate(nsc_ctx_t * ctx)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
delete_entry(nsc_db_t * nscdb,nsc_ctx_t * ctx,nsc_entry_t * entry)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
lookup_cache(nsc_lookup_args_t * largs,nscd_cfg_cache_t * cfgp,nss_XbyY_args_t * argp,char * whoami,nsc_entry_t ** entry)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
reaper(nsc_ctx_t * ctx)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
reap_cache(nsc_ctx_t * ctx,uint_t nodes_per_interval,uint_t seconds_per_interval)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