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