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 2006 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->wait, &nscdb->db_mutex, 1518 &this_stats->status); 1519 (void) _nscd_release_clearance(&ctx->throttle_sema); 1520 continue; 1521 } 1522 1523 break; 1524 } 1525 1526 1527 if (!(UPDATEBIT & flag)) 1528 this_stats->hits++; /* update hit count */ 1529 1530 if (next_action == _NSC_NSLOOKUP) { 1531 1532 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1533 (me, "%s: name service lookup required\n", whoami); 1534 1535 if (_nscd_get_clearance(&ctx->throttle_sema) != 0) { 1536 if (delete == nscd_true) 1537 delete_entry(nscdb, ctx, this_entry); 1538 else 1539 this_stats->status = ST_DISCARD; 1540 (void) mutex_lock(&ctx->stats_mutex); 1541 ctx->stats.drop_count++; 1542 (void) mutex_unlock(&ctx->stats_mutex); 1543 NSC_LOOKUP_RETURN(NOSERVER, WARNING, 1544 "%s: no clearance for lookup\n"); 1545 } 1546 1547 /* block any threads accessing this entry */ 1548 this_stats->status = (flag & UPDATEBIT)? 1549 ST_UPDATE_PENDING:ST_LOOKUP_PENDING; 1550 1551 /* release lock and do name service lookup */ 1552 (void) mutex_unlock(&nscdb->db_mutex); 1553 nss_psearch(largs->buffer, largs->bufsize); 1554 status = NSCD_GET_STATUS(largs->buffer); 1555 (void) mutex_lock(&nscdb->db_mutex); 1556 this_stats->status = 0; 1557 (void) _nscd_release_clearance(&ctx->throttle_sema); 1558 1559 /* signal waiting threads */ 1560 (void) nscd_signal(&ctx->wait, &this_stats->status); 1561 1562 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1563 (me, "%s: name service lookup status = %d\n", 1564 whoami, status); 1565 1566 if (status == NSS_SUCCESS) { 1567 int ttl; 1568 1569 /* 1570 * data found in name service 1571 * update cache 1572 */ 1573 status = dup_packed_buffer(largs, this_entry); 1574 if (status != NSS_SUCCESS) { 1575 delete_entry(nscdb, ctx, this_entry); 1576 NSC_LOOKUP_RETURN(SERVERERROR, ERROR, 1577 "%s: failed to update cache\n"); 1578 } 1579 1580 /* 1581 * store unpacked key in cache 1582 */ 1583 status = nss_packed_getkey(this_entry->buffer, 1584 this_entry->bufsize, 1585 &dbname, &dbop, &args); 1586 if (status != NSS_SUCCESS) { 1587 delete_entry(nscdb, ctx, this_entry); 1588 NSC_LOOKUP_RETURN(SERVERERROR, ERROR, 1589 "%s: failed to extract key\n"); 1590 } 1591 this_entry->key = args.key; /* struct copy */ 1592 1593 /* update +ve miss count */ 1594 if (!(UPDATEBIT & flag)) { 1595 (void) mutex_lock(&ctx->stats_mutex); 1596 ctx->stats.pos_misses++; 1597 (void) mutex_unlock(&ctx->stats_mutex); 1598 } 1599 1600 /* update +ve ttl */ 1601 ttl = get_dns_ttl(largs->buffer, dbname); 1602 /* honor the dns ttl less than postive ttl */ 1603 if (ttl < 0 || ttl > cfg.pos_ttl) 1604 ttl = cfg.pos_ttl; 1605 this_stats->timestamp = time(NULL) + ttl; 1606 1607 /* 1608 * start the revalidation and reaper threads 1609 * if not already started 1610 */ 1611 start_threads(ctx); 1612 1613 NSC_LOOKUP_RETURN(SUCCESS, DEBUG, 1614 "%s: cache updated with positive entry\n"); 1615 } else if (status == NSS_NOTFOUND) { 1616 /* 1617 * data not found in name service 1618 * update cache 1619 */ 1620 1621 if (NSCD_GET_ERRNO(largs->buffer) == ERANGE) { 1622 delete_entry(nscdb, ctx, this_entry); 1623 NSC_LOOKUP_RETURN(NOTFOUND, DEBUG, 1624 "%s: ERANGE, cache not updated with negative entry\n"); 1625 } 1626 1627 status = dup_packed_buffer(largs, this_entry); 1628 if (status != NSS_SUCCESS) { 1629 delete_entry(nscdb, ctx, this_entry); 1630 NSC_LOOKUP_RETURN(SERVERERROR, ERROR, 1631 "%s: failed to update cache\n"); 1632 } 1633 1634 /* store unpacked key in cache */ 1635 status = nss_packed_getkey(this_entry->buffer, 1636 this_entry->bufsize, 1637 &dbname, &dbop, &args); 1638 if (status != NSS_SUCCESS) { 1639 delete_entry(nscdb, ctx, this_entry); 1640 NSC_LOOKUP_RETURN(SERVERERROR, ERROR, 1641 "%s: failed to extract key\n"); 1642 } 1643 this_entry->key = args.key; /* struct copy */ 1644 1645 /* update -ve ttl */ 1646 this_stats->timestamp = time(NULL) + cfg.neg_ttl; 1647 1648 /* update -ve miss count */ 1649 if (!(UPDATEBIT & flag)) { 1650 (void) mutex_lock(&ctx->stats_mutex); 1651 ctx->stats.neg_misses++; 1652 (void) mutex_unlock(&ctx->stats_mutex); 1653 } 1654 1655 /* 1656 * start the revalidation and reaper threads 1657 * if not already started 1658 */ 1659 start_threads(ctx); 1660 1661 NSC_LOOKUP_RETURN(NOTFOUND, DEBUG, 1662 "%s: cache updated with negative entry\n"); 1663 } else { 1664 /* 1665 * name service lookup failed 1666 */ 1667 errnum = NSCD_GET_ERRNO(largs->buffer); 1668 if (delete == nscd_true) 1669 delete_entry(nscdb, ctx, this_entry); 1670 else 1671 this_stats->status = ST_DISCARD; 1672 1673 (void) mutex_unlock(&nscdb->db_mutex); 1674 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_WARNING) 1675 (me, "%s: name service lookup failed (status=%d, errno=%d)\n", 1676 whoami, status, errnum); 1677 1678 return (SERVERERROR); 1679 } 1680 } else if (next_action == _NSC_USECACHED) { 1681 /* 1682 * found entry in cache 1683 */ 1684 if (UPDATEBIT & flag) { 1685 NSC_LOOKUP_RETURN(SUCCESS, DEBUG, 1686 "%s: no need to update\n"); 1687 } 1688 1689 if (NSCD_GET_STATUS((nss_pheader_t *)this_entry->buffer) == 1690 NSS_SUCCESS) { 1691 /* positive hit */ 1692 (void) mutex_lock(&ctx->stats_mutex); 1693 ctx->stats.pos_hits++; 1694 (void) mutex_unlock(&ctx->stats_mutex); 1695 1696 /* update response buffer */ 1697 if (copy_result(largs->buffer, 1698 this_entry->buffer) != NSS_SUCCESS) { 1699 NSC_LOOKUP_RETURN(SERVERERROR, ERROR, 1700 "%s: response buffer insufficient\n"); 1701 } 1702 1703 NSC_LOOKUP_RETURN(SUCCESS, DEBUG, 1704 "%s: positive entry in cache\n"); 1705 } else { 1706 /* negative hit */ 1707 (void) mutex_lock(&ctx->stats_mutex); 1708 ctx->stats.neg_hits++; 1709 (void) mutex_unlock(&ctx->stats_mutex); 1710 1711 NSCD_SET_STATUS((nss_pheader_t *)largs->buffer, 1712 NSCD_GET_STATUS(this_entry->buffer), 1713 NSCD_GET_ERRNO(this_entry->buffer)); 1714 NSCD_SET_HERRNO((nss_pheader_t *)largs->buffer, 1715 NSCD_GET_HERRNO(this_entry->buffer)); 1716 1717 NSC_LOOKUP_RETURN(NOTFOUND, DEBUG, 1718 "%s: negative entry in cache\n"); 1719 } 1720 } 1721 1722 NSC_LOOKUP_RETURN(SERVERERROR, ERROR, 1723 "%s: cache backend failure\n"); 1724 } 1725 1726 /* 1727 * NSCD cache backend lookup function 1728 */ 1729 /*ARGSUSED*/ 1730 void 1731 nsc_lookup(nsc_lookup_args_t *largs, int flag) { 1732 1733 nss_pheader_t *phdr = (nss_pheader_t *)largs->buffer; 1734 int rc; 1735 1736 rc = lookup_int(largs, 0); 1737 1738 if (NSCD_GET_STATUS(phdr) == NSS_TRYLOCAL) 1739 return; 1740 1741 switch (rc) { 1742 1743 case SUCCESS: 1744 NSCD_RETURN_STATUS(phdr, NSS_SUCCESS, 0); 1745 break; 1746 1747 case NOTFOUND: 1748 NSCD_RETURN_STATUS(phdr, NSS_NOTFOUND, -1); 1749 break; 1750 1751 case SERVERERROR: 1752 /* status and errno already set in the phdr */ 1753 break; 1754 1755 case NOSERVER: 1756 NSCD_RETURN_STATUS(phdr, NSS_UNAVAIL, -1); 1757 break; 1758 } 1759 } 1760 1761 1762 static nsc_ctx_t * 1763 init_cache_ctx(int i) { 1764 nsc_ctx_t *ctx; 1765 1766 ctx = calloc(1, sizeof (nsc_ctx_t)); 1767 if (ctx == NULL) 1768 return (NULL); 1769 1770 /* init locks and semaphores */ 1771 (void) mutex_init(&ctx->file_mutex, USYNC_THREAD, NULL); 1772 (void) rwlock_init(&ctx->cfg_rwlp, USYNC_THREAD, NULL); 1773 (void) mutex_init(&ctx->stats_mutex, USYNC_THREAD, NULL); 1774 (void) _nscd_init_cache_sema(&ctx->throttle_sema, cache_name[i]); 1775 cache_init_ctx[i](ctx); 1776 cache_ctx_p[i] = ctx; 1777 1778 return (ctx); 1779 } 1780 1781 1782 static void 1783 revalidate(nsc_ctx_t *ctx) 1784 { 1785 for (;;) { 1786 int i, slp, interval, count; 1787 1788 (void) rw_rdlock(&ctx->cfg_rwlp); 1789 slp = ctx->cfg.pos_ttl; 1790 count = ctx->cfg.keephot; 1791 (void) rw_unlock(&ctx->cfg_rwlp); 1792 1793 if (slp < 60) 1794 slp = 60; 1795 if (count != 0) { 1796 interval = (slp/2)/count; 1797 if (interval == 0) 1798 interval = 1; 1799 (void) sleep(slp*2/3); 1800 for (i = 0; i < ctx->db_count; i++) { 1801 getxy_keepalive(ctx, ctx->nsc_db[i], 1802 count, interval); 1803 } 1804 } else { 1805 (void) sleep(slp); 1806 } 1807 } 1808 } 1809 1810 1811 static void 1812 getxy_keepalive(nsc_ctx_t *ctx, nsc_db_t *nscdb, int keep, int interval) 1813 { 1814 nsc_keephot_t *table; 1815 nsc_entry_t *entry, *ptr; 1816 int i; 1817 nsc_lookup_args_t *largs; 1818 nss_pheader_t *phdr; 1819 int bufsiz; 1820 char *me = "getxy_keepalive"; 1821 1822 /* we won't be here if keep == 0 so need to check that */ 1823 1824 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1825 (me, "%s: keep alive\n", nscdb->name); 1826 1827 if ((table = maken(keep)) == NULL) { 1828 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1829 (me, "memory allocation failure\n"); 1830 exit(1); 1831 } 1832 1833 (void) mutex_lock(&nscdb->db_mutex); 1834 entry = nscdb->qtail; 1835 while (entry != NULL) { 1836 /* leave pending calls alone */ 1837 if (!(entry->stats.status & ST_PENDING)) { 1838 /* do_revalidate */ 1839 (void) insertn(table, entry->stats.hits, 1840 entry); 1841 } 1842 entry = entry->qnext; 1843 } 1844 for (i = 1; i <= keep; i++) { 1845 if (table[i].ptr == NULL) 1846 continue; 1847 ptr = (nsc_entry_t *)table[i].ptr; 1848 phdr = (nss_pheader_t *)ptr->buffer; 1849 if (NSCD_GET_STATUS(phdr) == NSS_SUCCESS) 1850 /* 1851 * for positive cache, in addition to the packed 1852 * header size, allocate twice the size of the 1853 * existing result (in case the result grows 1854 * larger) plus 2K (for the file/compat backend to 1855 * process a possible large entry in the /etc files) 1856 */ 1857 bufsiz = phdr->data_off + 2 * phdr->data_len + 2048; 1858 else 1859 /* 1860 * for negative cache, allocate 8K buffer to 1861 * hold result in case the next lookup may 1862 * return something (in addition to the 1863 * packed header size) 1864 */ 1865 bufsiz = phdr->data_off + 8096; 1866 table[i].ptr = malloc(bufsiz); 1867 if (table[i].ptr == NULL) { 1868 (void) mutex_unlock(&nscdb->db_mutex); 1869 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1870 (me, "memory allocation failure\n"); 1871 exit(1); 1872 } 1873 (void) memcpy(table[i].ptr, ptr->buffer, ptr->bufsize); 1874 ((nss_pheader_t *)table[i].ptr)->pbufsiz = bufsiz; 1875 table[i].num = bufsiz; 1876 } 1877 (void) mutex_unlock(&nscdb->db_mutex); 1878 1879 /* launch update thread for each keep hot entry */ 1880 for (i = keep; i > 0; i--) { 1881 if (table[i].ptr == NULL) 1882 continue; /* unused slot in table */ 1883 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1884 (me, "%s: launching update\n", nscdb->name); 1885 largs = (nsc_lookup_args_t *)malloc(sizeof (*largs)); 1886 if (largs == NULL) { 1887 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1888 (me, "memory allocation failure\n"); 1889 exit(1); 1890 } 1891 largs->buffer = table[i].ptr; 1892 largs->bufsize = table[i].num; 1893 largs->ctx = ctx; 1894 largs->nscdb = nscdb; 1895 if (launch_update(largs) < 0) 1896 exit(1); 1897 (void) sleep(interval); 1898 } 1899 1900 /* 1901 * The update thread will handle freeing of buffer and largs. 1902 * Free the table here. 1903 */ 1904 free(table); 1905 } 1906 1907 1908 static int 1909 launch_update(nsc_lookup_args_t *in) 1910 { 1911 char *me = "launch_update"; 1912 int errnum; 1913 1914 errnum = thr_create(NULL, NULL, (void *(*)(void*))do_update, 1915 in, 0|THR_DETACHED, NULL); 1916 if (errnum != 0) { 1917 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 1918 (me, "%s: thread creation failure (%d)\n", 1919 in->nscdb->name, errnum); 1920 return (-1); 1921 } 1922 return (0); 1923 } 1924 1925 1926 static void 1927 do_update(nsc_lookup_args_t *in) { 1928 nss_pheader_t *phdr = (nss_pheader_t *)in->buffer; 1929 1930 /* update the length of the data buffer */ 1931 phdr->data_len = phdr->pbufsiz - phdr->data_off; 1932 1933 (void) lookup_int(in, UPDATEBIT); 1934 if (in->buffer) 1935 free(in->buffer); 1936 free(in); 1937 } 1938 1939 1940 /* 1941 * Invalidate cache 1942 */ 1943 void 1944 nsc_invalidate(nsc_ctx_t *ctx, char *dbname, nsc_ctx_t **ctxs) { 1945 int i; 1946 char *me = "nsc_invalidate"; 1947 1948 if (ctx) { 1949 ctx_invalidate(ctx); 1950 return; 1951 } 1952 1953 if (dbname) { 1954 if ((i = get_cache_idx(dbname)) == -1) { 1955 _NSCD_LOG(NSCD_LOG_CACHE, 1956 NSCD_LOG_LEVEL_WARNING) 1957 (me, "%s: invalid cache name\n", dbname); 1958 return; 1959 } 1960 if ((ctx = cache_ctx_p[i]) == NULL) { 1961 _NSCD_LOG(NSCD_LOG_CACHE, 1962 NSCD_LOG_LEVEL_WARNING) 1963 (me, "%s: no cache context found\n", 1964 dbname); 1965 return; 1966 } 1967 ctx_invalidate(ctx); 1968 return; 1969 } 1970 1971 if (ctxs == NULL) 1972 ctxs = cache_ctx_p; 1973 1974 for (i = 0; i < CACHE_CTX_COUNT; i++) { 1975 if (ctxs[i] != NULL) 1976 ctx_invalidate(ctxs[i]); 1977 } 1978 } 1979 1980 1981 /* 1982 * Invalidate cache by context 1983 */ 1984 static void 1985 ctx_invalidate(nsc_ctx_t *ctx) { 1986 int i; 1987 nsc_entry_t *entry; 1988 char *me = "ctx_invalidate"; 1989 1990 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 1991 (me, "%s: invalidate cache\n", ctx->dbname); 1992 1993 for (i = 0; i < ctx->db_count; i++) { 1994 if (ctx->nsc_db[i] == NULL) 1995 continue; 1996 (void) mutex_lock(&ctx->nsc_db[i]->db_mutex); 1997 entry = ctx->nsc_db[i]->qtail; 1998 while (entry != NULL) { 1999 /* leave pending calls alone */ 2000 if (!(entry->stats.status & ST_PENDING)) 2001 entry->stats.status = ST_DISCARD; 2002 entry = entry->qnext; 2003 } 2004 (void) mutex_unlock(&ctx->nsc_db[i]->db_mutex); 2005 } 2006 2007 (void) mutex_lock(&ctx->stats_mutex); 2008 ctx->stats.invalidate_count++; 2009 (void) mutex_unlock(&ctx->stats_mutex); 2010 } 2011 2012 2013 /* 2014 * Free nsc_entry_t 2015 * 2016 * Pre-reqs: 2017 * nscdb->db_mutex lock must be held before calling this function 2018 */ 2019 static void 2020 delete_entry(nsc_db_t *nscdb, nsc_ctx_t *ctx, nsc_entry_t *entry) { 2021 uint_t hash; 2022 2023 avl_remove(&nscdb->tree, entry); 2024 HASH_REMOVE(nscdb, entry, hash, nscd_false); 2025 queue_remove(nscdb, entry); 2026 if (entry->buffer != NULL) { 2027 free(entry->buffer); 2028 entry->buffer = NULL; 2029 } 2030 umem_cache_free(nsc_entry_cache, entry); 2031 (void) mutex_lock(&ctx->stats_mutex); 2032 ctx->stats.entries--; 2033 (void) mutex_unlock(&ctx->stats_mutex); 2034 } 2035 2036 2037 static nscd_rc_t 2038 lookup_cache(nsc_lookup_args_t *largs, nscd_cfg_cache_t *cfgp, 2039 nss_XbyY_args_t *argp, char *whoami, nsc_entry_t **entry) { 2040 2041 nsc_db_t *nscdb; 2042 nsc_ctx_t *ctx; 2043 uint_t hash; 2044 avl_index_t pos; 2045 ulong_t nentries; 2046 nsc_entry_t find_entry, *node; 2047 char *me = "lookup_cache"; 2048 2049 ctx = largs->ctx; 2050 nscdb = largs->nscdb; 2051 2052 /* set the search key */ 2053 find_entry.key = argp->key; /* struct copy (not deep) */ 2054 2055 /* lookup the hash table ==> O(1) */ 2056 if (nscdb->htable) { 2057 *entry = hash_find(nscdb, &find_entry, &hash, nscd_true); 2058 if (*entry != NULL) { 2059 (void) queue_adjust(nscdb, *entry); 2060 return (NSCD_SUCCESS); 2061 } 2062 } 2063 2064 /* if not found, lookup the AVL tree ==> O(log n) */ 2065 *entry = (nsc_entry_t *)avl_find(&nscdb->tree, &find_entry, &pos); 2066 if (*entry != NULL) { 2067 (void) queue_adjust(nscdb, *entry); 2068 /* move it to the hash table */ 2069 if (nscdb->htable) { 2070 if (nscdb->htable[hash] == NULL || 2071 (*entry)->stats.hits >= 2072 nscdb->htable[hash]->stats.hits) { 2073 nscdb->htable[hash] = *entry; 2074 } 2075 } 2076 return (NSCD_SUCCESS); 2077 } 2078 2079 /* entry not found in the cache */ 2080 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2081 (me, "%s: cache miss\n", whoami); 2082 2083 if (cfgp->avoid_ns == nscd_true) { 2084 _NSCD_LOG(NSCD_LOG_CACHE, 2085 NSCD_LOG_LEVEL_DEBUG) 2086 (me, "%s: avoid name service\n", whoami); 2087 return (NSCD_DB_ENTRY_NOT_FOUND); 2088 } 2089 2090 /* allocate memory for new entry (stub) */ 2091 *entry = (nsc_entry_t *)umem_cache_alloc(nsc_entry_cache, 2092 UMEM_DEFAULT); 2093 if (*entry == NULL) { 2094 _NSCD_LOG(NSCD_LOG_CACHE, 2095 NSCD_LOG_LEVEL_ERROR) 2096 (me, "%s: memory allocation failure\n", whoami); 2097 return (NSCD_NO_MEMORY); 2098 } 2099 (void) memset(*entry, 0, sizeof (**entry)); 2100 2101 /* 2102 * Note that the actual data for the key is stored within 2103 * the largs->buffer (input buffer to nsc_lookup). 2104 * find_entry.key only contains pointers to this data. 2105 * 2106 * If largs->buffer will be re-allocated by nss_psearch 2107 * then (*entry)->key will have dangling pointers. 2108 * In such case, the following assignment needs to be 2109 * replaced by code that duplicates the key. 2110 */ 2111 (*entry)->key = find_entry.key; 2112 2113 /* 2114 * Add the entry to the cache. 2115 */ 2116 avl_insert(&nscdb->tree, *entry, pos); /* O(log n) */ 2117 (void) queue_adjust(nscdb, *entry); /* constant */ 2118 if (nscdb->htable) /* constant */ 2119 nscdb->htable[hash] = *entry; 2120 (*entry)->stats.status = ST_NEW_ENTRY; 2121 2122 (void) mutex_lock(&ctx->stats_mutex); 2123 nentries = ++(ctx->stats.entries); 2124 (void) mutex_unlock(&ctx->stats_mutex); 2125 2126 /* Have we exceeded max entries ? */ 2127 if (cfgp->maxentries > 0 && nentries > cfgp->maxentries) { 2128 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2129 (me, "%s: maximum entries exceeded -- " 2130 "deleting least recently used entry\n", 2131 whoami); 2132 2133 node = nscdb->qhead; 2134 while (node != NULL && node != *entry) { 2135 if (node->stats.status == ST_DISCARD || 2136 !(node->stats.status & ST_PENDING)) { 2137 delete_entry(nscdb, ctx, node); 2138 break; 2139 } 2140 node = node->qprev; 2141 } 2142 2143 /* 2144 * It's okay if we were not able to find one to delete. 2145 * The reaper (when invoked) will return the cache to a 2146 * safe level. 2147 */ 2148 } 2149 2150 return (NSCD_SUCCESS); 2151 } 2152 2153 static void 2154 reaper(nsc_ctx_t *ctx) { 2155 uint_t ttl, extra_sleep, total_sleep, intervals; 2156 uint_t nodes_per_interval, seconds_per_interval; 2157 ulong_t nsc_entries; 2158 char *me = "reaper"; 2159 2160 for (;;) { 2161 (void) mutex_lock(&ctx->stats_mutex); 2162 nsc_entries = ctx->stats.entries; 2163 (void) mutex_unlock(&ctx->stats_mutex); 2164 2165 (void) rw_rdlock(&ctx->cfg_rwlp); 2166 ttl = ctx->cfg.pos_ttl; 2167 (void) rw_unlock(&ctx->cfg_rwlp); 2168 2169 if (nsc_entries == 0) { 2170 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2171 (me, "%s: nothing to reap\n", ctx->dbname); 2172 2173 /* sleep for atleast 60 seconds */ 2174 if (ttl < 60) 2175 ttl = 60; 2176 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2177 (me, "%s: sleep %d\n", ctx->dbname, ttl); 2178 (void) sleep(ttl); 2179 continue; 2180 } 2181 2182 if (ttl < 32) ttl = 32; 2183 if (ttl > (1<<28)) ttl = 1<<28; 2184 2185 /* 2186 * minimum nodes_per_interval = 256 or 1<<8 2187 * maximum nodes_per_interval = nsc_entries 2188 * minimum seconds_per_interval = 32 or 1<<5 2189 * maximum_seconds_per_interval = ttl 2190 */ 2191 if (nsc_entries <= ttl) { 2192 intervals = (nsc_entries >> 8) + 1; 2193 seconds_per_interval = ttl / intervals; 2194 nodes_per_interval = 256; 2195 } else { 2196 intervals = (ttl >> 5) + 1; 2197 seconds_per_interval = 32; 2198 nodes_per_interval = nsc_entries / intervals; 2199 if (nodes_per_interval < 256) 2200 nodes_per_interval = 256; 2201 } 2202 2203 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2204 (me, "%s: total entries = %d, " 2205 "seconds per interval = %d, " 2206 "nodes per interval = %d\n", 2207 ctx->dbname, nsc_entries, seconds_per_interval, 2208 nodes_per_interval); 2209 total_sleep = reap_cache(ctx, nodes_per_interval, 2210 seconds_per_interval); 2211 extra_sleep = 1 + ttl - total_sleep; 2212 if (extra_sleep > 0) 2213 (void) sleep(extra_sleep); 2214 } 2215 } 2216 2217 2218 static uint_t 2219 reap_cache(nsc_ctx_t *ctx, uint_t nodes_per_interval, 2220 uint_t seconds_per_interval) { 2221 uint_t nodes_togo, total_sleep; 2222 time_t now; 2223 nsc_entry_t *node, *next_node; 2224 nsc_db_t *nscdb; 2225 uint_t primes[] = {_NSC_HTSIZE_PRIMES}; 2226 ulong_t count, nentries, maxentries; 2227 int i, slot, value, newhtsize; 2228 char *me = "reap_cache"; 2229 2230 count = 0; 2231 total_sleep = 0; 2232 nodes_togo = nodes_per_interval; 2233 now = time(NULL); 2234 2235 for (i = 0; i < ctx->db_count; i++) { 2236 nscdb = ctx->nsc_db[i]; 2237 (void) mutex_lock(&nscdb->db_mutex); 2238 nscdb->reap_node = nscdb->qtail; 2239 while (nscdb->reap_node != NULL) { 2240 if (nodes_togo == 0) { 2241 (void) mutex_unlock(&nscdb->db_mutex); 2242 (void) sleep(seconds_per_interval); 2243 total_sleep += seconds_per_interval; 2244 nodes_togo = nodes_per_interval; 2245 now = time(NULL); 2246 (void) mutex_lock(&nscdb->db_mutex); 2247 } 2248 /* delete ST_DISCARD and expired nodes */ 2249 if ((node = nscdb->reap_node) == NULL) 2250 break; 2251 if (node->stats.status == ST_DISCARD || 2252 (!(node->stats.status & ST_PENDING) && 2253 node->stats.timestamp < now)) { 2254 /* 2255 * Delete entry if its discard flag is 2256 * set OR if it has expired. Entries 2257 * with pending updates are not 2258 * deleted. 2259 * nscdb->reap_node will be adjusted 2260 * by delete_entry() 2261 */ 2262 delete_entry(nscdb, ctx, node); 2263 count++; 2264 } else { 2265 nscdb->reap_node = node->qnext; 2266 } 2267 nodes_togo--; 2268 } 2269 2270 if (nscdb->htsize == 0) { 2271 (void) mutex_unlock(&nscdb->db_mutex); 2272 continue; 2273 } 2274 2275 /* 2276 * Dynamic adjustment of hash table size. 2277 * 2278 * Hash table size is roughly 1/8th of the 2279 * total entries. However the size is changed 2280 * only when the number of entries double or 2281 * reduced by half 2282 */ 2283 nentries = avl_numnodes(&nscdb->tree); 2284 for (slot = 0, value = _NSC_INIT_HTSIZE_SLOT_VALUE; 2285 slot < _NSC_HTSIZE_NUM_SLOTS && nentries > value; 2286 value = (value << 1) + 1, slot++); 2287 if (nscdb->hash_type == nsc_ht_power2) 2288 newhtsize = _NSC_INIT_HTSIZE_POWER2 << slot; 2289 else 2290 newhtsize = primes[slot]; 2291 2292 /* Recommended size is same as the current size. Done */ 2293 if (nscdb->htsize == newhtsize) { 2294 (void) mutex_unlock(&nscdb->db_mutex); 2295 continue; 2296 } 2297 2298 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2299 (me, "%s: resizing hash table from %d to %d\n", 2300 nscdb->name, nscdb->htsize, newhtsize); 2301 2302 /* 2303 * Dump old hashes because it would be time 2304 * consuming to rehash them. 2305 */ 2306 (void) free(nscdb->htable); 2307 nscdb->htable = calloc(newhtsize, sizeof (*(nscdb->htable))); 2308 if (nscdb->htable == NULL) { 2309 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_ERROR) 2310 (me, 2311 "%s: memory allocation failure\n", 2312 nscdb->name); 2313 /* -1 to try later */ 2314 nscdb->htsize = -1; 2315 } else { 2316 nscdb->htsize = newhtsize; 2317 } 2318 (void) mutex_unlock(&nscdb->db_mutex); 2319 } 2320 2321 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2322 (me, "%s: reaped %lu entries\n", ctx->dbname, count); 2323 2324 /* 2325 * if cache is almost full then reduce it to a safe level by 2326 * evicting LRU entries 2327 */ 2328 2329 (void) rw_rdlock(&ctx->cfg_rwlp); 2330 maxentries = ctx->cfg.maxentries; 2331 (void) rw_unlock(&ctx->cfg_rwlp); 2332 2333 /* No limit on number of entries. Done */ 2334 if (maxentries == 0) 2335 goto out; 2336 2337 (void) mutex_lock(&ctx->stats_mutex); 2338 nentries = ctx->stats.entries; 2339 (void) mutex_unlock(&ctx->stats_mutex); 2340 2341 /* what is the percentage of cache used ? */ 2342 value = (nentries * 100) / maxentries; 2343 if (value < _NSC_EVICTION_START_LEVEL) 2344 goto out; 2345 2346 /* 2347 * cache needs to be reduced to a safe level 2348 */ 2349 value -= _NSC_EVICTION_SAFE_LEVEL; 2350 for (i = 0, count = 0; i < ctx->db_count; i++) { 2351 /* 2352 * Reduce each subcache by 'value' percent 2353 */ 2354 nscdb = ctx->nsc_db[i]; 2355 (void) mutex_lock(&nscdb->db_mutex); 2356 nodes_togo = (value * avl_numnodes(&nscdb->tree)) / 100; 2357 2358 /* Start from LRU entry i.e queue head */ 2359 next_node = nscdb->qhead; 2360 while (nodes_togo > 0 && next_node != NULL) { 2361 node = next_node; 2362 next_node = next_node->qprev; 2363 if (node->stats.status == ST_DISCARD || 2364 !(node->stats.status & ST_PENDING)) { 2365 /* Leave nodes with pending updates alone */ 2366 delete_entry(nscdb, ctx, node); 2367 count++; 2368 nodes_togo--; 2369 } 2370 } 2371 (void) mutex_unlock(&nscdb->db_mutex); 2372 } 2373 2374 _NSCD_LOG(NSCD_LOG_CACHE, NSCD_LOG_LEVEL_DEBUG) 2375 (me, "%s: evicted %lu LRU entries\n", ctx->dbname, count); 2376 2377 out: 2378 return (total_sleep); 2379 } 2380