1/* $OpenLDAP$ */ 2/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 3 * 4 * Copyright 2003-2011 The OpenLDAP Foundation. 5 * Portions Copyright 2003 IBM Corporation. 6 * Portions Copyright 2003-2009 Symas Corporation. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted only as authorized by the OpenLDAP 11 * Public License. 12 * 13 * A copy of this license is available in the file LICENSE in the 14 * top-level directory of the distribution or, alternatively, at 15 * <http://www.OpenLDAP.org/license.html>. 16 */ 17/* ACKNOWLEDGEMENTS: 18 * This work was initially developed by Apurva Kumar for inclusion 19 * in OpenLDAP Software and subsequently rewritten by Howard Chu. 20 */ 21 22#include "portable.h" 23 24#ifdef SLAPD_OVER_PROXYCACHE 25 26#include <stdio.h> 27 28#include <ac/string.h> 29#include <ac/time.h> 30 31#include "slap.h" 32#include "lutil.h" 33#include "ldap_rq.h" 34#include "avl.h" 35 36#include "../back-monitor/back-monitor.h" 37 38#include "config.h" 39 40#ifdef LDAP_DEVEL 41/* 42 * Control that allows to access the private DB 43 * instead of the public one 44 */ 45#define PCACHE_CONTROL_PRIVDB "1.3.6.1.4.1.4203.666.11.9.5.1" 46 47/* 48 * Extended Operation that allows to remove a query from the cache 49 */ 50#define PCACHE_EXOP_QUERY_DELETE "1.3.6.1.4.1.4203.666.11.9.6.1" 51 52/* 53 * Monitoring 54 */ 55#define PCACHE_MONITOR 56#endif 57 58/* query cache structs */ 59/* query */ 60 61typedef struct Query_s { 62 Filter* filter; /* Search Filter */ 63 struct berval base; /* Search Base */ 64 int scope; /* Search scope */ 65} Query; 66 67struct query_template_s; 68 69typedef struct Qbase_s { 70 Avlnode *scopes[4]; /* threaded AVL trees of cached queries */ 71 struct berval base; 72 int queries; 73} Qbase; 74 75/* struct representing a cached query */ 76typedef struct cached_query_s { 77 Filter *filter; 78 Filter *first; 79 Qbase *qbase; 80 int scope; 81 struct berval q_uuid; /* query identifier */ 82 int q_sizelimit; 83 struct query_template_s *qtemp; /* template of the query */ 84 time_t expiry_time; /* time till the query is considered invalid */ 85 time_t refresh_time; /* time till the query is refreshed */ 86 time_t bindref_time; /* time till the bind is refreshed */ 87 int bind_refcnt; /* number of bind operation referencing this query */ 88 unsigned long answerable_cnt; /* how many times it was answerable */ 89 int refcnt; /* references since last refresh */ 90 ldap_pvt_thread_mutex_t answerable_cnt_mutex; 91 struct cached_query_s *next; /* next query in the template */ 92 struct cached_query_s *prev; /* previous query in the template */ 93 struct cached_query_s *lru_up; /* previous query in the LRU list */ 94 struct cached_query_s *lru_down; /* next query in the LRU list */ 95 ldap_pvt_thread_rdwr_t rwlock; 96} CachedQuery; 97 98/* 99 * URL representation: 100 * 101 * ldap:///<base>??<scope>?<filter>?x-uuid=<uid>,x-template=<template>,x-attrset=<attrset>,x-expiry=<expiry>,x-refresh=<refresh> 102 * 103 * <base> ::= CachedQuery.qbase->base 104 * <scope> ::= CachedQuery.scope 105 * <filter> ::= filter2bv(CachedQuery.filter) 106 * <uuid> ::= CachedQuery.q_uuid 107 * <attrset> ::= CachedQuery.qtemp->attr_set_index 108 * <expiry> ::= CachedQuery.expiry_time 109 * <refresh> ::= CachedQuery.refresh_time 110 * 111 * quick hack: parse URI, call add_query() and then fix 112 * CachedQuery.expiry_time and CachedQuery.q_uuid 113 * 114 * NOTE: if the <attrset> changes, all stored URLs will be invalidated. 115 */ 116 117/* 118 * Represents a set of projected attributes. 119 */ 120 121struct attr_set { 122 struct query_template_s *templates; 123 AttributeName* attrs; /* specifies the set */ 124 unsigned flags; 125#define PC_CONFIGURED (0x1) 126#define PC_REFERENCED (0x2) 127#define PC_GOT_OC (0x4) 128 int count; /* number of attributes */ 129}; 130 131/* struct representing a query template 132 * e.g. template string = &(cn=)(mail=) 133 */ 134typedef struct query_template_s { 135 struct query_template_s *qtnext; 136 struct query_template_s *qmnext; 137 138 Avlnode* qbase; 139 CachedQuery* query; /* most recent query cached for the template */ 140 CachedQuery* query_last; /* oldest query cached for the template */ 141 ldap_pvt_thread_rdwr_t t_rwlock; /* Rd/wr lock for accessing queries in the template */ 142 struct berval querystr; /* Filter string corresponding to the QT */ 143 struct berval bindbase; /* base DN for Bind request */ 144 struct berval bindfilterstr; /* Filter string for Bind request */ 145 struct berval bindftemp; /* bind filter template */ 146 Filter *bindfilter; 147 AttributeDescription **bindfattrs; /* attrs to substitute in ftemp */ 148 149 int bindnattrs; /* number of bindfattrs */ 150 int bindscope; 151 int attr_set_index; /* determines the projected attributes */ 152 int no_of_queries; /* Total number of queries in the template */ 153 time_t ttl; /* TTL for the queries of this template */ 154 time_t negttl; /* TTL for negative results */ 155 time_t limitttl; /* TTL for sizelimit exceeding results */ 156 time_t ttr; /* time to refresh */ 157 time_t bindttr; /* TTR for cached binds */ 158 struct attr_set t_attrs; /* filter attrs + attr_set */ 159} QueryTemplate; 160 161typedef enum { 162 PC_IGNORE = 0, 163 PC_POSITIVE, 164 PC_NEGATIVE, 165 PC_SIZELIMIT 166} pc_caching_reason_t; 167 168static const char *pc_caching_reason_str[] = { 169 "IGNORE", 170 "POSITIVE", 171 "NEGATIVE", 172 "SIZELIMIT", 173 174 NULL 175}; 176 177struct query_manager_s; 178 179/* prototypes for functions for 1) query containment 180 * 2) query addition, 3) cache replacement 181 */ 182typedef CachedQuery *(QCfunc)(Operation *op, struct query_manager_s*, 183 Query*, QueryTemplate*); 184typedef CachedQuery *(AddQueryfunc)(Operation *op, struct query_manager_s*, 185 Query*, QueryTemplate*, pc_caching_reason_t, int wlock); 186typedef void (CRfunc)(struct query_manager_s*, struct berval*); 187 188/* LDAP query cache */ 189typedef struct query_manager_s { 190 struct attr_set* attr_sets; /* possible sets of projected attributes */ 191 QueryTemplate* templates; /* cacheable templates */ 192 193 CachedQuery* lru_top; /* top and bottom of LRU list */ 194 CachedQuery* lru_bottom; 195 196 ldap_pvt_thread_mutex_t lru_mutex; /* mutex for accessing LRU list */ 197 198 /* Query cache methods */ 199 QCfunc *qcfunc; /* Query containment*/ 200 CRfunc *crfunc; /* cache replacement */ 201 AddQueryfunc *addfunc; /* add query */ 202} query_manager; 203 204/* LDAP query cache manager */ 205typedef struct cache_manager_s { 206 BackendDB db; /* underlying database */ 207 unsigned long num_cached_queries; /* total number of cached queries */ 208 unsigned long max_queries; /* upper bound on # of cached queries */ 209 int save_queries; /* save cached queries across restarts */ 210 int check_cacheability; /* check whether a query is cacheable */ 211 int numattrsets; /* number of attribute sets */ 212 int cur_entries; /* current number of entries cached */ 213 int max_entries; /* max number of entries cached */ 214 int num_entries_limit; /* max # of entries in a cacheable query */ 215 216 char response_cb; /* install the response callback 217 * at the tail of the callback list */ 218#define PCACHE_RESPONSE_CB_HEAD 0 219#define PCACHE_RESPONSE_CB_TAIL 1 220 char defer_db_open; /* defer open for online add */ 221 char cache_binds; /* cache binds or just passthru */ 222 223 time_t cc_period; /* interval between successive consistency checks (sec) */ 224#define PCACHE_CC_PAUSED 1 225#define PCACHE_CC_OFFLINE 2 226 int cc_paused; 227 void *cc_arg; 228 229 ldap_pvt_thread_mutex_t cache_mutex; 230 231 query_manager* qm; /* query cache managed by the cache manager */ 232 233#ifdef PCACHE_MONITOR 234 void *monitor_cb; 235 struct berval monitor_ndn; 236#endif /* PCACHE_MONITOR */ 237} cache_manager; 238 239#ifdef PCACHE_MONITOR 240static int pcache_monitor_db_init( BackendDB *be ); 241static int pcache_monitor_db_open( BackendDB *be ); 242static int pcache_monitor_db_close( BackendDB *be ); 243static int pcache_monitor_db_destroy( BackendDB *be ); 244#endif /* PCACHE_MONITOR */ 245 246static int pcache_debug; 247 248#ifdef PCACHE_CONTROL_PRIVDB 249static int privDB_cid; 250#endif /* PCACHE_CONTROL_PRIVDB */ 251 252static AttributeDescription *ad_queryId, *ad_cachedQueryURL; 253 254#ifdef PCACHE_MONITOR 255static AttributeDescription *ad_numQueries, *ad_numEntries; 256static ObjectClass *oc_olmPCache; 257#endif /* PCACHE_MONITOR */ 258 259static struct { 260 char *name; 261 char *oid; 262} s_oid[] = { 263 { "PCacheOID", "1.3.6.1.4.1.4203.666.11.9.1" }, 264 { "PCacheAttributes", "PCacheOID:1" }, 265 { "PCacheObjectClasses", "PCacheOID:2" }, 266 267 { NULL } 268}; 269 270static struct { 271 char *desc; 272 AttributeDescription **adp; 273} s_ad[] = { 274 { "( PCacheAttributes:1 " 275 "NAME 'pcacheQueryID' " 276 "DESC 'ID of query the entry belongs to, formatted as a UUID' " 277 "EQUALITY octetStringMatch " 278 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{64} " 279 "NO-USER-MODIFICATION " 280 "USAGE directoryOperation )", 281 &ad_queryId }, 282 { "( PCacheAttributes:2 " 283 "NAME 'pcacheQueryURL' " 284 "DESC 'URI describing a cached query' " 285 "EQUALITY caseExactMatch " 286 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 " 287 "NO-USER-MODIFICATION " 288 "USAGE directoryOperation )", 289 &ad_cachedQueryURL }, 290#ifdef PCACHE_MONITOR 291 { "( PCacheAttributes:3 " 292 "NAME 'pcacheNumQueries' " 293 "DESC 'Number of cached queries' " 294 "EQUALITY integerMatch " 295 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 " 296 "NO-USER-MODIFICATION " 297 "USAGE directoryOperation )", 298 &ad_numQueries }, 299 { "( PCacheAttributes:4 " 300 "NAME 'pcacheNumEntries' " 301 "DESC 'Number of cached entries' " 302 "EQUALITY integerMatch " 303 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 " 304 "NO-USER-MODIFICATION " 305 "USAGE directoryOperation )", 306 &ad_numEntries }, 307#endif /* PCACHE_MONITOR */ 308 309 { NULL } 310}; 311 312static struct { 313 char *desc; 314 ObjectClass **ocp; 315} s_oc[] = { 316#ifdef PCACHE_MONITOR 317 /* augments an existing object, so it must be AUXILIARY */ 318 { "( PCacheObjectClasses:1 " 319 "NAME ( 'olmPCache' ) " 320 "SUP top AUXILIARY " 321 "MAY ( " 322 "pcacheQueryURL " 323 "$ pcacheNumQueries " 324 "$ pcacheNumEntries " 325 " ) )", 326 &oc_olmPCache }, 327#endif /* PCACHE_MONITOR */ 328 329 { NULL } 330}; 331 332static int 333filter2template( 334 Operation *op, 335 Filter *f, 336 struct berval *fstr ); 337 338static CachedQuery * 339add_query( 340 Operation *op, 341 query_manager* qm, 342 Query* query, 343 QueryTemplate *templ, 344 pc_caching_reason_t why, 345 int wlock); 346 347static int 348remove_query_data( 349 Operation *op, 350 struct berval *query_uuid ); 351 352/* 353 * Turn a cached query into its URL representation 354 */ 355static int 356query2url( Operation *op, CachedQuery *q, struct berval *urlbv, int dolock ) 357{ 358 struct berval bv_scope, 359 bv_filter; 360 char attrset_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ], 361 expiry_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ], 362 refresh_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ], 363 answerable_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ], 364 *ptr; 365 ber_len_t attrset_len, 366 expiry_len, 367 refresh_len, 368 answerable_len; 369 370 if ( dolock ) { 371 ldap_pvt_thread_rdwr_rlock( &q->rwlock ); 372 } 373 374 ldap_pvt_scope2bv( q->scope, &bv_scope ); 375 filter2bv_x( op, q->filter, &bv_filter ); 376 attrset_len = sprintf( attrset_buf, 377 "%lu", (unsigned long)q->qtemp->attr_set_index ); 378 expiry_len = sprintf( expiry_buf, 379 "%lu", (unsigned long)q->expiry_time ); 380 answerable_len = snprintf( answerable_buf, sizeof( answerable_buf ), 381 "%lu", q->answerable_cnt ); 382 if ( q->refresh_time ) 383 refresh_len = sprintf( refresh_buf, 384 "%lu", (unsigned long)q->refresh_time ); 385 else 386 refresh_len = 0; 387 388 urlbv->bv_len = STRLENOF( "ldap:///" ) 389 + q->qbase->base.bv_len 390 + STRLENOF( "??" ) 391 + bv_scope.bv_len 392 + STRLENOF( "?" ) 393 + bv_filter.bv_len 394 + STRLENOF( "?x-uuid=" ) 395 + q->q_uuid.bv_len 396 + STRLENOF( ",x-attrset=" ) 397 + attrset_len 398 + STRLENOF( ",x-expiry=" ) 399 + expiry_len 400 + STRLENOF( ",x-answerable=" ) 401 + answerable_len; 402 if ( refresh_len ) 403 urlbv->bv_len += STRLENOF( ",x-refresh=" ) 404 + refresh_len; 405 406 ptr = urlbv->bv_val = ber_memalloc_x( urlbv->bv_len + 1, op->o_tmpmemctx ); 407 ptr = lutil_strcopy( ptr, "ldap:///" ); 408 ptr = lutil_strcopy( ptr, q->qbase->base.bv_val ); 409 ptr = lutil_strcopy( ptr, "??" ); 410 ptr = lutil_strcopy( ptr, bv_scope.bv_val ); 411 ptr = lutil_strcopy( ptr, "?" ); 412 ptr = lutil_strcopy( ptr, bv_filter.bv_val ); 413 ptr = lutil_strcopy( ptr, "?x-uuid=" ); 414 ptr = lutil_strcopy( ptr, q->q_uuid.bv_val ); 415 ptr = lutil_strcopy( ptr, ",x-attrset=" ); 416 ptr = lutil_strcopy( ptr, attrset_buf ); 417 ptr = lutil_strcopy( ptr, ",x-expiry=" ); 418 ptr = lutil_strcopy( ptr, expiry_buf ); 419 ptr = lutil_strcopy( ptr, ",x-answerable=" ); 420 ptr = lutil_strcopy( ptr, answerable_buf ); 421 if ( refresh_len ) { 422 ptr = lutil_strcopy( ptr, ",x-refresh=" ); 423 ptr = lutil_strcopy( ptr, refresh_buf ); 424 } 425 426 ber_memfree_x( bv_filter.bv_val, op->o_tmpmemctx ); 427 428 if ( dolock ) { 429 ldap_pvt_thread_rdwr_runlock( &q->rwlock ); 430 } 431 432 return 0; 433} 434 435/* Find and record the empty filter clauses */ 436 437static int 438ftemp_attrs( struct berval *ftemp, struct berval *template, 439 AttributeDescription ***ret, const char **text ) 440{ 441 int i; 442 int attr_cnt=0; 443 struct berval bv; 444 char *p1, *p2, *t1; 445 AttributeDescription *ad; 446 AttributeDescription **descs = NULL; 447 char *temp2; 448 449 temp2 = ch_malloc( ftemp->bv_len + 1 ); 450 p1 = ftemp->bv_val; 451 t1 = temp2; 452 453 *ret = NULL; 454 455 for (;;) { 456 while ( *p1 == '(' || *p1 == '&' || *p1 == '|' || *p1 == ')' ) 457 *t1++ = *p1++; 458 459 p2 = strchr( p1, '=' ); 460 if ( !p2 ) 461 break; 462 i = p2 - p1; 463 AC_MEMCPY( t1, p1, i ); 464 t1 += i; 465 *t1++ = '='; 466 467 if ( p2[-1] == '<' || p2[-1] == '>' ) p2--; 468 bv.bv_val = p1; 469 bv.bv_len = p2 - p1; 470 ad = NULL; 471 i = slap_bv2ad( &bv, &ad, text ); 472 if ( i ) { 473 ch_free( descs ); 474 return -1; 475 } 476 if ( *p2 == '<' || *p2 == '>' ) p2++; 477 if ( p2[1] != ')' ) { 478 p2++; 479 while ( *p2 != ')' ) p2++; 480 p1 = p2; 481 continue; 482 } 483 484 descs = (AttributeDescription **)ch_realloc(descs, 485 (attr_cnt + 2)*sizeof(AttributeDescription *)); 486 487 descs[attr_cnt++] = ad; 488 489 p1 = p2+1; 490 } 491 *t1 = '\0'; 492 descs[attr_cnt] = NULL; 493 *ret = descs; 494 template->bv_val = temp2; 495 template->bv_len = t1 - temp2; 496 return attr_cnt; 497} 498 499static int 500template_attrs( char *template, struct attr_set *set, AttributeName **ret, 501 const char **text ) 502{ 503 int got_oc = 0; 504 int alluser = 0; 505 int allop = 0; 506 int i; 507 int attr_cnt; 508 int t_cnt = 0; 509 struct berval bv; 510 char *p1, *p2; 511 AttributeDescription *ad; 512 AttributeName *attrs; 513 514 p1 = template; 515 516 *ret = NULL; 517 518 attrs = ch_calloc( set->count + 1, sizeof(AttributeName) ); 519 for ( i=0; i < set->count; i++ ) 520 attrs[i] = set->attrs[i]; 521 attr_cnt = i; 522 alluser = an_find( attrs, slap_bv_all_user_attrs ); 523 allop = an_find( attrs, slap_bv_all_operational_attrs ); 524 525 for (;;) { 526 while ( *p1 == '(' || *p1 == '&' || *p1 == '|' || *p1 == ')' ) p1++; 527 p2 = strchr( p1, '=' ); 528 if ( !p2 ) 529 break; 530 if ( p2[-1] == '<' || p2[-1] == '>' ) p2--; 531 bv.bv_val = p1; 532 bv.bv_len = p2 - p1; 533 ad = NULL; 534 i = slap_bv2ad( &bv, &ad, text ); 535 if ( i ) { 536 ch_free( attrs ); 537 return -1; 538 } 539 t_cnt++; 540 541 if ( ad == slap_schema.si_ad_objectClass ) 542 got_oc = 1; 543 544 if ( is_at_operational(ad->ad_type)) { 545 if ( allop ) { 546 goto bottom; 547 } 548 } else if ( alluser ) { 549 goto bottom; 550 } 551 if ( !ad_inlist( ad, attrs )) { 552 attrs = (AttributeName *)ch_realloc(attrs, 553 (attr_cnt + 2)*sizeof(AttributeName)); 554 555 attrs[attr_cnt].an_desc = ad; 556 attrs[attr_cnt].an_name = ad->ad_cname; 557 attrs[attr_cnt].an_oc = NULL; 558 attrs[attr_cnt].an_flags = 0; 559 BER_BVZERO( &attrs[attr_cnt+1].an_name ); 560 attr_cnt++; 561 } 562 563bottom: 564 p1 = p2+2; 565 } 566 if ( !t_cnt ) { 567 *text = "couldn't parse template"; 568 return -1; 569 } 570 if ( !got_oc && !( set->flags & PC_GOT_OC )) { 571 attrs = (AttributeName *)ch_realloc(attrs, 572 (attr_cnt + 2)*sizeof(AttributeName)); 573 574 ad = slap_schema.si_ad_objectClass; 575 attrs[attr_cnt].an_desc = ad; 576 attrs[attr_cnt].an_name = ad->ad_cname; 577 attrs[attr_cnt].an_oc = NULL; 578 attrs[attr_cnt].an_flags = 0; 579 BER_BVZERO( &attrs[attr_cnt+1].an_name ); 580 attr_cnt++; 581 } 582 *ret = attrs; 583 return attr_cnt; 584} 585 586/* 587 * Turn an URL representing a formerly cached query into a cached query, 588 * and try to cache it 589 */ 590static int 591url2query( 592 char *url, 593 Operation *op, 594 query_manager *qm ) 595{ 596 Query query = { 0 }; 597 QueryTemplate *qt; 598 CachedQuery *cq; 599 LDAPURLDesc *lud = NULL; 600 struct berval base, 601 tempstr = BER_BVNULL, 602 uuid = BER_BVNULL; 603 int attrset; 604 time_t expiry_time; 605 time_t refresh_time; 606 unsigned long answerable_cnt; 607 int i, 608 got = 0, 609#define GOT_UUID 0x1U 610#define GOT_ATTRSET 0x2U 611#define GOT_EXPIRY 0x4U 612#define GOT_ANSWERABLE 0x8U 613#define GOT_REFRESH 0x10U 614#define GOT_ALL (GOT_UUID|GOT_ATTRSET|GOT_EXPIRY|GOT_ANSWERABLE) 615 rc = 0; 616 617 rc = ldap_url_parse( url, &lud ); 618 if ( rc != LDAP_URL_SUCCESS ) { 619 return -1; 620 } 621 622 /* non-allowed fields */ 623 if ( lud->lud_host != NULL ) { 624 rc = 1; 625 goto error; 626 } 627 628 if ( lud->lud_attrs != NULL ) { 629 rc = 1; 630 goto error; 631 } 632 633 /* be pedantic */ 634 if ( strcmp( lud->lud_scheme, "ldap" ) != 0 ) { 635 rc = 1; 636 goto error; 637 } 638 639 /* required fields */ 640 if ( lud->lud_dn == NULL || lud->lud_dn[ 0 ] == '\0' ) { 641 rc = 1; 642 goto error; 643 } 644 645 switch ( lud->lud_scope ) { 646 case LDAP_SCOPE_BASE: 647 case LDAP_SCOPE_ONELEVEL: 648 case LDAP_SCOPE_SUBTREE: 649 case LDAP_SCOPE_SUBORDINATE: 650 break; 651 652 default: 653 rc = 1; 654 goto error; 655 } 656 657 if ( lud->lud_filter == NULL || lud->lud_filter[ 0 ] == '\0' ) { 658 rc = 1; 659 goto error; 660 } 661 662 if ( lud->lud_exts == NULL ) { 663 rc = 1; 664 goto error; 665 } 666 667 for ( i = 0; lud->lud_exts[ i ] != NULL; i++ ) { 668 if ( strncmp( lud->lud_exts[ i ], "x-uuid=", STRLENOF( "x-uuid=" ) ) == 0 ) { 669 struct berval tmpUUID; 670 Syntax *syn_UUID = slap_schema.si_ad_entryUUID->ad_type->sat_syntax; 671 672 if ( got & GOT_UUID ) { 673 rc = 1; 674 goto error; 675 } 676 677 ber_str2bv( &lud->lud_exts[ i ][ STRLENOF( "x-uuid=" ) ], 0, 0, &tmpUUID ); 678 if ( !BER_BVISEMPTY( &tmpUUID ) ) { 679 rc = syn_UUID->ssyn_pretty( syn_UUID, &tmpUUID, &uuid, NULL ); 680 if ( rc != LDAP_SUCCESS ) { 681 goto error; 682 } 683 } 684 got |= GOT_UUID; 685 686 } else if ( strncmp( lud->lud_exts[ i ], "x-attrset=", STRLENOF( "x-attrset=" ) ) == 0 ) { 687 if ( got & GOT_ATTRSET ) { 688 rc = 1; 689 goto error; 690 } 691 692 rc = lutil_atoi( &attrset, &lud->lud_exts[ i ][ STRLENOF( "x-attrset=" ) ] ); 693 if ( rc ) { 694 goto error; 695 } 696 got |= GOT_ATTRSET; 697 698 } else if ( strncmp( lud->lud_exts[ i ], "x-expiry=", STRLENOF( "x-expiry=" ) ) == 0 ) { 699 unsigned long l; 700 701 if ( got & GOT_EXPIRY ) { 702 rc = 1; 703 goto error; 704 } 705 706 rc = lutil_atoul( &l, &lud->lud_exts[ i ][ STRLENOF( "x-expiry=" ) ] ); 707 if ( rc ) { 708 goto error; 709 } 710 expiry_time = (time_t)l; 711 got |= GOT_EXPIRY; 712 713 } else if ( strncmp( lud->lud_exts[ i ], "x-answerable=", STRLENOF( "x-answerable=" ) ) == 0 ) { 714 if ( got & GOT_ANSWERABLE ) { 715 rc = 1; 716 goto error; 717 } 718 719 rc = lutil_atoul( &answerable_cnt, &lud->lud_exts[ i ][ STRLENOF( "x-answerable=" ) ] ); 720 if ( rc ) { 721 goto error; 722 } 723 got |= GOT_ANSWERABLE; 724 725 } else if ( strncmp( lud->lud_exts[ i ], "x-refresh=", STRLENOF( "x-refresh=" ) ) == 0 ) { 726 unsigned long l; 727 728 if ( got & GOT_REFRESH ) { 729 rc = 1; 730 goto error; 731 } 732 733 rc = lutil_atoul( &l, &lud->lud_exts[ i ][ STRLENOF( "x-refresh=" ) ] ); 734 if ( rc ) { 735 goto error; 736 } 737 refresh_time = (time_t)l; 738 got |= GOT_REFRESH; 739 740 } else { 741 rc = -1; 742 goto error; 743 } 744 } 745 746 if ( got != GOT_ALL ) { 747 rc = 1; 748 goto error; 749 } 750 751 if ( !(got & GOT_REFRESH )) 752 refresh_time = 0; 753 754 /* ignore expired queries */ 755 if ( expiry_time <= slap_get_time()) { 756 Operation op2 = *op; 757 758 memset( &op2.oq_search, 0, sizeof( op2.oq_search ) ); 759 760 (void)remove_query_data( &op2, &uuid ); 761 762 rc = 0; 763 764 } else { 765 ber_str2bv( lud->lud_dn, 0, 0, &base ); 766 rc = dnNormalize( 0, NULL, NULL, &base, &query.base, NULL ); 767 if ( rc != LDAP_SUCCESS ) { 768 goto error; 769 } 770 query.scope = lud->lud_scope; 771 query.filter = str2filter( lud->lud_filter ); 772 if ( query.filter == NULL ) { 773 rc = -1; 774 goto error; 775 } 776 777 tempstr.bv_val = ch_malloc( strlen( lud->lud_filter ) + 1 ); 778 tempstr.bv_len = 0; 779 if ( filter2template( op, query.filter, &tempstr ) ) { 780 ch_free( tempstr.bv_val ); 781 rc = -1; 782 goto error; 783 } 784 785 /* check for query containment */ 786 qt = qm->attr_sets[attrset].templates; 787 for ( ; qt; qt = qt->qtnext ) { 788 /* find if template i can potentially answer tempstr */ 789 if ( bvmatch( &qt->querystr, &tempstr ) ) { 790 break; 791 } 792 } 793 794 if ( qt == NULL ) { 795 rc = 1; 796 goto error; 797 } 798 799 cq = add_query( op, qm, &query, qt, PC_POSITIVE, 0 ); 800 if ( cq != NULL ) { 801 cq->expiry_time = expiry_time; 802 cq->refresh_time = refresh_time; 803 cq->q_uuid = uuid; 804 cq->answerable_cnt = answerable_cnt; 805 cq->refcnt = 0; 806 807 /* it's now into cq->filter */ 808 BER_BVZERO( &uuid ); 809 query.filter = NULL; 810 811 } else { 812 rc = 1; 813 } 814 } 815 816error:; 817 if ( query.filter != NULL ) filter_free( query.filter ); 818 if ( !BER_BVISNULL( &tempstr ) ) ch_free( tempstr.bv_val ); 819 if ( !BER_BVISNULL( &query.base ) ) ch_free( query.base.bv_val ); 820 if ( !BER_BVISNULL( &uuid ) ) ch_free( uuid.bv_val ); 821 if ( lud != NULL ) ldap_free_urldesc( lud ); 822 823 return rc; 824} 825 826/* Return 1 for an added entry, else 0 */ 827static int 828merge_entry( 829 Operation *op, 830 Entry *e, 831 int dup, 832 struct berval* query_uuid ) 833{ 834 int rc; 835 Modifications* modlist = NULL; 836 const char* text = NULL; 837 Attribute *attr; 838 char textbuf[SLAP_TEXT_BUFLEN]; 839 size_t textlen = sizeof(textbuf); 840 841 SlapReply sreply = {REP_RESULT}; 842 843 slap_callback cb = { NULL, slap_null_cb, NULL, NULL }; 844 845 if ( dup ) 846 e = entry_dup( e ); 847 attr = e->e_attrs; 848 e->e_attrs = NULL; 849 850 /* add queryId attribute */ 851 attr_merge_one( e, ad_queryId, query_uuid, NULL ); 852 853 /* append the attribute list from the fetched entry */ 854 e->e_attrs->a_next = attr; 855 856 op->o_tag = LDAP_REQ_ADD; 857 op->o_protocol = LDAP_VERSION3; 858 op->o_callback = &cb; 859 op->o_time = slap_get_time(); 860 op->o_do_not_cache = 1; 861 862 op->ora_e = e; 863 op->o_req_dn = e->e_name; 864 op->o_req_ndn = e->e_nname; 865 rc = op->o_bd->be_add( op, &sreply ); 866 867 if ( rc != LDAP_SUCCESS ) { 868 if ( rc == LDAP_ALREADY_EXISTS ) { 869 rs_reinit( &sreply, REP_RESULT ); 870 slap_entry2mods( e, &modlist, &text, textbuf, textlen ); 871 modlist->sml_op = LDAP_MOD_ADD; 872 op->o_tag = LDAP_REQ_MODIFY; 873 op->orm_modlist = modlist; 874 op->o_managedsait = SLAP_CONTROL_CRITICAL; 875 op->o_bd->be_modify( op, &sreply ); 876 slap_mods_free( modlist, 1 ); 877 } else if ( rc == LDAP_REFERRAL || 878 rc == LDAP_NO_SUCH_OBJECT ) { 879 syncrepl_add_glue( op, e ); 880 e = NULL; 881 rc = 1; 882 } 883 if ( e ) { 884 entry_free( e ); 885 rc = 0; 886 } 887 } else { 888 if ( op->ora_e == e ) 889 entry_free( e ); 890 rc = 1; 891 } 892 893 return rc; 894} 895 896/* Length-ordered sort on normalized DNs */ 897static int pcache_dn_cmp( const void *v1, const void *v2 ) 898{ 899 const Qbase *q1 = v1, *q2 = v2; 900 901 int rc = q1->base.bv_len - q2->base.bv_len; 902 if ( rc == 0 ) 903 rc = strncmp( q1->base.bv_val, q2->base.bv_val, q1->base.bv_len ); 904 return rc; 905} 906 907static int lex_bvcmp( struct berval *bv1, struct berval *bv2 ) 908{ 909 int len, dif; 910 dif = bv1->bv_len - bv2->bv_len; 911 len = bv1->bv_len; 912 if ( dif > 0 ) len -= dif; 913 len = memcmp( bv1->bv_val, bv2->bv_val, len ); 914 if ( !len ) 915 len = dif; 916 return len; 917} 918 919/* compare the current value in each filter */ 920static int pcache_filter_cmp( Filter *f1, Filter *f2 ) 921{ 922 int rc, weight1, weight2; 923 924 switch( f1->f_choice ) { 925 case LDAP_FILTER_AND: 926 case LDAP_FILTER_OR: 927 weight1 = 0; 928 break; 929 case LDAP_FILTER_PRESENT: 930 weight1 = 1; 931 break; 932 case LDAP_FILTER_EQUALITY: 933 case LDAP_FILTER_GE: 934 case LDAP_FILTER_LE: 935 weight1 = 2; 936 break; 937 default: 938 weight1 = 3; 939 } 940 switch( f2->f_choice ) { 941 case LDAP_FILTER_AND: 942 case LDAP_FILTER_OR: 943 weight2 = 0; 944 break; 945 case LDAP_FILTER_PRESENT: 946 weight2 = 1; 947 break; 948 case LDAP_FILTER_EQUALITY: 949 case LDAP_FILTER_GE: 950 case LDAP_FILTER_LE: 951 weight2 = 2; 952 break; 953 default: 954 weight2 = 3; 955 } 956 rc = weight1 - weight2; 957 if ( !rc ) { 958 switch( weight1 ) { 959 case 0: 960 rc = pcache_filter_cmp( f1->f_and, f2->f_and ); 961 break; 962 case 1: 963 break; 964 case 2: 965 rc = lex_bvcmp( &f1->f_av_value, &f2->f_av_value ); 966 break; 967 case 3: 968 if ( f1->f_choice == LDAP_FILTER_SUBSTRINGS ) { 969 rc = 0; 970 if ( !BER_BVISNULL( &f1->f_sub_initial )) { 971 if ( !BER_BVISNULL( &f2->f_sub_initial )) { 972 rc = lex_bvcmp( &f1->f_sub_initial, 973 &f2->f_sub_initial ); 974 } else { 975 rc = 1; 976 } 977 } else if ( !BER_BVISNULL( &f2->f_sub_initial )) { 978 rc = -1; 979 } 980 if ( rc ) break; 981 if ( f1->f_sub_any ) { 982 if ( f2->f_sub_any ) { 983 rc = lex_bvcmp( f1->f_sub_any, 984 f2->f_sub_any ); 985 } else { 986 rc = 1; 987 } 988 } else if ( f2->f_sub_any ) { 989 rc = -1; 990 } 991 if ( rc ) break; 992 if ( !BER_BVISNULL( &f1->f_sub_final )) { 993 if ( !BER_BVISNULL( &f2->f_sub_final )) { 994 rc = lex_bvcmp( &f1->f_sub_final, 995 &f2->f_sub_final ); 996 } else { 997 rc = 1; 998 } 999 } else if ( !BER_BVISNULL( &f2->f_sub_final )) { 1000 rc = -1; 1001 } 1002 } else { 1003 rc = lex_bvcmp( &f1->f_mr_value, 1004 &f2->f_mr_value ); 1005 } 1006 break; 1007 } 1008 while ( !rc ) { 1009 f1 = f1->f_next; 1010 f2 = f2->f_next; 1011 if ( f1 || f2 ) { 1012 if ( !f1 ) 1013 rc = -1; 1014 else if ( !f2 ) 1015 rc = 1; 1016 else { 1017 rc = pcache_filter_cmp( f1, f2 ); 1018 } 1019 } else { 1020 break; 1021 } 1022 } 1023 } 1024 return rc; 1025} 1026 1027/* compare filters in each query */ 1028static int pcache_query_cmp( const void *v1, const void *v2 ) 1029{ 1030 const CachedQuery *q1 = v1, *q2 =v2; 1031 return pcache_filter_cmp( q1->filter, q2->filter ); 1032} 1033 1034/* add query on top of LRU list */ 1035static void 1036add_query_on_top (query_manager* qm, CachedQuery* qc) 1037{ 1038 CachedQuery* top = qm->lru_top; 1039 1040 qm->lru_top = qc; 1041 1042 if (top) 1043 top->lru_up = qc; 1044 else 1045 qm->lru_bottom = qc; 1046 1047 qc->lru_down = top; 1048 qc->lru_up = NULL; 1049 Debug( pcache_debug, "Base of added query = %s\n", 1050 qc->qbase->base.bv_val, 0, 0 ); 1051} 1052 1053/* remove_query from LRU list */ 1054 1055static void 1056remove_query (query_manager* qm, CachedQuery* qc) 1057{ 1058 CachedQuery* up; 1059 CachedQuery* down; 1060 1061 if (!qc) 1062 return; 1063 1064 up = qc->lru_up; 1065 down = qc->lru_down; 1066 1067 if (!up) 1068 qm->lru_top = down; 1069 1070 if (!down) 1071 qm->lru_bottom = up; 1072 1073 if (down) 1074 down->lru_up = up; 1075 1076 if (up) 1077 up->lru_down = down; 1078 1079 qc->lru_up = qc->lru_down = NULL; 1080} 1081 1082/* find and remove string2 from string1 1083 * from start if position = 1, 1084 * from end if position = 3, 1085 * from anywhere if position = 2 1086 * string1 is overwritten if position = 2. 1087 */ 1088 1089static int 1090find_and_remove(struct berval* ber1, struct berval* ber2, int position) 1091{ 1092 int ret=0; 1093 1094 if ( !ber2->bv_val ) 1095 return 1; 1096 if ( !ber1->bv_val ) 1097 return 0; 1098 1099 switch( position ) { 1100 case 1: 1101 if ( ber1->bv_len >= ber2->bv_len && !memcmp( ber1->bv_val, 1102 ber2->bv_val, ber2->bv_len )) { 1103 ret = 1; 1104 ber1->bv_val += ber2->bv_len; 1105 ber1->bv_len -= ber2->bv_len; 1106 } 1107 break; 1108 case 2: { 1109 char *temp; 1110 ber1->bv_val[ber1->bv_len] = '\0'; 1111 temp = strstr( ber1->bv_val, ber2->bv_val ); 1112 if ( temp ) { 1113 strcpy( temp, temp+ber2->bv_len ); 1114 ber1->bv_len -= ber2->bv_len; 1115 ret = 1; 1116 } 1117 break; 1118 } 1119 case 3: 1120 if ( ber1->bv_len >= ber2->bv_len && 1121 !memcmp( ber1->bv_val+ber1->bv_len-ber2->bv_len, ber2->bv_val, 1122 ber2->bv_len )) { 1123 ret = 1; 1124 ber1->bv_len -= ber2->bv_len; 1125 } 1126 break; 1127 } 1128 return ret; 1129} 1130 1131 1132static struct berval* 1133merge_init_final(Operation *op, struct berval* init, struct berval* any, 1134 struct berval* final) 1135{ 1136 struct berval* merged, *temp; 1137 int i, any_count, count; 1138 1139 for (any_count=0; any && any[any_count].bv_val; any_count++) 1140 ; 1141 1142 count = any_count; 1143 1144 if (init->bv_val) 1145 count++; 1146 if (final->bv_val) 1147 count++; 1148 1149 merged = (struct berval*)op->o_tmpalloc( (count+1)*sizeof(struct berval), 1150 op->o_tmpmemctx ); 1151 temp = merged; 1152 1153 if (init->bv_val) { 1154 ber_dupbv_x( temp, init, op->o_tmpmemctx ); 1155 temp++; 1156 } 1157 1158 for (i=0; i<any_count; i++) { 1159 ber_dupbv_x( temp, any, op->o_tmpmemctx ); 1160 temp++; any++; 1161 } 1162 1163 if (final->bv_val){ 1164 ber_dupbv_x( temp, final, op->o_tmpmemctx ); 1165 temp++; 1166 } 1167 BER_BVZERO( temp ); 1168 return merged; 1169} 1170 1171/* Each element in stored must be found in incoming. Incoming is overwritten. 1172 */ 1173static int 1174strings_containment(struct berval* stored, struct berval* incoming) 1175{ 1176 struct berval* element; 1177 int k=0; 1178 int j, rc = 0; 1179 1180 for ( element=stored; element->bv_val != NULL; element++ ) { 1181 for (j = k; incoming[j].bv_val != NULL; j++) { 1182 if (find_and_remove(&(incoming[j]), element, 2)) { 1183 k = j; 1184 rc = 1; 1185 break; 1186 } 1187 rc = 0; 1188 } 1189 if ( rc ) { 1190 continue; 1191 } else { 1192 return 0; 1193 } 1194 } 1195 return 1; 1196} 1197 1198static int 1199substr_containment_substr(Operation *op, Filter* stored, Filter* incoming) 1200{ 1201 int rc = 0; 1202 1203 struct berval init_incoming; 1204 struct berval final_incoming; 1205 struct berval *remaining_incoming = NULL; 1206 1207 if ((!(incoming->f_sub_initial.bv_val) && (stored->f_sub_initial.bv_val)) 1208 || (!(incoming->f_sub_final.bv_val) && (stored->f_sub_final.bv_val))) 1209 return 0; 1210 1211 init_incoming = incoming->f_sub_initial; 1212 final_incoming = incoming->f_sub_final; 1213 1214 if (find_and_remove(&init_incoming, 1215 &(stored->f_sub_initial), 1) && find_and_remove(&final_incoming, 1216 &(stored->f_sub_final), 3)) 1217 { 1218 if (stored->f_sub_any == NULL) { 1219 rc = 1; 1220 goto final; 1221 } 1222 remaining_incoming = merge_init_final(op, &init_incoming, 1223 incoming->f_sub_any, &final_incoming); 1224 rc = strings_containment(stored->f_sub_any, remaining_incoming); 1225 ber_bvarray_free_x( remaining_incoming, op->o_tmpmemctx ); 1226 } 1227final: 1228 return rc; 1229} 1230 1231static int 1232substr_containment_equality(Operation *op, Filter* stored, Filter* incoming) 1233{ 1234 struct berval incoming_val[2]; 1235 int rc = 0; 1236 1237 incoming_val[1] = incoming->f_av_value; 1238 1239 if (find_and_remove(incoming_val+1, 1240 &(stored->f_sub_initial), 1) && find_and_remove(incoming_val+1, 1241 &(stored->f_sub_final), 3)) { 1242 if (stored->f_sub_any == NULL){ 1243 rc = 1; 1244 goto final; 1245 } 1246 ber_dupbv_x( incoming_val, incoming_val+1, op->o_tmpmemctx ); 1247 BER_BVZERO( incoming_val+1 ); 1248 rc = strings_containment(stored->f_sub_any, incoming_val); 1249 op->o_tmpfree( incoming_val[0].bv_val, op->o_tmpmemctx ); 1250 } 1251final: 1252 return rc; 1253} 1254 1255static Filter * 1256filter_first( Filter *f ) 1257{ 1258 while ( f->f_choice == LDAP_FILTER_OR || f->f_choice == LDAP_FILTER_AND ) 1259 f = f->f_and; 1260 return f; 1261} 1262 1263typedef struct fstack { 1264 struct fstack *fs_next; 1265 Filter *fs_fs; 1266 Filter *fs_fi; 1267} fstack; 1268 1269static CachedQuery * 1270find_filter( Operation *op, Avlnode *root, Filter *inputf, Filter *first ) 1271{ 1272 Filter* fs; 1273 Filter* fi; 1274 MatchingRule* mrule = NULL; 1275 int res=0, eqpass= 0; 1276 int ret, rc, dir; 1277 Avlnode *ptr; 1278 CachedQuery cq, *qc; 1279 fstack *stack = NULL, *fsp; 1280 1281 cq.filter = inputf; 1282 cq.first = first; 1283 1284 /* substring matches sort to the end, and we just have to 1285 * walk the entire list. 1286 */ 1287 if ( first->f_choice == LDAP_FILTER_SUBSTRINGS ) { 1288 ptr = tavl_end( root, 1 ); 1289 dir = TAVL_DIR_LEFT; 1290 } else { 1291 ptr = tavl_find3( root, &cq, pcache_query_cmp, &ret ); 1292 dir = (first->f_choice == LDAP_FILTER_GE) ? TAVL_DIR_LEFT : 1293 TAVL_DIR_RIGHT; 1294 } 1295 1296 while (ptr) { 1297 qc = ptr->avl_data; 1298 fi = inputf; 1299 fs = qc->filter; 1300 1301 /* an incoming substr query can only be satisfied by a cached 1302 * substr query. 1303 */ 1304 if ( first->f_choice == LDAP_FILTER_SUBSTRINGS && 1305 qc->first->f_choice != LDAP_FILTER_SUBSTRINGS ) 1306 break; 1307 1308 /* an incoming eq query can be satisfied by a cached eq or substr 1309 * query 1310 */ 1311 if ( first->f_choice == LDAP_FILTER_EQUALITY ) { 1312 if ( eqpass == 0 ) { 1313 if ( qc->first->f_choice != LDAP_FILTER_EQUALITY ) { 1314nextpass: eqpass = 1; 1315 ptr = tavl_end( root, 1 ); 1316 dir = TAVL_DIR_LEFT; 1317 continue; 1318 } 1319 } else { 1320 if ( qc->first->f_choice != LDAP_FILTER_SUBSTRINGS ) 1321 break; 1322 } 1323 } 1324 do { 1325 res=0; 1326 switch (fs->f_choice) { 1327 case LDAP_FILTER_EQUALITY: 1328 if (fi->f_choice == LDAP_FILTER_EQUALITY) 1329 mrule = fs->f_ava->aa_desc->ad_type->sat_equality; 1330 else 1331 ret = 1; 1332 break; 1333 case LDAP_FILTER_GE: 1334 case LDAP_FILTER_LE: 1335 mrule = fs->f_ava->aa_desc->ad_type->sat_ordering; 1336 break; 1337 default: 1338 mrule = NULL; 1339 } 1340 if (mrule) { 1341 const char *text; 1342 rc = value_match(&ret, fs->f_ava->aa_desc, mrule, 1343 SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, 1344 &(fi->f_ava->aa_value), 1345 &(fs->f_ava->aa_value), &text); 1346 if (rc != LDAP_SUCCESS) { 1347 return NULL; 1348 } 1349 if ( fi==first && fi->f_choice==LDAP_FILTER_EQUALITY && ret ) 1350 goto nextpass; 1351 } 1352 switch (fs->f_choice) { 1353 case LDAP_FILTER_OR: 1354 case LDAP_FILTER_AND: 1355 if ( fs->f_next ) { 1356 /* save our stack position */ 1357 fsp = op->o_tmpalloc(sizeof(fstack), op->o_tmpmemctx); 1358 fsp->fs_next = stack; 1359 fsp->fs_fs = fs->f_next; 1360 fsp->fs_fi = fi->f_next; 1361 stack = fsp; 1362 } 1363 fs = fs->f_and; 1364 fi = fi->f_and; 1365 res=1; 1366 break; 1367 case LDAP_FILTER_SUBSTRINGS: 1368 /* check if the equality query can be 1369 * answered with cached substring query */ 1370 if ((fi->f_choice == LDAP_FILTER_EQUALITY) 1371 && substr_containment_equality( op, 1372 fs, fi)) 1373 res=1; 1374 /* check if the substring query can be 1375 * answered with cached substring query */ 1376 if ((fi->f_choice ==LDAP_FILTER_SUBSTRINGS 1377 ) && substr_containment_substr( op, 1378 fs, fi)) 1379 res= 1; 1380 fs=fs->f_next; 1381 fi=fi->f_next; 1382 break; 1383 case LDAP_FILTER_PRESENT: 1384 res=1; 1385 fs=fs->f_next; 1386 fi=fi->f_next; 1387 break; 1388 case LDAP_FILTER_EQUALITY: 1389 if (ret == 0) 1390 res = 1; 1391 fs=fs->f_next; 1392 fi=fi->f_next; 1393 break; 1394 case LDAP_FILTER_GE: 1395 if (mrule && ret >= 0) 1396 res = 1; 1397 fs=fs->f_next; 1398 fi=fi->f_next; 1399 break; 1400 case LDAP_FILTER_LE: 1401 if (mrule && ret <= 0) 1402 res = 1; 1403 fs=fs->f_next; 1404 fi=fi->f_next; 1405 break; 1406 case LDAP_FILTER_NOT: 1407 res=0; 1408 break; 1409 default: 1410 break; 1411 } 1412 if (!fs && !fi && stack) { 1413 /* pop the stack */ 1414 fsp = stack; 1415 stack = fsp->fs_next; 1416 fs = fsp->fs_fs; 1417 fi = fsp->fs_fi; 1418 op->o_tmpfree(fsp, op->o_tmpmemctx); 1419 } 1420 } while((res) && (fi != NULL) && (fs != NULL)); 1421 1422 if ( res ) 1423 return qc; 1424 ptr = tavl_next( ptr, dir ); 1425 } 1426 return NULL; 1427} 1428 1429/* check whether query is contained in any of 1430 * the cached queries in template 1431 */ 1432static CachedQuery * 1433query_containment(Operation *op, query_manager *qm, 1434 Query *query, 1435 QueryTemplate *templa) 1436{ 1437 CachedQuery* qc; 1438 int depth = 0, tscope; 1439 Qbase qbase, *qbptr = NULL; 1440 struct berval pdn; 1441 1442 if (query->filter != NULL) { 1443 Filter *first; 1444 1445 Debug( pcache_debug, "Lock QC index = %p\n", 1446 (void *) templa, 0, 0 ); 1447 qbase.base = query->base; 1448 1449 first = filter_first( query->filter ); 1450 1451 ldap_pvt_thread_rdwr_rlock(&templa->t_rwlock); 1452 for( ;; ) { 1453 /* Find the base */ 1454 qbptr = avl_find( templa->qbase, &qbase, pcache_dn_cmp ); 1455 if ( qbptr ) { 1456 tscope = query->scope; 1457 /* Find a matching scope: 1458 * match at depth 0 OK 1459 * scope is BASE, 1460 * one at depth 1 OK 1461 * subord at depth > 0 OK 1462 * subtree at any depth OK 1463 * scope is ONE, 1464 * subtree or subord at any depth OK 1465 * scope is SUBORD, 1466 * subtree or subord at any depth OK 1467 * scope is SUBTREE, 1468 * subord at depth > 0 OK 1469 * subtree at any depth OK 1470 */ 1471 for ( tscope = 0 ; tscope <= LDAP_SCOPE_CHILDREN; tscope++ ) { 1472 switch ( query->scope ) { 1473 case LDAP_SCOPE_BASE: 1474 if ( tscope == LDAP_SCOPE_BASE && depth ) continue; 1475 if ( tscope == LDAP_SCOPE_ONE && depth != 1) continue; 1476 if ( tscope == LDAP_SCOPE_CHILDREN && !depth ) continue; 1477 break; 1478 case LDAP_SCOPE_ONE: 1479 if ( tscope == LDAP_SCOPE_BASE ) 1480 tscope = LDAP_SCOPE_ONE; 1481 if ( tscope == LDAP_SCOPE_ONE && depth ) continue; 1482 if ( !depth ) break; 1483 if ( tscope < LDAP_SCOPE_SUBTREE ) 1484 tscope = LDAP_SCOPE_SUBTREE; 1485 break; 1486 case LDAP_SCOPE_SUBTREE: 1487 if ( tscope < LDAP_SCOPE_SUBTREE ) 1488 tscope = LDAP_SCOPE_SUBTREE; 1489 if ( tscope == LDAP_SCOPE_CHILDREN && !depth ) continue; 1490 break; 1491 case LDAP_SCOPE_CHILDREN: 1492 if ( tscope < LDAP_SCOPE_SUBTREE ) 1493 tscope = LDAP_SCOPE_SUBTREE; 1494 break; 1495 } 1496 if ( !qbptr->scopes[tscope] ) continue; 1497 1498 /* Find filter */ 1499 qc = find_filter( op, qbptr->scopes[tscope], 1500 query->filter, first ); 1501 if ( qc ) { 1502 if ( qc->q_sizelimit ) { 1503 ldap_pvt_thread_rdwr_runlock(&templa->t_rwlock); 1504 return NULL; 1505 } 1506 ldap_pvt_thread_mutex_lock(&qm->lru_mutex); 1507 if (qm->lru_top != qc) { 1508 remove_query(qm, qc); 1509 add_query_on_top(qm, qc); 1510 } 1511 ldap_pvt_thread_mutex_unlock(&qm->lru_mutex); 1512 return qc; 1513 } 1514 } 1515 } 1516 if ( be_issuffix( op->o_bd, &qbase.base )) 1517 break; 1518 /* Up a level */ 1519 dnParent( &qbase.base, &pdn ); 1520 qbase.base = pdn; 1521 depth++; 1522 } 1523 1524 Debug( pcache_debug, 1525 "Not answerable: Unlock QC index=%p\n", 1526 (void *) templa, 0, 0 ); 1527 ldap_pvt_thread_rdwr_runlock(&templa->t_rwlock); 1528 } 1529 return NULL; 1530} 1531 1532static void 1533free_query (CachedQuery* qc) 1534{ 1535 free(qc->q_uuid.bv_val); 1536 filter_free(qc->filter); 1537 ldap_pvt_thread_mutex_destroy(&qc->answerable_cnt_mutex); 1538 ldap_pvt_thread_rdwr_destroy( &qc->rwlock ); 1539 memset(qc, 0, sizeof(*qc)); 1540 free(qc); 1541} 1542 1543 1544/* Add query to query cache, the returned Query is locked for writing */ 1545static CachedQuery * 1546add_query( 1547 Operation *op, 1548 query_manager* qm, 1549 Query* query, 1550 QueryTemplate *templ, 1551 pc_caching_reason_t why, 1552 int wlock) 1553{ 1554 CachedQuery* new_cached_query = (CachedQuery*) ch_malloc(sizeof(CachedQuery)); 1555 Qbase *qbase, qb; 1556 Filter *first; 1557 int rc; 1558 time_t ttl = 0, ttr = 0; 1559 time_t now; 1560 1561 new_cached_query->qtemp = templ; 1562 BER_BVZERO( &new_cached_query->q_uuid ); 1563 new_cached_query->q_sizelimit = 0; 1564 1565 now = slap_get_time(); 1566 switch ( why ) { 1567 case PC_POSITIVE: 1568 ttl = templ->ttl; 1569 if ( templ->ttr ) 1570 ttr = now + templ->ttr; 1571 break; 1572 1573 case PC_NEGATIVE: 1574 ttl = templ->negttl; 1575 break; 1576 1577 case PC_SIZELIMIT: 1578 ttl = templ->limitttl; 1579 break; 1580 1581 default: 1582 assert( 0 ); 1583 break; 1584 } 1585 new_cached_query->expiry_time = now + ttl; 1586 new_cached_query->refresh_time = ttr; 1587 new_cached_query->bindref_time = 0; 1588 1589 new_cached_query->bind_refcnt = 0; 1590 new_cached_query->answerable_cnt = 0; 1591 new_cached_query->refcnt = 1; 1592 ldap_pvt_thread_mutex_init(&new_cached_query->answerable_cnt_mutex); 1593 1594 new_cached_query->lru_up = NULL; 1595 new_cached_query->lru_down = NULL; 1596 Debug( pcache_debug, "Added query expires at %ld (%s)\n", 1597 (long) new_cached_query->expiry_time, 1598 pc_caching_reason_str[ why ], 0 ); 1599 1600 new_cached_query->scope = query->scope; 1601 new_cached_query->filter = query->filter; 1602 new_cached_query->first = first = filter_first( query->filter ); 1603 1604 ldap_pvt_thread_rdwr_init(&new_cached_query->rwlock); 1605 if (wlock) 1606 ldap_pvt_thread_rdwr_wlock(&new_cached_query->rwlock); 1607 1608 qb.base = query->base; 1609 1610 /* Adding a query */ 1611 Debug( pcache_debug, "Lock AQ index = %p\n", 1612 (void *) templ, 0, 0 ); 1613 ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock); 1614 qbase = avl_find( templ->qbase, &qb, pcache_dn_cmp ); 1615 if ( !qbase ) { 1616 qbase = ch_calloc( 1, sizeof(Qbase) + qb.base.bv_len + 1 ); 1617 qbase->base.bv_len = qb.base.bv_len; 1618 qbase->base.bv_val = (char *)(qbase+1); 1619 memcpy( qbase->base.bv_val, qb.base.bv_val, qb.base.bv_len ); 1620 qbase->base.bv_val[qbase->base.bv_len] = '\0'; 1621 avl_insert( &templ->qbase, qbase, pcache_dn_cmp, avl_dup_error ); 1622 } 1623 new_cached_query->next = templ->query; 1624 new_cached_query->prev = NULL; 1625 new_cached_query->qbase = qbase; 1626 rc = tavl_insert( &qbase->scopes[query->scope], new_cached_query, 1627 pcache_query_cmp, avl_dup_error ); 1628 if ( rc == 0 ) { 1629 qbase->queries++; 1630 if (templ->query == NULL) 1631 templ->query_last = new_cached_query; 1632 else 1633 templ->query->prev = new_cached_query; 1634 templ->query = new_cached_query; 1635 templ->no_of_queries++; 1636 } else { 1637 ldap_pvt_thread_mutex_destroy(&new_cached_query->answerable_cnt_mutex); 1638 if (wlock) 1639 ldap_pvt_thread_rdwr_wunlock(&new_cached_query->rwlock); 1640 ldap_pvt_thread_rdwr_destroy( &new_cached_query->rwlock ); 1641 ch_free( new_cached_query ); 1642 new_cached_query = find_filter( op, qbase->scopes[query->scope], 1643 query->filter, first ); 1644 filter_free( query->filter ); 1645 query->filter = NULL; 1646 } 1647 Debug( pcache_debug, "TEMPLATE %p QUERIES++ %d\n", 1648 (void *) templ, templ->no_of_queries, 0 ); 1649 1650 /* Adding on top of LRU list */ 1651 if ( rc == 0 ) { 1652 ldap_pvt_thread_mutex_lock(&qm->lru_mutex); 1653 add_query_on_top(qm, new_cached_query); 1654 ldap_pvt_thread_mutex_unlock(&qm->lru_mutex); 1655 } 1656 Debug( pcache_debug, "Unlock AQ index = %p \n", 1657 (void *) templ, 0, 0 ); 1658 ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock); 1659 1660 return rc == 0 ? new_cached_query : NULL; 1661} 1662 1663static void 1664remove_from_template (CachedQuery* qc, QueryTemplate* template) 1665{ 1666 if (!qc->prev && !qc->next) { 1667 template->query_last = template->query = NULL; 1668 } else if (qc->prev == NULL) { 1669 qc->next->prev = NULL; 1670 template->query = qc->next; 1671 } else if (qc->next == NULL) { 1672 qc->prev->next = NULL; 1673 template->query_last = qc->prev; 1674 } else { 1675 qc->next->prev = qc->prev; 1676 qc->prev->next = qc->next; 1677 } 1678 tavl_delete( &qc->qbase->scopes[qc->scope], qc, pcache_query_cmp ); 1679 qc->qbase->queries--; 1680 if ( qc->qbase->queries == 0 ) { 1681 avl_delete( &template->qbase, qc->qbase, pcache_dn_cmp ); 1682 ch_free( qc->qbase ); 1683 qc->qbase = NULL; 1684 } 1685 1686 template->no_of_queries--; 1687} 1688 1689/* remove bottom query of LRU list from the query cache */ 1690/* 1691 * NOTE: slight change in functionality. 1692 * 1693 * - if result->bv_val is NULL, the query at the bottom of the LRU 1694 * is removed 1695 * - otherwise, the query whose UUID is *result is removed 1696 * - if not found, result->bv_val is zeroed 1697 */ 1698static void 1699cache_replacement(query_manager* qm, struct berval *result) 1700{ 1701 CachedQuery* bottom; 1702 QueryTemplate *temp; 1703 1704 ldap_pvt_thread_mutex_lock(&qm->lru_mutex); 1705 if ( BER_BVISNULL( result ) ) { 1706 bottom = qm->lru_bottom; 1707 1708 if (!bottom) { 1709 Debug ( pcache_debug, 1710 "Cache replacement invoked without " 1711 "any query in LRU list\n", 0, 0, 0 ); 1712 ldap_pvt_thread_mutex_unlock(&qm->lru_mutex); 1713 return; 1714 } 1715 1716 } else { 1717 for ( bottom = qm->lru_bottom; 1718 bottom != NULL; 1719 bottom = bottom->lru_up ) 1720 { 1721 if ( bvmatch( result, &bottom->q_uuid ) ) { 1722 break; 1723 } 1724 } 1725 1726 if ( !bottom ) { 1727 Debug ( pcache_debug, 1728 "Could not find query with uuid=\"%s\"" 1729 "in LRU list\n", result->bv_val, 0, 0 ); 1730 ldap_pvt_thread_mutex_unlock(&qm->lru_mutex); 1731 BER_BVZERO( result ); 1732 return; 1733 } 1734 } 1735 1736 temp = bottom->qtemp; 1737 remove_query(qm, bottom); 1738 ldap_pvt_thread_mutex_unlock(&qm->lru_mutex); 1739 1740 *result = bottom->q_uuid; 1741 BER_BVZERO( &bottom->q_uuid ); 1742 1743 Debug( pcache_debug, "Lock CR index = %p\n", (void *) temp, 0, 0 ); 1744 ldap_pvt_thread_rdwr_wlock(&temp->t_rwlock); 1745 remove_from_template(bottom, temp); 1746 Debug( pcache_debug, "TEMPLATE %p QUERIES-- %d\n", 1747 (void *) temp, temp->no_of_queries, 0 ); 1748 Debug( pcache_debug, "Unlock CR index = %p\n", (void *) temp, 0, 0 ); 1749 ldap_pvt_thread_rdwr_wunlock(&temp->t_rwlock); 1750 free_query(bottom); 1751} 1752 1753struct query_info { 1754 struct query_info *next; 1755 struct berval xdn; 1756 int del; 1757}; 1758 1759static int 1760remove_func ( 1761 Operation *op, 1762 SlapReply *rs 1763) 1764{ 1765 Attribute *attr; 1766 struct query_info *qi; 1767 int count = 0; 1768 1769 if ( rs->sr_type != REP_SEARCH ) return 0; 1770 1771 attr = attr_find( rs->sr_entry->e_attrs, ad_queryId ); 1772 if ( attr == NULL ) return 0; 1773 1774 count = attr->a_numvals; 1775 assert( count > 0 ); 1776 qi = op->o_tmpalloc( sizeof( struct query_info ), op->o_tmpmemctx ); 1777 qi->next = op->o_callback->sc_private; 1778 op->o_callback->sc_private = qi; 1779 ber_dupbv_x( &qi->xdn, &rs->sr_entry->e_nname, op->o_tmpmemctx ); 1780 qi->del = ( count == 1 ); 1781 1782 return 0; 1783} 1784 1785static int 1786remove_query_data( 1787 Operation *op, 1788 struct berval *query_uuid ) 1789{ 1790 struct query_info *qi, *qnext; 1791 char filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ]; 1792 AttributeAssertion ava = ATTRIBUTEASSERTION_INIT; 1793 Filter filter = {LDAP_FILTER_EQUALITY}; 1794 SlapReply sreply = {REP_RESULT}; 1795 slap_callback cb = { NULL, remove_func, NULL, NULL }; 1796 int deleted = 0; 1797 1798 op->ors_filterstr.bv_len = snprintf(filter_str, sizeof(filter_str), 1799 "(%s=%s)", ad_queryId->ad_cname.bv_val, query_uuid->bv_val); 1800 filter.f_ava = &ava; 1801 filter.f_av_desc = ad_queryId; 1802 filter.f_av_value = *query_uuid; 1803 1804 op->o_tag = LDAP_REQ_SEARCH; 1805 op->o_protocol = LDAP_VERSION3; 1806 op->o_callback = &cb; 1807 op->o_time = slap_get_time(); 1808 op->o_do_not_cache = 1; 1809 1810 op->o_req_dn = op->o_bd->be_suffix[0]; 1811 op->o_req_ndn = op->o_bd->be_nsuffix[0]; 1812 op->ors_scope = LDAP_SCOPE_SUBTREE; 1813 op->ors_deref = LDAP_DEREF_NEVER; 1814 op->ors_slimit = SLAP_NO_LIMIT; 1815 op->ors_tlimit = SLAP_NO_LIMIT; 1816 op->ors_limit = NULL; 1817 op->ors_filter = &filter; 1818 op->ors_filterstr.bv_val = filter_str; 1819 op->ors_filterstr.bv_len = strlen(filter_str); 1820 op->ors_attrs = NULL; 1821 op->ors_attrsonly = 0; 1822 1823 op->o_bd->be_search( op, &sreply ); 1824 1825 for ( qi=cb.sc_private; qi; qi=qnext ) { 1826 qnext = qi->next; 1827 1828 op->o_req_dn = qi->xdn; 1829 op->o_req_ndn = qi->xdn; 1830 rs_reinit( &sreply, REP_RESULT ); 1831 1832 if ( qi->del ) { 1833 Debug( pcache_debug, "DELETING ENTRY TEMPLATE=%s\n", 1834 query_uuid->bv_val, 0, 0 ); 1835 1836 op->o_tag = LDAP_REQ_DELETE; 1837 1838 if (op->o_bd->be_delete(op, &sreply) == LDAP_SUCCESS) { 1839 deleted++; 1840 } 1841 1842 } else { 1843 Modifications mod; 1844 struct berval vals[2]; 1845 1846 vals[0] = *query_uuid; 1847 vals[1].bv_val = NULL; 1848 vals[1].bv_len = 0; 1849 mod.sml_op = LDAP_MOD_DELETE; 1850 mod.sml_flags = 0; 1851 mod.sml_desc = ad_queryId; 1852 mod.sml_type = ad_queryId->ad_cname; 1853 mod.sml_values = vals; 1854 mod.sml_nvalues = NULL; 1855 mod.sml_numvals = 1; 1856 mod.sml_next = NULL; 1857 Debug( pcache_debug, 1858 "REMOVING TEMP ATTR : TEMPLATE=%s\n", 1859 query_uuid->bv_val, 0, 0 ); 1860 1861 op->orm_modlist = &mod; 1862 1863 op->o_bd->be_modify( op, &sreply ); 1864 } 1865 op->o_tmpfree( qi->xdn.bv_val, op->o_tmpmemctx ); 1866 op->o_tmpfree( qi, op->o_tmpmemctx ); 1867 } 1868 return deleted; 1869} 1870 1871static int 1872get_attr_set( 1873 AttributeName* attrs, 1874 query_manager* qm, 1875 int num 1876); 1877 1878static int 1879filter2template( 1880 Operation *op, 1881 Filter *f, 1882 struct berval *fstr ) 1883{ 1884 AttributeDescription *ad; 1885 int len, ret; 1886 1887 switch ( f->f_choice ) { 1888 case LDAP_FILTER_EQUALITY: 1889 ad = f->f_av_desc; 1890 len = STRLENOF( "(=)" ) + ad->ad_cname.bv_len; 1891 ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s=)", ad->ad_cname.bv_val ); 1892 assert( ret == len ); 1893 fstr->bv_len += len; 1894 break; 1895 1896 case LDAP_FILTER_GE: 1897 ad = f->f_av_desc; 1898 len = STRLENOF( "(>=)" ) + ad->ad_cname.bv_len; 1899 ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s>=)", ad->ad_cname.bv_val); 1900 assert( ret == len ); 1901 fstr->bv_len += len; 1902 break; 1903 1904 case LDAP_FILTER_LE: 1905 ad = f->f_av_desc; 1906 len = STRLENOF( "(<=)" ) + ad->ad_cname.bv_len; 1907 ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s<=)", ad->ad_cname.bv_val); 1908 assert( ret == len ); 1909 fstr->bv_len += len; 1910 break; 1911 1912 case LDAP_FILTER_APPROX: 1913 ad = f->f_av_desc; 1914 len = STRLENOF( "(~=)" ) + ad->ad_cname.bv_len; 1915 ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s~=)", ad->ad_cname.bv_val); 1916 assert( ret == len ); 1917 fstr->bv_len += len; 1918 break; 1919 1920 case LDAP_FILTER_SUBSTRINGS: 1921 ad = f->f_sub_desc; 1922 len = STRLENOF( "(=)" ) + ad->ad_cname.bv_len; 1923 ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s=)", ad->ad_cname.bv_val ); 1924 assert( ret == len ); 1925 fstr->bv_len += len; 1926 break; 1927 1928 case LDAP_FILTER_PRESENT: 1929 ad = f->f_desc; 1930 len = STRLENOF( "(=*)" ) + ad->ad_cname.bv_len; 1931 ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s=*)", ad->ad_cname.bv_val ); 1932 assert( ret == len ); 1933 fstr->bv_len += len; 1934 break; 1935 1936 case LDAP_FILTER_AND: 1937 case LDAP_FILTER_OR: 1938 case LDAP_FILTER_NOT: { 1939 int rc = 0; 1940 fstr->bv_val[fstr->bv_len++] = '('; 1941 switch ( f->f_choice ) { 1942 case LDAP_FILTER_AND: 1943 fstr->bv_val[fstr->bv_len] = '&'; 1944 break; 1945 case LDAP_FILTER_OR: 1946 fstr->bv_val[fstr->bv_len] = '|'; 1947 break; 1948 case LDAP_FILTER_NOT: 1949 fstr->bv_val[fstr->bv_len] = '!'; 1950 break; 1951 } 1952 fstr->bv_len++; 1953 1954 for ( f = f->f_list; f != NULL; f = f->f_next ) { 1955 rc = filter2template( op, f, fstr ); 1956 if ( rc ) break; 1957 } 1958 fstr->bv_val[fstr->bv_len++] = ')'; 1959 fstr->bv_val[fstr->bv_len] = '\0'; 1960 1961 return rc; 1962 } 1963 1964 default: 1965 /* a filter should at least have room for "()", 1966 * an "=" and for a 1-char attr */ 1967 strcpy( fstr->bv_val, "(?=)" ); 1968 fstr->bv_len += STRLENOF("(?=)"); 1969 return -1; 1970 } 1971 1972 return 0; 1973} 1974 1975#define BI_HASHED 0x01 1976#define BI_DIDCB 0x02 1977#define BI_LOOKUP 0x04 1978 1979struct search_info; 1980 1981typedef struct bindinfo { 1982 cache_manager *bi_cm; 1983 CachedQuery *bi_cq; 1984 QueryTemplate *bi_templ; 1985 struct search_info *bi_si; 1986 int bi_flags; 1987 slap_callback bi_cb; 1988} bindinfo; 1989 1990struct search_info { 1991 slap_overinst *on; 1992 Query query; 1993 QueryTemplate *qtemp; 1994 AttributeName* save_attrs; /* original attributes, saved for response */ 1995 int swap_saved_attrs; 1996 int max; 1997 int over; 1998 int count; 1999 int slimit; 2000 int slimit_exceeded; 2001 pc_caching_reason_t caching_reason; 2002 Entry *head, *tail; 2003 bindinfo *pbi; 2004}; 2005 2006static void 2007remove_query_and_data( 2008 Operation *op, 2009 cache_manager *cm, 2010 struct berval *uuid ) 2011{ 2012 query_manager* qm = cm->qm; 2013 2014 qm->crfunc( qm, uuid ); 2015 if ( !BER_BVISNULL( uuid ) ) { 2016 int return_val; 2017 2018 Debug( pcache_debug, 2019 "Removing query UUID %s\n", 2020 uuid->bv_val, 0, 0 ); 2021 return_val = remove_query_data( op, uuid ); 2022 Debug( pcache_debug, 2023 "QUERY REMOVED, SIZE=%d\n", 2024 return_val, 0, 0); 2025 ldap_pvt_thread_mutex_lock( &cm->cache_mutex ); 2026 cm->cur_entries -= return_val; 2027 cm->num_cached_queries--; 2028 Debug( pcache_debug, 2029 "STORED QUERIES = %lu\n", 2030 cm->num_cached_queries, 0, 0 ); 2031 ldap_pvt_thread_mutex_unlock( &cm->cache_mutex ); 2032 Debug( pcache_debug, 2033 "QUERY REMOVED, CACHE =" 2034 "%d entries\n", 2035 cm->cur_entries, 0, 0 ); 2036 } 2037} 2038 2039/* 2040 * Callback used to fetch queryId values based on entryUUID; 2041 * used by pcache_remove_entries_from_cache() 2042 */ 2043static int 2044fetch_queryId_cb( Operation *op, SlapReply *rs ) 2045{ 2046 int rc = 0; 2047 2048 /* only care about searchEntry responses */ 2049 if ( rs->sr_type != REP_SEARCH ) { 2050 return 0; 2051 } 2052 2053 /* allow only one response per entryUUID */ 2054 if ( op->o_callback->sc_private != NULL ) { 2055 rc = 1; 2056 2057 } else { 2058 Attribute *a; 2059 2060 /* copy all queryId values into callback's private data */ 2061 a = attr_find( rs->sr_entry->e_attrs, ad_queryId ); 2062 if ( a != NULL ) { 2063 BerVarray vals = NULL; 2064 2065 ber_bvarray_dup_x( &vals, a->a_nvals, op->o_tmpmemctx ); 2066 op->o_callback->sc_private = (void *)vals; 2067 } 2068 } 2069 2070 /* clear entry if required */ 2071 rs_flush_entry( op, rs, (slap_overinst *) op->o_bd->bd_info ); 2072 2073 return rc; 2074} 2075 2076/* 2077 * Call that allows to remove a set of entries from the cache, 2078 * by forcing the removal of all the related queries. 2079 */ 2080int 2081pcache_remove_entries_from_cache( 2082 Operation *op, 2083 cache_manager *cm, 2084 BerVarray entryUUIDs ) 2085{ 2086 Connection conn = { 0 }; 2087 OperationBuffer opbuf; 2088 Operation op2; 2089 slap_callback sc = { 0 }; 2090 Filter f = { 0 }; 2091 char filtbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(entryUUID=)" ) ]; 2092 AttributeAssertion ava = ATTRIBUTEASSERTION_INIT; 2093 AttributeName attrs[ 2 ] = {{{ 0 }}}; 2094 int s, rc; 2095 2096 if ( op == NULL ) { 2097 void *thrctx = ldap_pvt_thread_pool_context(); 2098 2099 connection_fake_init( &conn, &opbuf, thrctx ); 2100 op = &opbuf.ob_op; 2101 2102 } else { 2103 op2 = *op; 2104 op = &op2; 2105 } 2106 2107 memset( &op->oq_search, 0, sizeof( op->oq_search ) ); 2108 op->ors_scope = LDAP_SCOPE_SUBTREE; 2109 op->ors_deref = LDAP_DEREF_NEVER; 2110 f.f_choice = LDAP_FILTER_EQUALITY; 2111 f.f_ava = &ava; 2112 ava.aa_desc = slap_schema.si_ad_entryUUID; 2113 op->ors_filter = &f; 2114 op->ors_slimit = 1; 2115 op->ors_tlimit = SLAP_NO_LIMIT; 2116 op->ors_limit = NULL; 2117 attrs[ 0 ].an_desc = ad_queryId; 2118 attrs[ 0 ].an_name = ad_queryId->ad_cname; 2119 op->ors_attrs = attrs; 2120 op->ors_attrsonly = 0; 2121 2122 op->o_req_dn = cm->db.be_suffix[ 0 ]; 2123 op->o_req_ndn = cm->db.be_nsuffix[ 0 ]; 2124 2125 op->o_tag = LDAP_REQ_SEARCH; 2126 op->o_protocol = LDAP_VERSION3; 2127 op->o_managedsait = SLAP_CONTROL_CRITICAL; 2128 op->o_bd = &cm->db; 2129 op->o_dn = op->o_bd->be_rootdn; 2130 op->o_ndn = op->o_bd->be_rootndn; 2131 sc.sc_response = fetch_queryId_cb; 2132 op->o_callback = ≻ 2133 2134 for ( s = 0; !BER_BVISNULL( &entryUUIDs[ s ] ); s++ ) { 2135 BerVarray vals = NULL; 2136 SlapReply rs = { REP_RESULT }; 2137 2138 op->ors_filterstr.bv_len = snprintf( filtbuf, sizeof( filtbuf ), 2139 "(entryUUID=%s)", entryUUIDs[ s ].bv_val ); 2140 op->ors_filterstr.bv_val = filtbuf; 2141 ava.aa_value = entryUUIDs[ s ]; 2142 2143 rc = op->o_bd->be_search( op, &rs ); 2144 if ( rc != LDAP_SUCCESS ) { 2145 continue; 2146 } 2147 2148 vals = (BerVarray)op->o_callback->sc_private; 2149 if ( vals != NULL ) { 2150 int i; 2151 2152 for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) { 2153 struct berval val = vals[ i ]; 2154 2155 remove_query_and_data( op, cm, &val ); 2156 2157 if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) { 2158 ch_free( val.bv_val ); 2159 } 2160 } 2161 2162 ber_bvarray_free_x( vals, op->o_tmpmemctx ); 2163 op->o_callback->sc_private = NULL; 2164 } 2165 } 2166 2167 return 0; 2168} 2169 2170/* 2171 * Call that allows to remove a query from the cache. 2172 */ 2173int 2174pcache_remove_query_from_cache( 2175 Operation *op, 2176 cache_manager *cm, 2177 struct berval *queryid ) 2178{ 2179 Operation op2 = *op; 2180 2181 op2.o_bd = &cm->db; 2182 2183 /* remove the selected query */ 2184 remove_query_and_data( &op2, cm, queryid ); 2185 2186 return LDAP_SUCCESS; 2187} 2188 2189/* 2190 * Call that allows to remove a set of queries related to an entry 2191 * from the cache; if queryid is not null, the entry must belong to 2192 * the query indicated by queryid. 2193 */ 2194int 2195pcache_remove_entry_queries_from_cache( 2196 Operation *op, 2197 cache_manager *cm, 2198 struct berval *ndn, 2199 struct berval *queryid ) 2200{ 2201 Connection conn = { 0 }; 2202 OperationBuffer opbuf; 2203 Operation op2; 2204 slap_callback sc = { 0 }; 2205 SlapReply rs = { REP_RESULT }; 2206 Filter f = { 0 }; 2207 char filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ]; 2208 AttributeAssertion ava = ATTRIBUTEASSERTION_INIT; 2209 AttributeName attrs[ 2 ] = {{{ 0 }}}; 2210 int rc; 2211 2212 BerVarray vals = NULL; 2213 2214 if ( op == NULL ) { 2215 void *thrctx = ldap_pvt_thread_pool_context(); 2216 2217 connection_fake_init( &conn, &opbuf, thrctx ); 2218 op = &opbuf.ob_op; 2219 2220 } else { 2221 op2 = *op; 2222 op = &op2; 2223 } 2224 2225 memset( &op->oq_search, 0, sizeof( op->oq_search ) ); 2226 op->ors_scope = LDAP_SCOPE_BASE; 2227 op->ors_deref = LDAP_DEREF_NEVER; 2228 if ( queryid == NULL || BER_BVISNULL( queryid ) ) { 2229 BER_BVSTR( &op->ors_filterstr, "(objectClass=*)" ); 2230 f.f_choice = LDAP_FILTER_PRESENT; 2231 f.f_desc = slap_schema.si_ad_objectClass; 2232 2233 } else { 2234 op->ors_filterstr.bv_len = snprintf( filter_str, 2235 sizeof( filter_str ), "(%s=%s)", 2236 ad_queryId->ad_cname.bv_val, queryid->bv_val ); 2237 f.f_choice = LDAP_FILTER_EQUALITY; 2238 f.f_ava = &ava; 2239 f.f_av_desc = ad_queryId; 2240 f.f_av_value = *queryid; 2241 } 2242 op->ors_filter = &f; 2243 op->ors_slimit = 1; 2244 op->ors_tlimit = SLAP_NO_LIMIT; 2245 op->ors_limit = NULL; 2246 attrs[ 0 ].an_desc = ad_queryId; 2247 attrs[ 0 ].an_name = ad_queryId->ad_cname; 2248 op->ors_attrs = attrs; 2249 op->ors_attrsonly = 0; 2250 2251 op->o_req_dn = *ndn; 2252 op->o_req_ndn = *ndn; 2253 2254 op->o_tag = LDAP_REQ_SEARCH; 2255 op->o_protocol = LDAP_VERSION3; 2256 op->o_managedsait = SLAP_CONTROL_CRITICAL; 2257 op->o_bd = &cm->db; 2258 op->o_dn = op->o_bd->be_rootdn; 2259 op->o_ndn = op->o_bd->be_rootndn; 2260 sc.sc_response = fetch_queryId_cb; 2261 op->o_callback = ≻ 2262 2263 rc = op->o_bd->be_search( op, &rs ); 2264 if ( rc != LDAP_SUCCESS ) { 2265 return rc; 2266 } 2267 2268 vals = (BerVarray)op->o_callback->sc_private; 2269 if ( vals != NULL ) { 2270 int i; 2271 2272 for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) { 2273 struct berval val = vals[ i ]; 2274 2275 remove_query_and_data( op, cm, &val ); 2276 2277 if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) { 2278 ch_free( val.bv_val ); 2279 } 2280 } 2281 2282 ber_bvarray_free_x( vals, op->o_tmpmemctx ); 2283 } 2284 2285 return LDAP_SUCCESS; 2286} 2287 2288static int 2289cache_entries( 2290 Operation *op, 2291 struct berval *query_uuid ) 2292{ 2293 struct search_info *si = op->o_callback->sc_private; 2294 slap_overinst *on = si->on; 2295 cache_manager *cm = on->on_bi.bi_private; 2296 int return_val = 0; 2297 Entry *e; 2298 struct berval crp_uuid; 2299 char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ]; 2300 Operation *op_tmp; 2301 Connection conn = {0}; 2302 OperationBuffer opbuf; 2303 void *thrctx = ldap_pvt_thread_pool_context(); 2304 2305 query_uuid->bv_len = lutil_uuidstr(uuidbuf, sizeof(uuidbuf)); 2306 ber_str2bv(uuidbuf, query_uuid->bv_len, 1, query_uuid); 2307 2308 connection_fake_init2( &conn, &opbuf, thrctx, 0 ); 2309 op_tmp = &opbuf.ob_op; 2310 op_tmp->o_bd = &cm->db; 2311 op_tmp->o_dn = cm->db.be_rootdn; 2312 op_tmp->o_ndn = cm->db.be_rootndn; 2313 2314 Debug( pcache_debug, "UUID for query being added = %s\n", 2315 uuidbuf, 0, 0 ); 2316 2317 for ( e=si->head; e; e=si->head ) { 2318 si->head = e->e_private; 2319 e->e_private = NULL; 2320 while ( cm->cur_entries > (cm->max_entries) ) { 2321 BER_BVZERO( &crp_uuid ); 2322 remove_query_and_data( op_tmp, cm, &crp_uuid ); 2323 } 2324 2325 return_val = merge_entry(op_tmp, e, 0, query_uuid); 2326 ldap_pvt_thread_mutex_lock(&cm->cache_mutex); 2327 cm->cur_entries += return_val; 2328 Debug( pcache_debug, 2329 "ENTRY ADDED/MERGED, CACHED ENTRIES=%d\n", 2330 cm->cur_entries, 0, 0 ); 2331 return_val = 0; 2332 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex); 2333 } 2334 2335 return return_val; 2336} 2337 2338static int 2339pcache_op_cleanup( Operation *op, SlapReply *rs ) { 2340 slap_callback *cb = op->o_callback; 2341 struct search_info *si = cb->sc_private; 2342 slap_overinst *on = si->on; 2343 cache_manager *cm = on->on_bi.bi_private; 2344 query_manager* qm = cm->qm; 2345 2346 if ( rs->sr_type == REP_RESULT || 2347 op->o_abandon || rs->sr_err == SLAPD_ABANDON ) 2348 { 2349 if ( si->swap_saved_attrs ) { 2350 rs->sr_attrs = si->save_attrs; 2351 op->ors_attrs = si->save_attrs; 2352 } 2353 if ( (op->o_abandon || rs->sr_err == SLAPD_ABANDON) && 2354 si->caching_reason == PC_IGNORE ) 2355 { 2356 filter_free( si->query.filter ); 2357 if ( si->count ) { 2358 /* duplicate query, free it */ 2359 Entry *e; 2360 for (;si->head; si->head=e) { 2361 e = si->head->e_private; 2362 si->head->e_private = NULL; 2363 entry_free(si->head); 2364 } 2365 } 2366 2367 } else if ( si->caching_reason != PC_IGNORE ) { 2368 CachedQuery *qc = qm->addfunc(op, qm, &si->query, 2369 si->qtemp, si->caching_reason, 1 ); 2370 2371 if ( qc != NULL ) { 2372 switch ( si->caching_reason ) { 2373 case PC_POSITIVE: 2374 cache_entries( op, &qc->q_uuid ); 2375 if ( si->pbi ) { 2376 qc->bind_refcnt++; 2377 si->pbi->bi_cq = qc; 2378 } 2379 break; 2380 2381 case PC_SIZELIMIT: 2382 qc->q_sizelimit = rs->sr_nentries; 2383 break; 2384 2385 case PC_NEGATIVE: 2386 break; 2387 2388 default: 2389 assert( 0 ); 2390 break; 2391 } 2392 ldap_pvt_thread_rdwr_wunlock(&qc->rwlock); 2393 ldap_pvt_thread_mutex_lock(&cm->cache_mutex); 2394 cm->num_cached_queries++; 2395 Debug( pcache_debug, "STORED QUERIES = %lu\n", 2396 cm->num_cached_queries, 0, 0 ); 2397 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex); 2398 2399 /* If the consistency checker suspended itself, 2400 * wake it back up 2401 */ 2402 if ( cm->cc_paused == PCACHE_CC_PAUSED ) { 2403 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); 2404 if ( cm->cc_paused == PCACHE_CC_PAUSED ) { 2405 cm->cc_paused = 0; 2406 ldap_pvt_runqueue_resched( &slapd_rq, cm->cc_arg, 0 ); 2407 } 2408 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); 2409 } 2410 2411 } else if ( si->count ) { 2412 /* duplicate query, free it */ 2413 Entry *e; 2414 for (;si->head; si->head=e) { 2415 e = si->head->e_private; 2416 si->head->e_private = NULL; 2417 entry_free(si->head); 2418 } 2419 } 2420 2421 } else { 2422 filter_free( si->query.filter ); 2423 } 2424 2425 op->o_callback = op->o_callback->sc_next; 2426 op->o_tmpfree( cb, op->o_tmpmemctx ); 2427 } 2428 2429 return SLAP_CB_CONTINUE; 2430} 2431 2432static int 2433pcache_response( 2434 Operation *op, 2435 SlapReply *rs ) 2436{ 2437 struct search_info *si = op->o_callback->sc_private; 2438 2439 if ( si->swap_saved_attrs ) { 2440 rs->sr_attrs = si->save_attrs; 2441 op->ors_attrs = si->save_attrs; 2442 } 2443 2444 if ( rs->sr_type == REP_SEARCH ) { 2445 Entry *e; 2446 2447 /* don't return more entries than requested by the client */ 2448 if ( si->slimit > 0 && rs->sr_nentries >= si->slimit ) { 2449 si->slimit_exceeded = 1; 2450 } 2451 2452 /* If we haven't exceeded the limit for this query, 2453 * build a chain of answers to store. If we hit the 2454 * limit, empty the chain and ignore the rest. 2455 */ 2456 if ( !si->over ) { 2457 slap_overinst *on = si->on; 2458 cache_manager *cm = on->on_bi.bi_private; 2459 2460 /* check if the entry contains undefined 2461 * attributes/objectClasses (ITS#5680) */ 2462 if ( cm->check_cacheability && test_filter( op, rs->sr_entry, si->query.filter ) != LDAP_COMPARE_TRUE ) { 2463 Debug( pcache_debug, "%s: query not cacheable because of schema issues in DN \"%s\"\n", 2464 op->o_log_prefix, rs->sr_entry->e_name.bv_val, 0 ); 2465 goto over; 2466 } 2467 2468 /* check for malformed entries: attrs with no values */ 2469 { 2470 Attribute *a = rs->sr_entry->e_attrs; 2471 for (; a; a=a->a_next) { 2472 if ( !a->a_numvals ) { 2473 Debug( pcache_debug, "%s: query not cacheable because of attrs without values in DN \"%s\" (%s)\n", 2474 op->o_log_prefix, rs->sr_entry->e_name.bv_val, 2475 a->a_desc->ad_cname.bv_val ); 2476 goto over; 2477 } 2478 } 2479 } 2480 2481 if ( si->count < si->max ) { 2482 si->count++; 2483 e = entry_dup( rs->sr_entry ); 2484 if ( !si->head ) si->head = e; 2485 if ( si->tail ) si->tail->e_private = e; 2486 si->tail = e; 2487 2488 } else { 2489over:; 2490 si->over = 1; 2491 si->count = 0; 2492 for (;si->head; si->head=e) { 2493 e = si->head->e_private; 2494 si->head->e_private = NULL; 2495 entry_free(si->head); 2496 } 2497 si->tail = NULL; 2498 } 2499 } 2500 if ( si->slimit_exceeded ) { 2501 return 0; 2502 } 2503 } else if ( rs->sr_type == REP_RESULT ) { 2504 2505 if ( si->count ) { 2506 if ( rs->sr_err == LDAP_SUCCESS ) { 2507 si->caching_reason = PC_POSITIVE; 2508 2509 } else if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED 2510 && si->qtemp->limitttl ) 2511 { 2512 Entry *e; 2513 2514 si->caching_reason = PC_SIZELIMIT; 2515 for (;si->head; si->head=e) { 2516 e = si->head->e_private; 2517 si->head->e_private = NULL; 2518 entry_free(si->head); 2519 } 2520 } 2521 2522 } else if ( si->qtemp->negttl && !si->count && !si->over && 2523 rs->sr_err == LDAP_SUCCESS ) 2524 { 2525 si->caching_reason = PC_NEGATIVE; 2526 } 2527 2528 2529 if ( si->slimit_exceeded ) { 2530 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED; 2531 } 2532 } 2533 2534 return SLAP_CB_CONTINUE; 2535} 2536 2537/* NOTE: this is a quick workaround to let pcache minimally interact 2538 * with pagedResults. A more articulated solutions would be to 2539 * perform the remote query without control and cache all results, 2540 * performing the pagedResults search only within the client 2541 * and the proxy. This requires pcache to understand pagedResults. */ 2542static int 2543pcache_chk_controls( 2544 Operation *op, 2545 SlapReply *rs ) 2546{ 2547 const char *non = ""; 2548 const char *stripped = ""; 2549 2550 switch( op->o_pagedresults ) { 2551 case SLAP_CONTROL_NONCRITICAL: 2552 non = "non-"; 2553 stripped = "; stripped"; 2554 /* fallthru */ 2555 2556 case SLAP_CONTROL_CRITICAL: 2557 Debug( pcache_debug, "%s: " 2558 "%scritical pagedResults control " 2559 "disabled with proxy cache%s.\n", 2560 op->o_log_prefix, non, stripped ); 2561 2562 slap_remove_control( op, rs, slap_cids.sc_pagedResults, NULL ); 2563 break; 2564 2565 default: 2566 rs->sr_err = SLAP_CB_CONTINUE; 2567 break; 2568 } 2569 2570 return rs->sr_err; 2571} 2572 2573static int 2574pc_setpw( Operation *op, struct berval *pwd, cache_manager *cm ) 2575{ 2576 struct berval vals[2]; 2577 2578 { 2579 const char *text = NULL; 2580 BER_BVZERO( &vals[0] ); 2581 slap_passwd_hash( pwd, &vals[0], &text ); 2582 if ( BER_BVISEMPTY( &vals[0] )) { 2583 Debug( pcache_debug, "pc_setpw: hash failed %s\n", 2584 text, 0, 0 ); 2585 return LDAP_OTHER; 2586 } 2587 } 2588 2589 BER_BVZERO( &vals[1] ); 2590 2591 { 2592 Modifications mod; 2593 SlapReply sr = { REP_RESULT }; 2594 slap_callback cb = { 0, slap_null_cb, 0, 0 }; 2595 int rc; 2596 2597 mod.sml_op = LDAP_MOD_REPLACE; 2598 mod.sml_flags = 0; 2599 mod.sml_desc = slap_schema.si_ad_userPassword; 2600 mod.sml_type = mod.sml_desc->ad_cname; 2601 mod.sml_values = vals; 2602 mod.sml_nvalues = NULL; 2603 mod.sml_numvals = 1; 2604 mod.sml_next = NULL; 2605 2606 op->o_tag = LDAP_REQ_MODIFY; 2607 op->orm_modlist = &mod; 2608 op->o_bd = &cm->db; 2609 op->o_dn = op->o_bd->be_rootdn; 2610 op->o_ndn = op->o_bd->be_rootndn; 2611 op->o_callback = &cb; 2612 Debug( pcache_debug, "pc_setpw: CACHING BIND for %s\n", 2613 op->o_req_dn.bv_val, 0, 0 ); 2614 rc = op->o_bd->be_modify( op, &sr ); 2615 ch_free( vals[0].bv_val ); 2616 return rc; 2617 } 2618} 2619 2620typedef struct bindcacheinfo { 2621 slap_overinst *on; 2622 CachedQuery *qc; 2623} bindcacheinfo; 2624 2625static int 2626pc_bind_save( Operation *op, SlapReply *rs ) 2627{ 2628 if ( rs->sr_err == LDAP_SUCCESS ) { 2629 bindcacheinfo *bci = op->o_callback->sc_private; 2630 slap_overinst *on = bci->on; 2631 cache_manager *cm = on->on_bi.bi_private; 2632 CachedQuery *qc = bci->qc; 2633 int delete = 0; 2634 2635 ldap_pvt_thread_rdwr_wlock( &qc->rwlock ); 2636 if ( qc->bind_refcnt-- ) { 2637 Operation op2 = *op; 2638 if ( pc_setpw( &op2, &op->orb_cred, cm ) == LDAP_SUCCESS ) 2639 bci->qc->bindref_time = op->o_time + bci->qc->qtemp->bindttr; 2640 } else { 2641 bci->qc = NULL; 2642 delete = 1; 2643 } 2644 ldap_pvt_thread_rdwr_wunlock( &qc->rwlock ); 2645 if ( delete ) free_query(qc); 2646 } 2647 return SLAP_CB_CONTINUE; 2648} 2649 2650static Filter * 2651pc_bind_attrs( Operation *op, Entry *e, QueryTemplate *temp, 2652 struct berval *fbv ) 2653{ 2654 int i, len = 0; 2655 struct berval *vals, pres = BER_BVC("*"); 2656 char *p1, *p2; 2657 Attribute *a; 2658 2659 vals = op->o_tmpalloc( temp->bindnattrs * sizeof( struct berval ), 2660 op->o_tmpmemctx ); 2661 2662 for ( i=0; i<temp->bindnattrs; i++ ) { 2663 a = attr_find( e->e_attrs, temp->bindfattrs[i] ); 2664 if ( a && a->a_vals ) { 2665 vals[i] = a->a_vals[0]; 2666 len += a->a_vals[0].bv_len; 2667 } else { 2668 vals[i] = pres; 2669 } 2670 } 2671 fbv->bv_len = len + temp->bindftemp.bv_len; 2672 fbv->bv_val = op->o_tmpalloc( fbv->bv_len + 1, op->o_tmpmemctx ); 2673 2674 p1 = temp->bindftemp.bv_val; 2675 p2 = fbv->bv_val; 2676 i = 0; 2677 while ( *p1 ) { 2678 *p2++ = *p1; 2679 if ( p1[0] == '=' && p1[1] == ')' ) { 2680 AC_MEMCPY( p2, vals[i].bv_val, vals[i].bv_len ); 2681 p2 += vals[i].bv_len; 2682 i++; 2683 } 2684 p1++; 2685 } 2686 *p2 = '\0'; 2687 op->o_tmpfree( vals, op->o_tmpmemctx ); 2688 2689 /* FIXME: are we sure str2filter_x can't fail? 2690 * caller needs to check */ 2691 { 2692 Filter *f = str2filter_x( op, fbv->bv_val ); 2693 assert( f != NULL ); 2694 return f; 2695 } 2696} 2697 2698/* Check if the requested entry is from the cache and has a valid 2699 * ttr and password hash 2700 */ 2701static int 2702pc_bind_search( Operation *op, SlapReply *rs ) 2703{ 2704 if ( rs->sr_type == REP_SEARCH ) { 2705 bindinfo *pbi = op->o_callback->sc_private; 2706 2707 /* We only care if this is an already cached result and we're 2708 * below the refresh time, or we're offline. 2709 */ 2710 if ( pbi->bi_cq ) { 2711 if (( pbi->bi_cm->cc_paused & PCACHE_CC_OFFLINE ) || 2712 op->o_time < pbi->bi_cq->bindref_time ) { 2713 Attribute *a; 2714 2715 /* See if a recognized password is hashed here */ 2716 a = attr_find( rs->sr_entry->e_attrs, 2717 slap_schema.si_ad_userPassword ); 2718 if ( a && a->a_vals[0].bv_val[0] == '{' && 2719 lutil_passwd_scheme( a->a_vals[0].bv_val )) 2720 pbi->bi_flags |= BI_HASHED; 2721 } else { 2722 Debug( pcache_debug, "pc_bind_search: cache is stale, " 2723 "reftime: %ld, current time: %ld\n", 2724 pbi->bi_cq->bindref_time, op->o_time, 0 ); 2725 } 2726 } else if ( pbi->bi_si ) { 2727 /* This search result is going into the cache */ 2728 struct berval fbv; 2729 Filter *f; 2730 2731 filter_free( pbi->bi_si->query.filter ); 2732 f = pc_bind_attrs( op, rs->sr_entry, pbi->bi_templ, &fbv ); 2733 op->o_tmpfree( fbv.bv_val, op->o_tmpmemctx ); 2734 pbi->bi_si->query.filter = filter_dup( f, NULL ); 2735 filter_free_x( op, f, 1 ); 2736 } 2737 } 2738 return 0; 2739} 2740 2741/* We always want pc_bind_search to run after the search handlers */ 2742static int 2743pc_bind_resp( Operation *op, SlapReply *rs ) 2744{ 2745 bindinfo *pbi = op->o_callback->sc_private; 2746 if ( !( pbi->bi_flags & BI_DIDCB )) { 2747 slap_callback *sc = op->o_callback; 2748 while ( sc && sc->sc_response != pcache_response ) 2749 sc = sc->sc_next; 2750 if ( !sc ) 2751 sc = op->o_callback; 2752 pbi->bi_cb.sc_next = sc->sc_next; 2753 sc->sc_next = &pbi->bi_cb; 2754 pbi->bi_flags |= BI_DIDCB; 2755 } 2756 return SLAP_CB_CONTINUE; 2757} 2758 2759#ifdef PCACHE_CONTROL_PRIVDB 2760static int 2761pcache_op_privdb( 2762 Operation *op, 2763 SlapReply *rs ) 2764{ 2765 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 2766 cache_manager *cm = on->on_bi.bi_private; 2767 slap_callback *save_cb; 2768 slap_op_t type; 2769 2770 /* skip if control is unset */ 2771 if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_CRITICAL ) { 2772 return SLAP_CB_CONTINUE; 2773 } 2774 2775 /* The cache DB isn't open yet */ 2776 if ( cm->defer_db_open ) { 2777 send_ldap_error( op, rs, LDAP_UNAVAILABLE, 2778 "pcachePrivDB: cacheDB not available" ); 2779 return rs->sr_err; 2780 } 2781 2782 /* FIXME: might be a little bit exaggerated... */ 2783 if ( !be_isroot( op ) ) { 2784 save_cb = op->o_callback; 2785 op->o_callback = NULL; 2786 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 2787 "pcachePrivDB: operation not allowed" ); 2788 op->o_callback = save_cb; 2789 2790 return rs->sr_err; 2791 } 2792 2793 /* map tag to operation */ 2794 type = slap_req2op( op->o_tag ); 2795 if ( type != SLAP_OP_LAST ) { 2796 BI_op_func **func; 2797 int rc; 2798 2799 /* execute, if possible */ 2800 func = &cm->db.be_bind; 2801 if ( func[ type ] != NULL ) { 2802 Operation op2 = *op; 2803 2804 op2.o_bd = &cm->db; 2805 2806 rc = func[ type ]( &op2, rs ); 2807 if ( type == SLAP_OP_BIND && rc == LDAP_SUCCESS ) { 2808 op->o_conn->c_authz_cookie = cm->db.be_private; 2809 } 2810 2811 return rs->sr_err; 2812 } 2813 } 2814 2815 /* otherwise fall back to error */ 2816 save_cb = op->o_callback; 2817 op->o_callback = NULL; 2818 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 2819 "operation not supported with pcachePrivDB control" ); 2820 op->o_callback = save_cb; 2821 2822 return rs->sr_err; 2823} 2824#endif /* PCACHE_CONTROL_PRIVDB */ 2825 2826static int 2827pcache_op_bind( 2828 Operation *op, 2829 SlapReply *rs ) 2830{ 2831 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 2832 cache_manager *cm = on->on_bi.bi_private; 2833 QueryTemplate *temp; 2834 Entry *e; 2835 slap_callback cb = { 0 }, *sc; 2836 bindinfo bi; 2837 bindcacheinfo *bci; 2838 Operation op2; 2839 int rc; 2840 2841#ifdef PCACHE_CONTROL_PRIVDB 2842 if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) 2843 return pcache_op_privdb( op, rs ); 2844#endif /* PCACHE_CONTROL_PRIVDB */ 2845 2846 /* Skip if we're not configured for Binds, or cache DB isn't open yet */ 2847 if ( !cm->cache_binds || cm->defer_db_open ) 2848 return SLAP_CB_CONTINUE; 2849 2850 /* First find a matching template with Bind info */ 2851 for ( temp=cm->qm->templates; temp; temp=temp->qmnext ) { 2852 if ( temp->bindttr && dnIsSuffix( &op->o_req_ndn, &temp->bindbase )) 2853 break; 2854 } 2855 /* Didn't find a suitable template, just passthru */ 2856 if ( !temp ) 2857 return SLAP_CB_CONTINUE; 2858 2859 /* See if the entry is already locally cached. If so, we can 2860 * populate the query filter to retrieve the cached query. We 2861 * need to check the bindrefresh time in the query. 2862 */ 2863 op2 = *op; 2864 op2.o_dn = op->o_bd->be_rootdn; 2865 op2.o_ndn = op->o_bd->be_rootndn; 2866 bi.bi_flags = 0; 2867 2868 op2.o_bd = &cm->db; 2869 e = NULL; 2870 rc = be_entry_get_rw( &op2, &op->o_req_ndn, NULL, NULL, 0, &e ); 2871 if ( rc == LDAP_SUCCESS && e ) { 2872 bi.bi_flags |= BI_LOOKUP; 2873 op2.ors_filter = pc_bind_attrs( op, e, temp, &op2.ors_filterstr ); 2874 be_entry_release_r( &op2, e ); 2875 } else { 2876 op2.ors_filter = temp->bindfilter; 2877 op2.ors_filterstr = temp->bindfilterstr; 2878 } 2879 2880 op2.o_bd = op->o_bd; 2881 op2.o_tag = LDAP_REQ_SEARCH; 2882 op2.ors_scope = LDAP_SCOPE_BASE; 2883 op2.ors_deref = LDAP_DEREF_NEVER; 2884 op2.ors_slimit = 1; 2885 op2.ors_tlimit = SLAP_NO_LIMIT; 2886 op2.ors_limit = NULL; 2887 op2.ors_attrs = cm->qm->attr_sets[temp->attr_set_index].attrs; 2888 op2.ors_attrsonly = 0; 2889 2890 /* We want to invoke search at the same level of the stack 2891 * as we're already at... 2892 */ 2893 bi.bi_cm = cm; 2894 bi.bi_templ = temp; 2895 bi.bi_cq = NULL; 2896 bi.bi_si = NULL; 2897 2898 bi.bi_cb.sc_response = pc_bind_search; 2899 bi.bi_cb.sc_cleanup = NULL; 2900 bi.bi_cb.sc_private = &bi; 2901 cb.sc_private = &bi; 2902 cb.sc_response = pc_bind_resp; 2903 op2.o_callback = &cb; 2904 overlay_op_walk( &op2, rs, op_search, on->on_info, on ); 2905 2906 /* OK, just bind locally */ 2907 if ( bi.bi_flags & BI_HASHED ) { 2908 int delete = 0; 2909 BackendDB *be = op->o_bd; 2910 op->o_bd = &cm->db; 2911 2912 Debug( pcache_debug, "pcache_op_bind: CACHED BIND for %s\n", 2913 op->o_req_dn.bv_val, 0, 0 ); 2914 2915 if ( op->o_bd->be_bind( op, rs ) == LDAP_SUCCESS ) { 2916 op->o_conn->c_authz_cookie = cm->db.be_private; 2917 } 2918 op->o_bd = be; 2919 ldap_pvt_thread_rdwr_wlock( &bi.bi_cq->rwlock ); 2920 if ( !bi.bi_cq->bind_refcnt-- ) { 2921 delete = 1; 2922 } 2923 ldap_pvt_thread_rdwr_wunlock( &bi.bi_cq->rwlock ); 2924 if ( delete ) free_query( bi.bi_cq ); 2925 return rs->sr_err; 2926 } 2927 2928 /* We have a cached query to work with */ 2929 if ( bi.bi_cq ) { 2930 sc = op->o_tmpalloc( sizeof(slap_callback) + sizeof(bindcacheinfo), 2931 op->o_tmpmemctx ); 2932 sc->sc_response = pc_bind_save; 2933 sc->sc_cleanup = NULL; 2934 sc->sc_private = sc+1; 2935 bci = sc->sc_private; 2936 sc->sc_next = op->o_callback; 2937 op->o_callback = sc; 2938 bci->on = on; 2939 bci->qc = bi.bi_cq; 2940 } 2941 return SLAP_CB_CONTINUE; 2942} 2943 2944static slap_response refresh_merge; 2945 2946static int 2947pcache_op_search( 2948 Operation *op, 2949 SlapReply *rs ) 2950{ 2951 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 2952 cache_manager *cm = on->on_bi.bi_private; 2953 query_manager* qm = cm->qm; 2954 2955 int i = -1; 2956 2957 Query query; 2958 QueryTemplate *qtemp = NULL; 2959 bindinfo *pbi = NULL; 2960 2961 int attr_set = -1; 2962 CachedQuery *answerable = NULL; 2963 int cacheable = 0; 2964 2965 struct berval tempstr; 2966 2967#ifdef PCACHE_CONTROL_PRIVDB 2968 if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) { 2969 return pcache_op_privdb( op, rs ); 2970 } 2971#endif /* PCACHE_CONTROL_PRIVDB */ 2972 2973 /* The cache DB isn't open yet */ 2974 if ( cm->defer_db_open ) { 2975 send_ldap_error( op, rs, LDAP_UNAVAILABLE, 2976 "pcachePrivDB: cacheDB not available" ); 2977 return rs->sr_err; 2978 } 2979 2980 /* pickup runtime ACL changes */ 2981 cm->db.be_acl = op->o_bd->be_acl; 2982 2983 { 2984 /* See if we're processing a Bind request 2985 * or a cache refresh */ 2986 slap_callback *cb = op->o_callback; 2987 2988 for ( ; cb; cb=cb->sc_next ) { 2989 if ( cb->sc_response == pc_bind_resp ) { 2990 pbi = cb->sc_private; 2991 break; 2992 } 2993 if ( cb->sc_response == refresh_merge ) { 2994 /* This is a refresh, do not search the cache */ 2995 return SLAP_CB_CONTINUE; 2996 } 2997 } 2998 } 2999 3000 /* FIXME: cannot cache/answer requests with pagedResults control */ 3001 3002 query.filter = op->ors_filter; 3003 3004 if ( pbi ) { 3005 query.base = pbi->bi_templ->bindbase; 3006 query.scope = pbi->bi_templ->bindscope; 3007 attr_set = pbi->bi_templ->attr_set_index; 3008 cacheable = 1; 3009 qtemp = pbi->bi_templ; 3010 if ( pbi->bi_flags & BI_LOOKUP ) 3011 answerable = qm->qcfunc(op, qm, &query, qtemp); 3012 3013 } else { 3014 tempstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len+1, 3015 op->o_tmpmemctx ); 3016 tempstr.bv_len = 0; 3017 if ( filter2template( op, op->ors_filter, &tempstr )) 3018 { 3019 op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx ); 3020 return SLAP_CB_CONTINUE; 3021 } 3022 3023 Debug( pcache_debug, "query template of incoming query = %s\n", 3024 tempstr.bv_val, 0, 0 ); 3025 3026 /* find attr set */ 3027 attr_set = get_attr_set(op->ors_attrs, qm, cm->numattrsets); 3028 3029 query.base = op->o_req_ndn; 3030 query.scope = op->ors_scope; 3031 3032 /* check for query containment */ 3033 if (attr_set > -1) { 3034 QueryTemplate *qt = qm->attr_sets[attr_set].templates; 3035 for (; qt; qt = qt->qtnext ) { 3036 /* find if template i can potentially answer tempstr */ 3037 if ( ber_bvstrcasecmp( &qt->querystr, &tempstr ) != 0 ) 3038 continue; 3039 cacheable = 1; 3040 qtemp = qt; 3041 Debug( pcache_debug, "Entering QC, querystr = %s\n", 3042 op->ors_filterstr.bv_val, 0, 0 ); 3043 answerable = qm->qcfunc(op, qm, &query, qt); 3044 3045 /* if != NULL, rlocks qtemp->t_rwlock */ 3046 if (answerable) 3047 break; 3048 } 3049 } 3050 op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx ); 3051 } 3052 3053 if (answerable) { 3054 BackendDB *save_bd = op->o_bd; 3055 3056 ldap_pvt_thread_mutex_lock( &answerable->answerable_cnt_mutex ); 3057 answerable->answerable_cnt++; 3058 /* we only care about refcnts if we're refreshing */ 3059 if ( answerable->refresh_time ) 3060 answerable->refcnt++; 3061 Debug( pcache_debug, "QUERY ANSWERABLE (answered %lu times)\n", 3062 answerable->answerable_cnt, 0, 0 ); 3063 ldap_pvt_thread_mutex_unlock( &answerable->answerable_cnt_mutex ); 3064 3065 ldap_pvt_thread_rdwr_wlock(&answerable->rwlock); 3066 if ( BER_BVISNULL( &answerable->q_uuid )) { 3067 /* No entries cached, just an empty result set */ 3068 i = rs->sr_err = 0; 3069 send_ldap_result( op, rs ); 3070 } else { 3071 /* Let Bind know we used a cached query */ 3072 if ( pbi ) { 3073 answerable->bind_refcnt++; 3074 pbi->bi_cq = answerable; 3075 } 3076 3077 op->o_bd = &cm->db; 3078 if ( cm->response_cb == PCACHE_RESPONSE_CB_TAIL ) { 3079 slap_callback cb; 3080 /* The cached entry was already processed by any 3081 * other overlays, so don't let it get processed again. 3082 * 3083 * This loop removes over_back_response from the stack. 3084 */ 3085 if ( overlay_callback_after_backover( op, &cb, 0) == 0 ) { 3086 slap_callback **scp; 3087 for ( scp = &op->o_callback; *scp != NULL; 3088 scp = &(*scp)->sc_next ) { 3089 if ( (*scp)->sc_next == &cb ) { 3090 *scp = cb.sc_next; 3091 break; 3092 } 3093 } 3094 } 3095 } 3096 i = cm->db.bd_info->bi_op_search( op, rs ); 3097 } 3098 ldap_pvt_thread_rdwr_wunlock(&answerable->rwlock); 3099 /* locked by qtemp->qcfunc (query_containment) */ 3100 ldap_pvt_thread_rdwr_runlock(&qtemp->t_rwlock); 3101 op->o_bd = save_bd; 3102 return i; 3103 } 3104 3105 Debug( pcache_debug, "QUERY NOT ANSWERABLE\n", 0, 0, 0 ); 3106 3107 ldap_pvt_thread_mutex_lock(&cm->cache_mutex); 3108 if (cm->num_cached_queries >= cm->max_queries) { 3109 cacheable = 0; 3110 } 3111 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex); 3112 3113 if (op->ors_attrsonly) 3114 cacheable = 0; 3115 3116 if (cacheable) { 3117 slap_callback *cb; 3118 struct search_info *si; 3119 3120 Debug( pcache_debug, "QUERY CACHEABLE\n", 0, 0, 0 ); 3121 query.filter = filter_dup(op->ors_filter, NULL); 3122 3123 cb = op->o_tmpalloc( sizeof(*cb) + sizeof(*si), op->o_tmpmemctx ); 3124 cb->sc_response = pcache_response; 3125 cb->sc_cleanup = pcache_op_cleanup; 3126 cb->sc_private = (cb+1); 3127 si = cb->sc_private; 3128 si->on = on; 3129 si->query = query; 3130 si->qtemp = qtemp; 3131 si->max = cm->num_entries_limit ; 3132 si->over = 0; 3133 si->count = 0; 3134 si->slimit = 0; 3135 si->slimit_exceeded = 0; 3136 si->caching_reason = PC_IGNORE; 3137 if ( op->ors_slimit > 0 && op->ors_slimit < cm->num_entries_limit ) { 3138 si->slimit = op->ors_slimit; 3139 op->ors_slimit = cm->num_entries_limit; 3140 } 3141 si->head = NULL; 3142 si->tail = NULL; 3143 si->swap_saved_attrs = 1; 3144 si->save_attrs = op->ors_attrs; 3145 si->pbi = pbi; 3146 if ( pbi ) 3147 pbi->bi_si = si; 3148 3149 op->ors_attrs = qtemp->t_attrs.attrs; 3150 3151 if ( cm->response_cb == PCACHE_RESPONSE_CB_HEAD ) { 3152 cb->sc_next = op->o_callback; 3153 op->o_callback = cb; 3154 3155 } else { 3156 slap_callback **pcb; 3157 3158 /* need to move the callback at the end, in case other 3159 * overlays are present, so that the final entry is 3160 * actually cached */ 3161 cb->sc_next = NULL; 3162 for ( pcb = &op->o_callback; *pcb; pcb = &(*pcb)->sc_next ); 3163 *pcb = cb; 3164 } 3165 3166 } else { 3167 Debug( pcache_debug, "QUERY NOT CACHEABLE\n", 3168 0, 0, 0); 3169 } 3170 3171 return SLAP_CB_CONTINUE; 3172} 3173 3174static int 3175get_attr_set( 3176 AttributeName* attrs, 3177 query_manager* qm, 3178 int num ) 3179{ 3180 int i = 0; 3181 int count = 0; 3182 3183 if ( attrs ) { 3184 for ( ; attrs[i].an_name.bv_val; i++ ) { 3185 /* only count valid attribute names 3186 * (searches ignore others, this overlay does the same) */ 3187 if ( attrs[i].an_desc ) { 3188 count++; 3189 } 3190 } 3191 } 3192 3193 /* recognize default or explicit single "*" */ 3194 if ( ! attrs || 3195 ( i == 1 && bvmatch( &attrs[0].an_name, slap_bv_all_user_attrs ) ) ) 3196 { 3197 count = 1; 3198 attrs = slap_anlist_all_user_attributes; 3199 3200 /* recognize implicit (no valid attributes) or explicit single "1.1" */ 3201 } else if ( count == 0 || 3202 ( i == 1 && bvmatch( &attrs[0].an_name, slap_bv_no_attrs ) ) ) 3203 { 3204 count = 0; 3205 attrs = NULL; 3206 } 3207 3208 for ( i = 0; i < num; i++ ) { 3209 AttributeName *a2; 3210 int found = 1; 3211 3212 if ( count > qm->attr_sets[i].count ) { 3213 continue; 3214 } 3215 3216 if ( !count ) { 3217 if ( !qm->attr_sets[i].count ) { 3218 break; 3219 } 3220 continue; 3221 } 3222 3223 for ( a2 = attrs; a2->an_name.bv_val; a2++ ) { 3224 if ( !a2->an_desc && !bvmatch( &a2->an_name, slap_bv_all_user_attrs ) ) continue; 3225 3226 if ( !an_find( qm->attr_sets[i].attrs, &a2->an_name ) ) { 3227 found = 0; 3228 break; 3229 } 3230 } 3231 3232 if ( found ) { 3233 break; 3234 } 3235 } 3236 3237 if ( i == num ) { 3238 i = -1; 3239 } 3240 3241 return i; 3242} 3243 3244/* Refresh a cached query: 3245 * 1: Replay the query on the remote DB and merge each entry into 3246 * the local DB. Remember the DNs of each remote entry. 3247 * 2: Search the local DB for all entries matching this queryID. 3248 * Delete any entry whose DN is not in the list from (1). 3249 */ 3250typedef struct dnlist { 3251 struct dnlist *next; 3252 struct berval dn; 3253 char delete; 3254} dnlist; 3255 3256typedef struct refresh_info { 3257 dnlist *ri_dns; 3258 dnlist *ri_tail; 3259 dnlist *ri_dels; 3260 BackendDB *ri_be; 3261 CachedQuery *ri_q; 3262} refresh_info; 3263 3264static dnlist *dnl_alloc( Operation *op, struct berval *bvdn ) 3265{ 3266 dnlist *dn = op->o_tmpalloc( sizeof(dnlist) + bvdn->bv_len + 1, 3267 op->o_tmpmemctx ); 3268 dn->dn.bv_len = bvdn->bv_len; 3269 dn->dn.bv_val = (char *)(dn+1); 3270 AC_MEMCPY( dn->dn.bv_val, bvdn->bv_val, dn->dn.bv_len ); 3271 dn->dn.bv_val[dn->dn.bv_len] = '\0'; 3272 return dn; 3273} 3274 3275static int 3276refresh_merge( Operation *op, SlapReply *rs ) 3277{ 3278 if ( rs->sr_type == REP_SEARCH ) { 3279 refresh_info *ri = op->o_callback->sc_private; 3280 Entry *e; 3281 dnlist *dnl; 3282 slap_callback *ocb; 3283 int rc; 3284 3285 ocb = op->o_callback; 3286 /* Find local entry, merge */ 3287 op->o_bd = ri->ri_be; 3288 rc = be_entry_get_rw( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &e ); 3289 if ( rc != LDAP_SUCCESS || e == NULL ) { 3290 /* No local entry, just add it. FIXME: we are not checking 3291 * the cache entry limit here 3292 */ 3293 merge_entry( op, rs->sr_entry, 1, &ri->ri_q->q_uuid ); 3294 } else { 3295 /* Entry exists, update it */ 3296 Entry ne; 3297 Attribute *a, **b; 3298 Modifications *modlist, *mods = NULL; 3299 const char* text = NULL; 3300 char textbuf[SLAP_TEXT_BUFLEN]; 3301 size_t textlen = sizeof(textbuf); 3302 slap_callback cb = { NULL, slap_null_cb, NULL, NULL }; 3303 3304 ne = *e; 3305 b = &ne.e_attrs; 3306 /* Get a copy of only the attrs we requested */ 3307 for ( a=e->e_attrs; a; a=a->a_next ) { 3308 if ( ad_inlist( a->a_desc, rs->sr_attrs )) { 3309 *b = attr_alloc( a->a_desc ); 3310 *(*b) = *a; 3311 /* The actual values still belong to e */ 3312 (*b)->a_flags |= SLAP_ATTR_DONT_FREE_VALS | 3313 SLAP_ATTR_DONT_FREE_DATA; 3314 b = &((*b)->a_next); 3315 } 3316 } 3317 *b = NULL; 3318 slap_entry2mods( rs->sr_entry, &modlist, &text, textbuf, textlen ); 3319 syncrepl_diff_entry( op, ne.e_attrs, rs->sr_entry->e_attrs, 3320 &mods, &modlist, 0 ); 3321 be_entry_release_r( op, e ); 3322 attrs_free( ne.e_attrs ); 3323 slap_mods_free( modlist, 1 ); 3324 /* mods is NULL if there are no changes */ 3325 if ( mods ) { 3326 SlapReply rs2 = { REP_RESULT }; 3327 struct berval dn = op->o_req_dn; 3328 struct berval ndn = op->o_req_ndn; 3329 op->o_tag = LDAP_REQ_MODIFY; 3330 op->orm_modlist = mods; 3331 op->o_req_dn = rs->sr_entry->e_name; 3332 op->o_req_ndn = rs->sr_entry->e_nname; 3333 op->o_callback = &cb; 3334 op->o_bd->be_modify( op, &rs2 ); 3335 rs->sr_err = rs2.sr_err; 3336 rs_assert_done( &rs2 ); 3337 slap_mods_free( mods, 1 ); 3338 op->o_req_dn = dn; 3339 op->o_req_ndn = ndn; 3340 } 3341 } 3342 3343 /* Add DN to list */ 3344 dnl = dnl_alloc( op, &rs->sr_entry->e_nname ); 3345 dnl->next = NULL; 3346 if ( ri->ri_tail ) { 3347 ri->ri_tail->next = dnl; 3348 } else { 3349 ri->ri_dns = dnl; 3350 } 3351 ri->ri_tail = dnl; 3352 op->o_callback = ocb; 3353 } 3354 return 0; 3355} 3356 3357static int 3358refresh_purge( Operation *op, SlapReply *rs ) 3359{ 3360 if ( rs->sr_type == REP_SEARCH ) { 3361 refresh_info *ri = op->o_callback->sc_private; 3362 dnlist **dn; 3363 int del = 1; 3364 3365 /* Did the entry exist on the remote? */ 3366 for ( dn=&ri->ri_dns; *dn; dn = &(*dn)->next ) { 3367 if ( dn_match( &(*dn)->dn, &rs->sr_entry->e_nname )) { 3368 dnlist *dnext = (*dn)->next; 3369 op->o_tmpfree( *dn, op->o_tmpmemctx ); 3370 *dn = dnext; 3371 del = 0; 3372 break; 3373 } 3374 } 3375 /* No, so put it on the list to delete */ 3376 if ( del ) { 3377 Attribute *a; 3378 dnlist *dnl = dnl_alloc( op, &rs->sr_entry->e_nname ); 3379 dnl->next = ri->ri_dels; 3380 ri->ri_dels = dnl; 3381 a = attr_find( rs->sr_entry->e_attrs, ad_queryId ); 3382 /* If ours is the only queryId, delete entry */ 3383 dnl->delete = ( a->a_numvals == 1 ); 3384 } 3385 } 3386 return 0; 3387} 3388 3389static int 3390refresh_query( Operation *op, CachedQuery *query, slap_overinst *on ) 3391{ 3392 SlapReply rs = {REP_RESULT}; 3393 slap_callback cb = { 0 }; 3394 refresh_info ri = { 0 }; 3395 char filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ]; 3396 AttributeAssertion ava = ATTRIBUTEASSERTION_INIT; 3397 Filter filter = {LDAP_FILTER_EQUALITY}; 3398 AttributeName attrs[ 2 ] = {{{ 0 }}}; 3399 dnlist *dn; 3400 int rc; 3401 3402 ldap_pvt_thread_mutex_lock( &query->answerable_cnt_mutex ); 3403 query->refcnt = 0; 3404 ldap_pvt_thread_mutex_unlock( &query->answerable_cnt_mutex ); 3405 3406 cb.sc_response = refresh_merge; 3407 cb.sc_private = &ri; 3408 3409 /* cache DB */ 3410 ri.ri_be = op->o_bd; 3411 ri.ri_q = query; 3412 3413 op->o_tag = LDAP_REQ_SEARCH; 3414 op->o_protocol = LDAP_VERSION3; 3415 op->o_callback = &cb; 3416 op->o_do_not_cache = 1; 3417 3418 op->o_req_dn = query->qbase->base; 3419 op->o_req_ndn = query->qbase->base; 3420 op->ors_scope = query->scope; 3421 op->ors_slimit = SLAP_NO_LIMIT; 3422 op->ors_tlimit = SLAP_NO_LIMIT; 3423 op->ors_limit = NULL; 3424 op->ors_filter = query->filter; 3425 filter2bv_x( op, query->filter, &op->ors_filterstr ); 3426 op->ors_attrs = query->qtemp->t_attrs.attrs; 3427 op->ors_attrsonly = 0; 3428 3429 op->o_bd = on->on_info->oi_origdb; 3430 rc = op->o_bd->be_search( op, &rs ); 3431 if ( rc ) { 3432 op->o_bd = ri.ri_be; 3433 goto leave; 3434 } 3435 3436 /* Get the DNs of all entries matching this query */ 3437 cb.sc_response = refresh_purge; 3438 3439 op->o_bd = ri.ri_be; 3440 op->o_req_dn = op->o_bd->be_suffix[0]; 3441 op->o_req_ndn = op->o_bd->be_nsuffix[0]; 3442 op->ors_scope = LDAP_SCOPE_SUBTREE; 3443 op->ors_deref = LDAP_DEREF_NEVER; 3444 op->ors_filterstr.bv_len = snprintf(filter_str, sizeof(filter_str), 3445 "(%s=%s)", ad_queryId->ad_cname.bv_val, query->q_uuid.bv_val); 3446 filter.f_ava = &ava; 3447 filter.f_av_desc = ad_queryId; 3448 filter.f_av_value = query->q_uuid; 3449 attrs[ 0 ].an_desc = ad_queryId; 3450 attrs[ 0 ].an_name = ad_queryId->ad_cname; 3451 op->ors_attrs = attrs; 3452 op->ors_attrsonly = 0; 3453 rs_reinit( &rs, REP_RESULT ); 3454 rc = op->o_bd->be_search( op, &rs ); 3455 if ( rc ) goto leave; 3456 3457 while (( dn = ri.ri_dels )) { 3458 op->o_req_dn = dn->dn; 3459 op->o_req_ndn = dn->dn; 3460 rs_reinit( &rs, REP_RESULT ); 3461 if ( dn->delete ) { 3462 op->o_tag = LDAP_REQ_DELETE; 3463 op->o_bd->be_delete( op, &rs ); 3464 } else { 3465 Modifications mod; 3466 struct berval vals[2]; 3467 3468 vals[0] = query->q_uuid; 3469 BER_BVZERO( &vals[1] ); 3470 mod.sml_op = LDAP_MOD_DELETE; 3471 mod.sml_flags = 0; 3472 mod.sml_desc = ad_queryId; 3473 mod.sml_type = ad_queryId->ad_cname; 3474 mod.sml_values = vals; 3475 mod.sml_nvalues = NULL; 3476 mod.sml_numvals = 1; 3477 mod.sml_next = NULL; 3478 3479 op->o_tag = LDAP_REQ_MODIFY; 3480 op->orm_modlist = &mod; 3481 op->o_bd->be_modify( op, &rs ); 3482 } 3483 ri.ri_dels = dn->next; 3484 op->o_tmpfree( dn, op->o_tmpmemctx ); 3485 } 3486 3487leave: 3488 /* reset our local heap, we're done with it */ 3489 slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, op->o_threadctx, 1 ); 3490 return rc; 3491} 3492 3493static void* 3494consistency_check( 3495 void *ctx, 3496 void *arg ) 3497{ 3498 struct re_s *rtask = arg; 3499 slap_overinst *on = rtask->arg; 3500 cache_manager *cm = on->on_bi.bi_private; 3501 query_manager *qm = cm->qm; 3502 Connection conn = {0}; 3503 OperationBuffer opbuf; 3504 Operation *op; 3505 3506 CachedQuery *query, *qprev; 3507 int return_val, pause = PCACHE_CC_PAUSED; 3508 QueryTemplate *templ; 3509 3510 /* Don't expire anything when we're offline */ 3511 if ( cm->cc_paused & PCACHE_CC_OFFLINE ) { 3512 pause = PCACHE_CC_OFFLINE; 3513 goto leave; 3514 } 3515 3516 connection_fake_init( &conn, &opbuf, ctx ); 3517 op = &opbuf.ob_op; 3518 3519 op->o_bd = &cm->db; 3520 op->o_dn = cm->db.be_rootdn; 3521 op->o_ndn = cm->db.be_rootndn; 3522 3523 cm->cc_arg = arg; 3524 3525 for (templ = qm->templates; templ; templ=templ->qmnext) { 3526 time_t ttl; 3527 if ( !templ->query_last ) continue; 3528 pause = 0; 3529 op->o_time = slap_get_time(); 3530 if ( !templ->ttr ) { 3531 ttl = templ->ttl; 3532 if ( templ->negttl && templ->negttl < ttl ) 3533 ttl = templ->negttl; 3534 if ( templ->limitttl && templ->limitttl < ttl ) 3535 ttl = templ->limitttl; 3536 /* The oldest timestamp that needs expiration checking */ 3537 ttl += op->o_time; 3538 } 3539 3540 for ( query=templ->query_last; query; query=qprev ) { 3541 qprev = query->prev; 3542 if ( query->refresh_time && query->refresh_time < op->o_time ) { 3543 /* A refresh will extend the expiry if the query has been 3544 * referenced, but not if it's unreferenced. If the 3545 * expiration has been hit, then skip the refresh since 3546 * we're just going to discard the result anyway. 3547 */ 3548 if ( query->refcnt ) 3549 query->expiry_time = op->o_time + templ->ttl; 3550 if ( query->expiry_time > op->o_time ) { 3551 refresh_query( op, query, on ); 3552 continue; 3553 } 3554 } 3555 3556 if (query->expiry_time < op->o_time) { 3557 int rem = 0; 3558 Debug( pcache_debug, "Lock CR index = %p\n", 3559 (void *) templ, 0, 0 ); 3560 ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock); 3561 if ( query == templ->query_last ) { 3562 rem = 1; 3563 remove_from_template(query, templ); 3564 Debug( pcache_debug, "TEMPLATE %p QUERIES-- %d\n", 3565 (void *) templ, templ->no_of_queries, 0 ); 3566 Debug( pcache_debug, "Unlock CR index = %p\n", 3567 (void *) templ, 0, 0 ); 3568 } 3569 if ( !rem ) { 3570 ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock); 3571 continue; 3572 } 3573 ldap_pvt_thread_mutex_lock(&qm->lru_mutex); 3574 remove_query(qm, query); 3575 ldap_pvt_thread_mutex_unlock(&qm->lru_mutex); 3576 if ( BER_BVISNULL( &query->q_uuid )) 3577 return_val = 0; 3578 else 3579 return_val = remove_query_data(op, &query->q_uuid); 3580 Debug( pcache_debug, "STALE QUERY REMOVED, SIZE=%d\n", 3581 return_val, 0, 0 ); 3582 ldap_pvt_thread_mutex_lock(&cm->cache_mutex); 3583 cm->cur_entries -= return_val; 3584 cm->num_cached_queries--; 3585 Debug( pcache_debug, "STORED QUERIES = %lu\n", 3586 cm->num_cached_queries, 0, 0 ); 3587 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex); 3588 Debug( pcache_debug, 3589 "STALE QUERY REMOVED, CACHE =" 3590 "%d entries\n", 3591 cm->cur_entries, 0, 0 ); 3592 ldap_pvt_thread_rdwr_wlock( &query->rwlock ); 3593 if ( query->bind_refcnt-- ) { 3594 rem = 0; 3595 } else { 3596 rem = 1; 3597 } 3598 ldap_pvt_thread_rdwr_wunlock( &query->rwlock ); 3599 if ( rem ) free_query(query); 3600 ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock); 3601 } else if ( !templ->ttr && query->expiry_time > ttl ) { 3602 /* We don't need to check for refreshes, and this 3603 * query's expiry is too new, and all subsequent queries 3604 * will be newer yet. So stop looking. 3605 * 3606 * If we have refreshes, then we always have to walk the 3607 * entire query list. 3608 */ 3609 break; 3610 } 3611 } 3612 } 3613 3614leave: 3615 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); 3616 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) { 3617 ldap_pvt_runqueue_stoptask( &slapd_rq, rtask ); 3618 } 3619 /* If there were no queries, defer processing for a while */ 3620 if ( cm->cc_paused != pause ) 3621 cm->cc_paused = pause; 3622 ldap_pvt_runqueue_resched( &slapd_rq, rtask, pause ); 3623 3624 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); 3625 return NULL; 3626} 3627 3628 3629#define MAX_ATTR_SETS 500 3630 3631enum { 3632 PC_MAIN = 1, 3633 PC_ATTR, 3634 PC_TEMP, 3635 PC_RESP, 3636 PC_QUERIES, 3637 PC_OFFLINE, 3638 PC_BIND, 3639 PC_PRIVATE_DB 3640}; 3641 3642static ConfigDriver pc_cf_gen; 3643static ConfigLDAPadd pc_ldadd; 3644static ConfigCfAdd pc_cfadd; 3645 3646static ConfigTable pccfg[] = { 3647 { "pcache", "backend> <max_entries> <numattrsets> <entry limit> " 3648 "<cycle_time", 3649 6, 6, 0, ARG_MAGIC|ARG_NO_DELETE|PC_MAIN, pc_cf_gen, 3650 "( OLcfgOvAt:2.1 NAME ( 'olcPcache' 'olcProxyCache' ) " 3651 "DESC 'Proxy Cache basic parameters' " 3652 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL }, 3653 { "pcacheAttrset", "index> <attributes...", 3654 2, 0, 0, ARG_MAGIC|PC_ATTR, pc_cf_gen, 3655 "( OLcfgOvAt:2.2 NAME ( 'olcPcacheAttrset' 'olcProxyAttrset' ) " 3656 "DESC 'A set of attributes to cache' " 3657 "SYNTAX OMsDirectoryString )", NULL, NULL }, 3658 { "pcacheTemplate", "filter> <attrset-index> <TTL> <negTTL> " 3659 "<limitTTL> <TTR", 3660 4, 7, 0, ARG_MAGIC|PC_TEMP, pc_cf_gen, 3661 "( OLcfgOvAt:2.3 NAME ( 'olcPcacheTemplate' 'olcProxyCacheTemplate' ) " 3662 "DESC 'Filter template, attrset, cache TTL, " 3663 "optional negative TTL, optional sizelimit TTL, " 3664 "optional TTR' " 3665 "SYNTAX OMsDirectoryString )", NULL, NULL }, 3666 { "pcachePosition", "head|tail(default)", 3667 2, 2, 0, ARG_MAGIC|PC_RESP, pc_cf_gen, 3668 "( OLcfgOvAt:2.4 NAME 'olcPcachePosition' " 3669 "DESC 'Response callback position in overlay stack' " 3670 "SYNTAX OMsDirectoryString )", NULL, NULL }, 3671 { "pcacheMaxQueries", "queries", 3672 2, 2, 0, ARG_INT|ARG_MAGIC|PC_QUERIES, pc_cf_gen, 3673 "( OLcfgOvAt:2.5 NAME ( 'olcPcacheMaxQueries' 'olcProxyCacheQueries' ) " 3674 "DESC 'Maximum number of queries to cache' " 3675 "SYNTAX OMsInteger )", NULL, NULL }, 3676 { "pcachePersist", "TRUE|FALSE", 3677 2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, save_queries), 3678 "( OLcfgOvAt:2.6 NAME ( 'olcPcachePersist' 'olcProxySaveQueries' ) " 3679 "DESC 'Save cached queries for hot restart' " 3680 "SYNTAX OMsBoolean )", NULL, NULL }, 3681 { "pcacheValidate", "TRUE|FALSE", 3682 2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, check_cacheability), 3683 "( OLcfgOvAt:2.7 NAME ( 'olcPcacheValidate' 'olcProxyCheckCacheability' ) " 3684 "DESC 'Check whether the results of a query are cacheable, e.g. for schema issues' " 3685 "SYNTAX OMsBoolean )", NULL, NULL }, 3686 { "pcacheOffline", "TRUE|FALSE", 3687 2, 2, 0, ARG_ON_OFF|ARG_MAGIC|PC_OFFLINE, pc_cf_gen, 3688 "( OLcfgOvAt:2.8 NAME 'olcPcacheOffline' " 3689 "DESC 'Set cache to offline mode and disable expiration' " 3690 "SYNTAX OMsBoolean )", NULL, NULL }, 3691 { "pcacheBind", "filter> <attrset-index> <TTR> <scope> <base", 3692 6, 6, 0, ARG_MAGIC|PC_BIND, pc_cf_gen, 3693 "( OLcfgOvAt:2.9 NAME 'olcPcacheBind' " 3694 "DESC 'Parameters for caching Binds' " 3695 "SYNTAX OMsDirectoryString )", NULL, NULL }, 3696 { "pcache-", "private database args", 3697 1, 0, STRLENOF("pcache-"), ARG_MAGIC|PC_PRIVATE_DB, pc_cf_gen, 3698 NULL, NULL, NULL }, 3699 3700 /* Legacy keywords */ 3701 { "proxycache", "backend> <max_entries> <numattrsets> <entry limit> " 3702 "<cycle_time", 3703 6, 6, 0, ARG_MAGIC|ARG_NO_DELETE|PC_MAIN, pc_cf_gen, 3704 NULL, NULL, NULL }, 3705 { "proxyattrset", "index> <attributes...", 3706 2, 0, 0, ARG_MAGIC|PC_ATTR, pc_cf_gen, 3707 NULL, NULL, NULL }, 3708 { "proxytemplate", "filter> <attrset-index> <TTL> <negTTL", 3709 4, 7, 0, ARG_MAGIC|PC_TEMP, pc_cf_gen, 3710 NULL, NULL, NULL }, 3711 { "response-callback", "head|tail(default)", 3712 2, 2, 0, ARG_MAGIC|PC_RESP, pc_cf_gen, 3713 NULL, NULL, NULL }, 3714 { "proxyCacheQueries", "queries", 3715 2, 2, 0, ARG_INT|ARG_MAGIC|PC_QUERIES, pc_cf_gen, 3716 NULL, NULL, NULL }, 3717 { "proxySaveQueries", "TRUE|FALSE", 3718 2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, save_queries), 3719 NULL, NULL, NULL }, 3720 { "proxyCheckCacheability", "TRUE|FALSE", 3721 2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, check_cacheability), 3722 NULL, NULL, NULL }, 3723 3724 { NULL, NULL, 0, 0, 0, ARG_IGNORED } 3725}; 3726 3727static ConfigOCs pcocs[] = { 3728 { "( OLcfgOvOc:2.1 " 3729 "NAME 'olcPcacheConfig' " 3730 "DESC 'ProxyCache configuration' " 3731 "SUP olcOverlayConfig " 3732 "MUST ( olcPcache $ olcPcacheAttrset $ olcPcacheTemplate ) " 3733 "MAY ( olcPcachePosition $ olcPcacheMaxQueries $ olcPcachePersist $ " 3734 "olcPcacheValidate $ olcPcacheOffline $ olcPcacheBind ) )", 3735 Cft_Overlay, pccfg, NULL, pc_cfadd }, 3736 { "( OLcfgOvOc:2.2 " 3737 "NAME 'olcPcacheDatabase' " 3738 "DESC 'Cache database configuration' " 3739 "AUXILIARY )", Cft_Misc, olcDatabaseDummy, pc_ldadd }, 3740 { NULL, 0, NULL } 3741}; 3742 3743static int pcache_db_open2( slap_overinst *on, ConfigReply *cr ); 3744 3745static int 3746pc_ldadd_cleanup( ConfigArgs *c ) 3747{ 3748 slap_overinst *on = c->ca_private; 3749 return pcache_db_open2( on, &c->reply ); 3750} 3751 3752static int 3753pc_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca ) 3754{ 3755 slap_overinst *on; 3756 cache_manager *cm; 3757 3758 if ( p->ce_type != Cft_Overlay || !p->ce_bi || 3759 p->ce_bi->bi_cf_ocs != pcocs ) 3760 return LDAP_CONSTRAINT_VIOLATION; 3761 3762 on = (slap_overinst *)p->ce_bi; 3763 cm = on->on_bi.bi_private; 3764 ca->be = &cm->db; 3765 /* Defer open if this is an LDAPadd */ 3766 if ( CONFIG_ONLINE_ADD( ca )) 3767 ca->cleanup = pc_ldadd_cleanup; 3768 else 3769 cm->defer_db_open = 0; 3770 ca->ca_private = on; 3771 return LDAP_SUCCESS; 3772} 3773 3774static int 3775pc_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca ) 3776{ 3777 CfEntryInfo *pe = p->e_private; 3778 slap_overinst *on = (slap_overinst *)pe->ce_bi; 3779 cache_manager *cm = on->on_bi.bi_private; 3780 struct berval bv; 3781 3782 /* FIXME: should not hardcode "olcDatabase" here */ 3783 bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ), 3784 "olcDatabase=" SLAP_X_ORDERED_FMT "%s", 3785 0, cm->db.bd_info->bi_type ); 3786 if ( bv.bv_len >= sizeof( ca->cr_msg ) ) { 3787 return -1; 3788 } 3789 bv.bv_val = ca->cr_msg; 3790 ca->be = &cm->db; 3791 cm->defer_db_open = 0; 3792 3793 /* We can only create this entry if the database is table-driven 3794 */ 3795 if ( cm->db.bd_info->bi_cf_ocs ) 3796 config_build_entry( op, rs, pe, ca, &bv, cm->db.bd_info->bi_cf_ocs, 3797 &pcocs[1] ); 3798 3799 return 0; 3800} 3801 3802static int 3803pc_cf_gen( ConfigArgs *c ) 3804{ 3805 slap_overinst *on = (slap_overinst *)c->bi; 3806 cache_manager* cm = on->on_bi.bi_private; 3807 query_manager* qm = cm->qm; 3808 QueryTemplate* temp; 3809 AttributeName* attr_name; 3810 AttributeName* attrarray; 3811 const char* text=NULL; 3812 int i, num, rc = 0; 3813 char *ptr; 3814 unsigned long t; 3815 3816 if ( c->op == SLAP_CONFIG_EMIT ) { 3817 struct berval bv; 3818 switch( c->type ) { 3819 case PC_MAIN: 3820 bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s %d %d %d %ld", 3821 cm->db.bd_info->bi_type, cm->max_entries, cm->numattrsets, 3822 cm->num_entries_limit, cm->cc_period ); 3823 bv.bv_val = c->cr_msg; 3824 value_add_one( &c->rvalue_vals, &bv ); 3825 break; 3826 case PC_ATTR: 3827 for (i=0; i<cm->numattrsets; i++) { 3828 if ( !qm->attr_sets[i].count ) continue; 3829 3830 bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), "%d", i ); 3831 3832 /* count the attr length */ 3833 for ( attr_name = qm->attr_sets[i].attrs; 3834 attr_name->an_name.bv_val; attr_name++ ) 3835 { 3836 bv.bv_len += attr_name->an_name.bv_len + 1; 3837 if ( attr_name->an_desc && 3838 ( attr_name->an_desc->ad_flags & SLAP_DESC_TEMPORARY ) ) { 3839 bv.bv_len += STRLENOF("undef:"); 3840 } 3841 } 3842 3843 bv.bv_val = ch_malloc( bv.bv_len+1 ); 3844 ptr = lutil_strcopy( bv.bv_val, c->cr_msg ); 3845 for ( attr_name = qm->attr_sets[i].attrs; 3846 attr_name->an_name.bv_val; attr_name++ ) { 3847 *ptr++ = ' '; 3848 if ( attr_name->an_desc && 3849 ( attr_name->an_desc->ad_flags & SLAP_DESC_TEMPORARY ) ) { 3850 ptr = lutil_strcopy( ptr, "undef:" ); 3851 } 3852 ptr = lutil_strcopy( ptr, attr_name->an_name.bv_val ); 3853 } 3854 ber_bvarray_add( &c->rvalue_vals, &bv ); 3855 } 3856 if ( !c->rvalue_vals ) 3857 rc = 1; 3858 break; 3859 case PC_TEMP: 3860 for (temp=qm->templates; temp; temp=temp->qmnext) { 3861 /* HEADS-UP: always print all; 3862 * if optional == 0, ignore */ 3863 bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), 3864 " %d %ld %ld %ld %ld", 3865 temp->attr_set_index, 3866 temp->ttl, 3867 temp->negttl, 3868 temp->limitttl, 3869 temp->ttr ); 3870 bv.bv_len += temp->querystr.bv_len + 2; 3871 bv.bv_val = ch_malloc( bv.bv_len+1 ); 3872 ptr = bv.bv_val; 3873 *ptr++ = '"'; 3874 ptr = lutil_strcopy( ptr, temp->querystr.bv_val ); 3875 *ptr++ = '"'; 3876 strcpy( ptr, c->cr_msg ); 3877 ber_bvarray_add( &c->rvalue_vals, &bv ); 3878 } 3879 if ( !c->rvalue_vals ) 3880 rc = 1; 3881 break; 3882 case PC_BIND: 3883 for (temp=qm->templates; temp; temp=temp->qmnext) { 3884 if ( !temp->bindttr ) continue; 3885 bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), 3886 " %d %ld %s ", 3887 temp->attr_set_index, 3888 temp->bindttr, 3889 ldap_pvt_scope2str( temp->bindscope )); 3890 bv.bv_len += temp->bindbase.bv_len + temp->bindftemp.bv_len + 4; 3891 bv.bv_val = ch_malloc( bv.bv_len + 1 ); 3892 ptr = bv.bv_val; 3893 *ptr++ = '"'; 3894 ptr = lutil_strcopy( ptr, temp->bindftemp.bv_val ); 3895 *ptr++ = '"'; 3896 ptr = lutil_strcopy( ptr, c->cr_msg ); 3897 *ptr++ = '"'; 3898 ptr = lutil_strcopy( ptr, temp->bindbase.bv_val ); 3899 *ptr++ = '"'; 3900 *ptr = '\0'; 3901 ber_bvarray_add( &c->rvalue_vals, &bv ); 3902 } 3903 if ( !c->rvalue_vals ) 3904 rc = 1; 3905 break; 3906 case PC_RESP: 3907 if ( cm->response_cb == PCACHE_RESPONSE_CB_HEAD ) { 3908 BER_BVSTR( &bv, "head" ); 3909 } else { 3910 BER_BVSTR( &bv, "tail" ); 3911 } 3912 value_add_one( &c->rvalue_vals, &bv ); 3913 break; 3914 case PC_QUERIES: 3915 c->value_int = cm->max_queries; 3916 break; 3917 case PC_OFFLINE: 3918 c->value_int = (cm->cc_paused & PCACHE_CC_OFFLINE) != 0; 3919 break; 3920 } 3921 return rc; 3922 } else if ( c->op == LDAP_MOD_DELETE ) { 3923 rc = 1; 3924 switch( c->type ) { 3925 case PC_ATTR: /* FIXME */ 3926 case PC_TEMP: 3927 case PC_BIND: 3928 break; 3929 case PC_OFFLINE: 3930 cm->cc_paused &= ~PCACHE_CC_OFFLINE; 3931 /* If there were cached queries when we went offline, 3932 * restart the checker now. 3933 */ 3934 if ( cm->num_cached_queries ) { 3935 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); 3936 cm->cc_paused = 0; 3937 ldap_pvt_runqueue_resched( &slapd_rq, cm->cc_arg, 0 ); 3938 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); 3939 } 3940 rc = 0; 3941 break; 3942 } 3943 return rc; 3944 } 3945 3946 switch( c->type ) { 3947 case PC_MAIN: 3948 if ( cm->numattrsets > 0 ) { 3949 snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive already provided" ); 3950 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 3951 return( 1 ); 3952 } 3953 3954 if ( lutil_atoi( &cm->numattrsets, c->argv[3] ) != 0 ) { 3955 snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse num attrsets=\"%s\" (arg #3)", 3956 c->argv[3] ); 3957 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 3958 return( 1 ); 3959 } 3960 if ( cm->numattrsets <= 0 ) { 3961 snprintf( c->cr_msg, sizeof( c->cr_msg ), "numattrsets (arg #3) must be positive" ); 3962 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 3963 return( 1 ); 3964 } 3965 if ( cm->numattrsets > MAX_ATTR_SETS ) { 3966 snprintf( c->cr_msg, sizeof( c->cr_msg ), "numattrsets (arg #3) must be <= %d", MAX_ATTR_SETS ); 3967 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 3968 return( 1 ); 3969 } 3970 3971 if ( !backend_db_init( c->argv[1], &cm->db, -1, NULL )) { 3972 snprintf( c->cr_msg, sizeof( c->cr_msg ), "unknown backend type (arg #1)" ); 3973 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 3974 return( 1 ); 3975 } 3976 3977 if ( lutil_atoi( &cm->max_entries, c->argv[2] ) != 0 ) { 3978 snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse max entries=\"%s\" (arg #2)", 3979 c->argv[2] ); 3980 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 3981 return( 1 ); 3982 } 3983 if ( cm->max_entries <= 0 ) { 3984 snprintf( c->cr_msg, sizeof( c->cr_msg ), "max entries (arg #2) must be positive.\n" ); 3985 Debug( LDAP_DEBUG_CONFIG, "%s: %s\n", c->log, c->cr_msg, 0 ); 3986 return( 1 ); 3987 } 3988 3989 if ( lutil_atoi( &cm->num_entries_limit, c->argv[4] ) != 0 ) { 3990 snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse entry limit=\"%s\" (arg #4)", 3991 c->argv[4] ); 3992 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 3993 return( 1 ); 3994 } 3995 if ( cm->num_entries_limit <= 0 ) { 3996 snprintf( c->cr_msg, sizeof( c->cr_msg ), "entry limit (arg #4) must be positive" ); 3997 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 3998 return( 1 ); 3999 } 4000 if ( cm->num_entries_limit > cm->max_entries ) { 4001 snprintf( c->cr_msg, sizeof( c->cr_msg ), "entry limit (arg #4) must be less than max entries %d (arg #2)", cm->max_entries ); 4002 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4003 return( 1 ); 4004 } 4005 4006 if ( lutil_parse_time( c->argv[5], &t ) != 0 ) { 4007 snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse period=\"%s\" (arg #5)", 4008 c->argv[5] ); 4009 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4010 return( 1 ); 4011 } 4012 4013 cm->cc_period = (time_t)t; 4014 Debug( pcache_debug, 4015 "Total # of attribute sets to be cached = %d.\n", 4016 cm->numattrsets, 0, 0 ); 4017 qm->attr_sets = ( struct attr_set * )ch_calloc( cm->numattrsets, 4018 sizeof( struct attr_set ) ); 4019 break; 4020 case PC_ATTR: 4021 if ( cm->numattrsets == 0 ) { 4022 snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive not provided yet" ); 4023 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4024 return( 1 ); 4025 } 4026 if ( lutil_atoi( &num, c->argv[1] ) != 0 ) { 4027 snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse attrset #=\"%s\"", 4028 c->argv[1] ); 4029 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4030 return( 1 ); 4031 } 4032 4033 if ( num < 0 || num >= cm->numattrsets ) { 4034 snprintf( c->cr_msg, sizeof( c->cr_msg ), "attrset index %d out of bounds (must be %s%d)", 4035 num, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 ); 4036 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4037 return 1; 4038 } 4039 qm->attr_sets[num].flags |= PC_CONFIGURED; 4040 if ( c->argc == 2 ) { 4041 /* assume "1.1" */ 4042 snprintf( c->cr_msg, sizeof( c->cr_msg ), 4043 "need an explicit attr in attrlist; use \"*\" to indicate all attrs" ); 4044 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4045 return 1; 4046 4047 } else if ( c->argc == 3 ) { 4048 if ( strcmp( c->argv[2], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) { 4049 qm->attr_sets[num].count = 1; 4050 qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 2, 4051 sizeof( AttributeName ) ); 4052 BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_USER_ATTRIBUTES ); 4053 break; 4054 4055 } else if ( strcmp( c->argv[2], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 ) { 4056 qm->attr_sets[num].count = 1; 4057 qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 2, 4058 sizeof( AttributeName ) ); 4059 BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES ); 4060 break; 4061 4062 } else if ( strcmp( c->argv[2], LDAP_NO_ATTRS ) == 0 ) { 4063 break; 4064 } 4065 /* else: fallthru */ 4066 4067 } else if ( c->argc == 4 ) { 4068 if ( ( strcmp( c->argv[2], LDAP_ALL_USER_ATTRIBUTES ) == 0 && strcmp( c->argv[3], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 ) 4069 || ( strcmp( c->argv[2], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 && strcmp( c->argv[3], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) ) 4070 { 4071 qm->attr_sets[num].count = 2; 4072 qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 3, 4073 sizeof( AttributeName ) ); 4074 BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_USER_ATTRIBUTES ); 4075 BER_BVSTR( &qm->attr_sets[num].attrs[1].an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES ); 4076 break; 4077 } 4078 /* else: fallthru */ 4079 } 4080 4081 if ( c->argc > 2 ) { 4082 int all_user = 0, all_op = 0; 4083 4084 qm->attr_sets[num].count = c->argc - 2; 4085 qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( c->argc - 1, 4086 sizeof( AttributeName ) ); 4087 attr_name = qm->attr_sets[num].attrs; 4088 for ( i = 2; i < c->argc; i++ ) { 4089 attr_name->an_desc = NULL; 4090 if ( strcmp( c->argv[i], LDAP_NO_ATTRS ) == 0 ) { 4091 snprintf( c->cr_msg, sizeof( c->cr_msg ), 4092 "invalid attr #%d \"%s\" in attrlist", 4093 i - 2, c->argv[i] ); 4094 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4095 ch_free( qm->attr_sets[num].attrs ); 4096 qm->attr_sets[num].attrs = NULL; 4097 qm->attr_sets[num].count = 0; 4098 return 1; 4099 } 4100 if ( strcmp( c->argv[i], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) { 4101 all_user = 1; 4102 BER_BVSTR( &attr_name->an_name, LDAP_ALL_USER_ATTRIBUTES ); 4103 } else if ( strcmp( c->argv[i], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 ) { 4104 all_op = 1; 4105 BER_BVSTR( &attr_name->an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES ); 4106 } else { 4107 if ( strncasecmp( c->argv[i], "undef:", STRLENOF("undef:") ) == 0 ) { 4108 struct berval bv; 4109 ber_str2bv( c->argv[i] + STRLENOF("undef:"), 0, 0, &bv ); 4110 attr_name->an_desc = slap_bv2tmp_ad( &bv, NULL ); 4111 4112 } else if ( slap_str2ad( c->argv[i], &attr_name->an_desc, &text ) ) { 4113 strcpy( c->cr_msg, text ); 4114 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4115 ch_free( qm->attr_sets[num].attrs ); 4116 qm->attr_sets[num].attrs = NULL; 4117 qm->attr_sets[num].count = 0; 4118 return 1; 4119 } 4120 attr_name->an_name = attr_name->an_desc->ad_cname; 4121 } 4122 attr_name->an_oc = NULL; 4123 attr_name->an_flags = 0; 4124 if ( attr_name->an_desc == slap_schema.si_ad_objectClass ) 4125 qm->attr_sets[num].flags |= PC_GOT_OC; 4126 attr_name++; 4127 BER_BVZERO( &attr_name->an_name ); 4128 } 4129 4130 /* warn if list contains both "*" and "+" */ 4131 if ( i > 4 && all_user && all_op ) { 4132 snprintf( c->cr_msg, sizeof( c->cr_msg ), 4133 "warning: attribute list contains \"*\" and \"+\"" ); 4134 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4135 } 4136 } 4137 break; 4138 case PC_TEMP: 4139 if ( cm->numattrsets == 0 ) { 4140 snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive not provided yet" ); 4141 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4142 return( 1 ); 4143 } 4144 if ( lutil_atoi( &i, c->argv[2] ) != 0 ) { 4145 snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template #=\"%s\"", 4146 c->argv[2] ); 4147 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4148 return( 1 ); 4149 } 4150 4151 if ( i < 0 || i >= cm->numattrsets || 4152 !(qm->attr_sets[i].flags & PC_CONFIGURED )) { 4153 snprintf( c->cr_msg, sizeof( c->cr_msg ), "template index %d invalid (%s%d)", 4154 i, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 ); 4155 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4156 return 1; 4157 } 4158 { 4159 AttributeName *attrs; 4160 int cnt; 4161 cnt = template_attrs( c->argv[1], &qm->attr_sets[i], &attrs, &text ); 4162 if ( cnt < 0 ) { 4163 snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template: %s", 4164 text ); 4165 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4166 return 1; 4167 } 4168 temp = ch_calloc( 1, sizeof( QueryTemplate )); 4169 temp->qmnext = qm->templates; 4170 qm->templates = temp; 4171 temp->t_attrs.attrs = attrs; 4172 temp->t_attrs.count = cnt; 4173 } 4174 ldap_pvt_thread_rdwr_init( &temp->t_rwlock ); 4175 temp->query = temp->query_last = NULL; 4176 if ( lutil_parse_time( c->argv[3], &t ) != 0 ) { 4177 snprintf( c->cr_msg, sizeof( c->cr_msg ), 4178 "unable to parse template ttl=\"%s\"", 4179 c->argv[3] ); 4180 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4181pc_temp_fail: 4182 ch_free( temp->t_attrs.attrs ); 4183 ch_free( temp ); 4184 return( 1 ); 4185 } 4186 temp->ttl = (time_t)t; 4187 temp->negttl = (time_t)0; 4188 temp->limitttl = (time_t)0; 4189 temp->ttr = (time_t)0; 4190 switch ( c->argc ) { 4191 case 7: 4192 if ( lutil_parse_time( c->argv[6], &t ) != 0 ) { 4193 snprintf( c->cr_msg, sizeof( c->cr_msg ), 4194 "unable to parse template ttr=\"%s\"", 4195 c->argv[6] ); 4196 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4197 goto pc_temp_fail; 4198 } 4199 temp->ttr = (time_t)t; 4200 /* fallthru */ 4201 4202 case 6: 4203 if ( lutil_parse_time( c->argv[5], &t ) != 0 ) { 4204 snprintf( c->cr_msg, sizeof( c->cr_msg ), 4205 "unable to parse template sizelimit ttl=\"%s\"", 4206 c->argv[5] ); 4207 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4208 goto pc_temp_fail; 4209 } 4210 temp->limitttl = (time_t)t; 4211 /* fallthru */ 4212 4213 case 5: 4214 if ( lutil_parse_time( c->argv[4], &t ) != 0 ) { 4215 snprintf( c->cr_msg, sizeof( c->cr_msg ), 4216 "unable to parse template negative ttl=\"%s\"", 4217 c->argv[4] ); 4218 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4219 goto pc_temp_fail; 4220 } 4221 temp->negttl = (time_t)t; 4222 break; 4223 } 4224 4225 temp->no_of_queries = 0; 4226 4227 ber_str2bv( c->argv[1], 0, 1, &temp->querystr ); 4228 Debug( pcache_debug, "Template:\n", 0, 0, 0 ); 4229 Debug( pcache_debug, " query template: %s\n", 4230 temp->querystr.bv_val, 0, 0 ); 4231 temp->attr_set_index = i; 4232 qm->attr_sets[i].flags |= PC_REFERENCED; 4233 temp->qtnext = qm->attr_sets[i].templates; 4234 qm->attr_sets[i].templates = temp; 4235 Debug( pcache_debug, " attributes: \n", 0, 0, 0 ); 4236 if ( ( attrarray = qm->attr_sets[i].attrs ) != NULL ) { 4237 for ( i=0; attrarray[i].an_name.bv_val; i++ ) 4238 Debug( pcache_debug, "\t%s\n", 4239 attrarray[i].an_name.bv_val, 0, 0 ); 4240 } 4241 break; 4242 case PC_BIND: 4243 if ( !qm->templates ) { 4244 snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcacheTemplate\" directive not provided yet" ); 4245 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4246 return( 1 ); 4247 } 4248 if ( lutil_atoi( &i, c->argv[2] ) != 0 ) { 4249 snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse Bind index #=\"%s\"", 4250 c->argv[2] ); 4251 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4252 return( 1 ); 4253 } 4254 4255 if ( i < 0 || i >= cm->numattrsets || 4256 !(qm->attr_sets[i].flags & PC_CONFIGURED )) { 4257 snprintf( c->cr_msg, sizeof( c->cr_msg ), "Bind index %d invalid (%s%d)", 4258 i, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 ); 4259 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4260 return 1; 4261 } 4262 { struct berval bv, tempbv; 4263 AttributeDescription **descs; 4264 int ndescs; 4265 ber_str2bv( c->argv[1], 0, 0, &bv ); 4266 ndescs = ftemp_attrs( &bv, &tempbv, &descs, &text ); 4267 if ( ndescs < 0 ) { 4268 snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template: %s", 4269 text ); 4270 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4271 return 1; 4272 } 4273 for ( temp = qm->templates; temp; temp=temp->qmnext ) { 4274 if ( temp->attr_set_index == i && bvmatch( &tempbv, 4275 &temp->querystr )) 4276 break; 4277 } 4278 ch_free( tempbv.bv_val ); 4279 if ( !temp ) { 4280 ch_free( descs ); 4281 snprintf( c->cr_msg, sizeof( c->cr_msg ), "Bind template %s %d invalid", 4282 c->argv[1], i ); 4283 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4284 return 1; 4285 } 4286 ber_dupbv( &temp->bindftemp, &bv ); 4287 temp->bindfattrs = descs; 4288 temp->bindnattrs = ndescs; 4289 } 4290 if ( lutil_parse_time( c->argv[3], &t ) != 0 ) { 4291 snprintf( c->cr_msg, sizeof( c->cr_msg ), 4292 "unable to parse bind ttr=\"%s\"", 4293 c->argv[3] ); 4294 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4295pc_bind_fail: 4296 ch_free( temp->bindfattrs ); 4297 temp->bindfattrs = NULL; 4298 ch_free( temp->bindftemp.bv_val ); 4299 BER_BVZERO( &temp->bindftemp ); 4300 return( 1 ); 4301 } 4302 num = ldap_pvt_str2scope( c->argv[4] ); 4303 if ( num < 0 ) { 4304 snprintf( c->cr_msg, sizeof( c->cr_msg ), 4305 "unable to parse bind scope=\"%s\"", 4306 c->argv[4] ); 4307 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4308 goto pc_bind_fail; 4309 } 4310 { 4311 struct berval dn, ndn; 4312 ber_str2bv( c->argv[5], 0, 0, &dn ); 4313 rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL ); 4314 if ( rc ) { 4315 snprintf( c->cr_msg, sizeof( c->cr_msg ), 4316 "invalid bind baseDN=\"%s\"", 4317 c->argv[5] ); 4318 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4319 goto pc_bind_fail; 4320 } 4321 if ( temp->bindbase.bv_val ) 4322 ch_free( temp->bindbase.bv_val ); 4323 temp->bindbase = ndn; 4324 } 4325 { 4326 /* convert the template into dummy filter */ 4327 struct berval bv; 4328 char *eq = temp->bindftemp.bv_val, *e2; 4329 Filter *f; 4330 i = 0; 4331 while ((eq = strchr(eq, '=' ))) { 4332 eq++; 4333 if ( eq[0] == ')' ) 4334 i++; 4335 } 4336 bv.bv_len = temp->bindftemp.bv_len + i; 4337 bv.bv_val = ch_malloc( bv.bv_len + 1 ); 4338 for ( e2 = bv.bv_val, eq = temp->bindftemp.bv_val; 4339 *eq; eq++ ) { 4340 if ( *eq == '=' ) { 4341 *e2++ = '='; 4342 if ( eq[1] == ')' ) 4343 *e2++ = '*'; 4344 } else { 4345 *e2++ = *eq; 4346 } 4347 } 4348 *e2 = '\0'; 4349 f = str2filter( bv.bv_val ); 4350 if ( !f ) { 4351 ch_free( bv.bv_val ); 4352 snprintf( c->cr_msg, sizeof( c->cr_msg ), 4353 "unable to parse bindfilter=\"%s\"", bv.bv_val ); 4354 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4355 ch_free( temp->bindbase.bv_val ); 4356 BER_BVZERO( &temp->bindbase ); 4357 goto pc_bind_fail; 4358 } 4359 if ( temp->bindfilter ) 4360 filter_free( temp->bindfilter ); 4361 if ( temp->bindfilterstr.bv_val ) 4362 ch_free( temp->bindfilterstr.bv_val ); 4363 temp->bindfilterstr = bv; 4364 temp->bindfilter = f; 4365 } 4366 temp->bindttr = (time_t)t; 4367 temp->bindscope = num; 4368 cm->cache_binds = 1; 4369 break; 4370 4371 case PC_RESP: 4372 if ( strcasecmp( c->argv[1], "head" ) == 0 ) { 4373 cm->response_cb = PCACHE_RESPONSE_CB_HEAD; 4374 4375 } else if ( strcasecmp( c->argv[1], "tail" ) == 0 ) { 4376 cm->response_cb = PCACHE_RESPONSE_CB_TAIL; 4377 4378 } else { 4379 snprintf( c->cr_msg, sizeof( c->cr_msg ), "unknown specifier" ); 4380 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4381 return 1; 4382 } 4383 break; 4384 case PC_QUERIES: 4385 if ( c->value_int <= 0 ) { 4386 snprintf( c->cr_msg, sizeof( c->cr_msg ), "max queries must be positive" ); 4387 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4388 return( 1 ); 4389 } 4390 cm->max_queries = c->value_int; 4391 break; 4392 case PC_OFFLINE: 4393 if ( c->value_int ) 4394 cm->cc_paused |= PCACHE_CC_OFFLINE; 4395 else 4396 cm->cc_paused &= ~PCACHE_CC_OFFLINE; 4397 break; 4398 case PC_PRIVATE_DB: 4399 if ( cm->db.be_private == NULL ) { 4400 snprintf( c->cr_msg, sizeof( c->cr_msg ), 4401 "private database must be defined before setting database specific options" ); 4402 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4403 return( 1 ); 4404 } 4405 4406 if ( cm->db.bd_info->bi_cf_ocs ) { 4407 ConfigTable *ct; 4408 ConfigArgs c2 = *c; 4409 char *argv0 = c->argv[ 0 ]; 4410 4411 c->argv[ 0 ] = &argv0[ STRLENOF( "pcache-" ) ]; 4412 4413 ct = config_find_keyword( cm->db.bd_info->bi_cf_ocs->co_table, c ); 4414 if ( ct == NULL ) { 4415 snprintf( c->cr_msg, sizeof( c->cr_msg ), 4416 "private database does not recognize specific option '%s'", 4417 c->argv[ 0 ] ); 4418 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4419 rc = 1; 4420 4421 } else { 4422 c->table = cm->db.bd_info->bi_cf_ocs->co_type; 4423 c->be = &cm->db; 4424 c->bi = c->be->bd_info; 4425 4426 rc = config_add_vals( ct, c ); 4427 4428 c->bi = c2.bi; 4429 c->be = c2.be; 4430 c->table = c2.table; 4431 } 4432 4433 c->argv[ 0 ] = argv0; 4434 4435 } else if ( cm->db.be_config != NULL ) { 4436 char *argv0 = c->argv[ 0 ]; 4437 4438 c->argv[ 0 ] = &argv0[ STRLENOF( "pcache-" ) ]; 4439 rc = cm->db.be_config( &cm->db, c->fname, c->lineno, c->argc, c->argv ); 4440 c->argv[ 0 ] = argv0; 4441 4442 } else { 4443 snprintf( c->cr_msg, sizeof( c->cr_msg ), 4444 "no means to set private database specific options" ); 4445 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); 4446 return 1; 4447 } 4448 break; 4449 default: 4450 rc = SLAP_CONF_UNKNOWN; 4451 break; 4452 } 4453 4454 return rc; 4455} 4456 4457static int 4458pcache_db_config( 4459 BackendDB *be, 4460 const char *fname, 4461 int lineno, 4462 int argc, 4463 char **argv 4464) 4465{ 4466 slap_overinst *on = (slap_overinst *)be->bd_info; 4467 cache_manager* cm = on->on_bi.bi_private; 4468 4469 /* Something for the cache database? */ 4470 if ( cm->db.bd_info && cm->db.bd_info->bi_db_config ) 4471 return cm->db.bd_info->bi_db_config( &cm->db, fname, lineno, 4472 argc, argv ); 4473 return SLAP_CONF_UNKNOWN; 4474} 4475 4476static int 4477pcache_db_init( 4478 BackendDB *be, 4479 ConfigReply *cr) 4480{ 4481 slap_overinst *on = (slap_overinst *)be->bd_info; 4482 cache_manager *cm; 4483 query_manager *qm; 4484 4485 cm = (cache_manager *)ch_malloc(sizeof(cache_manager)); 4486 on->on_bi.bi_private = cm; 4487 4488 qm = (query_manager*)ch_malloc(sizeof(query_manager)); 4489 4490 cm->db = *be; 4491 SLAP_DBFLAGS(&cm->db) |= SLAP_DBFLAG_NO_SCHEMA_CHECK; 4492 cm->db.be_private = NULL; 4493 cm->db.bd_self = &cm->db; 4494 cm->qm = qm; 4495 cm->numattrsets = 0; 4496 cm->num_entries_limit = 5; 4497 cm->num_cached_queries = 0; 4498 cm->max_entries = 0; 4499 cm->cur_entries = 0; 4500 cm->max_queries = 10000; 4501 cm->save_queries = 0; 4502 cm->check_cacheability = 0; 4503 cm->response_cb = PCACHE_RESPONSE_CB_TAIL; 4504 cm->defer_db_open = 1; 4505 cm->cache_binds = 0; 4506 cm->cc_period = 1000; 4507 cm->cc_paused = 0; 4508 cm->cc_arg = NULL; 4509#ifdef PCACHE_MONITOR 4510 cm->monitor_cb = NULL; 4511#endif /* PCACHE_MONITOR */ 4512 4513 qm->attr_sets = NULL; 4514 qm->templates = NULL; 4515 qm->lru_top = NULL; 4516 qm->lru_bottom = NULL; 4517 4518 qm->qcfunc = query_containment; 4519 qm->crfunc = cache_replacement; 4520 qm->addfunc = add_query; 4521 ldap_pvt_thread_mutex_init(&qm->lru_mutex); 4522 4523 ldap_pvt_thread_mutex_init(&cm->cache_mutex); 4524 4525#ifndef PCACHE_MONITOR 4526 return 0; 4527#else /* PCACHE_MONITOR */ 4528 return pcache_monitor_db_init( be ); 4529#endif /* PCACHE_MONITOR */ 4530} 4531 4532static int 4533pcache_cachedquery_open_cb( Operation *op, SlapReply *rs ) 4534{ 4535 assert( op->o_tag == LDAP_REQ_SEARCH ); 4536 4537 if ( rs->sr_type == REP_SEARCH ) { 4538 Attribute *a; 4539 4540 a = attr_find( rs->sr_entry->e_attrs, ad_cachedQueryURL ); 4541 if ( a != NULL ) { 4542 BerVarray *valsp; 4543 4544 assert( a->a_nvals != NULL ); 4545 4546 valsp = op->o_callback->sc_private; 4547 assert( *valsp == NULL ); 4548 4549 ber_bvarray_dup_x( valsp, a->a_nvals, op->o_tmpmemctx ); 4550 } 4551 } 4552 4553 return 0; 4554} 4555 4556static int 4557pcache_cachedquery_count_cb( Operation *op, SlapReply *rs ) 4558{ 4559 assert( op->o_tag == LDAP_REQ_SEARCH ); 4560 4561 if ( rs->sr_type == REP_SEARCH ) { 4562 int *countp = (int *)op->o_callback->sc_private; 4563 4564 (*countp)++; 4565 } 4566 4567 return 0; 4568} 4569 4570static int 4571pcache_db_open2( 4572 slap_overinst *on, 4573 ConfigReply *cr ) 4574{ 4575 cache_manager *cm = on->on_bi.bi_private; 4576 query_manager* qm = cm->qm; 4577 int rc; 4578 4579 rc = backend_startup_one( &cm->db, cr ); 4580 if ( rc == 0 ) { 4581 cm->defer_db_open = 0; 4582 } 4583 4584 /* There is no runqueue in TOOL mode */ 4585 if (( slapMode & SLAP_SERVER_MODE ) && rc == 0 ) { 4586 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); 4587 ldap_pvt_runqueue_insert( &slapd_rq, cm->cc_period, 4588 consistency_check, on, 4589 "pcache_consistency", cm->db.be_suffix[0].bv_val ); 4590 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); 4591 4592 /* Cached database must have the rootdn */ 4593 if ( BER_BVISNULL( &cm->db.be_rootndn ) 4594 || BER_BVISEMPTY( &cm->db.be_rootndn ) ) 4595 { 4596 Debug( LDAP_DEBUG_ANY, "pcache_db_open(): " 4597 "underlying database of type \"%s\"\n" 4598 " serving naming context \"%s\"\n" 4599 " has no \"rootdn\", required by \"pcache\".\n", 4600 on->on_info->oi_orig->bi_type, 4601 cm->db.be_suffix[0].bv_val, 0 ); 4602 return 1; 4603 } 4604 4605 if ( cm->save_queries ) { 4606 void *thrctx = ldap_pvt_thread_pool_context(); 4607 Connection conn = { 0 }; 4608 OperationBuffer opbuf; 4609 Operation *op; 4610 slap_callback cb = { 0 }; 4611 SlapReply rs = { REP_RESULT }; 4612 BerVarray vals = NULL; 4613 Filter f = { 0 }, f2 = { 0 }; 4614 AttributeAssertion ava = ATTRIBUTEASSERTION_INIT; 4615 AttributeName attrs[ 2 ] = {{{ 0 }}}; 4616 4617 connection_fake_init2( &conn, &opbuf, thrctx, 0 ); 4618 op = &opbuf.ob_op; 4619 4620 op->o_bd = &cm->db; 4621 4622 op->o_tag = LDAP_REQ_SEARCH; 4623 op->o_protocol = LDAP_VERSION3; 4624 cb.sc_response = pcache_cachedquery_open_cb; 4625 cb.sc_private = &vals; 4626 op->o_callback = &cb; 4627 op->o_time = slap_get_time(); 4628 op->o_do_not_cache = 1; 4629 op->o_managedsait = SLAP_CONTROL_CRITICAL; 4630 4631 op->o_dn = cm->db.be_rootdn; 4632 op->o_ndn = cm->db.be_rootndn; 4633 op->o_req_dn = cm->db.be_suffix[ 0 ]; 4634 op->o_req_ndn = cm->db.be_nsuffix[ 0 ]; 4635 4636 op->ors_scope = LDAP_SCOPE_BASE; 4637 op->ors_deref = LDAP_DEREF_NEVER; 4638 op->ors_slimit = 1; 4639 op->ors_tlimit = SLAP_NO_LIMIT; 4640 op->ors_limit = NULL; 4641 ber_str2bv( "(pcacheQueryURL=*)", 0, 0, &op->ors_filterstr ); 4642 f.f_choice = LDAP_FILTER_PRESENT; 4643 f.f_desc = ad_cachedQueryURL; 4644 op->ors_filter = &f; 4645 attrs[ 0 ].an_desc = ad_cachedQueryURL; 4646 attrs[ 0 ].an_name = ad_cachedQueryURL->ad_cname; 4647 op->ors_attrs = attrs; 4648 op->ors_attrsonly = 0; 4649 4650 rc = op->o_bd->be_search( op, &rs ); 4651 if ( rc == LDAP_SUCCESS && vals != NULL ) { 4652 int i; 4653 4654 for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) { 4655 if ( url2query( vals[ i ].bv_val, op, qm ) == 0 ) { 4656 cm->num_cached_queries++; 4657 } 4658 } 4659 4660 ber_bvarray_free_x( vals, op->o_tmpmemctx ); 4661 } 4662 4663 /* count cached entries */ 4664 f.f_choice = LDAP_FILTER_NOT; 4665 f.f_not = &f2; 4666 f2.f_choice = LDAP_FILTER_EQUALITY; 4667 f2.f_ava = &ava; 4668 f2.f_av_desc = slap_schema.si_ad_objectClass; 4669 BER_BVSTR( &f2.f_av_value, "glue" ); 4670 ber_str2bv( "(!(objectClass=glue))", 0, 0, &op->ors_filterstr ); 4671 4672 op->ors_slimit = SLAP_NO_LIMIT; 4673 op->ors_scope = LDAP_SCOPE_SUBTREE; 4674 op->ors_attrs = slap_anlist_no_attrs; 4675 4676 rs_reinit( &rs, REP_RESULT ); 4677 op->o_callback->sc_response = pcache_cachedquery_count_cb; 4678 op->o_callback->sc_private = &rs.sr_nentries; 4679 4680 rc = op->o_bd->be_search( op, &rs ); 4681 4682 cm->cur_entries = rs.sr_nentries; 4683 4684 /* ignore errors */ 4685 rc = 0; 4686 } 4687 } 4688 return rc; 4689} 4690 4691static int 4692pcache_db_open( 4693 BackendDB *be, 4694 ConfigReply *cr ) 4695{ 4696 slap_overinst *on = (slap_overinst *)be->bd_info; 4697 cache_manager *cm = on->on_bi.bi_private; 4698 query_manager* qm = cm->qm; 4699 int i, ncf = 0, rf = 0, nrf = 0, rc = 0; 4700 4701 /* check attr sets */ 4702 for ( i = 0; i < cm->numattrsets; i++) { 4703 if ( !( qm->attr_sets[i].flags & PC_CONFIGURED ) ) { 4704 if ( qm->attr_sets[i].flags & PC_REFERENCED ) { 4705 Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d not configured but referenced.\n", i, 0, 0 ); 4706 rf++; 4707 4708 } else { 4709 Debug( LDAP_DEBUG_CONFIG, "pcache: warning, attr set #%d not configured.\n", i, 0, 0 ); 4710 } 4711 ncf++; 4712 4713 } else if ( !( qm->attr_sets[i].flags & PC_REFERENCED ) ) { 4714 Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d configured but not referenced.\n", i, 0, 0 ); 4715 nrf++; 4716 } 4717 } 4718 4719 if ( ncf || rf || nrf ) { 4720 Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets configured but not referenced.\n", nrf, 0, 0 ); 4721 Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets not configured.\n", ncf, 0, 0 ); 4722 Debug( LDAP_DEBUG_CONFIG, "pcache: %d attr sets not configured but referenced.\n", rf, 0, 0 ); 4723 4724 if ( rf > 0 ) { 4725 return 1; 4726 } 4727 } 4728 4729 /* need to inherit something from the original database... */ 4730 cm->db.be_def_limit = be->be_def_limit; 4731 cm->db.be_limits = be->be_limits; 4732 cm->db.be_acl = be->be_acl; 4733 cm->db.be_dfltaccess = be->be_dfltaccess; 4734 4735 if ( SLAP_DBMONITORING( be ) ) { 4736 SLAP_DBFLAGS( &cm->db ) |= SLAP_DBFLAG_MONITORING; 4737 4738 } else { 4739 SLAP_DBFLAGS( &cm->db ) &= ~SLAP_DBFLAG_MONITORING; 4740 } 4741 4742 if ( !cm->defer_db_open ) { 4743 rc = pcache_db_open2( on, cr ); 4744 } 4745 4746#ifdef PCACHE_MONITOR 4747 if ( rc == LDAP_SUCCESS ) { 4748 rc = pcache_monitor_db_open( be ); 4749 } 4750#endif /* PCACHE_MONITOR */ 4751 4752 return rc; 4753} 4754 4755static void 4756pcache_free_qbase( void *v ) 4757{ 4758 Qbase *qb = v; 4759 int i; 4760 4761 for (i=0; i<3; i++) 4762 tavl_free( qb->scopes[i], NULL ); 4763 ch_free( qb ); 4764} 4765 4766static int 4767pcache_db_close( 4768 BackendDB *be, 4769 ConfigReply *cr 4770) 4771{ 4772 slap_overinst *on = (slap_overinst *)be->bd_info; 4773 cache_manager *cm = on->on_bi.bi_private; 4774 query_manager *qm = cm->qm; 4775 QueryTemplate *tm; 4776 int i, rc = 0; 4777 4778 /* stop the thread ... */ 4779 if ( cm->cc_arg ) { 4780 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); 4781 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, cm->cc_arg ) ) { 4782 ldap_pvt_runqueue_stoptask( &slapd_rq, cm->cc_arg ); 4783 } 4784 ldap_pvt_runqueue_remove( &slapd_rq, cm->cc_arg ); 4785 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); 4786 } 4787 4788 if ( cm->save_queries ) { 4789 CachedQuery *qc; 4790 BerVarray vals = NULL; 4791 4792 void *thrctx; 4793 Connection conn = { 0 }; 4794 OperationBuffer opbuf; 4795 Operation *op; 4796 slap_callback cb = { 0 }; 4797 4798 SlapReply rs = { REP_RESULT }; 4799 Modifications mod = {{ 0 }}; 4800 4801 thrctx = ldap_pvt_thread_pool_context(); 4802 4803 connection_fake_init2( &conn, &opbuf, thrctx, 0 ); 4804 op = &opbuf.ob_op; 4805 4806 mod.sml_numvals = 0; 4807 if ( qm->templates != NULL ) { 4808 for ( tm = qm->templates; tm != NULL; tm = tm->qmnext ) { 4809 for ( qc = tm->query; qc; qc = qc->next ) { 4810 struct berval bv; 4811 4812 if ( query2url( op, qc, &bv, 0 ) == 0 ) { 4813 ber_bvarray_add_x( &vals, &bv, op->o_tmpmemctx ); 4814 mod.sml_numvals++; 4815 } 4816 } 4817 } 4818 } 4819 4820 op->o_bd = &cm->db; 4821 op->o_dn = cm->db.be_rootdn; 4822 op->o_ndn = cm->db.be_rootndn; 4823 4824 op->o_tag = LDAP_REQ_MODIFY; 4825 op->o_protocol = LDAP_VERSION3; 4826 cb.sc_response = slap_null_cb; 4827 op->o_callback = &cb; 4828 op->o_time = slap_get_time(); 4829 op->o_do_not_cache = 1; 4830 op->o_managedsait = SLAP_CONTROL_CRITICAL; 4831 4832 op->o_req_dn = op->o_bd->be_suffix[0]; 4833 op->o_req_ndn = op->o_bd->be_nsuffix[0]; 4834 4835 mod.sml_op = LDAP_MOD_REPLACE; 4836 mod.sml_flags = 0; 4837 mod.sml_desc = ad_cachedQueryURL; 4838 mod.sml_type = ad_cachedQueryURL->ad_cname; 4839 mod.sml_values = vals; 4840 mod.sml_nvalues = NULL; 4841 mod.sml_next = NULL; 4842 Debug( pcache_debug, 4843 "%sSETTING CACHED QUERY URLS\n", 4844 vals == NULL ? "RE" : "", 0, 0 ); 4845 4846 op->orm_modlist = &mod; 4847 4848 op->o_bd->be_modify( op, &rs ); 4849 4850 ber_bvarray_free_x( vals, op->o_tmpmemctx ); 4851 } 4852 4853 /* cleanup stuff inherited from the original database... */ 4854 cm->db.be_limits = NULL; 4855 cm->db.be_acl = NULL; 4856 4857 4858 if ( cm->db.bd_info->bi_db_close ) { 4859 rc = cm->db.bd_info->bi_db_close( &cm->db, NULL ); 4860 } 4861 while ( (tm = qm->templates) != NULL ) { 4862 CachedQuery *qc, *qn; 4863 qm->templates = tm->qmnext; 4864 for ( qc = tm->query; qc; qc = qn ) { 4865 qn = qc->next; 4866 free_query( qc ); 4867 } 4868 avl_free( tm->qbase, pcache_free_qbase ); 4869 free( tm->querystr.bv_val ); 4870 free( tm->bindfattrs ); 4871 free( tm->bindftemp.bv_val ); 4872 free( tm->bindfilterstr.bv_val ); 4873 free( tm->bindbase.bv_val ); 4874 filter_free( tm->bindfilter ); 4875 ldap_pvt_thread_rdwr_destroy( &tm->t_rwlock ); 4876 free( tm->t_attrs.attrs ); 4877 free( tm ); 4878 } 4879 4880 for ( i = 0; i < cm->numattrsets; i++ ) { 4881 int j; 4882 4883 /* Account of LDAP_NO_ATTRS */ 4884 if ( !qm->attr_sets[i].count ) continue; 4885 4886 for ( j = 0; !BER_BVISNULL( &qm->attr_sets[i].attrs[j].an_name ); j++ ) { 4887 if ( qm->attr_sets[i].attrs[j].an_desc && 4888 ( qm->attr_sets[i].attrs[j].an_desc->ad_flags & 4889 SLAP_DESC_TEMPORARY ) ) { 4890 slap_sl_mfuncs.bmf_free( qm->attr_sets[i].attrs[j].an_desc, NULL ); 4891 } 4892 } 4893 free( qm->attr_sets[i].attrs ); 4894 } 4895 free( qm->attr_sets ); 4896 qm->attr_sets = NULL; 4897 4898#ifdef PCACHE_MONITOR 4899 if ( rc == LDAP_SUCCESS ) { 4900 rc = pcache_monitor_db_close( be ); 4901 } 4902#endif /* PCACHE_MONITOR */ 4903 4904 return rc; 4905} 4906 4907static int 4908pcache_db_destroy( 4909 BackendDB *be, 4910 ConfigReply *cr 4911) 4912{ 4913 slap_overinst *on = (slap_overinst *)be->bd_info; 4914 cache_manager *cm = on->on_bi.bi_private; 4915 query_manager *qm = cm->qm; 4916 4917 if ( cm->db.be_private != NULL ) { 4918 backend_stopdown_one( &cm->db ); 4919 } 4920 4921 ldap_pvt_thread_mutex_destroy( &qm->lru_mutex ); 4922 ldap_pvt_thread_mutex_destroy( &cm->cache_mutex ); 4923 free( qm ); 4924 free( cm ); 4925 4926#ifdef PCACHE_MONITOR 4927 pcache_monitor_db_destroy( be ); 4928#endif /* PCACHE_MONITOR */ 4929 4930 return 0; 4931} 4932 4933#ifdef PCACHE_CONTROL_PRIVDB 4934/* 4935 Control ::= SEQUENCE { 4936 controlType LDAPOID, 4937 criticality BOOLEAN DEFAULT FALSE, 4938 controlValue OCTET STRING OPTIONAL } 4939 4940 controlType ::= 1.3.6.1.4.1.4203.666.11.9.5.1 4941 4942 * criticality must be TRUE; controlValue must be absent. 4943 */ 4944static int 4945parse_privdb_ctrl( 4946 Operation *op, 4947 SlapReply *rs, 4948 LDAPControl *ctrl ) 4949{ 4950 if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_NONE ) { 4951 rs->sr_text = "privateDB control specified multiple times"; 4952 return LDAP_PROTOCOL_ERROR; 4953 } 4954 4955 if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) { 4956 rs->sr_text = "privateDB control value not absent"; 4957 return LDAP_PROTOCOL_ERROR; 4958 } 4959 4960 if ( !ctrl->ldctl_iscritical ) { 4961 rs->sr_text = "privateDB control criticality required"; 4962 return LDAP_PROTOCOL_ERROR; 4963 } 4964 4965 op->o_ctrlflag[ privDB_cid ] = SLAP_CONTROL_CRITICAL; 4966 4967 return LDAP_SUCCESS; 4968} 4969 4970static char *extops[] = { 4971 LDAP_EXOP_MODIFY_PASSWD, 4972 NULL 4973}; 4974#endif /* PCACHE_CONTROL_PRIVDB */ 4975 4976static struct berval pcache_exop_MODIFY_PASSWD = BER_BVC( LDAP_EXOP_MODIFY_PASSWD ); 4977#ifdef PCACHE_EXOP_QUERY_DELETE 4978static struct berval pcache_exop_QUERY_DELETE = BER_BVC( PCACHE_EXOP_QUERY_DELETE ); 4979 4980#define LDAP_TAG_EXOP_QUERY_DELETE_BASE ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 0) 4981#define LDAP_TAG_EXOP_QUERY_DELETE_DN ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 1) 4982#define LDAP_TAG_EXOP_QUERY_DELETE_UUID ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 2) 4983 4984/* 4985 ExtendedRequest ::= [APPLICATION 23] SEQUENCE { 4986 requestName [0] LDAPOID, 4987 requestValue [1] OCTET STRING OPTIONAL } 4988 4989 requestName ::= 1.3.6.1.4.1.4203.666.11.9.6.1 4990 4991 requestValue ::= SEQUENCE { CHOICE { 4992 baseDN [0] LDAPDN 4993 entryDN [1] LDAPDN }, 4994 queryID [2] OCTET STRING (SIZE(16)) 4995 -- constrained to UUID } 4996 4997 * Either baseDN or entryDN must be present, to allow database selection. 4998 * 4999 * 1. if baseDN and queryID are present, then the query corresponding 5000 * to queryID is deleted; 5001 * 2. if baseDN is present and queryID is absent, then all queries 5002 * are deleted; 5003 * 3. if entryDN is present and queryID is absent, then all queries 5004 * corresponding to the queryID values present in entryDN are deleted; 5005 * 4. if entryDN and queryID are present, then all queries 5006 * corresponding to the queryID values present in entryDN are deleted, 5007 * but only if the value of queryID is contained in the entry; 5008 * 5009 * Currently, only 1, 3 and 4 are implemented. 2 can be obtained by either 5010 * recursively deleting the database (ldapdelete -r) with PRIVDB control, 5011 * or by removing the database files. 5012 5013 ExtendedResponse ::= [APPLICATION 24] SEQUENCE { 5014 COMPONENTS OF LDAPResult, 5015 responseName [10] LDAPOID OPTIONAL, 5016 responseValue [11] OCTET STRING OPTIONAL } 5017 5018 * responseName and responseValue must be absent. 5019 */ 5020 5021/* 5022 * - on success, *tagp is either LDAP_TAG_EXOP_QUERY_DELETE_BASE 5023 * or LDAP_TAG_EXOP_QUERY_DELETE_DN. 5024 * - if ndn != NULL, it is set to the normalized DN in the request 5025 * corresponding to either the baseDN or the entryDN, according 5026 * to *tagp; memory is malloc'ed on the Operation's slab, and must 5027 * be freed by the caller. 5028 * - if uuid != NULL, it is set to point to the normalized UUID; 5029 * memory is malloc'ed on the Operation's slab, and must 5030 * be freed by the caller. 5031 */ 5032static int 5033pcache_parse_query_delete( 5034 struct berval *in, 5035 ber_tag_t *tagp, 5036 struct berval *ndn, 5037 struct berval *uuid, 5038 const char **text, 5039 void *ctx ) 5040{ 5041 int rc = LDAP_SUCCESS; 5042 ber_tag_t tag; 5043 ber_len_t len = -1; 5044 BerElementBuffer berbuf; 5045 BerElement *ber = (BerElement *)&berbuf; 5046 struct berval reqdata = BER_BVNULL; 5047 5048 *text = NULL; 5049 5050 if ( ndn ) { 5051 BER_BVZERO( ndn ); 5052 } 5053 5054 if ( uuid ) { 5055 BER_BVZERO( uuid ); 5056 } 5057 5058 if ( in == NULL || in->bv_len == 0 ) { 5059 *text = "empty request data field in queryDelete exop"; 5060 return LDAP_PROTOCOL_ERROR; 5061 } 5062 5063 ber_dupbv_x( &reqdata, in, ctx ); 5064 5065 /* ber_init2 uses reqdata directly, doesn't allocate new buffers */ 5066 ber_init2( ber, &reqdata, 0 ); 5067 5068 tag = ber_scanf( ber, "{" /*}*/ ); 5069 5070 if ( tag == LBER_ERROR ) { 5071 Debug( LDAP_DEBUG_TRACE, 5072 "pcache_parse_query_delete: decoding error.\n", 5073 0, 0, 0 ); 5074 goto decoding_error; 5075 } 5076 5077 tag = ber_peek_tag( ber, &len ); 5078 if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_BASE 5079 || tag == LDAP_TAG_EXOP_QUERY_DELETE_DN ) 5080 { 5081 *tagp = tag; 5082 5083 if ( ndn != NULL ) { 5084 struct berval dn; 5085 5086 tag = ber_scanf( ber, "m", &dn ); 5087 if ( tag == LBER_ERROR ) { 5088 Debug( LDAP_DEBUG_TRACE, 5089 "pcache_parse_query_delete: DN parse failed.\n", 5090 0, 0, 0 ); 5091 goto decoding_error; 5092 } 5093 5094 rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx ); 5095 if ( rc != LDAP_SUCCESS ) { 5096 *text = "invalid DN in queryDelete exop request data"; 5097 goto done; 5098 } 5099 5100 } else { 5101 tag = ber_scanf( ber, "x" /* "m" */ ); 5102 if ( tag == LBER_DEFAULT ) { 5103 goto decoding_error; 5104 } 5105 } 5106 5107 tag = ber_peek_tag( ber, &len ); 5108 } 5109 5110 if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_UUID ) { 5111 if ( uuid != NULL ) { 5112 struct berval bv; 5113 char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ]; 5114 5115 tag = ber_scanf( ber, "m", &bv ); 5116 if ( tag == LBER_ERROR ) { 5117 Debug( LDAP_DEBUG_TRACE, 5118 "pcache_parse_query_delete: UUID parse failed.\n", 5119 0, 0, 0 ); 5120 goto decoding_error; 5121 } 5122 5123 if ( bv.bv_len != 16 ) { 5124 Debug( LDAP_DEBUG_TRACE, 5125 "pcache_parse_query_delete: invalid UUID length %lu.\n", 5126 (unsigned long)bv.bv_len, 0, 0 ); 5127 goto decoding_error; 5128 } 5129 5130 rc = lutil_uuidstr_from_normalized( 5131 bv.bv_val, bv.bv_len, 5132 uuidbuf, sizeof( uuidbuf ) ); 5133 if ( rc == -1 ) { 5134 goto decoding_error; 5135 } 5136 ber_str2bv( uuidbuf, rc, 1, uuid ); 5137 rc = LDAP_SUCCESS; 5138 5139 } else { 5140 tag = ber_skip_tag( ber, &len ); 5141 if ( tag == LBER_DEFAULT ) { 5142 goto decoding_error; 5143 } 5144 5145 if ( len != 16 ) { 5146 Debug( LDAP_DEBUG_TRACE, 5147 "pcache_parse_query_delete: invalid UUID length %lu.\n", 5148 (unsigned long)len, 0, 0 ); 5149 goto decoding_error; 5150 } 5151 } 5152 5153 tag = ber_peek_tag( ber, &len ); 5154 } 5155 5156 if ( tag != LBER_DEFAULT || len != 0 ) { 5157decoding_error:; 5158 Debug( LDAP_DEBUG_TRACE, 5159 "pcache_parse_query_delete: decoding error\n", 5160 0, 0, 0 ); 5161 rc = LDAP_PROTOCOL_ERROR; 5162 *text = "queryDelete data decoding error"; 5163 5164done:; 5165 if ( ndn && !BER_BVISNULL( ndn ) ) { 5166 slap_sl_free( ndn->bv_val, ctx ); 5167 BER_BVZERO( ndn ); 5168 } 5169 5170 if ( uuid && !BER_BVISNULL( uuid ) ) { 5171 slap_sl_free( uuid->bv_val, ctx ); 5172 BER_BVZERO( uuid ); 5173 } 5174 } 5175 5176 if ( !BER_BVISNULL( &reqdata ) ) { 5177 ber_memfree_x( reqdata.bv_val, ctx ); 5178 } 5179 5180 return rc; 5181} 5182 5183static int 5184pcache_exop_query_delete( 5185 Operation *op, 5186 SlapReply *rs ) 5187{ 5188 BackendDB *bd = op->o_bd; 5189 5190 struct berval uuid = BER_BVNULL, 5191 *uuidp = NULL; 5192 char buf[ SLAP_TEXT_BUFLEN ]; 5193 unsigned len; 5194 ber_tag_t tag = LBER_DEFAULT; 5195 5196 if ( LogTest( LDAP_DEBUG_STATS ) ) { 5197 uuidp = &uuid; 5198 } 5199 5200 rs->sr_err = pcache_parse_query_delete( op->ore_reqdata, 5201 &tag, &op->o_req_ndn, uuidp, 5202 &rs->sr_text, op->o_tmpmemctx ); 5203 if ( rs->sr_err != LDAP_SUCCESS ) { 5204 return rs->sr_err; 5205 } 5206 5207 if ( LogTest( LDAP_DEBUG_STATS ) ) { 5208 assert( !BER_BVISNULL( &op->o_req_ndn ) ); 5209 len = snprintf( buf, sizeof( buf ), " dn=\"%s\"", op->o_req_ndn.bv_val ); 5210 5211 if ( !BER_BVISNULL( &uuid ) && len < sizeof( buf ) ) { 5212 snprintf( &buf[ len ], sizeof( buf ) - len, " pcacheQueryId=\"%s\"", uuid.bv_val ); 5213 } 5214 5215 Debug( LDAP_DEBUG_STATS, "%s QUERY DELETE%s\n", 5216 op->o_log_prefix, buf, 0 ); 5217 } 5218 op->o_req_dn = op->o_req_ndn; 5219 5220 op->o_bd = select_backend( &op->o_req_ndn, 0 ); 5221 if ( op->o_bd == NULL ) { 5222 send_ldap_error( op, rs, LDAP_NO_SUCH_OBJECT, 5223 "no global superior knowledge" ); 5224 } 5225 rs->sr_err = backend_check_restrictions( op, rs, 5226 (struct berval *)&pcache_exop_QUERY_DELETE ); 5227 if ( rs->sr_err != LDAP_SUCCESS ) { 5228 goto done; 5229 } 5230 5231 if ( op->o_bd->be_extended == NULL ) { 5232 send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION, 5233 "backend does not support extended operations" ); 5234 goto done; 5235 } 5236 5237 op->o_bd->be_extended( op, rs ); 5238 5239done:; 5240 if ( !BER_BVISNULL( &op->o_req_ndn ) ) { 5241 op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx ); 5242 BER_BVZERO( &op->o_req_ndn ); 5243 BER_BVZERO( &op->o_req_dn ); 5244 } 5245 5246 if ( !BER_BVISNULL( &uuid ) ) { 5247 op->o_tmpfree( uuid.bv_val, op->o_tmpmemctx ); 5248 } 5249 5250 op->o_bd = bd; 5251 5252 return rs->sr_err; 5253} 5254#endif /* PCACHE_EXOP_QUERY_DELETE */ 5255 5256static int 5257pcache_op_extended( Operation *op, SlapReply *rs ) 5258{ 5259 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 5260 cache_manager *cm = on->on_bi.bi_private; 5261 5262#ifdef PCACHE_CONTROL_PRIVDB 5263 if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) { 5264 return pcache_op_privdb( op, rs ); 5265 } 5266#endif /* PCACHE_CONTROL_PRIVDB */ 5267 5268#ifdef PCACHE_EXOP_QUERY_DELETE 5269 if ( bvmatch( &op->ore_reqoid, &pcache_exop_QUERY_DELETE ) ) { 5270 struct berval uuid = BER_BVNULL; 5271 ber_tag_t tag = LBER_DEFAULT; 5272 5273 rs->sr_err = pcache_parse_query_delete( op->ore_reqdata, 5274 &tag, NULL, &uuid, &rs->sr_text, op->o_tmpmemctx ); 5275 assert( rs->sr_err == LDAP_SUCCESS ); 5276 5277 if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_DN ) { 5278 /* remove all queries related to the selected entry */ 5279 rs->sr_err = pcache_remove_entry_queries_from_cache( op, 5280 cm, &op->o_req_ndn, &uuid ); 5281 5282 } else if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_BASE ) { 5283 if ( !BER_BVISNULL( &uuid ) ) { 5284 /* remove the selected query */ 5285 rs->sr_err = pcache_remove_query_from_cache( op, 5286 cm, &uuid ); 5287 5288 } else { 5289 /* TODO: remove all queries */ 5290 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 5291 rs->sr_text = "deletion of all queries not implemented"; 5292 } 5293 } 5294 5295 op->o_tmpfree( uuid.bv_val, op->o_tmpmemctx ); 5296 return rs->sr_err; 5297 } 5298#endif /* PCACHE_EXOP_QUERY_DELETE */ 5299 5300 /* We only care if we're configured for Bind caching */ 5301 if ( bvmatch( &op->ore_reqoid, &pcache_exop_MODIFY_PASSWD ) && 5302 cm->cache_binds ) { 5303 /* See if the local entry exists and has a password. 5304 * It's too much work to find the matching query, so 5305 * we just see if there's a hashed password to update. 5306 */ 5307 Operation op2 = *op; 5308 Entry *e = NULL; 5309 int rc; 5310 int doit = 0; 5311 5312 op2.o_bd = &cm->db; 5313 op2.o_dn = op->o_bd->be_rootdn; 5314 op2.o_ndn = op->o_bd->be_rootndn; 5315 rc = be_entry_get_rw( &op2, &op->o_req_ndn, NULL, 5316 slap_schema.si_ad_userPassword, 0, &e ); 5317 if ( rc == LDAP_SUCCESS && e ) { 5318 /* See if a recognized password is hashed here */ 5319 Attribute *a = attr_find( e->e_attrs, 5320 slap_schema.si_ad_userPassword ); 5321 if ( a && a->a_vals[0].bv_val[0] == '{' && 5322 lutil_passwd_scheme( a->a_vals[0].bv_val )) { 5323 doit = 1; 5324 } 5325 be_entry_release_r( &op2, e ); 5326 } 5327 5328 if ( doit ) { 5329 rc = overlay_op_walk( op, rs, op_extended, on->on_info, 5330 on->on_next ); 5331 if ( rc == LDAP_SUCCESS ) { 5332 req_pwdexop_s *qpw = &op->oq_pwdexop; 5333 5334 /* We don't care if it succeeds or not */ 5335 pc_setpw( &op2, &qpw->rs_new, cm ); 5336 } 5337 return rc; 5338 } 5339 } 5340 return SLAP_CB_CONTINUE; 5341} 5342 5343static int 5344pcache_entry_release( Operation *op, Entry *e, int rw ) 5345{ 5346 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 5347 cache_manager *cm = on->on_bi.bi_private; 5348 BackendDB *db = op->o_bd; 5349 int rc; 5350 5351 op->o_bd = &cm->db; 5352 rc = be_entry_release_rw( op, e, rw ); 5353 op->o_bd = db; 5354 return rc; 5355} 5356 5357#ifdef PCACHE_MONITOR 5358 5359static int 5360pcache_monitor_update( 5361 Operation *op, 5362 SlapReply *rs, 5363 Entry *e, 5364 void *priv ) 5365{ 5366 cache_manager *cm = (cache_manager *) priv; 5367 query_manager *qm = cm->qm; 5368 5369 CachedQuery *qc; 5370 BerVarray vals = NULL; 5371 5372 attr_delete( &e->e_attrs, ad_cachedQueryURL ); 5373 if ( ( SLAP_OPATTRS( rs->sr_attr_flags ) || ad_inlist( ad_cachedQueryURL, rs->sr_attrs ) ) 5374 && qm->templates != NULL ) 5375 { 5376 QueryTemplate *tm; 5377 5378 for ( tm = qm->templates; tm != NULL; tm = tm->qmnext ) { 5379 for ( qc = tm->query; qc; qc = qc->next ) { 5380 struct berval bv; 5381 5382 if ( query2url( op, qc, &bv, 1 ) == 0 ) { 5383 ber_bvarray_add_x( &vals, &bv, op->o_tmpmemctx ); 5384 } 5385 } 5386 } 5387 5388 5389 if ( vals != NULL ) { 5390 attr_merge_normalize( e, ad_cachedQueryURL, vals, NULL ); 5391 ber_bvarray_free_x( vals, op->o_tmpmemctx ); 5392 } 5393 } 5394 5395 { 5396 Attribute *a; 5397 char buf[ SLAP_TEXT_BUFLEN ]; 5398 struct berval bv; 5399 5400 /* number of cached queries */ 5401 a = attr_find( e->e_attrs, ad_numQueries ); 5402 assert( a != NULL ); 5403 5404 bv.bv_val = buf; 5405 bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", cm->num_cached_queries ); 5406 5407 if ( a->a_nvals != a->a_vals ) { 5408 ber_bvreplace( &a->a_nvals[ 0 ], &bv ); 5409 } 5410 ber_bvreplace( &a->a_vals[ 0 ], &bv ); 5411 5412 /* number of cached entries */ 5413 a = attr_find( e->e_attrs, ad_numEntries ); 5414 assert( a != NULL ); 5415 5416 bv.bv_val = buf; 5417 bv.bv_len = snprintf( buf, sizeof( buf ), "%d", cm->cur_entries ); 5418 5419 if ( a->a_nvals != a->a_vals ) { 5420 ber_bvreplace( &a->a_nvals[ 0 ], &bv ); 5421 } 5422 ber_bvreplace( &a->a_vals[ 0 ], &bv ); 5423 } 5424 5425 return SLAP_CB_CONTINUE; 5426} 5427 5428static int 5429pcache_monitor_free( 5430 Entry *e, 5431 void **priv ) 5432{ 5433 struct berval values[ 2 ]; 5434 Modification mod = { 0 }; 5435 5436 const char *text; 5437 char textbuf[ SLAP_TEXT_BUFLEN ]; 5438 5439 int rc; 5440 5441 /* NOTE: if slap_shutdown != 0, priv might have already been freed */ 5442 *priv = NULL; 5443 5444 /* Remove objectClass */ 5445 mod.sm_op = LDAP_MOD_DELETE; 5446 mod.sm_desc = slap_schema.si_ad_objectClass; 5447 mod.sm_values = values; 5448 mod.sm_numvals = 1; 5449 values[ 0 ] = oc_olmPCache->soc_cname; 5450 BER_BVZERO( &values[ 1 ] ); 5451 5452 rc = modify_delete_values( e, &mod, 1, &text, 5453 textbuf, sizeof( textbuf ) ); 5454 /* don't care too much about return code... */ 5455 5456 /* remove attrs */ 5457 mod.sm_values = NULL; 5458 mod.sm_desc = ad_cachedQueryURL; 5459 mod.sm_numvals = 0; 5460 rc = modify_delete_values( e, &mod, 1, &text, 5461 textbuf, sizeof( textbuf ) ); 5462 /* don't care too much about return code... */ 5463 5464 /* remove attrs */ 5465 mod.sm_values = NULL; 5466 mod.sm_desc = ad_numQueries; 5467 mod.sm_numvals = 0; 5468 rc = modify_delete_values( e, &mod, 1, &text, 5469 textbuf, sizeof( textbuf ) ); 5470 /* don't care too much about return code... */ 5471 5472 /* remove attrs */ 5473 mod.sm_values = NULL; 5474 mod.sm_desc = ad_numEntries; 5475 mod.sm_numvals = 0; 5476 rc = modify_delete_values( e, &mod, 1, &text, 5477 textbuf, sizeof( textbuf ) ); 5478 /* don't care too much about return code... */ 5479 5480 return SLAP_CB_CONTINUE; 5481} 5482 5483/* 5484 * call from within pcache_initialize() 5485 */ 5486static int 5487pcache_monitor_initialize( void ) 5488{ 5489 static int pcache_monitor_initialized = 0; 5490 5491 if ( backend_info( "monitor" ) == NULL ) { 5492 return -1; 5493 } 5494 5495 if ( pcache_monitor_initialized++ ) { 5496 return 0; 5497 } 5498 5499 return 0; 5500} 5501 5502static int 5503pcache_monitor_db_init( BackendDB *be ) 5504{ 5505 if ( pcache_monitor_initialize() == LDAP_SUCCESS ) { 5506 SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_MONITORING; 5507 } 5508 5509 return 0; 5510} 5511 5512static int 5513pcache_monitor_db_open( BackendDB *be ) 5514{ 5515 slap_overinst *on = (slap_overinst *)be->bd_info; 5516 cache_manager *cm = on->on_bi.bi_private; 5517 Attribute *a, *next; 5518 monitor_callback_t *cb = NULL; 5519 int rc = 0; 5520 BackendInfo *mi; 5521 monitor_extra_t *mbe; 5522 struct berval dummy = BER_BVC( "" ); 5523 5524 if ( !SLAP_DBMONITORING( be ) ) { 5525 return 0; 5526 } 5527 5528 mi = backend_info( "monitor" ); 5529 if ( !mi || !mi->bi_extra ) { 5530 SLAP_DBFLAGS( be ) ^= SLAP_DBFLAG_MONITORING; 5531 return 0; 5532 } 5533 mbe = mi->bi_extra; 5534 5535 /* don't bother if monitor is not configured */ 5536 if ( !mbe->is_configured() ) { 5537 static int warning = 0; 5538 5539 if ( warning++ == 0 ) { 5540 Debug( LDAP_DEBUG_ANY, "pcache_monitor_db_open: " 5541 "monitoring disabled; " 5542 "configure monitor database to enable\n", 5543 0, 0, 0 ); 5544 } 5545 5546 return 0; 5547 } 5548 5549 /* alloc as many as required (plus 1 for objectClass) */ 5550 a = attrs_alloc( 1 + 2 ); 5551 if ( a == NULL ) { 5552 rc = 1; 5553 goto cleanup; 5554 } 5555 5556 a->a_desc = slap_schema.si_ad_objectClass; 5557 attr_valadd( a, &oc_olmPCache->soc_cname, NULL, 1 ); 5558 next = a->a_next; 5559 5560 { 5561 struct berval bv = BER_BVC( "0" ); 5562 5563 next->a_desc = ad_numQueries; 5564 attr_valadd( next, &bv, NULL, 1 ); 5565 next = next->a_next; 5566 5567 next->a_desc = ad_numEntries; 5568 attr_valadd( next, &bv, NULL, 1 ); 5569 next = next->a_next; 5570 } 5571 5572 cb = ch_calloc( sizeof( monitor_callback_t ), 1 ); 5573 cb->mc_update = pcache_monitor_update; 5574 cb->mc_free = pcache_monitor_free; 5575 cb->mc_private = (void *)cm; 5576 5577 /* make sure the database is registered; then add monitor attributes */ 5578 BER_BVZERO( &cm->monitor_ndn ); 5579 rc = mbe->register_overlay( be, on, &cm->monitor_ndn ); 5580 if ( rc == 0 ) { 5581 rc = mbe->register_entry_attrs( &cm->monitor_ndn, a, cb, 5582 &dummy, -1, &dummy); 5583 } 5584 5585cleanup:; 5586 if ( rc != 0 ) { 5587 if ( cb != NULL ) { 5588 ch_free( cb ); 5589 cb = NULL; 5590 } 5591 5592 if ( a != NULL ) { 5593 attrs_free( a ); 5594 a = NULL; 5595 } 5596 } 5597 5598 /* store for cleanup */ 5599 cm->monitor_cb = (void *)cb; 5600 5601 /* we don't need to keep track of the attributes, because 5602 * bdb_monitor_free() takes care of everything */ 5603 if ( a != NULL ) { 5604 attrs_free( a ); 5605 } 5606 5607 return rc; 5608} 5609 5610static int 5611pcache_monitor_db_close( BackendDB *be ) 5612{ 5613 slap_overinst *on = (slap_overinst *)be->bd_info; 5614 cache_manager *cm = on->on_bi.bi_private; 5615 5616 if ( cm->monitor_cb != NULL ) { 5617 BackendInfo *mi = backend_info( "monitor" ); 5618 monitor_extra_t *mbe; 5619 5620 if ( mi && &mi->bi_extra ) { 5621 mbe = mi->bi_extra; 5622 mbe->unregister_entry_callback( &cm->monitor_ndn, 5623 (monitor_callback_t *)cm->monitor_cb, 5624 NULL, 0, NULL ); 5625 } 5626 } 5627 5628 return 0; 5629} 5630 5631static int 5632pcache_monitor_db_destroy( BackendDB *be ) 5633{ 5634 return 0; 5635} 5636 5637#endif /* PCACHE_MONITOR */ 5638 5639static slap_overinst pcache; 5640 5641static char *obsolete_names[] = { 5642 "proxycache", 5643 NULL 5644}; 5645 5646#if SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC 5647static 5648#endif /* SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC */ 5649int 5650pcache_initialize() 5651{ 5652 int i, code; 5653 struct berval debugbv = BER_BVC("pcache"); 5654 ConfigArgs c; 5655 char *argv[ 4 ]; 5656 5657 code = slap_loglevel_get( &debugbv, &pcache_debug ); 5658 if ( code ) { 5659 return code; 5660 } 5661 5662#ifdef PCACHE_CONTROL_PRIVDB 5663 code = register_supported_control( PCACHE_CONTROL_PRIVDB, 5664 SLAP_CTRL_BIND|SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, extops, 5665 parse_privdb_ctrl, &privDB_cid ); 5666 if ( code != LDAP_SUCCESS ) { 5667 Debug( LDAP_DEBUG_ANY, 5668 "pcache_initialize: failed to register control %s (%d)\n", 5669 PCACHE_CONTROL_PRIVDB, code, 0 ); 5670 return code; 5671 } 5672#endif /* PCACHE_CONTROL_PRIVDB */ 5673 5674#ifdef PCACHE_EXOP_QUERY_DELETE 5675 code = load_extop2( (struct berval *)&pcache_exop_QUERY_DELETE, 5676 SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, pcache_exop_query_delete, 5677 0 ); 5678 if ( code != LDAP_SUCCESS ) { 5679 Debug( LDAP_DEBUG_ANY, 5680 "pcache_initialize: unable to register queryDelete exop: %d.\n", 5681 code, 0, 0 ); 5682 return code; 5683 } 5684#endif /* PCACHE_EXOP_QUERY_DELETE */ 5685 5686 argv[ 0 ] = "back-bdb/back-hdb monitor"; 5687 c.argv = argv; 5688 c.argc = 3; 5689 c.fname = argv[0]; 5690 5691 for ( i = 0; s_oid[ i ].name; i++ ) { 5692 c.lineno = i; 5693 argv[ 1 ] = s_oid[ i ].name; 5694 argv[ 2 ] = s_oid[ i ].oid; 5695 5696 if ( parse_oidm( &c, 0, NULL ) != 0 ) { 5697 Debug( LDAP_DEBUG_ANY, "pcache_initialize: " 5698 "unable to add objectIdentifier \"%s=%s\"\n", 5699 s_oid[ i ].name, s_oid[ i ].oid, 0 ); 5700 return 1; 5701 } 5702 } 5703 5704 for ( i = 0; s_ad[i].desc != NULL; i++ ) { 5705 code = register_at( s_ad[i].desc, s_ad[i].adp, 0 ); 5706 if ( code ) { 5707 Debug( LDAP_DEBUG_ANY, 5708 "pcache_initialize: register_at #%d failed\n", i, 0, 0 ); 5709 return code; 5710 } 5711 (*s_ad[i].adp)->ad_type->sat_flags |= SLAP_AT_HIDE; 5712 } 5713 5714 for ( i = 0; s_oc[i].desc != NULL; i++ ) { 5715 code = register_oc( s_oc[i].desc, s_oc[i].ocp, 0 ); 5716 if ( code ) { 5717 Debug( LDAP_DEBUG_ANY, 5718 "pcache_initialize: register_oc #%d failed\n", i, 0, 0 ); 5719 return code; 5720 } 5721 (*s_oc[i].ocp)->soc_flags |= SLAP_OC_HIDE; 5722 } 5723 5724 pcache.on_bi.bi_type = "pcache"; 5725 pcache.on_bi.bi_obsolete_names = obsolete_names; 5726 pcache.on_bi.bi_db_init = pcache_db_init; 5727 pcache.on_bi.bi_db_config = pcache_db_config; 5728 pcache.on_bi.bi_db_open = pcache_db_open; 5729 pcache.on_bi.bi_db_close = pcache_db_close; 5730 pcache.on_bi.bi_db_destroy = pcache_db_destroy; 5731 5732 pcache.on_bi.bi_op_search = pcache_op_search; 5733 pcache.on_bi.bi_op_bind = pcache_op_bind; 5734#ifdef PCACHE_CONTROL_PRIVDB 5735 pcache.on_bi.bi_op_compare = pcache_op_privdb; 5736 pcache.on_bi.bi_op_modrdn = pcache_op_privdb; 5737 pcache.on_bi.bi_op_modify = pcache_op_privdb; 5738 pcache.on_bi.bi_op_add = pcache_op_privdb; 5739 pcache.on_bi.bi_op_delete = pcache_op_privdb; 5740#endif /* PCACHE_CONTROL_PRIVDB */ 5741 pcache.on_bi.bi_extended = pcache_op_extended; 5742 5743 pcache.on_bi.bi_entry_release_rw = pcache_entry_release; 5744 pcache.on_bi.bi_chk_controls = pcache_chk_controls; 5745 5746 pcache.on_bi.bi_cf_ocs = pcocs; 5747 5748 code = config_register_schema( pccfg, pcocs ); 5749 if ( code ) return code; 5750 5751 return overlay_register( &pcache ); 5752} 5753 5754#if SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC 5755int init_module(int argc, char *argv[]) { 5756 return pcache_initialize(); 5757} 5758#endif 5759 5760#endif /* defined(SLAPD_OVER_PROXYCACHE) */ 5761