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