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