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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 * 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 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 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 * 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 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 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 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 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 * 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 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