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