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