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