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(nsc_lookup_args_t *); 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(nsc_ctx_t *); 111 static void revalidate(nsc_ctx_t *); 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: %10.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, (void *(*)(void *))revalidate, 1164 ctx, 0, NULL) != 0) { 1165 errnum = errno; 1166 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1167 (me, "thr_create (revalidate thread for %s): %s\n", 1168 ctx->dbname, strerror(errnum)); 1169 exit(1); 1170 } 1171 ctx->revalidate_on = nscd_true; 1172 } 1173 1174 /* 1175 * kick off the reaper thread (if necessary) 1176 */ 1177 if (ctx->reaper_on != nscd_true) { 1178 if (thr_create(NULL, 0, (void *(*)(void *))reaper, 1179 ctx, 0, NULL) != 0) { 1180 errnum = errno; 1181 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1182 (me, "thr_create (reaper thread for %s): %s\n", 1183 ctx->dbname, strerror(errnum)); 1184 exit(1); 1185 } 1186 ctx->reaper_on = nscd_true; 1187 } 1188 } 1189 1190 /* 1191 * Examine the packed buffer, see if the front-end parameters 1192 * indicate that the caller specified nsswitch config should be 1193 * used for the lookup. Return 1 if yes, otherwise 0. 1194 */ 1195 static int 1196 nsw_config_in_phdr(void *buf) 1197 { 1198 nss_pheader_t *pbuf = (nss_pheader_t *)buf; 1199 nssuint_t off; 1200 nss_dbd_t *pdbd; 1201 char *me = "nsw_config_in_phdr"; 1202 1203 off = pbuf->dbd_off; 1204 if (off == 0) 1205 return (0); 1206 pdbd = (nss_dbd_t *)((void *)((char *)pbuf + off)); 1207 if (pdbd->o_default_config == 0) 1208 return (0); 1209 1210 if ((enum nss_dbp_flags)pdbd->flags & NSS_USE_DEFAULT_CONFIG) { 1211 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1212 (me, "use caller specified nsswitch config\n"); 1213 return (1); 1214 } else 1215 return (0); 1216 } 1217 1218 static nss_status_t 1219 copy_result(void *rbuf, void *cbuf) 1220 { 1221 nss_pheader_t *rphdr = (nss_pheader_t *)rbuf; 1222 nss_pheader_t *cphdr = (nss_pheader_t *)cbuf; 1223 char *me = "copy_result"; 1224 1225 /* return NSS_ERROR if not enough room to copy result */ 1226 if (cphdr->data_len + 1 > rphdr->data_len) { 1227 NSCD_SET_STATUS(rphdr, NSS_ERROR, ERANGE); 1228 return (NSS_ERROR); 1229 } else { 1230 char *dst; 1231 1232 if (cphdr->data_len == 0) 1233 return (NSS_SUCCESS); 1234 1235 dst = (char *)rphdr + rphdr->data_off; 1236 (void) memcpy(dst, (char *)cphdr + cphdr->data_off, 1237 cphdr->data_len); 1238 rphdr->data_len = cphdr->data_len; 1239 /* some frontend code expects a terminating NULL char */ 1240 *(dst + rphdr->data_len) = '\0'; 1241 1242 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1243 (me, "cache data (len = %lld): >>%s<<\n", 1244 cphdr->data_len, (char *)cphdr + cphdr->data_off); 1245 1246 return (NSS_SUCCESS); 1247 } 1248 } 1249 1250 static int 1251 get_dns_ttl(void *pbuf, char *dbname) 1252 { 1253 nss_pheader_t *phdr = (nss_pheader_t *)pbuf; 1254 int ttl; 1255 char *me = "get_dns_ttl"; 1256 1257 /* if returned, dns ttl is stored in the extended data area */ 1258 if (phdr->ext_off == 0) 1259 return (-1); 1260 1261 if (strcmp(dbname, NSS_DBNAM_HOSTS) != 0 && 1262 strcmp(dbname, NSS_DBNAM_IPNODES) != 0) 1263 return (-1); 1264 1265 ttl = *(nssuint_t *)((void *)((char *)pbuf + phdr->ext_off)); 1266 1267 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1268 (me, "dns ttl is %d seconds\n", ttl); 1269 1270 return (ttl); 1271 } 1272 1273 static int 1274 check_config(nsc_lookup_args_t *largs, nscd_cfg_cache_t *cfgp, 1275 char *whoami, int flag) 1276 { 1277 nsc_db_t *nscdb; 1278 nsc_ctx_t *ctx; 1279 char *me = "check_config"; 1280 1281 ctx = largs->ctx; 1282 nscdb = largs->nscdb; 1283 1284 /* see if the cached config needs update */ 1285 if (nscdb->cfg_mtime != ctx->cfg_mtime) { 1286 (void) rw_rdlock(&ctx->cfg_rwlp); 1287 nscdb->cfg = ctx->cfg; 1288 nscdb->cfg_mtime = ctx->cfg_mtime; 1289 (void) rw_unlock(&ctx->cfg_rwlp); 1290 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1291 (me, "config for context %s, database %s updated\n", 1292 ctx->dbname, nscdb->name); 1293 } 1294 *cfgp = nscdb->cfg; 1295 1296 if (cfgp->enable == nscd_false) { 1297 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1298 (me, "%s: cache disabled\n", ctx->dbname); 1299 1300 if (UPDATEBIT & flag) 1301 return (NOTFOUND); 1302 else 1303 return (nsc_lookup_no_cache(largs, whoami)); 1304 } 1305 1306 /* 1307 * if caller requests lookup using its 1308 * own nsswitch config, bypass cache 1309 */ 1310 if (nsw_config_in_phdr(largs->buffer)) 1311 return (nsc_lookup_no_cache(largs, whoami)); 1312 1313 /* no need of cache if we are dealing with 0 ttls */ 1314 if (cfgp->pos_ttl <= 0 && cfgp->neg_ttl <= 0) { 1315 if (flag & UPDATEBIT) 1316 return (NOTFOUND); 1317 else if (cfgp->avoid_ns == nscd_true) 1318 return (SERVERERROR); 1319 return (nsc_lookup_no_cache(largs, whoami)); 1320 } 1321 1322 return (CONTINUE); 1323 } 1324 1325 /* 1326 * Invalidate cache if database file has been modified. 1327 * See check_files config param for details. 1328 */ 1329 static void 1330 check_db_file(nsc_ctx_t *ctx, nscd_cfg_cache_t cfg, 1331 char *whoami, time_t now) 1332 { 1333 struct stat buf; 1334 nscd_bool_t file_modified = nscd_false; 1335 char *me = "check_db_file"; 1336 1337 if (cfg.check_interval != 0 && 1338 (now - ctx->file_chktime) < cfg.check_interval) 1339 return; 1340 1341 ctx->file_chktime = now; 1342 if (stat(ctx->file_name, &buf) == 0) { 1343 if (ctx->file_mtime == 0) { 1344 (void) mutex_lock(&ctx->file_mutex); 1345 if (ctx->file_mtime == 0) { 1346 ctx->file_mtime = buf.st_mtime; 1347 ctx->file_size = buf.st_size; 1348 ctx->file_ino = buf.st_ino; 1349 } 1350 (void) mutex_unlock(&ctx->file_mutex); 1351 } else if (ctx->file_mtime < buf.st_mtime || 1352 ctx->file_size != buf.st_size || 1353 ctx->file_ino != buf.st_ino) { 1354 (void) mutex_lock(&ctx->file_mutex); 1355 if (ctx->file_mtime < buf.st_mtime || 1356 ctx->file_size != buf.st_size || 1357 ctx->file_ino != buf.st_ino) { 1358 file_modified = nscd_true; 1359 ctx->file_mtime = buf.st_mtime; 1360 ctx->file_size = buf.st_size; 1361 ctx->file_ino = buf.st_ino; 1362 } 1363 (void) mutex_unlock(&ctx->file_mutex); 1364 } 1365 } 1366 1367 if (file_modified == nscd_true) { 1368 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1369 (me, "%s: file %s has been modified - invalidating cache\n", 1370 whoami, ctx->file_name); 1371 ctx_invalidate(ctx); 1372 } 1373 } 1374 1375 static int 1376 lookup_int(nsc_lookup_args_t *largs, int flag) 1377 { 1378 nsc_ctx_t *ctx; 1379 nsc_db_t *nscdb; 1380 nscd_cfg_cache_t cfg; 1381 nsc_entry_t *this_entry; 1382 nsc_entry_stat_t *this_stats; 1383 nsc_action_t next_action; 1384 nss_status_t status; 1385 nscd_bool_t delete; 1386 nscd_rc_t rc; 1387 char *dbname; 1388 int dbop, errnum; 1389 int cfg_rc; 1390 nss_XbyY_args_t args; 1391 char whoami[128]; 1392 time_t now = time(NULL); /* current time */ 1393 char *me = "lookup_int"; 1394 1395 /* extract dbop, dbname, key and cred */ 1396 status = nss_packed_getkey(largs->buffer, largs->bufsize, &dbname, 1397 &dbop, &args); 1398 if (status != NSS_SUCCESS) { 1399 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1400 (me, "nss_packed_getkey failure (%d)\n", status); 1401 return (SERVERERROR); 1402 } 1403 1404 /* get the cache context */ 1405 if (largs->ctx == NULL) { 1406 if (get_cache_ctx(dbname, &largs->ctx) != NSCD_SUCCESS) { 1407 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) 1408 (me, "%s: no cache context found\n", dbname); 1409 1410 if (UPDATEBIT & flag) 1411 return (NOTFOUND); 1412 else 1413 return (nsc_lookup_no_cache(largs, dbname)); 1414 } 1415 } 1416 ctx = largs->ctx; 1417 1418 if (largs->nscdb == NULL) { 1419 if ((largs->nscdb = nsc_get_db(ctx, dbop)) == NULL) { 1420 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) 1421 (me, "%s:%d: no cache found\n", 1422 dbname, dbop); 1423 1424 if (UPDATEBIT & flag) 1425 return (NOTFOUND); 1426 else 1427 return (nsc_lookup_no_cache(largs, dbname)); 1428 } 1429 } 1430 1431 nscdb = largs->nscdb; 1432 1433 _NSCD_LOG_IF(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ALL) { 1434 (void) nscdb->getlogstr(nscdb->name, whoami, 1435 sizeof (whoami), &args); 1436 } 1437 1438 if (UPDATEBIT & flag) { 1439 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1440 (me, "%s: refresh start\n", whoami); 1441 } else { 1442 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1443 (me, "%s: lookup start\n", whoami); 1444 } 1445 1446 cfg_rc = check_config(largs, &cfg, whoami, flag); 1447 if (cfg_rc != CONTINUE) 1448 return (cfg_rc); 1449 1450 /* 1451 * Invalidate cache if file has been modified. 1452 */ 1453 if (cfg.check_files == nscd_true) 1454 check_db_file(ctx, cfg, whoami, now); 1455 1456 (void) mutex_lock(&nscdb->db_mutex); 1457 1458 /* Lookup the cache table */ 1459 for (;;) { 1460 delete = nscd_false; 1461 rc = lookup_cache(largs, &cfg, &args, whoami, &this_entry); 1462 if (rc != NSCD_SUCCESS) { 1463 (void) mutex_unlock(&nscdb->db_mutex); 1464 1465 /* Either no entry and avoid name service */ 1466 if (rc == NSCD_DB_ENTRY_NOT_FOUND || 1467 rc == NSCD_INVALID_ARGUMENT) 1468 return (NOTFOUND); 1469 1470 /* OR memory error */ 1471 return (SERVERERROR); 1472 } 1473 1474 /* get the stats from the entry */ 1475 this_stats = &this_entry->stats; 1476 1477 /* 1478 * What should we do next ? 1479 */ 1480 switch (this_stats->status) { 1481 case ST_NEW_ENTRY: 1482 delete = nscd_true; 1483 next_action = _NSC_NSLOOKUP; 1484 break; 1485 case ST_UPDATE_PENDING: 1486 if (flag & UPDATEBIT) { 1487 (void) mutex_unlock(&nscdb->db_mutex); 1488 return (NOTFOUND); 1489 } else if (this_stats->timestamp < now) 1490 next_action = _NSC_WAIT; 1491 else 1492 next_action = _NSC_USECACHED; 1493 break; 1494 case ST_LOOKUP_PENDING: 1495 if (flag & UPDATEBIT) { 1496 (void) mutex_unlock(&nscdb->db_mutex); 1497 return (NOTFOUND); 1498 } 1499 next_action = _NSC_WAIT; 1500 break; 1501 case ST_DISCARD: 1502 if (cfg.avoid_ns == nscd_true) { 1503 (void) mutex_unlock(&nscdb->db_mutex); 1504 return (NOTFOUND); 1505 } 1506 /* otherwise reuse the entry */ 1507 (void) memset(this_stats, 0, sizeof (*this_stats)); 1508 next_action = _NSC_NSLOOKUP; 1509 break; 1510 default: 1511 if (cfg.avoid_ns == nscd_true) 1512 next_action = _NSC_USECACHED; 1513 else if ((flag & UPDATEBIT) || 1514 (this_stats->timestamp < now)) { 1515 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1516 (me, "%s: cached entry needs to be updated\n", 1517 whoami); 1518 next_action = _NSC_NSLOOKUP; 1519 } else 1520 next_action = _NSC_USECACHED; 1521 break; 1522 } 1523 1524 if (next_action == _NSC_WAIT) { 1525 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1526 (me, "%s: need to wait\n", whoami); 1527 1528 /* do we have clearance ? */ 1529 if (_nscd_get_clearance(&ctx->throttle_sema) != 0) { 1530 /* nope. quit */ 1531 (void) mutex_lock(&ctx->stats_mutex); 1532 ctx->stats.drop_count++; 1533 (void) mutex_unlock(&ctx->stats_mutex); 1534 _NSCD_LOG(NSCD_LOG_CACHE, 1535 NSCD_LOG_LEVEL_DEBUG_6) 1536 (me, "%s: throttling load\n", whoami); 1537 (void) mutex_unlock(&nscdb->db_mutex); 1538 NSC_LOOKUP_LOG(WARNING, 1539 "%s: no clearance to wait\n"); 1540 return (NOSERVER); 1541 } 1542 /* yes can wait */ 1543 (void) nscd_wait(ctx, nscdb, this_entry); 1544 (void) _nscd_release_clearance(&ctx->throttle_sema); 1545 continue; 1546 } 1547 1548 break; 1549 } 1550 1551 1552 if (!(UPDATEBIT & flag)) 1553 this_stats->hits++; /* update hit count */ 1554 1555 if (next_action == _NSC_NSLOOKUP) { 1556 1557 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1558 (me, "%s: name service lookup required\n", whoami); 1559 1560 if (_nscd_get_clearance(&ctx->throttle_sema) != 0) { 1561 if (delete == nscd_true) 1562 delete_entry(nscdb, ctx, this_entry); 1563 else 1564 this_stats->status = ST_DISCARD; 1565 (void) mutex_lock(&ctx->stats_mutex); 1566 ctx->stats.drop_count++; 1567 (void) mutex_unlock(&ctx->stats_mutex); 1568 (void) mutex_unlock(&nscdb->db_mutex); 1569 NSC_LOOKUP_LOG(WARNING, 1570 "%s: no clearance for lookup\n"); 1571 return (NOSERVER); 1572 } 1573 1574 /* block any threads accessing this entry */ 1575 this_stats->status = (flag & UPDATEBIT) ? 1576 ST_UPDATE_PENDING : ST_LOOKUP_PENDING; 1577 1578 /* release lock and do name service lookup */ 1579 (void) mutex_unlock(&nscdb->db_mutex); 1580 nss_psearch(largs->buffer, largs->bufsize); 1581 status = NSCD_GET_STATUS(largs->buffer); 1582 (void) mutex_lock(&nscdb->db_mutex); 1583 this_stats->status = 0; 1584 (void) _nscd_release_clearance(&ctx->throttle_sema); 1585 1586 /* signal waiting threads */ 1587 (void) nscd_signal(ctx, nscdb, this_entry); 1588 1589 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1590 (me, "%s: name service lookup status = %d\n", 1591 whoami, status); 1592 1593 if (status == NSS_SUCCESS) { 1594 int ttl; 1595 1596 /* 1597 * data found in name service 1598 * update cache 1599 */ 1600 status = dup_packed_buffer(largs, this_entry); 1601 if (status != NSS_SUCCESS) { 1602 delete_entry(nscdb, ctx, this_entry); 1603 (void) mutex_unlock(&nscdb->db_mutex); 1604 NSC_LOOKUP_LOG(ERROR, 1605 "%s: failed to update cache\n"); 1606 return (SERVERERROR); 1607 } 1608 1609 /* 1610 * store unpacked key in cache 1611 */ 1612 status = nss_packed_getkey(this_entry->buffer, 1613 this_entry->bufsize, 1614 &dbname, &dbop, &args); 1615 if (status != NSS_SUCCESS) { 1616 delete_entry(nscdb, ctx, this_entry); 1617 (void) mutex_unlock(&nscdb->db_mutex); 1618 NSC_LOOKUP_LOG(ERROR, 1619 "%s: failed to extract key\n"); 1620 return (SERVERERROR); 1621 } 1622 this_entry->key = args.key; /* struct copy */ 1623 1624 /* update +ve miss count */ 1625 if (!(UPDATEBIT & flag)) { 1626 (void) mutex_lock(&ctx->stats_mutex); 1627 ctx->stats.pos_misses++; 1628 (void) mutex_unlock(&ctx->stats_mutex); 1629 } 1630 1631 /* update +ve ttl */ 1632 ttl = get_dns_ttl(largs->buffer, dbname); 1633 /* honor the dns ttl less than postive ttl */ 1634 if (ttl < 0 || ttl > cfg.pos_ttl) 1635 ttl = cfg.pos_ttl; 1636 this_stats->timestamp = time(NULL) + ttl; 1637 1638 /* 1639 * start the revalidation and reaper threads 1640 * if not already started 1641 */ 1642 start_threads(ctx); 1643 1644 (void) mutex_unlock(&nscdb->db_mutex); 1645 NSC_LOOKUP_LOG(DEBUG, 1646 "%s: cache updated with positive entry\n"); 1647 return (SUCCESS); 1648 } else if (status == NSS_NOTFOUND) { 1649 /* 1650 * data not found in name service 1651 * update cache 1652 */ 1653 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG_6) 1654 (me, "%s: name service lookup failed\n", whoami); 1655 1656 if (NSCD_GET_ERRNO(largs->buffer) == ERANGE) { 1657 delete_entry(nscdb, ctx, this_entry); 1658 (void) mutex_unlock(&nscdb->db_mutex); 1659 NSC_LOOKUP_LOG(DEBUG, 1660 "%s: ERANGE, cache not updated " 1661 "with negative entry\n"); 1662 return (NOTFOUND); 1663 } 1664 1665 status = dup_packed_buffer(largs, this_entry); 1666 if (status != NSS_SUCCESS) { 1667 delete_entry(nscdb, ctx, this_entry); 1668 (void) mutex_unlock(&nscdb->db_mutex); 1669 NSC_LOOKUP_LOG(ERROR, 1670 "%s: failed to update cache\n"); 1671 return (SERVERERROR); 1672 } 1673 1674 /* store unpacked key in cache */ 1675 status = nss_packed_getkey(this_entry->buffer, 1676 this_entry->bufsize, 1677 &dbname, &dbop, &args); 1678 if (status != NSS_SUCCESS) { 1679 delete_entry(nscdb, ctx, this_entry); 1680 (void) mutex_unlock(&nscdb->db_mutex); 1681 NSC_LOOKUP_LOG(ERROR, 1682 "%s: failed to extract key\n"); 1683 return (SERVERERROR); 1684 } 1685 this_entry->key = args.key; /* struct copy */ 1686 1687 /* update -ve ttl */ 1688 this_stats->timestamp = time(NULL) + cfg.neg_ttl; 1689 1690 /* update -ve miss count */ 1691 if (!(UPDATEBIT & flag)) { 1692 (void) mutex_lock(&ctx->stats_mutex); 1693 ctx->stats.neg_misses++; 1694 (void) mutex_unlock(&ctx->stats_mutex); 1695 } 1696 1697 /* 1698 * start the revalidation and reaper threads 1699 * if not already started 1700 */ 1701 start_threads(ctx); 1702 1703 (void) mutex_unlock(&nscdb->db_mutex); 1704 NSC_LOOKUP_LOG(DEBUG, 1705 "%s: cache updated with negative entry\n"); 1706 return (NOTFOUND); 1707 } else { 1708 /* 1709 * name service lookup failed 1710 */ 1711 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG_6) 1712 (me, "%s: name service lookup failed\n", whoami); 1713 1714 errnum = NSCD_GET_ERRNO(largs->buffer); 1715 if (delete == nscd_true) 1716 delete_entry(nscdb, ctx, this_entry); 1717 else 1718 this_stats->status = ST_DISCARD; 1719 1720 (void) mutex_unlock(&nscdb->db_mutex); 1721 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) 1722 (me, "%s: name service lookup failed " 1723 "(status=%d, errno=%d)\n", 1724 whoami, status, errnum); 1725 1726 return (SERVERERROR); 1727 } 1728 } else if (next_action == _NSC_USECACHED) { 1729 /* 1730 * found entry in cache 1731 */ 1732 if (UPDATEBIT & flag) { 1733 (void) mutex_unlock(&nscdb->db_mutex); 1734 NSC_LOOKUP_LOG(DEBUG, "%s: no need to update\n"); 1735 return (SUCCESS); 1736 } 1737 1738 if (NSCD_GET_STATUS((nss_pheader_t *)this_entry->buffer) == 1739 NSS_SUCCESS) { 1740 /* positive hit */ 1741 (void) mutex_lock(&ctx->stats_mutex); 1742 ctx->stats.pos_hits++; 1743 (void) mutex_unlock(&ctx->stats_mutex); 1744 1745 /* update response buffer */ 1746 if (copy_result(largs->buffer, 1747 this_entry->buffer) != NSS_SUCCESS) { 1748 (void) mutex_unlock(&nscdb->db_mutex); 1749 NSC_LOOKUP_LOG(ERROR, 1750 "%s: response buffer insufficient\n"); 1751 return (SERVERERROR); 1752 } 1753 1754 (void) mutex_unlock(&nscdb->db_mutex); 1755 NSC_LOOKUP_LOG(DEBUG, 1756 "%s: positive entry in cache\n"); 1757 return (SUCCESS); 1758 } else { 1759 /* negative hit */ 1760 (void) mutex_lock(&ctx->stats_mutex); 1761 ctx->stats.neg_hits++; 1762 (void) mutex_unlock(&ctx->stats_mutex); 1763 1764 NSCD_SET_STATUS((nss_pheader_t *)largs->buffer, 1765 NSCD_GET_STATUS(this_entry->buffer), 1766 NSCD_GET_ERRNO(this_entry->buffer)); 1767 NSCD_SET_HERRNO((nss_pheader_t *)largs->buffer, 1768 NSCD_GET_HERRNO(this_entry->buffer)); 1769 1770 (void) mutex_unlock(&nscdb->db_mutex); 1771 NSC_LOOKUP_LOG(DEBUG, 1772 "%s: negative entry in cache\n"); 1773 return (NOTFOUND); 1774 } 1775 } 1776 1777 (void) mutex_unlock(&nscdb->db_mutex); 1778 NSC_LOOKUP_LOG(ERROR, "%s: cache backend failure\n"); 1779 return (SERVERERROR); 1780 } 1781 1782 /* 1783 * NSCD cache backend lookup function 1784 */ 1785 /*ARGSUSED*/ 1786 void 1787 nsc_lookup(nsc_lookup_args_t *largs, int flag) 1788 { 1789 1790 nss_pheader_t *phdr = (nss_pheader_t *)largs->buffer; 1791 int rc; 1792 1793 rc = lookup_int(largs, 0); 1794 1795 if (NSCD_GET_STATUS(phdr) == NSS_TRYLOCAL) 1796 return; 1797 1798 switch (rc) { 1799 1800 case SUCCESS: 1801 NSCD_SET_STATUS(phdr, NSS_SUCCESS, 0); 1802 break; 1803 1804 case NOTFOUND: 1805 NSCD_SET_STATUS(phdr, NSS_NOTFOUND, -1); 1806 break; 1807 1808 case SERVERERROR: 1809 /* 1810 * status and errno should have been set in the phdr, 1811 * if not, set status to NSS_ERROR 1812 */ 1813 if (NSCD_STATUS_IS_OK(phdr)) { 1814 NSCD_SET_STATUS(phdr, NSS_ERROR, 0); 1815 } 1816 break; 1817 1818 case NOSERVER: 1819 NSCD_SET_STATUS(phdr, NSS_TRYLOCAL, -1); 1820 break; 1821 } 1822 } 1823 1824 1825 static nsc_ctx_t * 1826 init_cache_ctx(int i) 1827 { 1828 nsc_ctx_t *ctx; 1829 1830 ctx = calloc(1, sizeof (nsc_ctx_t)); 1831 if (ctx == NULL) 1832 return (NULL); 1833 1834 /* init locks and semaphores */ 1835 (void) mutex_init(&ctx->file_mutex, USYNC_THREAD, NULL); 1836 (void) rwlock_init(&ctx->cfg_rwlp, USYNC_THREAD, NULL); 1837 (void) mutex_init(&ctx->stats_mutex, USYNC_THREAD, NULL); 1838 (void) _nscd_init_cache_sema(&ctx->throttle_sema, cache_name[i]); 1839 cache_init_ctx[i](ctx); 1840 cache_ctx_p[i] = ctx; 1841 1842 return (ctx); 1843 } 1844 1845 1846 static void 1847 revalidate(nsc_ctx_t *ctx) 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 } 1875 1876 1877 static void 1878 getxy_keepalive(nsc_ctx_t *ctx, nsc_db_t *nscdb, int keep, int interval) 1879 { 1880 nsc_keephot_t *table; 1881 nsc_entry_t *entry, *ptr; 1882 int i; 1883 nsc_lookup_args_t *largs; 1884 nss_pheader_t *phdr; 1885 int bufsiz; 1886 char *me = "getxy_keepalive"; 1887 1888 /* we won't be here if keep == 0 so need to check that */ 1889 1890 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1891 (me, "%s: keep alive\n", nscdb->name); 1892 1893 if ((table = maken(keep)) == NULL) { 1894 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1895 (me, "memory allocation failure\n"); 1896 exit(1); 1897 } 1898 1899 (void) mutex_lock(&nscdb->db_mutex); 1900 entry = nscdb->qtail; 1901 while (entry != NULL) { 1902 /* leave pending calls alone */ 1903 if (!(entry->stats.status & ST_PENDING)) { 1904 /* do_revalidate */ 1905 (void) insertn(table, entry->stats.hits, entry); 1906 } 1907 entry = entry->qnext; 1908 } 1909 for (i = 1; i <= keep; i++) { 1910 if (table[i].ptr == NULL) 1911 continue; 1912 ptr = (nsc_entry_t *)table[i].ptr; 1913 phdr = (nss_pheader_t *)ptr->buffer; 1914 if (NSCD_GET_STATUS(phdr) == NSS_SUCCESS) 1915 /* 1916 * for positive cache, in addition to the packed 1917 * header size, allocate twice the size of the 1918 * existing result (in case the result grows 1919 * larger) plus 2K (for the file/compat backend to 1920 * process a possible large entry in the /etc files) 1921 */ 1922 bufsiz = phdr->data_off + 2 * phdr->data_len + 2048; 1923 else 1924 /* 1925 * for negative cache, allocate 8K buffer to 1926 * hold result in case the next lookup may 1927 * return something (in addition to the 1928 * packed header size) 1929 */ 1930 bufsiz = phdr->data_off + 8096; 1931 table[i].ptr = malloc(bufsiz); 1932 if (table[i].ptr == NULL) { 1933 (void) mutex_unlock(&nscdb->db_mutex); 1934 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1935 (me, "memory allocation failure\n"); 1936 exit(1); 1937 } 1938 (void) memcpy(table[i].ptr, ptr->buffer, ptr->bufsize); 1939 ((nss_pheader_t *)table[i].ptr)->pbufsiz = bufsiz; 1940 table[i].num = bufsiz; 1941 } 1942 (void) mutex_unlock(&nscdb->db_mutex); 1943 1944 /* launch update thread for each keep hot entry */ 1945 for (i = keep; i > 0; i--) { 1946 if (table[i].ptr == NULL) 1947 continue; /* unused slot in table */ 1948 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1949 (me, "%s: launching update\n", nscdb->name); 1950 largs = (nsc_lookup_args_t *)malloc(sizeof (*largs)); 1951 if (largs == NULL) { 1952 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1953 (me, "memory allocation failure\n"); 1954 exit(1); 1955 } 1956 largs->buffer = table[i].ptr; 1957 largs->bufsize = table[i].num; 1958 largs->ctx = ctx; 1959 largs->nscdb = nscdb; 1960 if (launch_update(largs) < 0) 1961 exit(1); 1962 (void) sleep(interval); 1963 } 1964 1965 /* 1966 * The update thread will handle freeing of buffer and largs. 1967 * Free the table here. 1968 */ 1969 free(table); 1970 } 1971 1972 1973 static int 1974 launch_update(nsc_lookup_args_t *in) 1975 { 1976 char *me = "launch_update"; 1977 int errnum; 1978 1979 errnum = thr_create(NULL, 0, (void *(*)(void*))do_update, 1980 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(nsc_lookup_args_t *in) 1993 { 1994 nss_pheader_t *phdr = (nss_pheader_t *)in->buffer; 1995 1996 (void) thr_setname(thr_self(), "do_update"); 1997 1998 /* update the length of the data buffer */ 1999 phdr->data_len = phdr->pbufsiz - phdr->data_off; 2000 2001 (void) lookup_int(in, UPDATEBIT); 2002 if (in->buffer) 2003 free(in->buffer); 2004 free(in); 2005 } 2006 2007 2008 /* 2009 * Invalidate cache 2010 */ 2011 void 2012 nsc_invalidate(nsc_ctx_t *ctx, char *dbname, nsc_ctx_t **ctxs) 2013 { 2014 int i; 2015 char *me = "nsc_invalidate"; 2016 2017 if (ctx) { 2018 ctx_invalidate(ctx); 2019 return; 2020 } 2021 2022 if (dbname) { 2023 if ((i = get_cache_idx(dbname)) == -1) { 2024 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) 2025 (me, "%s: invalid cache name\n", dbname); 2026 return; 2027 } 2028 if ((ctx = cache_ctx_p[i]) == NULL) { 2029 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) 2030 (me, "%s: no cache context found\n", 2031 dbname); 2032 return; 2033 } 2034 ctx_invalidate(ctx); 2035 return; 2036 } 2037 2038 if (ctxs == NULL) 2039 ctxs = cache_ctx_p; 2040 2041 for (i = 0; i < CACHE_CTX_COUNT; i++) { 2042 if (ctxs[i] != NULL) 2043 ctx_invalidate(ctxs[i]); 2044 } 2045 } 2046 2047 2048 /* 2049 * Invalidate cache by context 2050 */ 2051 static void 2052 ctx_invalidate(nsc_ctx_t *ctx) 2053 { 2054 int i; 2055 nsc_entry_t *entry; 2056 char *me = "ctx_invalidate"; 2057 2058 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2059 (me, "%s: invalidate cache\n", ctx->dbname); 2060 2061 for (i = 0; i < ctx->db_count; i++) { 2062 if (ctx->nsc_db[i] == NULL) 2063 continue; 2064 (void) mutex_lock(&ctx->nsc_db[i]->db_mutex); 2065 entry = ctx->nsc_db[i]->qtail; 2066 while (entry != NULL) { 2067 /* leave pending calls alone */ 2068 if (!(entry->stats.status & ST_PENDING)) 2069 entry->stats.status = ST_DISCARD; 2070 entry = entry->qnext; 2071 } 2072 (void) mutex_unlock(&ctx->nsc_db[i]->db_mutex); 2073 } 2074 2075 (void) mutex_lock(&ctx->stats_mutex); 2076 ctx->stats.invalidate_count++; 2077 (void) mutex_unlock(&ctx->stats_mutex); 2078 } 2079 2080 2081 /* 2082 * Free nsc_entry_t 2083 * 2084 * Pre-reqs: 2085 * nscdb->db_mutex lock must be held before calling this function 2086 */ 2087 static void 2088 delete_entry(nsc_db_t *nscdb, nsc_ctx_t *ctx, nsc_entry_t *entry) 2089 { 2090 uint_t hash; 2091 2092 avl_remove(&nscdb->tree, entry); 2093 HASH_REMOVE(nscdb, entry, hash, nscd_false); 2094 queue_remove(nscdb, entry); 2095 if (entry->buffer != NULL) { 2096 free(entry->buffer); 2097 entry->buffer = NULL; 2098 } 2099 umem_cache_free(nsc_entry_cache, entry); 2100 (void) mutex_lock(&ctx->stats_mutex); 2101 ctx->stats.entries--; 2102 (void) mutex_unlock(&ctx->stats_mutex); 2103 } 2104 2105 2106 static nscd_rc_t 2107 lookup_cache(nsc_lookup_args_t *largs, nscd_cfg_cache_t *cfgp, 2108 nss_XbyY_args_t *argp, char *whoami, nsc_entry_t **entry) 2109 { 2110 nsc_db_t *nscdb; 2111 nsc_ctx_t *ctx; 2112 uint_t hash; 2113 avl_index_t pos; 2114 ulong_t nentries; 2115 nsc_entry_t find_entry, *node; 2116 char *me = "lookup_cache"; 2117 2118 ctx = largs->ctx; 2119 nscdb = largs->nscdb; 2120 2121 /* set the search key */ 2122 find_entry.key = argp->key; /* struct copy (not deep) */ 2123 2124 /* lookup the hash table ==> O(1) */ 2125 if (nscdb->htable) { 2126 *entry = hash_find(nscdb, &find_entry, &hash, nscd_true); 2127 if (*entry != NULL) { 2128 (void) queue_adjust(nscdb, *entry); 2129 return (NSCD_SUCCESS); 2130 } 2131 } 2132 2133 /* if not found, lookup the AVL tree ==> O(log n) */ 2134 *entry = (nsc_entry_t *)avl_find(&nscdb->tree, &find_entry, &pos); 2135 if (*entry != NULL) { 2136 (void) queue_adjust(nscdb, *entry); 2137 /* move it to the hash table */ 2138 if (nscdb->htable) { 2139 if (nscdb->htable[hash] == NULL || 2140 (*entry)->stats.hits >= 2141 nscdb->htable[hash]->stats.hits) { 2142 nscdb->htable[hash] = *entry; 2143 } 2144 } 2145 return (NSCD_SUCCESS); 2146 } 2147 2148 /* entry not found in the cache */ 2149 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2150 (me, "%s: cache miss\n", whoami); 2151 2152 if (cfgp->avoid_ns == nscd_true) { 2153 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2154 (me, "%s: avoid name service\n", whoami); 2155 return (NSCD_DB_ENTRY_NOT_FOUND); 2156 } 2157 2158 /* allocate memory for new entry (stub) */ 2159 *entry = (nsc_entry_t *)umem_cache_alloc(nsc_entry_cache, 2160 UMEM_DEFAULT); 2161 if (*entry == NULL) { 2162 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 2163 (me, "%s: memory allocation failure\n", whoami); 2164 return (NSCD_NO_MEMORY); 2165 } 2166 (void) memset(*entry, 0, sizeof (**entry)); 2167 2168 /* 2169 * Note that the actual data for the key is stored within 2170 * the largs->buffer (input buffer to nsc_lookup). 2171 * find_entry.key only contains pointers to this data. 2172 * 2173 * If largs->buffer will be re-allocated by nss_psearch 2174 * then (*entry)->key will have dangling pointers. 2175 * In such case, the following assignment needs to be 2176 * replaced by code that duplicates the key. 2177 */ 2178 (*entry)->key = find_entry.key; 2179 2180 /* 2181 * Add the entry to the cache. 2182 */ 2183 avl_insert(&nscdb->tree, *entry, pos); /* O(log n) */ 2184 (void) queue_adjust(nscdb, *entry); /* constant */ 2185 if (nscdb->htable) /* constant */ 2186 nscdb->htable[hash] = *entry; 2187 (*entry)->stats.status = ST_NEW_ENTRY; 2188 2189 (void) mutex_lock(&ctx->stats_mutex); 2190 nentries = ++(ctx->stats.entries); 2191 (void) mutex_unlock(&ctx->stats_mutex); 2192 2193 /* Have we exceeded max entries ? */ 2194 if (cfgp->maxentries > 0 && nentries > cfgp->maxentries) { 2195 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2196 (me, "%s: maximum entries exceeded -- " 2197 "deleting least recently used entry\n", 2198 whoami); 2199 2200 node = nscdb->qhead; 2201 while (node != NULL && node != *entry) { 2202 if (node->stats.status == ST_DISCARD || 2203 !(node->stats.status & ST_PENDING)) { 2204 delete_entry(nscdb, ctx, node); 2205 break; 2206 } 2207 node = node->qprev; 2208 } 2209 2210 /* 2211 * It's okay if we were not able to find one to delete. 2212 * The reaper (when invoked) will return the cache to a 2213 * safe level. 2214 */ 2215 } 2216 2217 return (NSCD_SUCCESS); 2218 } 2219 2220 static void 2221 reaper(nsc_ctx_t *ctx) 2222 { 2223 uint_t ttl, extra_sleep, total_sleep, intervals; 2224 uint_t nodes_per_interval, seconds_per_interval; 2225 ulong_t nsc_entries; 2226 char *me = "reaper"; 2227 2228 (void) thr_setname(thr_self(), me); 2229 2230 for (;;) { 2231 (void) mutex_lock(&ctx->stats_mutex); 2232 nsc_entries = ctx->stats.entries; 2233 (void) mutex_unlock(&ctx->stats_mutex); 2234 2235 (void) rw_rdlock(&ctx->cfg_rwlp); 2236 ttl = ctx->cfg.pos_ttl; 2237 (void) rw_unlock(&ctx->cfg_rwlp); 2238 2239 if (nsc_entries == 0) { 2240 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2241 (me, "%s: nothing to reap\n", ctx->dbname); 2242 2243 /* sleep for atleast 60 seconds */ 2244 if (ttl < 60) 2245 ttl = 60; 2246 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2247 (me, "%s: sleep %d\n", ctx->dbname, ttl); 2248 (void) sleep(ttl); 2249 continue; 2250 } 2251 2252 if (ttl < 32) ttl = 32; 2253 if (ttl > (1<<28)) ttl = 1<<28; 2254 2255 /* 2256 * minimum nodes_per_interval = 256 or 1<<8 2257 * maximum nodes_per_interval = nsc_entries 2258 * minimum seconds_per_interval = 32 or 1<<5 2259 * maximum_seconds_per_interval = ttl 2260 */ 2261 if (nsc_entries <= ttl) { 2262 intervals = (nsc_entries >> 8) + 1; 2263 seconds_per_interval = ttl / intervals; 2264 nodes_per_interval = 256; 2265 } else { 2266 intervals = (ttl >> 5) + 1; 2267 seconds_per_interval = 32; 2268 nodes_per_interval = nsc_entries / intervals; 2269 if (nodes_per_interval < 256) 2270 nodes_per_interval = 256; 2271 } 2272 2273 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2274 (me, "%s: total entries = %d, " 2275 "seconds per interval = %d, " 2276 "nodes per interval = %d\n", 2277 ctx->dbname, nsc_entries, seconds_per_interval, 2278 nodes_per_interval); 2279 total_sleep = reap_cache(ctx, nodes_per_interval, 2280 seconds_per_interval); 2281 extra_sleep = 1 + ttl - total_sleep; 2282 if (extra_sleep > 0) 2283 (void) sleep(extra_sleep); 2284 } 2285 } 2286 2287 2288 static uint_t 2289 reap_cache(nsc_ctx_t *ctx, uint_t nodes_per_interval, 2290 uint_t seconds_per_interval) 2291 { 2292 uint_t nodes_togo, total_sleep; 2293 time_t now; 2294 nsc_entry_t *node, *next_node; 2295 nsc_db_t *nscdb; 2296 uint_t primes[] = {_NSC_HTSIZE_PRIMES}; 2297 ulong_t count, nentries, maxentries; 2298 int i, slot, value, newhtsize; 2299 char *me = "reap_cache"; 2300 2301 count = 0; 2302 total_sleep = 0; 2303 nodes_togo = nodes_per_interval; 2304 now = time(NULL); 2305 2306 for (i = 0; i < ctx->db_count; i++) { 2307 nscdb = ctx->nsc_db[i]; 2308 (void) mutex_lock(&nscdb->db_mutex); 2309 nscdb->reap_node = nscdb->qtail; 2310 while (nscdb->reap_node != NULL) { 2311 if (nodes_togo == 0) { 2312 (void) mutex_unlock(&nscdb->db_mutex); 2313 (void) sleep(seconds_per_interval); 2314 total_sleep += seconds_per_interval; 2315 nodes_togo = nodes_per_interval; 2316 now = time(NULL); 2317 (void) mutex_lock(&nscdb->db_mutex); 2318 } 2319 /* delete ST_DISCARD and expired nodes */ 2320 if ((node = nscdb->reap_node) == NULL) 2321 break; 2322 if (node->stats.status == ST_DISCARD || 2323 (!(node->stats.status & ST_PENDING) && 2324 node->stats.timestamp < now)) { 2325 /* 2326 * Delete entry if its discard flag is 2327 * set OR if it has expired. Entries 2328 * with pending updates are not 2329 * deleted. 2330 * nscdb->reap_node will be adjusted 2331 * by delete_entry() 2332 */ 2333 delete_entry(nscdb, ctx, node); 2334 count++; 2335 } else { 2336 nscdb->reap_node = node->qnext; 2337 } 2338 nodes_togo--; 2339 } 2340 2341 if (nscdb->htsize == 0) { 2342 (void) mutex_unlock(&nscdb->db_mutex); 2343 continue; 2344 } 2345 2346 /* 2347 * Dynamic adjustment of hash table size. 2348 * 2349 * Hash table size is roughly 1/8th of the 2350 * total entries. However the size is changed 2351 * only when the number of entries double or 2352 * reduced by half 2353 */ 2354 nentries = avl_numnodes(&nscdb->tree); 2355 for (slot = 0, value = _NSC_INIT_HTSIZE_SLOT_VALUE; 2356 slot < _NSC_HTSIZE_NUM_SLOTS && nentries > value; 2357 value = (value << 1) + 1, slot++) 2358 ; 2359 if (nscdb->hash_type == nsc_ht_power2) 2360 newhtsize = _NSC_INIT_HTSIZE_POWER2 << slot; 2361 else 2362 newhtsize = primes[slot]; 2363 2364 /* Recommended size is same as the current size. Done */ 2365 if (nscdb->htsize == newhtsize) { 2366 (void) mutex_unlock(&nscdb->db_mutex); 2367 continue; 2368 } 2369 2370 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2371 (me, "%s: resizing hash table from %d to %d\n", 2372 nscdb->name, nscdb->htsize, newhtsize); 2373 2374 /* 2375 * Dump old hashes because it would be time 2376 * consuming to rehash them. 2377 */ 2378 (void) free(nscdb->htable); 2379 nscdb->htable = calloc(newhtsize, sizeof (*(nscdb->htable))); 2380 if (nscdb->htable == NULL) { 2381 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 2382 (me, "%s: memory allocation failure\n", 2383 nscdb->name); 2384 /* -1 to try later */ 2385 nscdb->htsize = -1; 2386 } else { 2387 nscdb->htsize = newhtsize; 2388 } 2389 (void) mutex_unlock(&nscdb->db_mutex); 2390 } 2391 2392 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2393 (me, "%s: reaped %lu entries\n", ctx->dbname, count); 2394 2395 /* 2396 * if cache is almost full then reduce it to a safe level by 2397 * evicting LRU entries 2398 */ 2399 2400 (void) rw_rdlock(&ctx->cfg_rwlp); 2401 maxentries = ctx->cfg.maxentries; 2402 (void) rw_unlock(&ctx->cfg_rwlp); 2403 2404 /* No limit on number of entries. Done */ 2405 if (maxentries == 0) 2406 goto out; 2407 2408 (void) mutex_lock(&ctx->stats_mutex); 2409 nentries = ctx->stats.entries; 2410 (void) mutex_unlock(&ctx->stats_mutex); 2411 2412 /* what is the percentage of cache used ? */ 2413 value = (nentries * 100) / maxentries; 2414 if (value < _NSC_EVICTION_START_LEVEL) 2415 goto out; 2416 2417 /* 2418 * cache needs to be reduced to a safe level 2419 */ 2420 value -= _NSC_EVICTION_SAFE_LEVEL; 2421 for (i = 0, count = 0; i < ctx->db_count; i++) { 2422 /* 2423 * Reduce each subcache by 'value' percent 2424 */ 2425 nscdb = ctx->nsc_db[i]; 2426 (void) mutex_lock(&nscdb->db_mutex); 2427 nodes_togo = (value * avl_numnodes(&nscdb->tree)) / 100; 2428 2429 /* Start from LRU entry i.e queue head */ 2430 next_node = nscdb->qhead; 2431 while (nodes_togo > 0 && next_node != NULL) { 2432 node = next_node; 2433 next_node = next_node->qprev; 2434 if (node->stats.status == ST_DISCARD || 2435 !(node->stats.status & ST_PENDING)) { 2436 /* Leave nodes with pending updates alone */ 2437 delete_entry(nscdb, ctx, node); 2438 count++; 2439 nodes_togo--; 2440 } 2441 } 2442 (void) mutex_unlock(&nscdb->db_mutex); 2443 } 2444 2445 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2446 (me, "%s: evicted %lu LRU entries\n", ctx->dbname, count); 2447 2448 out: 2449 return (total_sleep); 2450 } 2451