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