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