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