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