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