Back to index

openldap  2.4.31
pcache.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00003  *
00004  * Copyright 2003-2012 The OpenLDAP Foundation.
00005  * Portions Copyright 2003 IBM Corporation.
00006  * Portions Copyright 2003-2009 Symas Corporation.
00007  * All rights reserved.
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted only as authorized by the OpenLDAP
00011  * Public License.
00012  *
00013  * A copy of this license is available in the file LICENSE in the
00014  * top-level directory of the distribution or, alternatively, at
00015  * <http://www.OpenLDAP.org/license.html>.
00016  */
00017 /* ACKNOWLEDGEMENTS:
00018  * This work was initially developed by Apurva Kumar for inclusion
00019  * in OpenLDAP Software and subsequently rewritten by Howard Chu.
00020  */
00021 
00022 #include "portable.h"
00023 
00024 #ifdef SLAPD_OVER_PROXYCACHE
00025 
00026 #include <stdio.h>
00027 
00028 #include <ac/string.h>
00029 #include <ac/time.h>
00030 
00031 #include "slap.h"
00032 #include "lutil.h"
00033 #include "ldap_rq.h"
00034 #include "avl.h"
00035 
00036 #include "../back-monitor/back-monitor.h"
00037 
00038 #include "config.h"
00039 
00040 #ifdef LDAP_DEVEL
00041 /*
00042  * Control that allows to access the private DB
00043  * instead of the public one
00044  */
00045 #define       PCACHE_CONTROL_PRIVDB              "1.3.6.1.4.1.4203.666.11.9.5.1"
00046 
00047 /*
00048  * Extended Operation that allows to remove a query from the cache
00049  */
00050 #define PCACHE_EXOP_QUERY_DELETE   "1.3.6.1.4.1.4203.666.11.9.6.1"
00051 
00052 /*
00053  * Monitoring
00054  */
00055 #define PCACHE_MONITOR
00056 #endif
00057 
00058 /* query cache structs */
00059 /* query */
00060 
00061 typedef struct Query_s {
00062        Filter*       filter;       /* Search Filter */
00063        struct berval        base;         /* Search Base */
00064        int           scope;        /* Search scope */
00065 } Query;
00066 
00067 struct query_template_s;
00068 
00069 typedef struct Qbase_s {
00070        Avlnode *scopes[4];         /* threaded AVL trees of cached queries */
00071        struct berval base;
00072        int queries;
00073 } Qbase;
00074 
00075 /* struct representing a cached query */
00076 typedef struct cached_query_s {
00077        Filter                             *filter;
00078        Filter                             *first;
00079        Qbase                              *qbase;
00080        int                                       scope;
00081        struct berval               q_uuid;              /* query identifier */
00082        int                                       q_sizelimit;
00083        struct query_template_s            *qtemp;       /* template of the query */
00084        time_t                                    expiry_time;  /* time till the query is considered invalid */
00085        time_t                                    refresh_time; /* time till the query is refreshed */
00086        time_t                                    bindref_time; /* time till the bind is refreshed */
00087        int                                       bind_refcnt;  /* number of bind operation referencing this query */
00088        unsigned long               answerable_cnt; /* how many times it was answerable */
00089        int                                       refcnt;       /* references since last refresh */
00090        ldap_pvt_thread_mutex_t            answerable_cnt_mutex;
00091        struct cached_query_s              *next;        /* next query in the template */
00092        struct cached_query_s              *prev;        /* previous query in the template */
00093        struct cached_query_s              *lru_up;      /* previous query in the LRU list */
00094        struct cached_query_s              *lru_down;    /* next query in the LRU list */
00095        ldap_pvt_thread_rdwr_t             rwlock;
00096 } CachedQuery;
00097 
00098 /*
00099  * URL representation:
00100  *
00101  * ldap:///<base>??<scope>?<filter>?x-uuid=<uid>,x-template=<template>,x-attrset=<attrset>,x-expiry=<expiry>,x-refresh=<refresh>
00102  *
00103  * <base> ::= CachedQuery.qbase->base
00104  * <scope> ::= CachedQuery.scope
00105  * <filter> ::= filter2bv(CachedQuery.filter)
00106  * <uuid> ::= CachedQuery.q_uuid
00107  * <attrset> ::= CachedQuery.qtemp->attr_set_index
00108  * <expiry> ::= CachedQuery.expiry_time
00109  * <refresh> ::= CachedQuery.refresh_time
00110  *
00111  * quick hack: parse URI, call add_query() and then fix
00112  * CachedQuery.expiry_time and CachedQuery.q_uuid
00113  *
00114  * NOTE: if the <attrset> changes, all stored URLs will be invalidated.
00115  */
00116 
00117 /*
00118  * Represents a set of projected attributes.
00119  */
00120 
00121 struct attr_set {
00122        struct query_template_s *templates;
00123        AttributeName*       attrs;               /* specifies the set */
00124        unsigned      flags;
00125 #define       PC_CONFIGURED (0x1)
00126 #define       PC_REFERENCED (0x2)
00127 #define       PC_GOT_OC            (0x4)
00128        int           count;        /* number of attributes */
00129 };
00130 
00131 /* struct representing a query template
00132  * e.g. template string = &(cn=)(mail=)
00133  */
00134 typedef struct query_template_s {
00135        struct query_template_s *qtnext;
00136        struct query_template_s *qmnext;
00137 
00138        Avlnode*             qbase;
00139        CachedQuery*  query;         /* most recent query cached for the template */
00140        CachedQuery*  query_last;     /* oldest query cached for the template */
00141        ldap_pvt_thread_rdwr_t t_rwlock; /* Rd/wr lock for accessing queries in the template */
00142        struct berval querystr;     /* Filter string corresponding to the QT */
00143        struct berval bindbase;     /* base DN for Bind request */
00144        struct berval bindfilterstr;       /* Filter string for Bind request */
00145        struct berval bindftemp;    /* bind filter template */
00146        Filter        *bindfilter;
00147        AttributeDescription **bindfattrs; /* attrs to substitute in ftemp */
00148 
00149        int                  bindnattrs;          /* number of bindfattrs */
00150        int                  bindscope;
00151        int           attr_set_index; /* determines the projected attributes */
00152        int           no_of_queries;  /* Total number of queries in the template */
00153        time_t        ttl;          /* TTL for the queries of this template */
00154        time_t        negttl;              /* TTL for negative results */
00155        time_t        limitttl;     /* TTL for sizelimit exceeding results */
00156        time_t        ttr;   /* time to refresh */
00157        time_t        bindttr;      /* TTR for cached binds */
00158        struct attr_set t_attrs;    /* filter attrs + attr_set */
00159 } QueryTemplate;
00160 
00161 typedef enum {
00162        PC_IGNORE = 0,
00163        PC_POSITIVE,
00164        PC_NEGATIVE,
00165        PC_SIZELIMIT
00166 } pc_caching_reason_t;
00167 
00168 static const char *pc_caching_reason_str[] = {
00169        "IGNORE",
00170        "POSITIVE",
00171        "NEGATIVE",
00172        "SIZELIMIT",
00173 
00174        NULL
00175 };
00176 
00177 struct query_manager_s;
00178 
00179 /* prototypes for functions for 1) query containment
00180  * 2) query addition, 3) cache replacement
00181  */
00182 typedef CachedQuery *(QCfunc)(Operation *op, struct query_manager_s*,
00183        Query*, QueryTemplate*);
00184 typedef CachedQuery *(AddQueryfunc)(Operation *op, struct query_manager_s*,
00185        Query*, QueryTemplate*, pc_caching_reason_t, int wlock);
00186 typedef void (CRfunc)(struct query_manager_s*, struct berval*);
00187 
00188 /* LDAP query cache */
00189 typedef struct query_manager_s {
00190        struct attr_set*     attr_sets;           /* possible sets of projected attributes */
00191        QueryTemplate*              templates;           /* cacheable templates */
00192 
00193        CachedQuery*         lru_top;             /* top and bottom of LRU list */
00194        CachedQuery*         lru_bottom;
00195 
00196        ldap_pvt_thread_mutex_t            lru_mutex;    /* mutex for accessing LRU list */
00197 
00198        /* Query cache methods */
00199        QCfunc               *qcfunc;                    /* Query containment*/
00200        CRfunc                      *crfunc;                    /* cache replacement */
00201        AddQueryfunc  *addfunc;                   /* add query */
00202 } query_manager;
00203 
00204 /* LDAP query cache manager */
00205 typedef struct cache_manager_s {
00206        BackendDB     db;    /* underlying database */
00207        unsigned long num_cached_queries;         /* total number of cached queries */
00208        unsigned long   max_queries;                     /* upper bound on # of cached queries */
00209        int           save_queries;               /* save cached queries across restarts */
00210        int    check_cacheability;         /* check whether a query is cacheable */
00211        int    numattrsets;                /* number of attribute sets */
00212        int    cur_entries;                /* current number of entries cached */
00213        int    max_entries;                /* max number of entries cached */
00214        int    num_entries_limit;          /* max # of entries in a cacheable query */
00215 
00216        char   response_cb;                /* install the response callback
00217                                            * at the tail of the callback list */
00218 #define PCACHE_RESPONSE_CB_HEAD    0
00219 #define PCACHE_RESPONSE_CB_TAIL    1
00220        char   defer_db_open;                     /* defer open for online add */
00221        char   cache_binds;                /* cache binds or just passthru */
00222 
00223        time_t cc_period;           /* interval between successive consistency checks (sec) */
00224 #define PCACHE_CC_PAUSED    1
00225 #define PCACHE_CC_OFFLINE   2
00226        int    cc_paused;
00227        void   *cc_arg;
00228 
00229        ldap_pvt_thread_mutex_t            cache_mutex;
00230 
00231        query_manager*   qm; /* query cache managed by the cache manager */
00232 
00233 #ifdef PCACHE_MONITOR
00234        void          *monitor_cb;
00235        struct berval monitor_ndn;
00236 #endif /* PCACHE_MONITOR */
00237 } cache_manager;
00238 
00239 #ifdef PCACHE_MONITOR
00240 static int pcache_monitor_db_init( BackendDB *be );
00241 static int pcache_monitor_db_open( BackendDB *be );
00242 static int pcache_monitor_db_close( BackendDB *be );
00243 static int pcache_monitor_db_destroy( BackendDB *be );
00244 #endif /* PCACHE_MONITOR */
00245 
00246 static int pcache_debug;
00247 
00248 #ifdef PCACHE_CONTROL_PRIVDB
00249 static int privDB_cid;
00250 #endif /* PCACHE_CONTROL_PRIVDB */
00251 
00252 static AttributeDescription *ad_queryId, *ad_cachedQueryURL;
00253 
00254 #ifdef PCACHE_MONITOR
00255 static AttributeDescription *ad_numQueries, *ad_numEntries;
00256 static ObjectClass          *oc_olmPCache;
00257 #endif /* PCACHE_MONITOR */
00258 
00259 static struct {
00260        char                 *name;
00261        char                 *oid;
00262 }             s_oid[] = {
00263        { "PCacheOID",                     "1.3.6.1.4.1.4203.666.11.9.1" },
00264        { "PCacheAttributes",              "PCacheOID:1" },
00265        { "PCacheObjectClasses",    "PCacheOID:2" },
00266 
00267        { NULL }
00268 };
00269 
00270 static struct {
00271        char   *desc;
00272        AttributeDescription **adp;
00273 } s_ad[] = {
00274        { "( PCacheAttributes:1 "
00275               "NAME 'pcacheQueryID' "
00276               "DESC 'ID of query the entry belongs to, formatted as a UUID' "
00277               "EQUALITY octetStringMatch "
00278               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{64} "
00279               "NO-USER-MODIFICATION "
00280               "USAGE directoryOperation )",
00281               &ad_queryId },
00282        { "( PCacheAttributes:2 "
00283               "NAME 'pcacheQueryURL' "
00284               "DESC 'URI describing a cached query' "
00285               "EQUALITY caseExactMatch "
00286               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
00287               "NO-USER-MODIFICATION "
00288               "USAGE directoryOperation )",
00289               &ad_cachedQueryURL },
00290 #ifdef PCACHE_MONITOR
00291        { "( PCacheAttributes:3 "
00292               "NAME 'pcacheNumQueries' "
00293               "DESC 'Number of cached queries' "
00294               "EQUALITY integerMatch "
00295               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
00296               "NO-USER-MODIFICATION "
00297               "USAGE directoryOperation )",
00298               &ad_numQueries },
00299        { "( PCacheAttributes:4 "
00300               "NAME 'pcacheNumEntries' "
00301               "DESC 'Number of cached entries' "
00302               "EQUALITY integerMatch "
00303               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
00304               "NO-USER-MODIFICATION "
00305               "USAGE directoryOperation )",
00306               &ad_numEntries },
00307 #endif /* PCACHE_MONITOR */
00308 
00309        { NULL }
00310 };
00311 
00312 static struct {
00313        char          *desc;
00314        ObjectClass   **ocp;
00315 }             s_oc[] = {
00316 #ifdef PCACHE_MONITOR
00317        /* augments an existing object, so it must be AUXILIARY */
00318        { "( PCacheObjectClasses:1 "
00319               "NAME ( 'olmPCache' ) "
00320               "SUP top AUXILIARY "
00321               "MAY ( "
00322                      "pcacheQueryURL "
00323                      "$ pcacheNumQueries "
00324                      "$ pcacheNumEntries "
00325                      " ) )",
00326               &oc_olmPCache },
00327 #endif /* PCACHE_MONITOR */
00328 
00329        { NULL }
00330 };
00331 
00332 static int
00333 filter2template(
00334        Operation            *op,
00335        Filter               *f,
00336        struct               berval *fstr );
00337 
00338 static CachedQuery *
00339 add_query(
00340        Operation *op,
00341        query_manager* qm,
00342        Query* query,
00343        QueryTemplate *templ,
00344        pc_caching_reason_t why,
00345        int wlock);
00346 
00347 static int
00348 remove_query_data(
00349        Operation     *op,
00350        struct berval *query_uuid );
00351 
00352 /*
00353  * Turn a cached query into its URL representation
00354  */
00355 static int
00356 query2url( Operation *op, CachedQuery *q, struct berval *urlbv, int dolock )
00357 {
00358        struct berval bv_scope,
00359                      bv_filter;
00360        char          attrset_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
00361                      expiry_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
00362                      refresh_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
00363                      answerable_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
00364                      *ptr;
00365        ber_len_t     attrset_len,
00366                      expiry_len,
00367                      refresh_len,
00368                      answerable_len;
00369 
00370        if ( dolock ) {
00371               ldap_pvt_thread_rdwr_rlock( &q->rwlock );
00372        }
00373 
00374        ldap_pvt_scope2bv( q->scope, &bv_scope );
00375        filter2bv_x( op, q->filter, &bv_filter );
00376        attrset_len = sprintf( attrset_buf,
00377               "%lu", (unsigned long)q->qtemp->attr_set_index );
00378        expiry_len = sprintf( expiry_buf,
00379               "%lu", (unsigned long)q->expiry_time );
00380        answerable_len = snprintf( answerable_buf, sizeof( answerable_buf ),
00381               "%lu", q->answerable_cnt );
00382        if ( q->refresh_time )
00383               refresh_len = sprintf( refresh_buf,
00384                      "%lu", (unsigned long)q->refresh_time );
00385        else
00386               refresh_len = 0;
00387 
00388        urlbv->bv_len = STRLENOF( "ldap:///" )
00389               + q->qbase->base.bv_len
00390               + STRLENOF( "??" )
00391               + bv_scope.bv_len
00392               + STRLENOF( "?" )
00393               + bv_filter.bv_len
00394               + STRLENOF( "?x-uuid=" )
00395               + q->q_uuid.bv_len
00396               + STRLENOF( ",x-attrset=" )
00397               + attrset_len
00398               + STRLENOF( ",x-expiry=" )
00399               + expiry_len
00400               + STRLENOF( ",x-answerable=" )
00401               + answerable_len;
00402        if ( refresh_len )
00403               urlbv->bv_len += STRLENOF( ",x-refresh=" )
00404               + refresh_len;
00405 
00406        ptr = urlbv->bv_val = ber_memalloc_x( urlbv->bv_len + 1, op->o_tmpmemctx );
00407        ptr = lutil_strcopy( ptr, "ldap:///" );
00408        ptr = lutil_strcopy( ptr, q->qbase->base.bv_val );
00409        ptr = lutil_strcopy( ptr, "??" );
00410        ptr = lutil_strcopy( ptr, bv_scope.bv_val );
00411        ptr = lutil_strcopy( ptr, "?" );
00412        ptr = lutil_strcopy( ptr, bv_filter.bv_val );
00413        ptr = lutil_strcopy( ptr, "?x-uuid=" );
00414        ptr = lutil_strcopy( ptr, q->q_uuid.bv_val );
00415        ptr = lutil_strcopy( ptr, ",x-attrset=" );
00416        ptr = lutil_strcopy( ptr, attrset_buf );
00417        ptr = lutil_strcopy( ptr, ",x-expiry=" );
00418        ptr = lutil_strcopy( ptr, expiry_buf );
00419        ptr = lutil_strcopy( ptr, ",x-answerable=" );
00420        ptr = lutil_strcopy( ptr, answerable_buf );
00421        if ( refresh_len ) {
00422               ptr = lutil_strcopy( ptr, ",x-refresh=" );
00423               ptr = lutil_strcopy( ptr, refresh_buf );
00424        }
00425 
00426        ber_memfree_x( bv_filter.bv_val, op->o_tmpmemctx );
00427 
00428        if ( dolock ) {
00429               ldap_pvt_thread_rdwr_runlock( &q->rwlock );
00430        }
00431 
00432        return 0;
00433 }
00434 
00435 /* Find and record the empty filter clauses */
00436 
00437 static int
00438 ftemp_attrs( struct berval *ftemp, struct berval *template,
00439        AttributeDescription ***ret, const char **text )
00440 {
00441        int i;
00442        int attr_cnt=0;
00443        struct berval bv;
00444        char *p1, *p2, *t1;
00445        AttributeDescription *ad;
00446        AttributeDescription **descs = NULL;
00447        char *temp2;
00448 
00449        temp2 = ch_malloc( ftemp->bv_len + 1 );
00450        p1 = ftemp->bv_val;
00451        t1 = temp2;
00452 
00453        *ret = NULL;
00454 
00455        for (;;) {
00456               while ( *p1 == '(' || *p1 == '&' || *p1 == '|' || *p1 == ')' )
00457                      *t1++ = *p1++;
00458 
00459               p2 = strchr( p1, '=' );
00460               if ( !p2 )
00461                      break;
00462               i = p2 - p1;
00463               AC_MEMCPY( t1, p1, i );
00464               t1 += i;
00465               *t1++ = '=';
00466 
00467               if ( p2[-1] == '<' || p2[-1] == '>' ) p2--;
00468               bv.bv_val = p1;
00469               bv.bv_len = p2 - p1;
00470               ad = NULL;
00471               i = slap_bv2ad( &bv, &ad, text );
00472               if ( i ) {
00473                      ch_free( descs );
00474                      return -1;
00475               }
00476               if ( *p2 == '<' || *p2 == '>' ) p2++;
00477               if ( p2[1] != ')' ) {
00478                      p2++;
00479                      while ( *p2 != ')' ) p2++;
00480                      p1 = p2;
00481                      continue;
00482               }
00483 
00484               descs = (AttributeDescription **)ch_realloc(descs,
00485                             (attr_cnt + 2)*sizeof(AttributeDescription *));
00486 
00487               descs[attr_cnt++] = ad;
00488 
00489               p1 = p2+1;
00490        }
00491        *t1 = '\0';
00492        descs[attr_cnt] = NULL;
00493        *ret = descs;
00494        template->bv_val = temp2;
00495        template->bv_len = t1 - temp2;
00496        return attr_cnt;
00497 }
00498 
00499 static int
00500 template_attrs( char *template, struct attr_set *set, AttributeName **ret,
00501        const char **text )
00502 {
00503        int got_oc = 0;
00504        int alluser = 0;
00505        int allop = 0;
00506        int i;
00507        int attr_cnt;
00508        int t_cnt = 0;
00509        struct berval bv;
00510        char *p1, *p2;
00511        AttributeDescription *ad;
00512        AttributeName *attrs;
00513 
00514        p1 = template;
00515 
00516        *ret = NULL;
00517 
00518        attrs = ch_calloc( set->count + 1, sizeof(AttributeName) );
00519        for ( i=0; i < set->count; i++ )
00520               attrs[i] = set->attrs[i];
00521        attr_cnt = i;
00522        alluser = an_find( attrs, slap_bv_all_user_attrs );
00523        allop = an_find( attrs, slap_bv_all_operational_attrs );
00524 
00525        for (;;) {
00526               while ( *p1 == '(' || *p1 == '&' || *p1 == '|' || *p1 == ')' ) p1++;
00527               p2 = strchr( p1, '=' );
00528               if ( !p2 )
00529                      break;
00530               if ( p2[-1] == '<' || p2[-1] == '>' ) p2--;
00531               bv.bv_val = p1;
00532               bv.bv_len = p2 - p1;
00533               ad = NULL;
00534               i = slap_bv2ad( &bv, &ad, text );
00535               if ( i ) {
00536                      ch_free( attrs );
00537                      return -1;
00538               }
00539               t_cnt++;
00540 
00541               if ( ad == slap_schema.si_ad_objectClass )
00542                      got_oc = 1;
00543 
00544               if ( is_at_operational(ad->ad_type)) {
00545                      if ( allop ) {
00546                             goto bottom;
00547                      }
00548               } else if ( alluser ) {
00549                      goto bottom;
00550               }
00551               if ( !ad_inlist( ad, attrs )) {
00552                      attrs = (AttributeName *)ch_realloc(attrs,
00553                                    (attr_cnt + 2)*sizeof(AttributeName));
00554 
00555                      attrs[attr_cnt].an_desc = ad;
00556                      attrs[attr_cnt].an_name = ad->ad_cname;
00557                      attrs[attr_cnt].an_oc = NULL;
00558                      attrs[attr_cnt].an_flags = 0;
00559                      BER_BVZERO( &attrs[attr_cnt+1].an_name );
00560                      attr_cnt++;
00561               }
00562 
00563 bottom:
00564               p1 = p2+2;
00565        }
00566        if ( !t_cnt ) {
00567               *text = "couldn't parse template";
00568               return -1;
00569        }
00570        if ( !got_oc && !( set->flags & PC_GOT_OC )) {
00571               attrs = (AttributeName *)ch_realloc(attrs,
00572                             (attr_cnt + 2)*sizeof(AttributeName));
00573 
00574               ad = slap_schema.si_ad_objectClass;
00575               attrs[attr_cnt].an_desc = ad;
00576               attrs[attr_cnt].an_name = ad->ad_cname;
00577               attrs[attr_cnt].an_oc = NULL;
00578               attrs[attr_cnt].an_flags = 0;
00579               BER_BVZERO( &attrs[attr_cnt+1].an_name );
00580               attr_cnt++;
00581        }
00582        *ret = attrs;
00583        return attr_cnt;
00584 }
00585 
00586 /*
00587  * Turn an URL representing a formerly cached query into a cached query,
00588  * and try to cache it
00589  */
00590 static int
00591 url2query(
00592        char          *url,
00593        Operation     *op,
00594        query_manager *qm )
00595 {
00596        Query         query = { 0 };
00597        QueryTemplate *qt;
00598        CachedQuery   *cq;
00599        LDAPURLDesc   *lud = NULL;
00600        struct berval base,
00601                      tempstr = BER_BVNULL,
00602                      uuid = BER_BVNULL;
00603        int           attrset;
00604        time_t        expiry_time;
00605        time_t        refresh_time;
00606        unsigned long answerable_cnt;
00607        int           i,
00608                      got = 0,
00609 #define GOT_UUID     0x1U
00610 #define GOT_ATTRSET  0x2U
00611 #define GOT_EXPIRY   0x4U
00612 #define GOT_ANSWERABLE      0x8U
00613 #define GOT_REFRESH  0x10U
00614 #define GOT_ALL             (GOT_UUID|GOT_ATTRSET|GOT_EXPIRY|GOT_ANSWERABLE)
00615                      rc = 0;
00616 
00617        rc = ldap_url_parse( url, &lud );
00618        if ( rc != LDAP_URL_SUCCESS ) {
00619               return -1;
00620        }
00621 
00622        /* non-allowed fields */
00623        if ( lud->lud_host != NULL ) {
00624               rc = 1;
00625               goto error;
00626        }
00627 
00628        if ( lud->lud_attrs != NULL ) {
00629               rc = 1;
00630               goto error;
00631        }
00632 
00633        /* be pedantic */
00634        if ( strcmp( lud->lud_scheme, "ldap" ) != 0 ) {
00635               rc = 1;
00636               goto error;
00637        }
00638 
00639        /* required fields */
00640        if ( lud->lud_dn == NULL || lud->lud_dn[ 0 ] == '\0' ) {
00641               rc = 1;
00642               goto error;
00643        }
00644 
00645        switch ( lud->lud_scope ) {
00646        case LDAP_SCOPE_BASE:
00647        case LDAP_SCOPE_ONELEVEL:
00648        case LDAP_SCOPE_SUBTREE:
00649        case LDAP_SCOPE_SUBORDINATE:
00650               break;
00651 
00652        default:
00653               rc = 1;
00654               goto error;
00655        }
00656 
00657        if ( lud->lud_filter == NULL || lud->lud_filter[ 0 ] == '\0' ) {
00658               rc = 1;
00659               goto error;
00660        }
00661 
00662        if ( lud->lud_exts == NULL ) {
00663               rc = 1;
00664               goto error;
00665        }
00666 
00667        for ( i = 0; lud->lud_exts[ i ] != NULL; i++ ) {
00668               if ( strncmp( lud->lud_exts[ i ], "x-uuid=", STRLENOF( "x-uuid=" ) ) == 0 ) {
00669                      struct berval tmpUUID;
00670                      Syntax        *syn_UUID = slap_schema.si_ad_entryUUID->ad_type->sat_syntax;
00671 
00672                      if ( got & GOT_UUID ) {
00673                             rc = 1;
00674                             goto error;
00675                      }
00676 
00677                      ber_str2bv( &lud->lud_exts[ i ][ STRLENOF( "x-uuid=" ) ], 0, 0, &tmpUUID );
00678                      if ( !BER_BVISEMPTY( &tmpUUID ) ) {
00679                             rc = syn_UUID->ssyn_pretty( syn_UUID, &tmpUUID, &uuid, NULL );
00680                             if ( rc != LDAP_SUCCESS ) {
00681                                    goto error;
00682                             }
00683                      }
00684                      got |= GOT_UUID;
00685 
00686               } else if ( strncmp( lud->lud_exts[ i ], "x-attrset=", STRLENOF( "x-attrset=" ) ) == 0 ) {
00687                      if ( got & GOT_ATTRSET ) {
00688                             rc = 1;
00689                             goto error;
00690                      }
00691 
00692                      rc = lutil_atoi( &attrset, &lud->lud_exts[ i ][ STRLENOF( "x-attrset=" ) ] );
00693                      if ( rc ) {
00694                             goto error;
00695                      }
00696                      got |= GOT_ATTRSET;
00697 
00698               } else if ( strncmp( lud->lud_exts[ i ], "x-expiry=", STRLENOF( "x-expiry=" ) ) == 0 ) {
00699                      unsigned long l;
00700 
00701                      if ( got & GOT_EXPIRY ) {
00702                             rc = 1;
00703                             goto error;
00704                      }
00705 
00706                      rc = lutil_atoul( &l, &lud->lud_exts[ i ][ STRLENOF( "x-expiry=" ) ] );
00707                      if ( rc ) {
00708                             goto error;
00709                      }
00710                      expiry_time = (time_t)l;
00711                      got |= GOT_EXPIRY;
00712 
00713               } else if ( strncmp( lud->lud_exts[ i ], "x-answerable=", STRLENOF( "x-answerable=" ) ) == 0 ) {
00714                      if ( got & GOT_ANSWERABLE ) {
00715                             rc = 1;
00716                             goto error;
00717                      }
00718 
00719                      rc = lutil_atoul( &answerable_cnt, &lud->lud_exts[ i ][ STRLENOF( "x-answerable=" ) ] );
00720                      if ( rc ) {
00721                             goto error;
00722                      }
00723                      got |= GOT_ANSWERABLE;
00724 
00725               } else if ( strncmp( lud->lud_exts[ i ], "x-refresh=", STRLENOF( "x-refresh=" ) ) == 0 ) {
00726                      unsigned long l;
00727 
00728                      if ( got & GOT_REFRESH ) {
00729                             rc = 1;
00730                             goto error;
00731                      }
00732 
00733                      rc = lutil_atoul( &l, &lud->lud_exts[ i ][ STRLENOF( "x-refresh=" ) ] );
00734                      if ( rc ) {
00735                             goto error;
00736                      }
00737                      refresh_time = (time_t)l;
00738                      got |= GOT_REFRESH;
00739 
00740               } else {
00741                      rc = -1;
00742                      goto error;
00743               }
00744        }
00745 
00746        if ( got != GOT_ALL ) {
00747               rc = 1;
00748               goto error;
00749        }
00750 
00751        if ( !(got & GOT_REFRESH ))
00752               refresh_time = 0;
00753 
00754        /* ignore expired queries */
00755        if ( expiry_time <= slap_get_time()) {
00756               Operation     op2 = *op;
00757 
00758               memset( &op2.oq_search, 0, sizeof( op2.oq_search ) );
00759 
00760               (void)remove_query_data( &op2, &uuid );
00761 
00762               rc = 0;
00763 
00764        } else {
00765               ber_str2bv( lud->lud_dn, 0, 0, &base );
00766               rc = dnNormalize( 0, NULL, NULL, &base, &query.base, NULL );
00767               if ( rc != LDAP_SUCCESS ) {
00768                      goto error;
00769               }
00770               query.scope = lud->lud_scope;
00771               query.filter = str2filter( lud->lud_filter );
00772               if ( query.filter == NULL ) {
00773                      rc = -1;
00774                      goto error;
00775               }
00776 
00777               tempstr.bv_val = ch_malloc( strlen( lud->lud_filter ) + 1 );
00778               tempstr.bv_len = 0;
00779               if ( filter2template( op, query.filter, &tempstr ) ) {
00780                      ch_free( tempstr.bv_val );
00781                      rc = -1;
00782                      goto error;
00783               }
00784 
00785               /* check for query containment */
00786               qt = qm->attr_sets[attrset].templates;
00787               for ( ; qt; qt = qt->qtnext ) {
00788                      /* find if template i can potentially answer tempstr */
00789                      if ( bvmatch( &qt->querystr, &tempstr ) ) {
00790                             break;
00791                      }
00792               }
00793 
00794               if ( qt == NULL ) {
00795                      rc = 1;
00796                      goto error;
00797               }
00798 
00799               cq = add_query( op, qm, &query, qt, PC_POSITIVE, 0 );
00800               if ( cq != NULL ) {
00801                      cq->expiry_time = expiry_time;
00802                      cq->refresh_time = refresh_time;
00803                      cq->q_uuid = uuid;
00804                      cq->answerable_cnt = answerable_cnt;
00805                      cq->refcnt = 0;
00806 
00807                      /* it's now into cq->filter */
00808                      BER_BVZERO( &uuid );
00809                      query.filter = NULL;
00810 
00811               } else {
00812                      rc = 1;
00813               }
00814        }
00815 
00816 error:;
00817        if ( query.filter != NULL ) filter_free( query.filter );
00818        if ( !BER_BVISNULL( &tempstr ) ) ch_free( tempstr.bv_val );
00819        if ( !BER_BVISNULL( &query.base ) ) ch_free( query.base.bv_val );
00820        if ( !BER_BVISNULL( &uuid ) ) ch_free( uuid.bv_val );
00821        if ( lud != NULL ) ldap_free_urldesc( lud );
00822 
00823        return rc;
00824 }
00825 
00826 /* Return 1 for an added entry, else 0 */
00827 static int
00828 merge_entry(
00829        Operation            *op,
00830        Entry                *e,
00831        int                  dup,
00832        struct berval*              query_uuid )
00833 {
00834        int           rc;
00835        Modifications* modlist = NULL;
00836        const char*   text = NULL;
00837        Attribute            *attr;
00838        char                 textbuf[SLAP_TEXT_BUFLEN];
00839        size_t               textlen = sizeof(textbuf);
00840 
00841        SlapReply sreply = {REP_RESULT};
00842 
00843        slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
00844 
00845        if ( dup )
00846               e = entry_dup( e );
00847        attr = e->e_attrs;
00848        e->e_attrs = NULL;
00849 
00850        /* add queryId attribute */
00851        attr_merge_one( e, ad_queryId, query_uuid, NULL );
00852 
00853        /* append the attribute list from the fetched entry */
00854        e->e_attrs->a_next = attr;
00855 
00856        op->o_tag = LDAP_REQ_ADD;
00857        op->o_protocol = LDAP_VERSION3;
00858        op->o_callback = &cb;
00859        op->o_time = slap_get_time();
00860        op->o_do_not_cache = 1;
00861 
00862        op->ora_e = e;
00863        op->o_req_dn = e->e_name;
00864        op->o_req_ndn = e->e_nname;
00865        rc = op->o_bd->be_add( op, &sreply );
00866 
00867        if ( rc != LDAP_SUCCESS ) {
00868               if ( rc == LDAP_ALREADY_EXISTS ) {
00869                      rs_reinit( &sreply, REP_RESULT );
00870                      slap_entry2mods( e, &modlist, &text, textbuf, textlen );
00871                      modlist->sml_op = LDAP_MOD_ADD;
00872                      op->o_tag = LDAP_REQ_MODIFY;
00873                      op->orm_modlist = modlist;
00874                      op->o_managedsait = SLAP_CONTROL_CRITICAL;
00875                      op->o_bd->be_modify( op, &sreply );
00876                      slap_mods_free( modlist, 1 );
00877               } else if ( rc == LDAP_REFERRAL ||
00878                                    rc == LDAP_NO_SUCH_OBJECT ) {
00879                      syncrepl_add_glue( op, e );
00880                      e = NULL;
00881                      rc = 1;
00882               }
00883               if ( e ) {
00884                      entry_free( e );
00885                      rc = 0;
00886               }
00887        } else {
00888               if ( op->ora_e == e )
00889                      entry_free( e );
00890               rc = 1;
00891        }
00892 
00893        return rc;
00894 }
00895 
00896 /* Length-ordered sort on normalized DNs */
00897 static int pcache_dn_cmp( const void *v1, const void *v2 )
00898 {
00899        const Qbase *q1 = v1, *q2 = v2;
00900 
00901        int rc = q1->base.bv_len - q2->base.bv_len;
00902        if ( rc == 0 )
00903               rc = strncmp( q1->base.bv_val, q2->base.bv_val, q1->base.bv_len );
00904        return rc;
00905 }
00906 
00907 static int lex_bvcmp( struct berval *bv1, struct berval *bv2 )
00908 {
00909        int len, dif;
00910        dif = bv1->bv_len - bv2->bv_len;
00911        len = bv1->bv_len;
00912        if ( dif > 0 ) len -= dif;
00913        len = memcmp( bv1->bv_val, bv2->bv_val, len );
00914        if ( !len )
00915               len = dif;
00916        return len;
00917 }
00918 
00919 /* compare the current value in each filter */
00920 static int pcache_filter_cmp( Filter *f1, Filter *f2 )
00921 {
00922        int rc, weight1, weight2;
00923 
00924        switch( f1->f_choice ) {
00925        case LDAP_FILTER_AND:
00926        case LDAP_FILTER_OR:
00927               weight1 = 0;
00928               break;
00929        case LDAP_FILTER_PRESENT:
00930               weight1 = 1;
00931               break;
00932        case LDAP_FILTER_EQUALITY:
00933        case LDAP_FILTER_GE:
00934        case LDAP_FILTER_LE:
00935               weight1 = 2;
00936               break;
00937        default:
00938               weight1 = 3;
00939        }
00940        switch( f2->f_choice ) {
00941        case LDAP_FILTER_AND:
00942        case LDAP_FILTER_OR:
00943               weight2 = 0;
00944               break;
00945        case LDAP_FILTER_PRESENT:
00946               weight2 = 1;
00947               break;
00948        case LDAP_FILTER_EQUALITY:
00949        case LDAP_FILTER_GE:
00950        case LDAP_FILTER_LE:
00951               weight2 = 2;
00952               break;
00953        default:
00954               weight2 = 3;
00955        }
00956        rc = weight1 - weight2;
00957        if ( !rc ) {
00958               switch( weight1 ) {
00959               case 0:
00960                      rc = pcache_filter_cmp( f1->f_and, f2->f_and );
00961                      break;
00962               case 1:
00963                      break;
00964               case 2:
00965                      rc = lex_bvcmp( &f1->f_av_value, &f2->f_av_value );
00966                      break;
00967               case 3:
00968                      if ( f1->f_choice == LDAP_FILTER_SUBSTRINGS ) {
00969                             rc = 0;
00970                             if ( !BER_BVISNULL( &f1->f_sub_initial )) {
00971                                    if ( !BER_BVISNULL( &f2->f_sub_initial )) {
00972                                           rc = lex_bvcmp( &f1->f_sub_initial,
00973                                                  &f2->f_sub_initial );
00974                                    } else {
00975                                           rc = 1;
00976                                    }
00977                             } else if ( !BER_BVISNULL( &f2->f_sub_initial )) {
00978                                    rc = -1;
00979                             }
00980                             if ( rc ) break;
00981                             if ( f1->f_sub_any ) {
00982                                    if ( f2->f_sub_any ) {
00983                                           rc = lex_bvcmp( f1->f_sub_any,
00984                                                  f2->f_sub_any );
00985                                    } else {
00986                                           rc = 1;
00987                                    }
00988                             } else if ( f2->f_sub_any ) {
00989                                    rc = -1;
00990                             }
00991                             if ( rc ) break;
00992                             if ( !BER_BVISNULL( &f1->f_sub_final )) {
00993                                    if ( !BER_BVISNULL( &f2->f_sub_final )) {
00994                                           rc = lex_bvcmp( &f1->f_sub_final,
00995                                                  &f2->f_sub_final );
00996                                    } else {
00997                                           rc = 1;
00998                                    }
00999                             } else if ( !BER_BVISNULL( &f2->f_sub_final )) {
01000                                    rc = -1;
01001                             }
01002                      } else {
01003                             rc = lex_bvcmp( &f1->f_mr_value,
01004                                    &f2->f_mr_value );
01005                      }
01006                      break;
01007               }
01008               while ( !rc ) {
01009                      f1 = f1->f_next;
01010                      f2 = f2->f_next;
01011                      if ( f1 || f2 ) {
01012                             if ( !f1 )
01013                                    rc = -1;
01014                             else if ( !f2 )
01015                                    rc = 1;
01016                             else {
01017                                    rc = pcache_filter_cmp( f1, f2 );
01018                             }
01019                      } else {
01020                             break;
01021                      }
01022               }
01023        }
01024        return rc;
01025 }
01026 
01027 /* compare filters in each query */
01028 static int pcache_query_cmp( const void *v1, const void *v2 )
01029 {
01030        const CachedQuery *q1 = v1, *q2 =v2;
01031        return pcache_filter_cmp( q1->filter, q2->filter );
01032 }
01033 
01034 /* add query on top of LRU list */
01035 static void
01036 add_query_on_top (query_manager* qm, CachedQuery* qc)
01037 {
01038        CachedQuery* top = qm->lru_top;
01039 
01040        qm->lru_top = qc;
01041 
01042        if (top)
01043               top->lru_up = qc;
01044        else
01045               qm->lru_bottom = qc;
01046 
01047        qc->lru_down = top;
01048        qc->lru_up = NULL;
01049        Debug( pcache_debug, "Base of added query = %s\n",
01050                      qc->qbase->base.bv_val, 0, 0 );
01051 }
01052 
01053 /* remove_query from LRU list */
01054 
01055 static void
01056 remove_query (query_manager* qm, CachedQuery* qc)
01057 {
01058        CachedQuery* up;
01059        CachedQuery* down;
01060 
01061        if (!qc)
01062               return;
01063 
01064        up = qc->lru_up;
01065        down = qc->lru_down;
01066 
01067        if (!up)
01068               qm->lru_top = down;
01069 
01070        if (!down)
01071               qm->lru_bottom = up;
01072 
01073        if (down)
01074               down->lru_up = up;
01075 
01076        if (up)
01077               up->lru_down = down;
01078 
01079        qc->lru_up = qc->lru_down = NULL;
01080 }
01081 
01082 /* find and remove string2 from string1
01083  * from start if position = 1,
01084  * from end if position = 3,
01085  * from anywhere if position = 2
01086  * string1 is overwritten if position = 2.
01087  */
01088 
01089 static int
01090 find_and_remove(struct berval* ber1, struct berval* ber2, int position)
01091 {
01092        int ret=0;
01093 
01094        if ( !ber2->bv_val )
01095               return 1;
01096        if ( !ber1->bv_val )
01097               return 0;
01098 
01099        switch( position ) {
01100        case 1:
01101               if ( ber1->bv_len >= ber2->bv_len && !memcmp( ber1->bv_val,
01102                      ber2->bv_val, ber2->bv_len )) {
01103                      ret = 1;
01104                      ber1->bv_val += ber2->bv_len;
01105                      ber1->bv_len -= ber2->bv_len;
01106               }
01107               break;
01108        case 2: {
01109               char *temp;
01110               ber1->bv_val[ber1->bv_len] = '\0';
01111               temp = strstr( ber1->bv_val, ber2->bv_val );
01112               if ( temp ) {
01113                      strcpy( temp, temp+ber2->bv_len );
01114                      ber1->bv_len -= ber2->bv_len;
01115                      ret = 1;
01116               }
01117               break;
01118               }
01119        case 3:
01120               if ( ber1->bv_len >= ber2->bv_len &&
01121                      !memcmp( ber1->bv_val+ber1->bv_len-ber2->bv_len, ber2->bv_val,
01122                             ber2->bv_len )) {
01123                      ret = 1;
01124                      ber1->bv_len -= ber2->bv_len;
01125               }
01126               break;
01127        }
01128        return ret;
01129 }
01130 
01131 
01132 static struct berval*
01133 merge_init_final(Operation *op, struct berval* init, struct berval* any,
01134        struct berval* final)
01135 {
01136        struct berval* merged, *temp;
01137        int i, any_count, count;
01138 
01139        for (any_count=0; any && any[any_count].bv_val; any_count++)
01140               ;
01141 
01142        count = any_count;
01143 
01144        if (init->bv_val)
01145               count++;
01146        if (final->bv_val)
01147               count++;
01148 
01149        merged = (struct berval*)op->o_tmpalloc( (count+1)*sizeof(struct berval),
01150               op->o_tmpmemctx );
01151        temp = merged;
01152 
01153        if (init->bv_val) {
01154               ber_dupbv_x( temp, init, op->o_tmpmemctx );
01155               temp++;
01156        }
01157 
01158        for (i=0; i<any_count; i++) {
01159               ber_dupbv_x( temp, any, op->o_tmpmemctx );
01160               temp++; any++;
01161        }
01162 
01163        if (final->bv_val){
01164               ber_dupbv_x( temp, final, op->o_tmpmemctx );
01165               temp++;
01166        }
01167        BER_BVZERO( temp );
01168        return merged;
01169 }
01170 
01171 /* Each element in stored must be found in incoming. Incoming is overwritten.
01172  */
01173 static int
01174 strings_containment(struct berval* stored, struct berval* incoming)
01175 {
01176        struct berval* element;
01177        int k=0;
01178        int j, rc = 0;
01179 
01180        for ( element=stored; element->bv_val != NULL; element++ ) {
01181               for (j = k; incoming[j].bv_val != NULL; j++) {
01182                      if (find_and_remove(&(incoming[j]), element, 2)) {
01183                             k = j;
01184                             rc = 1;
01185                             break;
01186                      }
01187                      rc = 0;
01188               }
01189               if ( rc ) {
01190                      continue;
01191               } else {
01192                      return 0;
01193               }
01194        }
01195        return 1;
01196 }
01197 
01198 static int
01199 substr_containment_substr(Operation *op, Filter* stored, Filter* incoming)
01200 {
01201        int rc = 0;
01202 
01203        struct berval init_incoming;
01204        struct berval final_incoming;
01205        struct berval *remaining_incoming = NULL;
01206 
01207        if ((!(incoming->f_sub_initial.bv_val) && (stored->f_sub_initial.bv_val))
01208           || (!(incoming->f_sub_final.bv_val) && (stored->f_sub_final.bv_val)))
01209               return 0;
01210 
01211        init_incoming = incoming->f_sub_initial;
01212        final_incoming =  incoming->f_sub_final;
01213 
01214        if (find_and_remove(&init_incoming,
01215                      &(stored->f_sub_initial), 1) && find_and_remove(&final_incoming,
01216                      &(stored->f_sub_final), 3))
01217        {
01218               if (stored->f_sub_any == NULL) {
01219                      rc = 1;
01220                      goto final;
01221               }
01222               remaining_incoming = merge_init_final(op, &init_incoming,
01223                                           incoming->f_sub_any, &final_incoming);
01224               rc = strings_containment(stored->f_sub_any, remaining_incoming);
01225               ber_bvarray_free_x( remaining_incoming, op->o_tmpmemctx );
01226        }
01227 final:
01228        return rc;
01229 }
01230 
01231 static int
01232 substr_containment_equality(Operation *op, Filter* stored, Filter* incoming)
01233 {
01234        struct berval incoming_val[2];
01235        int rc = 0;
01236 
01237        incoming_val[1] = incoming->f_av_value;
01238 
01239        if (find_and_remove(incoming_val+1,
01240                      &(stored->f_sub_initial), 1) && find_and_remove(incoming_val+1,
01241                      &(stored->f_sub_final), 3)) {
01242               if (stored->f_sub_any == NULL){
01243                      rc = 1;
01244                      goto final;
01245               }
01246               ber_dupbv_x( incoming_val, incoming_val+1, op->o_tmpmemctx );
01247               BER_BVZERO( incoming_val+1 );
01248               rc = strings_containment(stored->f_sub_any, incoming_val);
01249               op->o_tmpfree( incoming_val[0].bv_val, op->o_tmpmemctx );
01250        }
01251 final:
01252        return rc;
01253 }
01254 
01255 static Filter *
01256 filter_first( Filter *f )
01257 {
01258        while ( f->f_choice == LDAP_FILTER_OR || f->f_choice == LDAP_FILTER_AND )
01259               f = f->f_and;
01260        return f;
01261 }
01262 
01263 typedef struct fstack {
01264        struct fstack *fs_next;
01265        Filter *fs_fs;
01266        Filter *fs_fi;
01267 } fstack;
01268 
01269 static CachedQuery *
01270 find_filter( Operation *op, Avlnode *root, Filter *inputf, Filter *first )
01271 {
01272        Filter* fs;
01273        Filter* fi;
01274        MatchingRule* mrule = NULL;
01275        int res=0, eqpass= 0;
01276        int ret, rc, dir;
01277        Avlnode *ptr;
01278        CachedQuery cq, *qc;
01279        fstack *stack = NULL, *fsp;
01280 
01281        cq.filter = inputf;
01282        cq.first = first;
01283 
01284        /* substring matches sort to the end, and we just have to
01285         * walk the entire list.
01286         */
01287        if ( first->f_choice == LDAP_FILTER_SUBSTRINGS ) {
01288               ptr = tavl_end( root, 1 );
01289               dir = TAVL_DIR_LEFT;
01290        } else {
01291               ptr = tavl_find3( root, &cq, pcache_query_cmp, &ret );
01292               dir = (first->f_choice == LDAP_FILTER_GE) ? TAVL_DIR_LEFT :
01293                      TAVL_DIR_RIGHT;
01294        }
01295 
01296        while (ptr) {
01297               qc = ptr->avl_data;
01298               fi = inputf;
01299               fs = qc->filter;
01300 
01301               /* an incoming substr query can only be satisfied by a cached
01302                * substr query.
01303                */
01304               if ( first->f_choice == LDAP_FILTER_SUBSTRINGS &&
01305                      qc->first->f_choice != LDAP_FILTER_SUBSTRINGS )
01306                      break;
01307 
01308               /* an incoming eq query can be satisfied by a cached eq or substr
01309                * query
01310                */
01311               if ( first->f_choice == LDAP_FILTER_EQUALITY ) {
01312                      if ( eqpass == 0 ) {
01313                             if ( qc->first->f_choice != LDAP_FILTER_EQUALITY ) {
01314 nextpass:                   eqpass = 1;
01315                                    ptr = tavl_end( root, 1 );
01316                                    dir = TAVL_DIR_LEFT;
01317                                    continue;
01318                             }
01319                      } else {
01320                             if ( qc->first->f_choice != LDAP_FILTER_SUBSTRINGS )
01321                                    break;
01322                      }
01323               }
01324               do {
01325                      res=0;
01326                      switch (fs->f_choice) {
01327                      case LDAP_FILTER_EQUALITY:
01328                             if (fi->f_choice == LDAP_FILTER_EQUALITY)
01329                                    mrule = fs->f_ava->aa_desc->ad_type->sat_equality;
01330                             else
01331                                    ret = 1;
01332                             break;
01333                      case LDAP_FILTER_GE:
01334                      case LDAP_FILTER_LE:
01335                             mrule = fs->f_ava->aa_desc->ad_type->sat_ordering;
01336                             break;
01337                      default:
01338                             mrule = NULL; 
01339                      }
01340                      if (mrule) {
01341                             const char *text;
01342                             rc = value_match(&ret, fs->f_ava->aa_desc, mrule,
01343                                    SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
01344                                    &(fi->f_ava->aa_value),
01345                                    &(fs->f_ava->aa_value), &text);
01346                             if (rc != LDAP_SUCCESS) {
01347                                    return NULL;
01348                             }
01349                             if ( fi==first && fi->f_choice==LDAP_FILTER_EQUALITY && ret )
01350                                    goto nextpass;
01351                      }
01352                      switch (fs->f_choice) {
01353                      case LDAP_FILTER_OR:
01354                      case LDAP_FILTER_AND:
01355                             if ( fs->f_next ) {
01356                                    /* save our stack position */
01357                                    fsp = op->o_tmpalloc(sizeof(fstack), op->o_tmpmemctx);
01358                                    fsp->fs_next = stack;
01359                                    fsp->fs_fs = fs->f_next;
01360                                    fsp->fs_fi = fi->f_next;
01361                                    stack = fsp;
01362                             }
01363                             fs = fs->f_and;
01364                             fi = fi->f_and;
01365                             res=1;
01366                             break;
01367                      case LDAP_FILTER_SUBSTRINGS:
01368                             /* check if the equality query can be
01369                             * answered with cached substring query */
01370                             if ((fi->f_choice == LDAP_FILTER_EQUALITY)
01371                                    && substr_containment_equality( op,
01372                                    fs, fi))
01373                                    res=1;
01374                             /* check if the substring query can be
01375                             * answered with cached substring query */
01376                             if ((fi->f_choice ==LDAP_FILTER_SUBSTRINGS
01377                                    ) && substr_containment_substr( op,
01378                                    fs, fi))
01379                                    res= 1;
01380                             fs=fs->f_next;
01381                             fi=fi->f_next;
01382                             break;
01383                      case LDAP_FILTER_PRESENT:
01384                             res=1;
01385                             fs=fs->f_next;
01386                             fi=fi->f_next;
01387                             break;
01388                      case LDAP_FILTER_EQUALITY:
01389                             if (ret == 0)
01390                                    res = 1;
01391                             fs=fs->f_next;
01392                             fi=fi->f_next;
01393                             break;
01394                      case LDAP_FILTER_GE:
01395                             if (mrule && ret >= 0)
01396                                    res = 1;
01397                             fs=fs->f_next;
01398                             fi=fi->f_next;
01399                             break;
01400                      case LDAP_FILTER_LE:
01401                             if (mrule && ret <= 0)
01402                                    res = 1;
01403                             fs=fs->f_next;
01404                             fi=fi->f_next;
01405                             break;
01406                      case LDAP_FILTER_NOT:
01407                             res=0;
01408                             break;
01409                      default:
01410                             break;
01411                      }
01412                      if (!fs && !fi && stack) {
01413                             /* pop the stack */
01414                             fsp = stack;
01415                             stack = fsp->fs_next;
01416                             fs = fsp->fs_fs;
01417                             fi = fsp->fs_fi;
01418                             op->o_tmpfree(fsp, op->o_tmpmemctx);
01419                      }
01420               } while((res) && (fi != NULL) && (fs != NULL));
01421 
01422               if ( res )
01423                      return qc;
01424               ptr = tavl_next( ptr, dir );
01425        }
01426        return NULL;
01427 }
01428 
01429 /* check whether query is contained in any of
01430  * the cached queries in template
01431  */
01432 static CachedQuery *
01433 query_containment(Operation *op, query_manager *qm,
01434                 Query *query,
01435                 QueryTemplate *templa)
01436 {
01437        CachedQuery* qc;
01438        int depth = 0, tscope;
01439        Qbase qbase, *qbptr = NULL;
01440        struct berval pdn;
01441 
01442        if (query->filter != NULL) {
01443               Filter *first;
01444 
01445               Debug( pcache_debug, "Lock QC index = %p\n",
01446                             (void *) templa, 0, 0 );
01447               qbase.base = query->base;
01448 
01449               first = filter_first( query->filter );
01450 
01451               ldap_pvt_thread_rdwr_rlock(&templa->t_rwlock);
01452               for( ;; ) {
01453                      /* Find the base */
01454                      qbptr = avl_find( templa->qbase, &qbase, pcache_dn_cmp );
01455                      if ( qbptr ) {
01456                             tscope = query->scope;
01457                             /* Find a matching scope:
01458                              * match at depth 0 OK
01459                              * scope is BASE,
01460                              *     one at depth 1 OK
01461                              *  subord at depth > 0 OK
01462                              *     subtree at any depth OK
01463                              * scope is ONE,
01464                              *  subtree or subord at any depth OK
01465                              * scope is SUBORD,
01466                              *  subtree or subord at any depth OK
01467                              * scope is SUBTREE,
01468                              *  subord at depth > 0 OK
01469                              *  subtree at any depth OK
01470                              */
01471                             for ( tscope = 0 ; tscope <= LDAP_SCOPE_CHILDREN; tscope++ ) {
01472                                    switch ( query->scope ) {
01473                                    case LDAP_SCOPE_BASE:
01474                                           if ( tscope == LDAP_SCOPE_BASE && depth ) continue;
01475                                           if ( tscope == LDAP_SCOPE_ONE && depth != 1) continue;
01476                                           if ( tscope == LDAP_SCOPE_CHILDREN && !depth ) continue;
01477                                           break;
01478                                    case LDAP_SCOPE_ONE:
01479                                           if ( tscope == LDAP_SCOPE_BASE )
01480                                                  tscope = LDAP_SCOPE_ONE;
01481                                           if ( tscope == LDAP_SCOPE_ONE && depth ) continue;
01482                                           if ( !depth ) break;
01483                                           if ( tscope < LDAP_SCOPE_SUBTREE )
01484                                                  tscope = LDAP_SCOPE_SUBTREE;
01485                                           break;
01486                                    case LDAP_SCOPE_SUBTREE:
01487                                           if ( tscope < LDAP_SCOPE_SUBTREE )
01488                                                  tscope = LDAP_SCOPE_SUBTREE;
01489                                           if ( tscope == LDAP_SCOPE_CHILDREN && !depth ) continue;
01490                                           break;
01491                                    case LDAP_SCOPE_CHILDREN:
01492                                           if ( tscope < LDAP_SCOPE_SUBTREE )
01493                                                  tscope = LDAP_SCOPE_SUBTREE;
01494                                           break;
01495                                    }
01496                                    if ( !qbptr->scopes[tscope] ) continue;
01497 
01498                                    /* Find filter */
01499                                    qc = find_filter( op, qbptr->scopes[tscope],
01500                                                  query->filter, first );
01501                                    if ( qc ) {
01502                                           if ( qc->q_sizelimit ) {
01503                                                  ldap_pvt_thread_rdwr_runlock(&templa->t_rwlock);
01504                                                  return NULL;
01505                                           }
01506                                           ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
01507                                           if (qm->lru_top != qc) {
01508                                                  remove_query(qm, qc);
01509                                                  add_query_on_top(qm, qc);
01510                                           }
01511                                           ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
01512                                           return qc;
01513                                    }
01514                             }
01515                      }
01516                      if ( be_issuffix( op->o_bd, &qbase.base ))
01517                             break;
01518                      /* Up a level */
01519                      dnParent( &qbase.base, &pdn );
01520                      qbase.base = pdn;
01521                      depth++;
01522               }
01523 
01524               Debug( pcache_debug,
01525                      "Not answerable: Unlock QC index=%p\n",
01526                      (void *) templa, 0, 0 );
01527               ldap_pvt_thread_rdwr_runlock(&templa->t_rwlock);
01528        }
01529        return NULL;
01530 }
01531 
01532 static void
01533 free_query (CachedQuery* qc)
01534 {
01535        free(qc->q_uuid.bv_val);
01536        filter_free(qc->filter);
01537        ldap_pvt_thread_mutex_destroy(&qc->answerable_cnt_mutex);
01538        ldap_pvt_thread_rdwr_destroy( &qc->rwlock );
01539        memset(qc, 0, sizeof(*qc));
01540        free(qc);
01541 }
01542 
01543 
01544 /* Add query to query cache, the returned Query is locked for writing */
01545 static CachedQuery *
01546 add_query(
01547        Operation *op,
01548        query_manager* qm,
01549        Query* query,
01550        QueryTemplate *templ,
01551        pc_caching_reason_t why,
01552        int wlock)
01553 {
01554        CachedQuery* new_cached_query = (CachedQuery*) ch_malloc(sizeof(CachedQuery));
01555        Qbase *qbase, qb;
01556        Filter *first;
01557        int rc;
01558        time_t ttl = 0, ttr = 0;
01559        time_t now;
01560 
01561        new_cached_query->qtemp = templ;
01562        BER_BVZERO( &new_cached_query->q_uuid );
01563        new_cached_query->q_sizelimit = 0;
01564 
01565        now = slap_get_time();
01566        switch ( why ) {
01567        case PC_POSITIVE:
01568               ttl = templ->ttl;
01569               if ( templ->ttr )
01570                      ttr = now + templ->ttr;
01571               break;
01572 
01573        case PC_NEGATIVE:
01574               ttl = templ->negttl;
01575               break;
01576 
01577        case PC_SIZELIMIT:
01578               ttl = templ->limitttl;
01579               break;
01580 
01581        default:
01582               assert( 0 );
01583               break;
01584        }
01585        new_cached_query->expiry_time = now + ttl;
01586        new_cached_query->refresh_time = ttr;
01587        new_cached_query->bindref_time = 0;
01588 
01589        new_cached_query->bind_refcnt = 0;
01590        new_cached_query->answerable_cnt = 0;
01591        new_cached_query->refcnt = 1;
01592        ldap_pvt_thread_mutex_init(&new_cached_query->answerable_cnt_mutex);
01593 
01594        new_cached_query->lru_up = NULL;
01595        new_cached_query->lru_down = NULL;
01596        Debug( pcache_debug, "Added query expires at %ld (%s)\n",
01597                      (long) new_cached_query->expiry_time,
01598                      pc_caching_reason_str[ why ], 0 );
01599 
01600        new_cached_query->scope = query->scope;
01601        new_cached_query->filter = query->filter;
01602        new_cached_query->first = first = filter_first( query->filter );
01603        
01604        ldap_pvt_thread_rdwr_init(&new_cached_query->rwlock);
01605        if (wlock)
01606               ldap_pvt_thread_rdwr_wlock(&new_cached_query->rwlock);
01607 
01608        qb.base = query->base;
01609 
01610        /* Adding a query    */
01611        Debug( pcache_debug, "Lock AQ index = %p\n",
01612                      (void *) templ, 0, 0 );
01613        ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock);
01614        qbase = avl_find( templ->qbase, &qb, pcache_dn_cmp );
01615        if ( !qbase ) {
01616               qbase = ch_calloc( 1, sizeof(Qbase) + qb.base.bv_len + 1 );
01617               qbase->base.bv_len = qb.base.bv_len;
01618               qbase->base.bv_val = (char *)(qbase+1);
01619               memcpy( qbase->base.bv_val, qb.base.bv_val, qb.base.bv_len );
01620               qbase->base.bv_val[qbase->base.bv_len] = '\0';
01621               avl_insert( &templ->qbase, qbase, pcache_dn_cmp, avl_dup_error );
01622        }
01623        new_cached_query->next = templ->query;
01624        new_cached_query->prev = NULL;
01625        new_cached_query->qbase = qbase;
01626        rc = tavl_insert( &qbase->scopes[query->scope], new_cached_query,
01627               pcache_query_cmp, avl_dup_error );
01628        if ( rc == 0 ) {
01629               qbase->queries++;
01630               if (templ->query == NULL)
01631                      templ->query_last = new_cached_query;
01632               else
01633                      templ->query->prev = new_cached_query;
01634               templ->query = new_cached_query;
01635               templ->no_of_queries++;
01636        } else {
01637               ldap_pvt_thread_mutex_destroy(&new_cached_query->answerable_cnt_mutex);
01638               if (wlock)
01639                      ldap_pvt_thread_rdwr_wunlock(&new_cached_query->rwlock);
01640               ldap_pvt_thread_rdwr_destroy( &new_cached_query->rwlock );
01641               ch_free( new_cached_query );
01642               new_cached_query = find_filter( op, qbase->scopes[query->scope],
01643                                                  query->filter, first );
01644               filter_free( query->filter );
01645               query->filter = NULL;
01646        }
01647        Debug( pcache_debug, "TEMPLATE %p QUERIES++ %d\n",
01648                      (void *) templ, templ->no_of_queries, 0 );
01649 
01650        /* Adding on top of LRU list  */
01651        if ( rc == 0 ) {
01652               ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
01653               add_query_on_top(qm, new_cached_query);
01654               ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
01655        }
01656        Debug( pcache_debug, "Unlock AQ index = %p \n",
01657                      (void *) templ, 0, 0 );
01658        ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
01659 
01660        return rc == 0 ? new_cached_query : NULL;
01661 }
01662 
01663 static void
01664 remove_from_template (CachedQuery* qc, QueryTemplate* template)
01665 {
01666        if (!qc->prev && !qc->next) {
01667               template->query_last = template->query = NULL;
01668        } else if (qc->prev == NULL) {
01669               qc->next->prev = NULL;
01670               template->query = qc->next;
01671        } else if (qc->next == NULL) {
01672               qc->prev->next = NULL;
01673               template->query_last = qc->prev;
01674        } else {
01675               qc->next->prev = qc->prev;
01676               qc->prev->next = qc->next;
01677        }
01678        tavl_delete( &qc->qbase->scopes[qc->scope], qc, pcache_query_cmp );
01679        qc->qbase->queries--;
01680        if ( qc->qbase->queries == 0 ) {
01681               avl_delete( &template->qbase, qc->qbase, pcache_dn_cmp );
01682               ch_free( qc->qbase );
01683               qc->qbase = NULL;
01684        }
01685 
01686        template->no_of_queries--;
01687 }
01688 
01689 /* remove bottom query of LRU list from the query cache */
01690 /*
01691  * NOTE: slight change in functionality.
01692  *
01693  * - if result->bv_val is NULL, the query at the bottom of the LRU
01694  *   is removed
01695  * - otherwise, the query whose UUID is *result is removed
01696  *     - if not found, result->bv_val is zeroed
01697  */
01698 static void
01699 cache_replacement(query_manager* qm, struct berval *result)
01700 {
01701        CachedQuery* bottom;
01702        QueryTemplate *temp;
01703 
01704        ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
01705        if ( BER_BVISNULL( result ) ) {
01706               bottom = qm->lru_bottom;
01707 
01708               if (!bottom) {
01709                      Debug ( pcache_debug,
01710                             "Cache replacement invoked without "
01711                             "any query in LRU list\n", 0, 0, 0 );
01712                      ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
01713                      return;
01714               }
01715 
01716        } else {
01717               for ( bottom = qm->lru_bottom;
01718                      bottom != NULL;
01719                      bottom = bottom->lru_up )
01720               {
01721                      if ( bvmatch( result, &bottom->q_uuid ) ) {
01722                             break;
01723                      }
01724               }
01725 
01726               if ( !bottom ) {
01727                      Debug ( pcache_debug,
01728                             "Could not find query with uuid=\"%s\""
01729                             "in LRU list\n", result->bv_val, 0, 0 );
01730                      ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
01731                      BER_BVZERO( result );
01732                      return;
01733               }
01734        }
01735 
01736        temp = bottom->qtemp;
01737        remove_query(qm, bottom);
01738        ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
01739 
01740        *result = bottom->q_uuid;
01741        BER_BVZERO( &bottom->q_uuid );
01742 
01743        Debug( pcache_debug, "Lock CR index = %p\n", (void *) temp, 0, 0 );
01744        ldap_pvt_thread_rdwr_wlock(&temp->t_rwlock);
01745        remove_from_template(bottom, temp);
01746        Debug( pcache_debug, "TEMPLATE %p QUERIES-- %d\n",
01747               (void *) temp, temp->no_of_queries, 0 );
01748        Debug( pcache_debug, "Unlock CR index = %p\n", (void *) temp, 0, 0 );
01749        ldap_pvt_thread_rdwr_wunlock(&temp->t_rwlock);
01750        free_query(bottom);
01751 }
01752 
01753 struct query_info {
01754        struct query_info *next;
01755        struct berval xdn;
01756        int del;
01757 };
01758 
01759 static int
01760 remove_func (
01761        Operation     *op,
01762        SlapReply     *rs
01763 )
01764 {
01765        Attribute *attr;
01766        struct query_info *qi;
01767        int count = 0;
01768 
01769        if ( rs->sr_type != REP_SEARCH ) return 0;
01770 
01771        attr = attr_find( rs->sr_entry->e_attrs,  ad_queryId );
01772        if ( attr == NULL ) return 0;
01773 
01774        count = attr->a_numvals;
01775        assert( count > 0 );
01776        qi = op->o_tmpalloc( sizeof( struct query_info ), op->o_tmpmemctx );
01777        qi->next = op->o_callback->sc_private;
01778        op->o_callback->sc_private = qi;
01779        ber_dupbv_x( &qi->xdn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
01780        qi->del = ( count == 1 );
01781 
01782        return 0;
01783 }
01784 
01785 static int
01786 remove_query_data(
01787        Operation     *op,
01788        struct berval *query_uuid )
01789 {
01790        struct query_info    *qi, *qnext;
01791        char                 filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ];
01792        AttributeAssertion   ava = ATTRIBUTEASSERTION_INIT;
01793        Filter               filter = {LDAP_FILTER_EQUALITY};
01794        SlapReply            sreply = {REP_RESULT};
01795        slap_callback cb = { NULL, remove_func, NULL, NULL };
01796        int deleted = 0;
01797 
01798        op->ors_filterstr.bv_len = snprintf(filter_str, sizeof(filter_str),
01799               "(%s=%s)", ad_queryId->ad_cname.bv_val, query_uuid->bv_val);
01800        filter.f_ava = &ava;
01801        filter.f_av_desc = ad_queryId;
01802        filter.f_av_value = *query_uuid;
01803 
01804        op->o_tag = LDAP_REQ_SEARCH;
01805        op->o_protocol = LDAP_VERSION3;
01806        op->o_callback = &cb;
01807        op->o_time = slap_get_time();
01808        op->o_do_not_cache = 1;
01809 
01810        op->o_req_dn = op->o_bd->be_suffix[0];
01811        op->o_req_ndn = op->o_bd->be_nsuffix[0];
01812        op->ors_scope = LDAP_SCOPE_SUBTREE;
01813        op->ors_deref = LDAP_DEREF_NEVER;
01814        op->ors_slimit = SLAP_NO_LIMIT;
01815        op->ors_tlimit = SLAP_NO_LIMIT;
01816        op->ors_limit = NULL;
01817        op->ors_filter = &filter;
01818        op->ors_filterstr.bv_val = filter_str;
01819        op->ors_filterstr.bv_len = strlen(filter_str);
01820        op->ors_attrs = NULL;
01821        op->ors_attrsonly = 0;
01822 
01823        op->o_bd->be_search( op, &sreply );
01824 
01825        for ( qi=cb.sc_private; qi; qi=qnext ) {
01826               qnext = qi->next;
01827 
01828               op->o_req_dn = qi->xdn;
01829               op->o_req_ndn = qi->xdn;
01830               rs_reinit( &sreply, REP_RESULT );
01831 
01832               if ( qi->del ) {
01833                      Debug( pcache_debug, "DELETING ENTRY TEMPLATE=%s\n",
01834                             query_uuid->bv_val, 0, 0 );
01835 
01836                      op->o_tag = LDAP_REQ_DELETE;
01837 
01838                      if (op->o_bd->be_delete(op, &sreply) == LDAP_SUCCESS) {
01839                             deleted++;
01840                      }
01841 
01842               } else {
01843                      Modifications mod;
01844                      struct berval vals[2];
01845 
01846                      vals[0] = *query_uuid;
01847                      vals[1].bv_val = NULL;
01848                      vals[1].bv_len = 0;
01849                      mod.sml_op = LDAP_MOD_DELETE;
01850                      mod.sml_flags = 0;
01851                      mod.sml_desc = ad_queryId;
01852                      mod.sml_type = ad_queryId->ad_cname;
01853                      mod.sml_values = vals;
01854                      mod.sml_nvalues = NULL;
01855                         mod.sml_numvals = 1;
01856                      mod.sml_next = NULL;
01857                      Debug( pcache_debug,
01858                             "REMOVING TEMP ATTR : TEMPLATE=%s\n",
01859                             query_uuid->bv_val, 0, 0 );
01860 
01861                      op->orm_modlist = &mod;
01862 
01863                      op->o_bd->be_modify( op, &sreply );
01864               }
01865               op->o_tmpfree( qi->xdn.bv_val, op->o_tmpmemctx );
01866               op->o_tmpfree( qi, op->o_tmpmemctx );
01867        }
01868        return deleted;
01869 }
01870 
01871 static int
01872 get_attr_set(
01873        AttributeName* attrs,
01874        query_manager* qm,
01875        int num
01876 );
01877 
01878 static int
01879 filter2template(
01880        Operation            *op,
01881        Filter               *f,
01882        struct               berval *fstr )
01883 {
01884        AttributeDescription *ad;
01885        int len, ret;
01886 
01887        switch ( f->f_choice ) {
01888        case LDAP_FILTER_EQUALITY:
01889               ad = f->f_av_desc;
01890               len = STRLENOF( "(=)" ) + ad->ad_cname.bv_len;
01891               ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s=)", ad->ad_cname.bv_val );
01892               assert( ret == len );
01893               fstr->bv_len += len;
01894               break;
01895 
01896        case LDAP_FILTER_GE:
01897               ad = f->f_av_desc;
01898               len = STRLENOF( "(>=)" ) + ad->ad_cname.bv_len;
01899               ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s>=)", ad->ad_cname.bv_val);
01900               assert( ret == len );
01901               fstr->bv_len += len;
01902               break;
01903 
01904        case LDAP_FILTER_LE:
01905               ad = f->f_av_desc;
01906               len = STRLENOF( "(<=)" ) + ad->ad_cname.bv_len;
01907               ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s<=)", ad->ad_cname.bv_val);
01908               assert( ret == len );
01909               fstr->bv_len += len;
01910               break;
01911 
01912        case LDAP_FILTER_APPROX:
01913               ad = f->f_av_desc;
01914               len = STRLENOF( "(~=)" ) + ad->ad_cname.bv_len;
01915               ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s~=)", ad->ad_cname.bv_val);
01916               assert( ret == len );
01917               fstr->bv_len += len;
01918               break;
01919 
01920        case LDAP_FILTER_SUBSTRINGS:
01921               ad = f->f_sub_desc;
01922               len = STRLENOF( "(=)" ) + ad->ad_cname.bv_len;
01923               ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s=)", ad->ad_cname.bv_val );
01924               assert( ret == len );
01925               fstr->bv_len += len;
01926               break;
01927 
01928        case LDAP_FILTER_PRESENT:
01929               ad = f->f_desc;
01930               len = STRLENOF( "(=*)" ) + ad->ad_cname.bv_len;
01931               ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s=*)", ad->ad_cname.bv_val );
01932               assert( ret == len );
01933               fstr->bv_len += len;
01934               break;
01935 
01936        case LDAP_FILTER_AND:
01937        case LDAP_FILTER_OR:
01938        case LDAP_FILTER_NOT: {
01939               int rc = 0;
01940               fstr->bv_val[fstr->bv_len++] = '(';
01941               switch ( f->f_choice ) {
01942               case LDAP_FILTER_AND:
01943                      fstr->bv_val[fstr->bv_len] = '&';
01944                      break;
01945               case LDAP_FILTER_OR:
01946                      fstr->bv_val[fstr->bv_len] = '|';
01947                      break;
01948               case LDAP_FILTER_NOT:
01949                      fstr->bv_val[fstr->bv_len] = '!';
01950                      break;
01951               }
01952               fstr->bv_len++;
01953 
01954               for ( f = f->f_list; f != NULL; f = f->f_next ) {
01955                      rc = filter2template( op, f, fstr );
01956                      if ( rc ) break;
01957               }
01958               fstr->bv_val[fstr->bv_len++] = ')';
01959               fstr->bv_val[fstr->bv_len] = '\0';
01960 
01961               return rc;
01962               }
01963 
01964        default:
01965               /* a filter should at least have room for "()",
01966                * an "=" and for a 1-char attr */
01967               strcpy( fstr->bv_val, "(?=)" );
01968               fstr->bv_len += STRLENOF("(?=)");
01969               return -1;
01970        }
01971 
01972        return 0;
01973 }
01974 
01975 #define       BI_HASHED     0x01
01976 #define       BI_DIDCB      0x02
01977 #define       BI_LOOKUP     0x04
01978 
01979 struct search_info;
01980 
01981 typedef struct bindinfo {
01982        cache_manager *bi_cm;
01983        CachedQuery *bi_cq;
01984        QueryTemplate *bi_templ;
01985        struct search_info *bi_si;
01986        int bi_flags;
01987        slap_callback bi_cb;
01988 } bindinfo;
01989 
01990 struct search_info {
01991        slap_overinst *on;
01992        Query query;
01993        QueryTemplate *qtemp;
01994        AttributeName*  save_attrs; /* original attributes, saved for response */
01995        int swap_saved_attrs;
01996        int max;
01997        int over;
01998        int count;
01999        int slimit;
02000        int slimit_exceeded;
02001        pc_caching_reason_t caching_reason;
02002        Entry *head, *tail;
02003        bindinfo *pbi;
02004 };
02005 
02006 static void
02007 remove_query_and_data(
02008        Operation     *op,
02009        cache_manager *cm,
02010        struct berval *uuid )
02011 {
02012        query_manager*              qm = cm->qm;
02013 
02014        qm->crfunc( qm, uuid );
02015        if ( !BER_BVISNULL( uuid ) ) {
02016               int    return_val;
02017 
02018               Debug( pcache_debug,
02019                      "Removing query UUID %s\n",
02020                      uuid->bv_val, 0, 0 );
02021               return_val = remove_query_data( op, uuid );
02022               Debug( pcache_debug,
02023                      "QUERY REMOVED, SIZE=%d\n",
02024                      return_val, 0, 0);
02025               ldap_pvt_thread_mutex_lock( &cm->cache_mutex );
02026               cm->cur_entries -= return_val;
02027               cm->num_cached_queries--;
02028               Debug( pcache_debug,
02029                      "STORED QUERIES = %lu\n",
02030                      cm->num_cached_queries, 0, 0 );
02031               ldap_pvt_thread_mutex_unlock( &cm->cache_mutex );
02032               Debug( pcache_debug,
02033                      "QUERY REMOVED, CACHE ="
02034                      "%d entries\n",
02035                      cm->cur_entries, 0, 0 );
02036        }
02037 }
02038 
02039 /*
02040  * Callback used to fetch queryId values based on entryUUID;
02041  * used by pcache_remove_entries_from_cache()
02042  */
02043 static int
02044 fetch_queryId_cb( Operation *op, SlapReply *rs )
02045 {
02046        int           rc = 0;
02047 
02048        /* only care about searchEntry responses */
02049        if ( rs->sr_type != REP_SEARCH ) {
02050               return 0;
02051        }
02052 
02053        /* allow only one response per entryUUID */
02054        if ( op->o_callback->sc_private != NULL ) {
02055               rc = 1;
02056 
02057        } else {
02058               Attribute     *a;
02059 
02060               /* copy all queryId values into callback's private data */
02061               a = attr_find( rs->sr_entry->e_attrs, ad_queryId );
02062               if ( a != NULL ) {
02063                      BerVarray     vals = NULL;
02064 
02065                      ber_bvarray_dup_x( &vals, a->a_nvals, op->o_tmpmemctx );
02066                      op->o_callback->sc_private = (void *)vals;
02067               }
02068        }
02069 
02070        /* clear entry if required */
02071        rs_flush_entry( op, rs, (slap_overinst *) op->o_bd->bd_info );
02072 
02073        return rc;
02074 }
02075 
02076 /*
02077  * Call that allows to remove a set of entries from the cache,
02078  * by forcing the removal of all the related queries.
02079  */
02080 int
02081 pcache_remove_entries_from_cache(
02082        Operation     *op,
02083        cache_manager *cm,
02084        BerVarray     entryUUIDs )
02085 {
02086        Connection    conn = { 0 };
02087        OperationBuffer opbuf;
02088        Operation     op2;
02089        slap_callback sc = { 0 };
02090        Filter        f = { 0 };
02091        char          filtbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(entryUUID=)" ) ];
02092        AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
02093        AttributeName attrs[ 2 ] = {{{ 0 }}};
02094        int           s, rc;
02095 
02096        if ( op == NULL ) {
02097               void   *thrctx = ldap_pvt_thread_pool_context();
02098 
02099               connection_fake_init( &conn, &opbuf, thrctx );
02100               op = &opbuf.ob_op;
02101 
02102        } else {
02103               op2 = *op;
02104               op = &op2;
02105        }
02106 
02107        memset( &op->oq_search, 0, sizeof( op->oq_search ) );
02108        op->ors_scope = LDAP_SCOPE_SUBTREE;
02109        op->ors_deref = LDAP_DEREF_NEVER;
02110        f.f_choice = LDAP_FILTER_EQUALITY;
02111        f.f_ava = &ava;
02112        ava.aa_desc = slap_schema.si_ad_entryUUID;
02113        op->ors_filter = &f;
02114        op->ors_slimit = 1;
02115        op->ors_tlimit = SLAP_NO_LIMIT;
02116        op->ors_limit = NULL;
02117        attrs[ 0 ].an_desc = ad_queryId;
02118        attrs[ 0 ].an_name = ad_queryId->ad_cname;
02119        op->ors_attrs = attrs;
02120        op->ors_attrsonly = 0;
02121 
02122        op->o_req_dn = cm->db.be_suffix[ 0 ];
02123        op->o_req_ndn = cm->db.be_nsuffix[ 0 ];
02124 
02125        op->o_tag = LDAP_REQ_SEARCH;
02126        op->o_protocol = LDAP_VERSION3;
02127        op->o_managedsait = SLAP_CONTROL_CRITICAL;
02128        op->o_bd = &cm->db;
02129        op->o_dn = op->o_bd->be_rootdn;
02130        op->o_ndn = op->o_bd->be_rootndn;
02131        sc.sc_response = fetch_queryId_cb;
02132        op->o_callback = &sc;
02133 
02134        for ( s = 0; !BER_BVISNULL( &entryUUIDs[ s ] ); s++ ) {
02135               BerVarray     vals = NULL;
02136               SlapReply     rs = { REP_RESULT };
02137 
02138               op->ors_filterstr.bv_len = snprintf( filtbuf, sizeof( filtbuf ),
02139                      "(entryUUID=%s)", entryUUIDs[ s ].bv_val );
02140               op->ors_filterstr.bv_val = filtbuf;
02141               ava.aa_value = entryUUIDs[ s ];
02142 
02143               rc = op->o_bd->be_search( op, &rs );
02144               if ( rc != LDAP_SUCCESS ) {
02145                      continue;
02146               }
02147 
02148               vals = (BerVarray)op->o_callback->sc_private;
02149               if ( vals != NULL ) {
02150                      int           i;
02151 
02152                      for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
02153                             struct berval val = vals[ i ];
02154 
02155                             remove_query_and_data( op, cm, &val );
02156 
02157                             if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) {
02158                                    ch_free( val.bv_val );
02159                             }
02160                      }
02161 
02162                      ber_bvarray_free_x( vals, op->o_tmpmemctx );
02163                      op->o_callback->sc_private = NULL;
02164               }
02165        }
02166 
02167        return 0;
02168 }
02169 
02170 /*
02171  * Call that allows to remove a query from the cache.
02172  */
02173 int
02174 pcache_remove_query_from_cache(
02175        Operation     *op,
02176        cache_manager *cm,
02177        struct berval *queryid )
02178 {
02179        Operation     op2 = *op;
02180 
02181        op2.o_bd = &cm->db;
02182 
02183        /* remove the selected query */
02184        remove_query_and_data( &op2, cm, queryid );
02185 
02186        return LDAP_SUCCESS;
02187 }
02188 
02189 /*
02190  * Call that allows to remove a set of queries related to an entry 
02191  * from the cache; if queryid is not null, the entry must belong to
02192  * the query indicated by queryid.
02193  */
02194 int
02195 pcache_remove_entry_queries_from_cache(
02196        Operation     *op,
02197        cache_manager *cm,
02198        struct berval *ndn,
02199        struct berval *queryid )
02200 {
02201        Connection           conn = { 0 };
02202        OperationBuffer      opbuf;
02203        Operation            op2;
02204        slap_callback        sc = { 0 };
02205        SlapReply            rs = { REP_RESULT };
02206        Filter               f = { 0 };
02207        char                 filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ];
02208        AttributeAssertion   ava = ATTRIBUTEASSERTION_INIT;
02209        AttributeName        attrs[ 2 ] = {{{ 0 }}};
02210        int                  rc;
02211 
02212        BerVarray            vals = NULL;
02213 
02214        if ( op == NULL ) {
02215               void   *thrctx = ldap_pvt_thread_pool_context();
02216 
02217               connection_fake_init( &conn, &opbuf, thrctx );
02218               op = &opbuf.ob_op;
02219 
02220        } else {
02221               op2 = *op;
02222               op = &op2;
02223        }
02224 
02225        memset( &op->oq_search, 0, sizeof( op->oq_search ) );
02226        op->ors_scope = LDAP_SCOPE_BASE;
02227        op->ors_deref = LDAP_DEREF_NEVER;
02228        if ( queryid == NULL || BER_BVISNULL( queryid ) ) {
02229               BER_BVSTR( &op->ors_filterstr, "(objectClass=*)" );
02230               f.f_choice = LDAP_FILTER_PRESENT;
02231               f.f_desc = slap_schema.si_ad_objectClass;
02232 
02233        } else {
02234               op->ors_filterstr.bv_len = snprintf( filter_str,
02235                      sizeof( filter_str ), "(%s=%s)",
02236                      ad_queryId->ad_cname.bv_val, queryid->bv_val );
02237               f.f_choice = LDAP_FILTER_EQUALITY;
02238               f.f_ava = &ava;
02239               f.f_av_desc = ad_queryId;
02240               f.f_av_value = *queryid;
02241        }
02242        op->ors_filter = &f;
02243        op->ors_slimit = 1;
02244        op->ors_tlimit = SLAP_NO_LIMIT;
02245        op->ors_limit = NULL;
02246        attrs[ 0 ].an_desc = ad_queryId;
02247        attrs[ 0 ].an_name = ad_queryId->ad_cname;
02248        op->ors_attrs = attrs;
02249        op->ors_attrsonly = 0;
02250 
02251        op->o_req_dn = *ndn;
02252        op->o_req_ndn = *ndn;
02253 
02254        op->o_tag = LDAP_REQ_SEARCH;
02255        op->o_protocol = LDAP_VERSION3;
02256        op->o_managedsait = SLAP_CONTROL_CRITICAL;
02257        op->o_bd = &cm->db;
02258        op->o_dn = op->o_bd->be_rootdn;
02259        op->o_ndn = op->o_bd->be_rootndn;
02260        sc.sc_response = fetch_queryId_cb;
02261        op->o_callback = &sc;
02262 
02263        rc = op->o_bd->be_search( op, &rs );
02264        if ( rc != LDAP_SUCCESS ) {
02265               return rc;
02266        }
02267 
02268        vals = (BerVarray)op->o_callback->sc_private;
02269        if ( vals != NULL ) {
02270               int           i;
02271 
02272               for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
02273                      struct berval val = vals[ i ];
02274 
02275                      remove_query_and_data( op, cm, &val );
02276 
02277                      if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) {
02278                             ch_free( val.bv_val );
02279                      }
02280               }
02281 
02282               ber_bvarray_free_x( vals, op->o_tmpmemctx );
02283        }
02284 
02285        return LDAP_SUCCESS;
02286 }
02287 
02288 static int
02289 cache_entries(
02290        Operation     *op,
02291        struct berval *query_uuid )
02292 {
02293        struct search_info *si = op->o_callback->sc_private;
02294        slap_overinst *on = si->on;
02295        cache_manager *cm = on->on_bi.bi_private;
02296        int           return_val = 0;
02297        Entry         *e;
02298        struct berval crp_uuid;
02299        char          uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
02300        Operation     *op_tmp;
02301        Connection    conn = {0};
02302        OperationBuffer opbuf;
02303        void          *thrctx = ldap_pvt_thread_pool_context();
02304 
02305        query_uuid->bv_len = lutil_uuidstr(uuidbuf, sizeof(uuidbuf));
02306        ber_str2bv(uuidbuf, query_uuid->bv_len, 1, query_uuid);
02307 
02308        connection_fake_init2( &conn, &opbuf, thrctx, 0 );
02309        op_tmp = &opbuf.ob_op;
02310        op_tmp->o_bd = &cm->db;
02311        op_tmp->o_dn = cm->db.be_rootdn;
02312        op_tmp->o_ndn = cm->db.be_rootndn;
02313 
02314        Debug( pcache_debug, "UUID for query being added = %s\n",
02315                      uuidbuf, 0, 0 );
02316 
02317        for ( e=si->head; e; e=si->head ) {
02318               si->head = e->e_private;
02319               e->e_private = NULL;
02320               while ( cm->cur_entries > (cm->max_entries) ) {
02321                      BER_BVZERO( &crp_uuid );
02322                      remove_query_and_data( op_tmp, cm, &crp_uuid );
02323               }
02324 
02325               return_val = merge_entry(op_tmp, e, 0, query_uuid);
02326               ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
02327               cm->cur_entries += return_val;
02328               Debug( pcache_debug,
02329                      "ENTRY ADDED/MERGED, CACHED ENTRIES=%d\n",
02330                      cm->cur_entries, 0, 0 );
02331               return_val = 0;
02332               ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
02333        }
02334 
02335        return return_val;
02336 }
02337 
02338 static int
02339 pcache_op_cleanup( Operation *op, SlapReply *rs ) {
02340        slap_callback *cb = op->o_callback;
02341        struct search_info *si = cb->sc_private;
02342        slap_overinst *on = si->on;
02343        cache_manager *cm = on->on_bi.bi_private;
02344        query_manager*              qm = cm->qm;
02345 
02346        if ( rs->sr_type == REP_RESULT || 
02347               op->o_abandon || rs->sr_err == SLAPD_ABANDON )
02348        {
02349               if ( si->swap_saved_attrs ) {
02350                      rs->sr_attrs = si->save_attrs;
02351                      op->ors_attrs = si->save_attrs;
02352               }
02353               if ( (op->o_abandon || rs->sr_err == SLAPD_ABANDON) && 
02354                             si->caching_reason == PC_IGNORE )
02355               {
02356                      filter_free( si->query.filter );
02357                      if ( si->count ) {
02358                             /* duplicate query, free it */
02359                             Entry *e;
02360                             for (;si->head; si->head=e) {
02361                                    e = si->head->e_private;
02362                                    si->head->e_private = NULL;
02363                                    entry_free(si->head);
02364                             }
02365                      }
02366 
02367               } else if ( si->caching_reason != PC_IGNORE ) {
02368                      CachedQuery *qc = qm->addfunc(op, qm, &si->query,
02369                             si->qtemp, si->caching_reason, 1 );
02370 
02371                      if ( qc != NULL ) {
02372                             switch ( si->caching_reason ) {
02373                             case PC_POSITIVE:
02374                                    cache_entries( op, &qc->q_uuid );
02375                                    if ( si->pbi ) {
02376                                           qc->bind_refcnt++;
02377                                           si->pbi->bi_cq = qc;
02378                                    }
02379                                    break;
02380 
02381                             case PC_SIZELIMIT:
02382                                    qc->q_sizelimit = rs->sr_nentries;
02383                                    break;
02384 
02385                             case PC_NEGATIVE:
02386                                    break;
02387 
02388                             default:
02389                                    assert( 0 );
02390                                    break;
02391                             }
02392                             ldap_pvt_thread_rdwr_wunlock(&qc->rwlock);
02393                             ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
02394                             cm->num_cached_queries++;
02395                             Debug( pcache_debug, "STORED QUERIES = %lu\n",
02396                                           cm->num_cached_queries, 0, 0 );
02397                             ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
02398 
02399                             /* If the consistency checker suspended itself,
02400                              * wake it back up
02401                              */
02402                             if ( cm->cc_paused == PCACHE_CC_PAUSED ) {
02403                                    ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
02404                                    if ( cm->cc_paused == PCACHE_CC_PAUSED ) {
02405                                           cm->cc_paused = 0;
02406                                           ldap_pvt_runqueue_resched( &slapd_rq, cm->cc_arg, 0 );
02407                                    }
02408                                    ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
02409                             }
02410 
02411                      } else if ( si->count ) {
02412                             /* duplicate query, free it */
02413                             Entry *e;
02414                             for (;si->head; si->head=e) {
02415                                    e = si->head->e_private;
02416                                    si->head->e_private = NULL;
02417                                    entry_free(si->head);
02418                             }
02419                      }
02420 
02421               } else {
02422                      filter_free( si->query.filter );
02423               }
02424 
02425               op->o_callback = op->o_callback->sc_next;
02426               op->o_tmpfree( cb, op->o_tmpmemctx );
02427        }
02428 
02429        return SLAP_CB_CONTINUE;
02430 }
02431 
02432 static int
02433 pcache_response(
02434        Operation     *op,
02435        SlapReply     *rs )
02436 {
02437        struct search_info *si = op->o_callback->sc_private;
02438 
02439        if ( si->swap_saved_attrs ) {
02440               rs->sr_attrs = si->save_attrs;
02441               op->ors_attrs = si->save_attrs;
02442        }
02443 
02444        if ( rs->sr_type == REP_SEARCH ) {
02445               Entry *e;
02446 
02447               /* don't return more entries than requested by the client */
02448               if ( si->slimit > 0 && rs->sr_nentries >= si->slimit ) {
02449                      si->slimit_exceeded = 1;
02450               }
02451 
02452               /* If we haven't exceeded the limit for this query,
02453                * build a chain of answers to store. If we hit the
02454                * limit, empty the chain and ignore the rest.
02455                */
02456               if ( !si->over ) {
02457                      slap_overinst *on = si->on;
02458                      cache_manager *cm = on->on_bi.bi_private;
02459 
02460                      /* check if the entry contains undefined
02461                       * attributes/objectClasses (ITS#5680) */
02462                      if ( cm->check_cacheability && test_filter( op, rs->sr_entry, si->query.filter ) != LDAP_COMPARE_TRUE ) {
02463                             Debug( pcache_debug, "%s: query not cacheable because of schema issues in DN \"%s\"\n",
02464                                    op->o_log_prefix, rs->sr_entry->e_name.bv_val, 0 );
02465                             goto over;
02466                      }
02467 
02468                      /* check for malformed entries: attrs with no values */
02469                      {
02470                             Attribute *a = rs->sr_entry->e_attrs;
02471                             for (; a; a=a->a_next) {
02472                                    if ( !a->a_numvals ) {
02473                                           Debug( pcache_debug, "%s: query not cacheable because of attrs without values in DN \"%s\" (%s)\n",
02474                                           op->o_log_prefix, rs->sr_entry->e_name.bv_val,
02475                                           a->a_desc->ad_cname.bv_val );
02476                                           goto over;
02477                                    }
02478                             }
02479                      }
02480 
02481                      if ( si->count < si->max ) {
02482                             si->count++;
02483                             e = entry_dup( rs->sr_entry );
02484                             if ( !si->head ) si->head = e;
02485                             if ( si->tail ) si->tail->e_private = e;
02486                             si->tail = e;
02487 
02488                      } else {
02489 over:;
02490                             si->over = 1;
02491                             si->count = 0;
02492                             for (;si->head; si->head=e) {
02493                                    e = si->head->e_private;
02494                                    si->head->e_private = NULL;
02495                                    entry_free(si->head);
02496                             }
02497                             si->tail = NULL;
02498                      }
02499               }
02500               if ( si->slimit_exceeded ) {
02501                      return 0;
02502               }
02503        } else if ( rs->sr_type == REP_RESULT ) {
02504 
02505               if ( si->count ) {
02506                      if ( rs->sr_err == LDAP_SUCCESS ) {
02507                             si->caching_reason = PC_POSITIVE;
02508 
02509                      } else if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED
02510                             && si->qtemp->limitttl )
02511                      {
02512                             Entry *e;
02513 
02514                             si->caching_reason = PC_SIZELIMIT;
02515                             for (;si->head; si->head=e) {
02516                                    e = si->head->e_private;
02517                                    si->head->e_private = NULL;
02518                                    entry_free(si->head);
02519                             }
02520                      }
02521 
02522               } else if ( si->qtemp->negttl && !si->count && !si->over &&
02523                             rs->sr_err == LDAP_SUCCESS )
02524               {
02525                      si->caching_reason = PC_NEGATIVE;
02526               }
02527 
02528 
02529               if ( si->slimit_exceeded ) {
02530                      rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
02531               }
02532        }
02533 
02534        return SLAP_CB_CONTINUE;
02535 }
02536 
02537 /* NOTE: this is a quick workaround to let pcache minimally interact
02538  * with pagedResults.  A more articulated solutions would be to
02539  * perform the remote query without control and cache all results,
02540  * performing the pagedResults search only within the client
02541  * and the proxy.  This requires pcache to understand pagedResults. */
02542 static int
02543 pcache_chk_controls(
02544        Operation     *op,
02545        SlapReply     *rs )
02546 {
02547        const char    *non = "";
02548        const char    *stripped = "";
02549 
02550        switch( op->o_pagedresults ) {
02551        case SLAP_CONTROL_NONCRITICAL:
02552               non = "non-";
02553               stripped = "; stripped";
02554               /* fallthru */
02555 
02556        case SLAP_CONTROL_CRITICAL:
02557               Debug( pcache_debug, "%s: "
02558                      "%scritical pagedResults control "
02559                      "disabled with proxy cache%s.\n",
02560                      op->o_log_prefix, non, stripped );
02561               
02562               slap_remove_control( op, rs, slap_cids.sc_pagedResults, NULL );
02563               break;
02564 
02565        default:
02566               rs->sr_err = SLAP_CB_CONTINUE;
02567               break;
02568        }
02569 
02570        return rs->sr_err;
02571 }
02572 
02573 static int
02574 pc_setpw( Operation *op, struct berval *pwd, cache_manager *cm )
02575 {
02576        struct berval vals[2];
02577 
02578        {
02579               const char *text = NULL;
02580               BER_BVZERO( &vals[0] );
02581               slap_passwd_hash( pwd, &vals[0], &text );
02582               if ( BER_BVISEMPTY( &vals[0] )) {
02583                      Debug( pcache_debug, "pc_setpw: hash failed %s\n",
02584                             text, 0, 0 );
02585                      return LDAP_OTHER;
02586               }
02587        }
02588 
02589        BER_BVZERO( &vals[1] );
02590 
02591        {
02592               Modifications mod;
02593               SlapReply sr = { REP_RESULT };
02594               slap_callback cb = { 0, slap_null_cb, 0, 0 };
02595               int rc;
02596 
02597               mod.sml_op = LDAP_MOD_REPLACE;
02598               mod.sml_flags = 0;
02599               mod.sml_desc = slap_schema.si_ad_userPassword;
02600               mod.sml_type = mod.sml_desc->ad_cname;
02601               mod.sml_values = vals;
02602               mod.sml_nvalues = NULL;
02603               mod.sml_numvals = 1;
02604               mod.sml_next = NULL;
02605 
02606               op->o_tag = LDAP_REQ_MODIFY;
02607               op->orm_modlist = &mod;
02608               op->o_bd = &cm->db;
02609               op->o_dn = op->o_bd->be_rootdn;
02610               op->o_ndn = op->o_bd->be_rootndn;
02611               op->o_callback = &cb;
02612               Debug( pcache_debug, "pc_setpw: CACHING BIND for %s\n",
02613                      op->o_req_dn.bv_val, 0, 0 );
02614               rc = op->o_bd->be_modify( op, &sr );
02615               ch_free( vals[0].bv_val );
02616               return rc;
02617        }
02618 }
02619 
02620 typedef struct bindcacheinfo {
02621        slap_overinst *on;
02622        CachedQuery *qc;
02623 } bindcacheinfo;
02624 
02625 static int
02626 pc_bind_save( Operation *op, SlapReply *rs )
02627 {
02628        if ( rs->sr_err == LDAP_SUCCESS ) {
02629               bindcacheinfo *bci = op->o_callback->sc_private;
02630               slap_overinst *on = bci->on;
02631               cache_manager *cm = on->on_bi.bi_private;
02632               CachedQuery *qc = bci->qc;
02633               int delete = 0;
02634 
02635               ldap_pvt_thread_rdwr_wlock( &qc->rwlock );
02636               if ( qc->bind_refcnt-- ) {
02637                      Operation op2 = *op;
02638                      if ( pc_setpw( &op2, &op->orb_cred, cm ) == LDAP_SUCCESS )
02639                             bci->qc->bindref_time = op->o_time + bci->qc->qtemp->bindttr;
02640               } else {
02641                      bci->qc = NULL;
02642                      delete = 1;
02643               }
02644               ldap_pvt_thread_rdwr_wunlock( &qc->rwlock );
02645               if ( delete ) free_query(qc);
02646        }
02647        return SLAP_CB_CONTINUE;
02648 }
02649 
02650 static Filter *
02651 pc_bind_attrs( Operation *op, Entry *e, QueryTemplate *temp,
02652        struct berval *fbv )
02653 {
02654        int i, len = 0;
02655        struct berval *vals, pres = BER_BVC("*");
02656        char *p1, *p2;
02657        Attribute *a;
02658 
02659        vals = op->o_tmpalloc( temp->bindnattrs * sizeof( struct berval ),
02660               op->o_tmpmemctx );
02661 
02662        for ( i=0; i<temp->bindnattrs; i++ ) {
02663               a = attr_find( e->e_attrs, temp->bindfattrs[i] );
02664               if ( a && a->a_vals ) {
02665                      vals[i] = a->a_vals[0];
02666                      len += a->a_vals[0].bv_len;
02667               } else {
02668                      vals[i] = pres;
02669               }
02670        }
02671        fbv->bv_len = len + temp->bindftemp.bv_len;
02672        fbv->bv_val = op->o_tmpalloc( fbv->bv_len + 1, op->o_tmpmemctx );
02673 
02674        p1 = temp->bindftemp.bv_val;
02675        p2 = fbv->bv_val;
02676        i = 0;
02677        while ( *p1 ) {
02678               *p2++ = *p1;
02679               if ( p1[0] == '=' && p1[1] == ')' ) {
02680                      AC_MEMCPY( p2, vals[i].bv_val, vals[i].bv_len );
02681                      p2 += vals[i].bv_len;
02682                      i++;
02683               }
02684               p1++;
02685        }
02686        *p2 = '\0';
02687        op->o_tmpfree( vals, op->o_tmpmemctx );
02688 
02689        /* FIXME: are we sure str2filter_x can't fail?
02690         * caller needs to check */
02691        {
02692               Filter *f = str2filter_x( op, fbv->bv_val );
02693               assert( f != NULL );
02694               return f;
02695        }
02696 }
02697 
02698 /* Check if the requested entry is from the cache and has a valid
02699  * ttr and password hash
02700  */
02701 static int
02702 pc_bind_search( Operation *op, SlapReply *rs )
02703 {
02704        if ( rs->sr_type == REP_SEARCH ) {
02705               bindinfo *pbi = op->o_callback->sc_private;
02706 
02707               /* We only care if this is an already cached result and we're
02708                * below the refresh time, or we're offline.
02709                */
02710               if ( pbi->bi_cq ) {
02711                      if (( pbi->bi_cm->cc_paused & PCACHE_CC_OFFLINE ) ||
02712                             op->o_time < pbi->bi_cq->bindref_time ) {
02713                             Attribute *a;
02714 
02715                             /* See if a recognized password is hashed here */
02716                             a = attr_find( rs->sr_entry->e_attrs,
02717                                    slap_schema.si_ad_userPassword );
02718                             if ( a && a->a_vals[0].bv_val[0] == '{' &&
02719                                    lutil_passwd_scheme( a->a_vals[0].bv_val ))
02720                                    pbi->bi_flags |= BI_HASHED;
02721                      } else {
02722                             Debug( pcache_debug, "pc_bind_search: cache is stale, "
02723                                    "reftime: %ld, current time: %ld\n",
02724                                    pbi->bi_cq->bindref_time, op->o_time, 0 );
02725                      }
02726               } else if ( pbi->bi_si ) {
02727                      /* This search result is going into the cache */
02728                      struct berval fbv;
02729                      Filter *f;
02730 
02731                      filter_free( pbi->bi_si->query.filter );
02732                      f = pc_bind_attrs( op, rs->sr_entry, pbi->bi_templ, &fbv );
02733                      op->o_tmpfree( fbv.bv_val, op->o_tmpmemctx );
02734                      pbi->bi_si->query.filter = filter_dup( f, NULL );
02735                      filter_free_x( op, f, 1 );
02736               }
02737        }
02738        return 0;
02739 }
02740 
02741 /* We always want pc_bind_search to run after the search handlers */
02742 static int
02743 pc_bind_resp( Operation *op, SlapReply *rs )
02744 {
02745        bindinfo *pbi = op->o_callback->sc_private;
02746        if ( !( pbi->bi_flags & BI_DIDCB )) {
02747               slap_callback *sc = op->o_callback;
02748               while ( sc && sc->sc_response != pcache_response )
02749                      sc = sc->sc_next;
02750               if ( !sc )
02751                      sc = op->o_callback;
02752               pbi->bi_cb.sc_next = sc->sc_next;
02753               sc->sc_next = &pbi->bi_cb;
02754               pbi->bi_flags |= BI_DIDCB;
02755        }
02756        return SLAP_CB_CONTINUE;
02757 }
02758 
02759 #ifdef PCACHE_CONTROL_PRIVDB
02760 static int
02761 pcache_op_privdb(
02762        Operation            *op,
02763        SlapReply            *rs )
02764 {
02765        slap_overinst        *on = (slap_overinst *)op->o_bd->bd_info;
02766        cache_manager        *cm = on->on_bi.bi_private;
02767        slap_callback *save_cb;
02768        slap_op_t     type;
02769 
02770        /* skip if control is unset */
02771        if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_CRITICAL ) {
02772               return SLAP_CB_CONTINUE;
02773        }
02774 
02775        /* The cache DB isn't open yet */
02776        if ( cm->defer_db_open ) {
02777               send_ldap_error( op, rs, LDAP_UNAVAILABLE,
02778                      "pcachePrivDB: cacheDB not available" );
02779               return rs->sr_err;
02780        }
02781 
02782        /* FIXME: might be a little bit exaggerated... */
02783        if ( !be_isroot( op ) ) {
02784               save_cb = op->o_callback;
02785               op->o_callback = NULL;
02786               send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
02787                      "pcachePrivDB: operation not allowed" );
02788               op->o_callback = save_cb;
02789 
02790               return rs->sr_err;
02791        }
02792 
02793        /* map tag to operation */
02794        type = slap_req2op( op->o_tag );
02795        if ( type != SLAP_OP_LAST ) {
02796               BI_op_func    **func;
02797               int           rc;
02798 
02799               /* execute, if possible */
02800               func = &cm->db.be_bind;
02801               if ( func[ type ] != NULL ) {
02802                      Operation     op2 = *op;
02803        
02804                      op2.o_bd = &cm->db;
02805 
02806                      rc = func[ type ]( &op2, rs );
02807                      if ( type == SLAP_OP_BIND && rc == LDAP_SUCCESS ) {
02808                             op->o_conn->c_authz_cookie = cm->db.be_private;
02809                      }
02810 
02811                      return rs->sr_err;
02812               }
02813        }
02814 
02815        /* otherwise fall back to error */
02816        save_cb = op->o_callback;
02817        op->o_callback = NULL;
02818        send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
02819               "operation not supported with pcachePrivDB control" );
02820        op->o_callback = save_cb;
02821 
02822        return rs->sr_err;
02823 }
02824 #endif /* PCACHE_CONTROL_PRIVDB */
02825 
02826 static int
02827 pcache_op_bind(
02828        Operation            *op,
02829        SlapReply            *rs )
02830 {
02831        slap_overinst        *on = (slap_overinst *)op->o_bd->bd_info;
02832        cache_manager        *cm = on->on_bi.bi_private;
02833        QueryTemplate *temp;
02834        Entry *e;
02835        slap_callback cb = { 0 }, *sc;
02836        bindinfo bi;
02837        bindcacheinfo *bci;
02838        Operation op2;
02839        int rc;
02840 
02841 #ifdef PCACHE_CONTROL_PRIVDB
02842        if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL )
02843               return pcache_op_privdb( op, rs );
02844 #endif /* PCACHE_CONTROL_PRIVDB */
02845 
02846        /* Skip if we're not configured for Binds, or cache DB isn't open yet */
02847        if ( !cm->cache_binds || cm->defer_db_open )
02848               return SLAP_CB_CONTINUE;
02849 
02850        /* First find a matching template with Bind info */
02851        for ( temp=cm->qm->templates; temp; temp=temp->qmnext ) {
02852               if ( temp->bindttr && dnIsSuffix( &op->o_req_ndn, &temp->bindbase ))
02853                      break;
02854        }
02855        /* Didn't find a suitable template, just passthru */
02856        if ( !temp )
02857               return SLAP_CB_CONTINUE;
02858 
02859        /* See if the entry is already locally cached. If so, we can
02860         * populate the query filter to retrieve the cached query. We
02861         * need to check the bindrefresh time in the query.
02862         */
02863        op2 = *op;
02864        op2.o_dn = op->o_bd->be_rootdn;
02865        op2.o_ndn = op->o_bd->be_rootndn;
02866        bi.bi_flags = 0;
02867 
02868        op2.o_bd = &cm->db;
02869        e = NULL;
02870        rc = be_entry_get_rw( &op2, &op->o_req_ndn, NULL, NULL, 0, &e );
02871        if ( rc == LDAP_SUCCESS && e ) {
02872               bi.bi_flags |= BI_LOOKUP;
02873               op2.ors_filter = pc_bind_attrs( op, e, temp, &op2.ors_filterstr );
02874               be_entry_release_r( &op2, e );
02875        } else {
02876               op2.ors_filter = temp->bindfilter;
02877               op2.ors_filterstr = temp->bindfilterstr;
02878        }
02879 
02880        op2.o_bd = op->o_bd;
02881        op2.o_tag = LDAP_REQ_SEARCH;
02882        op2.ors_scope = LDAP_SCOPE_BASE;
02883        op2.ors_deref = LDAP_DEREF_NEVER;
02884        op2.ors_slimit = 1;
02885        op2.ors_tlimit = SLAP_NO_LIMIT;
02886        op2.ors_limit = NULL;
02887        op2.ors_attrs = cm->qm->attr_sets[temp->attr_set_index].attrs;
02888        op2.ors_attrsonly = 0;
02889 
02890        /* We want to invoke search at the same level of the stack
02891         * as we're already at...
02892         */
02893        bi.bi_cm = cm;
02894        bi.bi_templ = temp;
02895        bi.bi_cq = NULL;
02896        bi.bi_si = NULL;
02897 
02898        bi.bi_cb.sc_response = pc_bind_search;
02899        bi.bi_cb.sc_cleanup = NULL;
02900        bi.bi_cb.sc_private = &bi;
02901        cb.sc_private = &bi;
02902        cb.sc_response = pc_bind_resp;
02903        op2.o_callback = &cb;
02904        overlay_op_walk( &op2, rs, op_search, on->on_info, on );
02905 
02906        /* OK, just bind locally */
02907        if ( bi.bi_flags & BI_HASHED ) {
02908               int delete = 0;
02909               BackendDB *be = op->o_bd;
02910               op->o_bd = &cm->db;
02911 
02912               Debug( pcache_debug, "pcache_op_bind: CACHED BIND for %s\n",
02913                      op->o_req_dn.bv_val, 0, 0 );
02914 
02915               if ( op->o_bd->be_bind( op, rs ) == LDAP_SUCCESS ) {
02916                      op->o_conn->c_authz_cookie = cm->db.be_private;
02917               }
02918               op->o_bd = be;
02919               ldap_pvt_thread_rdwr_wlock( &bi.bi_cq->rwlock );
02920               if ( !bi.bi_cq->bind_refcnt-- ) {
02921                      delete = 1;
02922               }
02923               ldap_pvt_thread_rdwr_wunlock( &bi.bi_cq->rwlock );
02924               if ( delete ) free_query( bi.bi_cq );
02925               return rs->sr_err;
02926        }
02927 
02928        /* We have a cached query to work with */
02929        if ( bi.bi_cq ) {
02930               sc = op->o_tmpalloc( sizeof(slap_callback) + sizeof(bindcacheinfo),
02931                      op->o_tmpmemctx );
02932               sc->sc_response = pc_bind_save;
02933               sc->sc_cleanup = NULL;
02934               sc->sc_private = sc+1;
02935               bci = sc->sc_private;
02936               sc->sc_next = op->o_callback;
02937               op->o_callback = sc;
02938               bci->on = on;
02939               bci->qc = bi.bi_cq;
02940        }
02941        return SLAP_CB_CONTINUE;
02942 }
02943 
02944 static slap_response refresh_merge;
02945 
02946 static int
02947 pcache_op_search(
02948        Operation     *op,
02949        SlapReply     *rs )
02950 {
02951        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
02952        cache_manager *cm = on->on_bi.bi_private;
02953        query_manager*              qm = cm->qm;
02954 
02955        int i = -1;
02956 
02957        Query         query;
02958        QueryTemplate *qtemp = NULL;
02959        bindinfo *pbi = NULL;
02960 
02961        int           attr_set = -1;
02962        CachedQuery   *answerable = NULL;
02963        int           cacheable = 0;
02964 
02965        struct berval tempstr;
02966 
02967 #ifdef PCACHE_CONTROL_PRIVDB
02968        if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) {
02969               return pcache_op_privdb( op, rs );
02970        }
02971 #endif /* PCACHE_CONTROL_PRIVDB */
02972 
02973        /* The cache DB isn't open yet */
02974        if ( cm->defer_db_open ) {
02975               send_ldap_error( op, rs, LDAP_UNAVAILABLE,
02976                      "pcachePrivDB: cacheDB not available" );
02977               return rs->sr_err;
02978        }
02979 
02980        /* pickup runtime ACL changes */
02981        cm->db.be_acl = op->o_bd->be_acl;
02982 
02983        {
02984               /* See if we're processing a Bind request
02985                * or a cache refresh */
02986               slap_callback *cb = op->o_callback;
02987 
02988               for ( ; cb; cb=cb->sc_next ) {
02989                      if ( cb->sc_response == pc_bind_resp ) {
02990                             pbi = cb->sc_private;
02991                             break;
02992                      }
02993                      if ( cb->sc_response == refresh_merge ) {
02994                             /* This is a refresh, do not search the cache */
02995                             return SLAP_CB_CONTINUE;
02996                      }
02997               }
02998        }
02999 
03000        /* FIXME: cannot cache/answer requests with pagedResults control */
03001 
03002        query.filter = op->ors_filter;
03003 
03004        if ( pbi ) {
03005               query.base = pbi->bi_templ->bindbase;
03006               query.scope = pbi->bi_templ->bindscope;
03007               attr_set = pbi->bi_templ->attr_set_index;
03008               cacheable = 1;
03009               qtemp = pbi->bi_templ;
03010               if ( pbi->bi_flags & BI_LOOKUP )
03011                      answerable = qm->qcfunc(op, qm, &query, qtemp);
03012 
03013        } else {
03014               tempstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len+1,
03015                      op->o_tmpmemctx );
03016               tempstr.bv_len = 0;
03017               if ( filter2template( op, op->ors_filter, &tempstr ))
03018               {
03019                      op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx );
03020                      return SLAP_CB_CONTINUE;
03021               }
03022 
03023               Debug( pcache_debug, "query template of incoming query = %s\n",
03024                                           tempstr.bv_val, 0, 0 );
03025 
03026               /* find attr set */
03027               attr_set = get_attr_set(op->ors_attrs, qm, cm->numattrsets);
03028 
03029               query.base = op->o_req_ndn;
03030               query.scope = op->ors_scope;
03031 
03032               /* check for query containment */
03033               if (attr_set > -1) {
03034                      QueryTemplate *qt = qm->attr_sets[attr_set].templates;
03035                      for (; qt; qt = qt->qtnext ) {
03036                             /* find if template i can potentially answer tempstr */
03037                             if ( ber_bvstrcasecmp( &qt->querystr, &tempstr ) != 0 )
03038                                    continue;
03039                             cacheable = 1;
03040                             qtemp = qt;
03041                             Debug( pcache_debug, "Entering QC, querystr = %s\n",
03042                                           op->ors_filterstr.bv_val, 0, 0 );
03043                             answerable = qm->qcfunc(op, qm, &query, qt);
03044 
03045                             /* if != NULL, rlocks qtemp->t_rwlock */
03046                             if (answerable)
03047                                    break;
03048                      }
03049               }
03050               op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx );
03051        }
03052 
03053        if (answerable) {
03054               BackendDB     *save_bd = op->o_bd;
03055 
03056               ldap_pvt_thread_mutex_lock( &answerable->answerable_cnt_mutex );
03057               answerable->answerable_cnt++;
03058               /* we only care about refcnts if we're refreshing */
03059               if ( answerable->refresh_time )
03060                      answerable->refcnt++;
03061               Debug( pcache_debug, "QUERY ANSWERABLE (answered %lu times)\n",
03062                      answerable->answerable_cnt, 0, 0 );
03063               ldap_pvt_thread_mutex_unlock( &answerable->answerable_cnt_mutex );
03064 
03065               ldap_pvt_thread_rdwr_wlock(&answerable->rwlock);
03066               if ( BER_BVISNULL( &answerable->q_uuid )) {
03067                      /* No entries cached, just an empty result set */
03068                      i = rs->sr_err = 0;
03069                      send_ldap_result( op, rs );
03070               } else {
03071                      /* Let Bind know we used a cached query */
03072                      if ( pbi ) {
03073                             answerable->bind_refcnt++;
03074                             pbi->bi_cq = answerable;
03075                      }
03076 
03077                      op->o_bd = &cm->db;
03078                      if ( cm->response_cb == PCACHE_RESPONSE_CB_TAIL ) {
03079                             slap_callback cb;
03080                             /* The cached entry was already processed by any
03081                              * other overlays, so don't let it get processed again.
03082                              *
03083                              * This loop removes over_back_response from the stack.
03084                              */
03085                             if ( overlay_callback_after_backover( op, &cb, 0) == 0 ) {
03086                                    slap_callback **scp;
03087                                    for ( scp = &op->o_callback; *scp != NULL;
03088                                           scp = &(*scp)->sc_next ) {
03089                                           if ( (*scp)->sc_next == &cb ) {
03090                                                  *scp = cb.sc_next;
03091                                                  break;
03092                                           }
03093                                    }
03094                             }
03095                      }
03096                      i = cm->db.bd_info->bi_op_search( op, rs );
03097               }
03098               ldap_pvt_thread_rdwr_wunlock(&answerable->rwlock);
03099               /* locked by qtemp->qcfunc (query_containment) */
03100               ldap_pvt_thread_rdwr_runlock(&qtemp->t_rwlock);
03101               op->o_bd = save_bd;
03102               return i;
03103        }
03104 
03105        Debug( pcache_debug, "QUERY NOT ANSWERABLE\n", 0, 0, 0 );
03106 
03107        ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
03108        if (cm->num_cached_queries >= cm->max_queries) {
03109               cacheable = 0;
03110        }
03111        ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
03112 
03113        if (op->ors_attrsonly)
03114               cacheable = 0;
03115 
03116        if (cacheable) {
03117               slap_callback        *cb;
03118               struct search_info   *si;
03119 
03120               Debug( pcache_debug, "QUERY CACHEABLE\n", 0, 0, 0 );
03121               query.filter = filter_dup(op->ors_filter, NULL);
03122 
03123               cb = op->o_tmpalloc( sizeof(*cb) + sizeof(*si), op->o_tmpmemctx );
03124               cb->sc_response = pcache_response;
03125               cb->sc_cleanup = pcache_op_cleanup;
03126               cb->sc_private = (cb+1);
03127               si = cb->sc_private;
03128               si->on = on;
03129               si->query = query;
03130               si->qtemp = qtemp;
03131               si->max = cm->num_entries_limit ;
03132               si->over = 0;
03133               si->count = 0;
03134               si->slimit = 0;
03135               si->slimit_exceeded = 0;
03136               si->caching_reason = PC_IGNORE;
03137               if ( op->ors_slimit > 0 && op->ors_slimit < cm->num_entries_limit ) {
03138                      si->slimit = op->ors_slimit;
03139                      op->ors_slimit = cm->num_entries_limit;
03140               }
03141               si->head = NULL;
03142               si->tail = NULL;
03143               si->swap_saved_attrs = 1;
03144               si->save_attrs = op->ors_attrs;
03145               si->pbi = pbi;
03146               if ( pbi )
03147                      pbi->bi_si = si;
03148 
03149               op->ors_attrs = qtemp->t_attrs.attrs;
03150 
03151               if ( cm->response_cb == PCACHE_RESPONSE_CB_HEAD ) {
03152                      cb->sc_next = op->o_callback;
03153                      op->o_callback = cb;
03154 
03155               } else {
03156                      slap_callback        **pcb;
03157 
03158                      /* need to move the callback at the end, in case other
03159                       * overlays are present, so that the final entry is
03160                       * actually cached */
03161                      cb->sc_next = NULL;
03162                      for ( pcb = &op->o_callback; *pcb; pcb = &(*pcb)->sc_next );
03163                      *pcb = cb;
03164               }
03165 
03166        } else {
03167               Debug( pcache_debug, "QUERY NOT CACHEABLE\n",
03168                                    0, 0, 0);
03169        }
03170 
03171        return SLAP_CB_CONTINUE;
03172 }
03173 
03174 static int
03175 get_attr_set(
03176        AttributeName* attrs,
03177        query_manager* qm,
03178        int num )
03179 {
03180        int i = 0;
03181        int count = 0;
03182 
03183        if ( attrs ) {
03184               for ( ; attrs[i].an_name.bv_val; i++ ) {
03185                      /* only count valid attribute names
03186                       * (searches ignore others, this overlay does the same) */
03187                      if ( attrs[i].an_desc ) {
03188                             count++;
03189                      }
03190               }
03191        }
03192 
03193        /* recognize default or explicit single "*" */
03194        if ( ! attrs ||
03195               ( i == 1 && bvmatch( &attrs[0].an_name, slap_bv_all_user_attrs ) ) )
03196        {
03197               count = 1;
03198               attrs = slap_anlist_all_user_attributes;
03199 
03200        /* recognize implicit (no valid attributes) or explicit single "1.1" */
03201        } else if ( count == 0 ||
03202               ( i == 1 && bvmatch( &attrs[0].an_name, slap_bv_no_attrs ) ) )
03203        {
03204               count = 0;
03205               attrs = NULL;
03206        }
03207 
03208        for ( i = 0; i < num; i++ ) {
03209               AttributeName *a2;
03210               int found = 1;
03211 
03212               if ( count > qm->attr_sets[i].count ) {
03213                      continue;
03214               }
03215 
03216               if ( !count ) {
03217                      if ( !qm->attr_sets[i].count ) {
03218                             break;
03219                      }
03220                      continue;
03221               }
03222 
03223               for ( a2 = attrs; a2->an_name.bv_val; a2++ ) {
03224                      if ( !a2->an_desc && !bvmatch( &a2->an_name, slap_bv_all_user_attrs ) ) continue;
03225 
03226                      if ( !an_find( qm->attr_sets[i].attrs, &a2->an_name ) ) {
03227                             found = 0;
03228                             break;
03229                      }
03230               }
03231 
03232               if ( found ) {
03233                      break;
03234               }
03235        }
03236 
03237        if ( i == num ) {
03238               i = -1;
03239        }
03240 
03241        return i;
03242 }
03243 
03244 /* Refresh a cached query:
03245  * 1: Replay the query on the remote DB and merge each entry into
03246  * the local DB. Remember the DNs of each remote entry.
03247  * 2: Search the local DB for all entries matching this queryID.
03248  * Delete any entry whose DN is not in the list from (1).
03249  */
03250 typedef struct dnlist {
03251        struct dnlist *next;
03252        struct berval dn;
03253        char delete;
03254 } dnlist;
03255 
03256 typedef struct refresh_info {
03257        dnlist *ri_dns;
03258        dnlist *ri_tail;
03259        dnlist *ri_dels;
03260        BackendDB *ri_be;
03261        CachedQuery *ri_q;
03262 } refresh_info;
03263 
03264 static dnlist *dnl_alloc( Operation *op, struct berval *bvdn )
03265 {
03266        dnlist *dn = op->o_tmpalloc( sizeof(dnlist) + bvdn->bv_len + 1,
03267                      op->o_tmpmemctx );
03268        dn->dn.bv_len = bvdn->bv_len;
03269        dn->dn.bv_val = (char *)(dn+1);
03270        AC_MEMCPY( dn->dn.bv_val, bvdn->bv_val, dn->dn.bv_len );
03271        dn->dn.bv_val[dn->dn.bv_len] = '\0';
03272        return dn;
03273 }
03274 
03275 static int
03276 refresh_merge( Operation *op, SlapReply *rs )
03277 {
03278        if ( rs->sr_type == REP_SEARCH ) {
03279               refresh_info *ri = op->o_callback->sc_private;
03280               Entry *e;
03281               dnlist *dnl;
03282               slap_callback *ocb;
03283               int rc;
03284 
03285               ocb = op->o_callback;
03286               /* Find local entry, merge */
03287               op->o_bd = ri->ri_be;
03288               rc = be_entry_get_rw( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &e );
03289               if ( rc != LDAP_SUCCESS || e == NULL ) {
03290                      /* No local entry, just add it. FIXME: we are not checking
03291                       * the cache entry limit here
03292                       */
03293                       merge_entry( op, rs->sr_entry, 1, &ri->ri_q->q_uuid );
03294               } else {
03295                      /* Entry exists, update it */
03296                      Entry ne;
03297                      Attribute *a, **b;
03298                      Modifications *modlist, *mods = NULL;
03299                      const char*   text = NULL;
03300                      char                 textbuf[SLAP_TEXT_BUFLEN];
03301                      size_t               textlen = sizeof(textbuf);
03302                      slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
03303 
03304                      ne = *e;
03305                      b = &ne.e_attrs;
03306                      /* Get a copy of only the attrs we requested */
03307                      for ( a=e->e_attrs; a; a=a->a_next ) {
03308                             if ( ad_inlist( a->a_desc, rs->sr_attrs )) {
03309                                    *b = attr_alloc( a->a_desc );
03310                                    *(*b) = *a;
03311                                    /* The actual values still belong to e */
03312                                    (*b)->a_flags |= SLAP_ATTR_DONT_FREE_VALS |
03313                                           SLAP_ATTR_DONT_FREE_DATA;
03314                                    b = &((*b)->a_next);
03315                             }
03316                      }
03317                      *b = NULL;
03318                      slap_entry2mods( rs->sr_entry, &modlist, &text, textbuf, textlen );
03319                      syncrepl_diff_entry( op, ne.e_attrs, rs->sr_entry->e_attrs,
03320                             &mods, &modlist, 0 );
03321                      be_entry_release_r( op, e );
03322                      attrs_free( ne.e_attrs );
03323                      slap_mods_free( modlist, 1 );
03324                      /* mods is NULL if there are no changes */
03325                      if ( mods ) {
03326                             SlapReply rs2 = { REP_RESULT };
03327                             struct berval dn = op->o_req_dn;
03328                             struct berval ndn = op->o_req_ndn;
03329                             op->o_tag = LDAP_REQ_MODIFY;
03330                             op->orm_modlist = mods;
03331                             op->o_req_dn = rs->sr_entry->e_name;
03332                             op->o_req_ndn = rs->sr_entry->e_nname;
03333                             op->o_callback = &cb;
03334                             op->o_bd->be_modify( op, &rs2 );
03335                             rs->sr_err = rs2.sr_err;
03336                             rs_assert_done( &rs2 );
03337                             slap_mods_free( mods, 1 );
03338                             op->o_req_dn = dn;
03339                             op->o_req_ndn = ndn;
03340                      }
03341               }
03342 
03343               /* Add DN to list */
03344               dnl = dnl_alloc( op, &rs->sr_entry->e_nname );
03345               dnl->next = NULL;
03346               if ( ri->ri_tail ) {
03347                      ri->ri_tail->next = dnl;
03348               } else {
03349                      ri->ri_dns = dnl;
03350               }
03351               ri->ri_tail = dnl;
03352               op->o_callback = ocb;
03353        }
03354        return 0;
03355 }
03356 
03357 static int
03358 refresh_purge( Operation *op, SlapReply *rs )
03359 {
03360        if ( rs->sr_type == REP_SEARCH ) {
03361               refresh_info *ri = op->o_callback->sc_private;
03362               dnlist **dn;
03363               int del = 1;
03364 
03365               /* Did the entry exist on the remote? */
03366               for ( dn=&ri->ri_dns; *dn; dn = &(*dn)->next ) {
03367                      if ( dn_match( &(*dn)->dn, &rs->sr_entry->e_nname )) {
03368                             dnlist *dnext = (*dn)->next;
03369                             op->o_tmpfree( *dn, op->o_tmpmemctx );
03370                             *dn = dnext;
03371                             del = 0;
03372                             break;
03373                      }
03374               }
03375               /* No, so put it on the list to delete */
03376               if ( del ) {
03377                      Attribute *a;
03378                      dnlist *dnl = dnl_alloc( op, &rs->sr_entry->e_nname );
03379                      dnl->next = ri->ri_dels;
03380                      ri->ri_dels = dnl;
03381                      a = attr_find( rs->sr_entry->e_attrs, ad_queryId );
03382                      /* If ours is the only queryId, delete entry */
03383                      dnl->delete = ( a->a_numvals == 1 );
03384               }
03385        }
03386        return 0;
03387 }
03388 
03389 static int
03390 refresh_query( Operation *op, CachedQuery *query, slap_overinst *on )
03391 {
03392        SlapReply rs = {REP_RESULT};
03393        slap_callback cb = { 0 };
03394        refresh_info ri = { 0 };
03395        char filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ];
03396        AttributeAssertion   ava = ATTRIBUTEASSERTION_INIT;
03397        Filter filter = {LDAP_FILTER_EQUALITY};
03398        AttributeName attrs[ 2 ] = {{{ 0 }}};
03399        dnlist *dn;
03400        int rc;
03401 
03402        ldap_pvt_thread_mutex_lock( &query->answerable_cnt_mutex );
03403        query->refcnt = 0;
03404        ldap_pvt_thread_mutex_unlock( &query->answerable_cnt_mutex );
03405 
03406        cb.sc_response = refresh_merge;
03407        cb.sc_private = &ri;
03408 
03409        /* cache DB */
03410        ri.ri_be = op->o_bd;
03411        ri.ri_q = query;
03412 
03413        op->o_tag = LDAP_REQ_SEARCH;
03414        op->o_protocol = LDAP_VERSION3;
03415        op->o_callback = &cb;
03416        op->o_do_not_cache = 1;
03417 
03418        op->o_req_dn = query->qbase->base;
03419        op->o_req_ndn = query->qbase->base;
03420        op->ors_scope = query->scope;
03421        op->ors_deref = LDAP_DEREF_NEVER;
03422        op->ors_slimit = SLAP_NO_LIMIT;
03423        op->ors_tlimit = SLAP_NO_LIMIT;
03424        op->ors_limit = NULL;
03425        op->ors_filter = query->filter;
03426        filter2bv_x( op, query->filter, &op->ors_filterstr );
03427        op->ors_attrs = query->qtemp->t_attrs.attrs;
03428        op->ors_attrsonly = 0;
03429 
03430        op->o_bd = on->on_info->oi_origdb;
03431        rc = op->o_bd->be_search( op, &rs );
03432        if ( rc ) {
03433               op->o_bd = ri.ri_be;
03434               goto leave;
03435        }
03436 
03437        /* Get the DNs of all entries matching this query */
03438        cb.sc_response = refresh_purge;
03439 
03440        op->o_bd = ri.ri_be;
03441        op->o_req_dn = op->o_bd->be_suffix[0];
03442        op->o_req_ndn = op->o_bd->be_nsuffix[0];
03443        op->ors_scope = LDAP_SCOPE_SUBTREE;
03444        op->ors_deref = LDAP_DEREF_NEVER;
03445        op->ors_filterstr.bv_len = snprintf(filter_str, sizeof(filter_str),
03446               "(%s=%s)", ad_queryId->ad_cname.bv_val, query->q_uuid.bv_val);
03447        filter.f_ava = &ava;
03448        filter.f_av_desc = ad_queryId;
03449        filter.f_av_value = query->q_uuid;
03450        attrs[ 0 ].an_desc = ad_queryId;
03451        attrs[ 0 ].an_name = ad_queryId->ad_cname;
03452        op->ors_attrs = attrs;
03453        op->ors_attrsonly = 0;
03454        rs_reinit( &rs, REP_RESULT );
03455        rc = op->o_bd->be_search( op, &rs );
03456        if ( rc ) goto leave;
03457 
03458        while (( dn = ri.ri_dels )) {
03459               op->o_req_dn = dn->dn;
03460               op->o_req_ndn = dn->dn;
03461               rs_reinit( &rs, REP_RESULT );
03462               if ( dn->delete ) {
03463                      op->o_tag = LDAP_REQ_DELETE;
03464                      op->o_bd->be_delete( op, &rs );
03465               } else {
03466                      Modifications mod;
03467                      struct berval vals[2];
03468 
03469                      vals[0] = query->q_uuid;
03470                      BER_BVZERO( &vals[1] );
03471                      mod.sml_op = LDAP_MOD_DELETE;
03472                      mod.sml_flags = 0;
03473                      mod.sml_desc = ad_queryId;
03474                      mod.sml_type = ad_queryId->ad_cname;
03475                      mod.sml_values = vals;
03476                      mod.sml_nvalues = NULL;
03477                      mod.sml_numvals = 1;
03478                      mod.sml_next = NULL;
03479 
03480                      op->o_tag = LDAP_REQ_MODIFY;
03481                      op->orm_modlist = &mod;
03482                      op->o_bd->be_modify( op, &rs );
03483               }
03484               ri.ri_dels = dn->next;
03485               op->o_tmpfree( dn, op->o_tmpmemctx );
03486        }
03487 
03488 leave:
03489        /* reset our local heap, we're done with it */
03490        slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, op->o_threadctx, 1 );
03491        return rc;
03492 }
03493 
03494 static void*
03495 consistency_check(
03496        void *ctx,
03497        void *arg )
03498 {
03499        struct re_s *rtask = arg;
03500        slap_overinst *on = rtask->arg;
03501        cache_manager *cm = on->on_bi.bi_private;
03502        query_manager *qm = cm->qm;
03503        Connection conn = {0};
03504        OperationBuffer opbuf;
03505        Operation *op;
03506 
03507        CachedQuery *query, *qprev;
03508        int return_val, pause = PCACHE_CC_PAUSED;
03509        QueryTemplate *templ;
03510 
03511        /* Don't expire anything when we're offline */
03512        if ( cm->cc_paused & PCACHE_CC_OFFLINE ) {
03513               pause = PCACHE_CC_OFFLINE;
03514               goto leave;
03515        }
03516 
03517        connection_fake_init( &conn, &opbuf, ctx );
03518        op = &opbuf.ob_op;
03519 
03520        op->o_bd = &cm->db;
03521        op->o_dn = cm->db.be_rootdn;
03522        op->o_ndn = cm->db.be_rootndn;
03523 
03524        cm->cc_arg = arg;
03525 
03526        for (templ = qm->templates; templ; templ=templ->qmnext) {
03527               time_t ttl;
03528               if ( !templ->query_last ) continue;
03529               pause = 0;
03530               op->o_time = slap_get_time();
03531               if ( !templ->ttr ) {
03532                      ttl = templ->ttl;
03533                      if ( templ->negttl && templ->negttl < ttl )
03534                             ttl = templ->negttl;
03535                      if ( templ->limitttl && templ->limitttl < ttl )
03536                             ttl = templ->limitttl;
03537                      /* The oldest timestamp that needs expiration checking */
03538                      ttl += op->o_time;
03539               }
03540 
03541               for ( query=templ->query_last; query; query=qprev ) {
03542                      qprev = query->prev;
03543                      if ( query->refresh_time && query->refresh_time < op->o_time ) {
03544                             /* A refresh will extend the expiry if the query has been
03545                              * referenced, but not if it's unreferenced. If the
03546                              * expiration has been hit, then skip the refresh since
03547                              * we're just going to discard the result anyway.
03548                              */
03549                             if ( query->refcnt )
03550                                    query->expiry_time = op->o_time + templ->ttl;
03551                             if ( query->expiry_time > op->o_time ) {
03552                                    refresh_query( op, query, on );
03553                                    continue;
03554                             }
03555                      }
03556 
03557                      if (query->expiry_time < op->o_time) {
03558                             int rem = 0;
03559                             Debug( pcache_debug, "Lock CR index = %p\n",
03560                                           (void *) templ, 0, 0 );
03561                             ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock);
03562                             if ( query == templ->query_last ) {
03563                                    rem = 1;
03564                                    remove_from_template(query, templ);
03565                                    Debug( pcache_debug, "TEMPLATE %p QUERIES-- %d\n",
03566                                                  (void *) templ, templ->no_of_queries, 0 );
03567                                    Debug( pcache_debug, "Unlock CR index = %p\n",
03568                                                  (void *) templ, 0, 0 );
03569                             }
03570                             if ( !rem ) {
03571                                    ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
03572                                    continue;
03573                             }
03574                             ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
03575                             remove_query(qm, query);
03576                             ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
03577                             if ( BER_BVISNULL( &query->q_uuid ))
03578                                    return_val = 0;
03579                             else
03580                                    return_val = remove_query_data(op, &query->q_uuid);
03581                             Debug( pcache_debug, "STALE QUERY REMOVED, SIZE=%d\n",
03582                                                  return_val, 0, 0 );
03583                             ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
03584                             cm->cur_entries -= return_val;
03585                             cm->num_cached_queries--;
03586                             Debug( pcache_debug, "STORED QUERIES = %lu\n",
03587                                           cm->num_cached_queries, 0, 0 );
03588                             ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
03589                             Debug( pcache_debug,
03590                                    "STALE QUERY REMOVED, CACHE ="
03591                                    "%d entries\n",
03592                                    cm->cur_entries, 0, 0 );
03593                             ldap_pvt_thread_rdwr_wlock( &query->rwlock );
03594                             if ( query->bind_refcnt-- ) {
03595                                    rem = 0;
03596                             } else {
03597                                    rem = 1;
03598                             }
03599                             ldap_pvt_thread_rdwr_wunlock( &query->rwlock );
03600                             if ( rem ) free_query(query);
03601                             ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
03602                      } else if ( !templ->ttr && query->expiry_time > ttl ) {
03603                             /* We don't need to check for refreshes, and this
03604                              * query's expiry is too new, and all subsequent queries
03605                              * will be newer yet. So stop looking.
03606                              *
03607                              * If we have refreshes, then we always have to walk the
03608                              * entire query list.
03609                              */
03610                             break;
03611                      }
03612               }
03613        }
03614 
03615 leave:
03616        ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
03617        if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
03618               ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
03619        }
03620        /* If there were no queries, defer processing for a while */
03621        if ( cm->cc_paused != pause )
03622               cm->cc_paused = pause;
03623        ldap_pvt_runqueue_resched( &slapd_rq, rtask, pause );
03624 
03625        ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
03626        return NULL;
03627 }
03628 
03629 
03630 #define MAX_ATTR_SETS 500
03631 
03632 enum {
03633        PC_MAIN = 1,
03634        PC_ATTR,
03635        PC_TEMP,
03636        PC_RESP,
03637        PC_QUERIES,
03638        PC_OFFLINE,
03639        PC_BIND,
03640        PC_PRIVATE_DB
03641 };
03642 
03643 static ConfigDriver pc_cf_gen;
03644 static ConfigLDAPadd pc_ldadd;
03645 static ConfigCfAdd pc_cfadd;
03646 
03647 static ConfigTable pccfg[] = {
03648        { "pcache", "backend> <max_entries> <numattrsets> <entry limit> "
03649                             "<cycle_time",
03650               6, 6, 0, ARG_MAGIC|ARG_NO_DELETE|PC_MAIN, pc_cf_gen,
03651               "( OLcfgOvAt:2.1 NAME ( 'olcPcache' 'olcProxyCache' ) "
03652                      "DESC 'Proxy Cache basic parameters' "
03653                      "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
03654        { "pcacheAttrset", "index> <attributes...",
03655               2, 0, 0, ARG_MAGIC|PC_ATTR, pc_cf_gen,
03656               "( OLcfgOvAt:2.2 NAME ( 'olcPcacheAttrset' 'olcProxyAttrset' ) "
03657                      "DESC 'A set of attributes to cache' "
03658                      "SYNTAX OMsDirectoryString )", NULL, NULL },
03659        { "pcacheTemplate", "filter> <attrset-index> <TTL> <negTTL> "
03660                      "<limitTTL> <TTR",
03661               4, 7, 0, ARG_MAGIC|PC_TEMP, pc_cf_gen,
03662               "( OLcfgOvAt:2.3 NAME ( 'olcPcacheTemplate' 'olcProxyCacheTemplate' ) "
03663                      "DESC 'Filter template, attrset, cache TTL, "
03664                             "optional negative TTL, optional sizelimit TTL, "
03665                             "optional TTR' "
03666                      "SYNTAX OMsDirectoryString )", NULL, NULL },
03667        { "pcachePosition", "head|tail(default)",
03668               2, 2, 0, ARG_MAGIC|PC_RESP, pc_cf_gen,
03669               "( OLcfgOvAt:2.4 NAME 'olcPcachePosition' "
03670                      "DESC 'Response callback position in overlay stack' "
03671                      "SYNTAX OMsDirectoryString )", NULL, NULL },
03672        { "pcacheMaxQueries", "queries",
03673               2, 2, 0, ARG_INT|ARG_MAGIC|PC_QUERIES, pc_cf_gen,
03674               "( OLcfgOvAt:2.5 NAME ( 'olcPcacheMaxQueries' 'olcProxyCacheQueries' ) "
03675                      "DESC 'Maximum number of queries to cache' "
03676                      "SYNTAX OMsInteger )", NULL, NULL },
03677        { "pcachePersist", "TRUE|FALSE",
03678               2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, save_queries),
03679               "( OLcfgOvAt:2.6 NAME ( 'olcPcachePersist' 'olcProxySaveQueries' ) "
03680                      "DESC 'Save cached queries for hot restart' "
03681                      "SYNTAX OMsBoolean )", NULL, NULL },
03682        { "pcacheValidate", "TRUE|FALSE",
03683               2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, check_cacheability),
03684               "( OLcfgOvAt:2.7 NAME ( 'olcPcacheValidate' 'olcProxyCheckCacheability' ) "
03685                      "DESC 'Check whether the results of a query are cacheable, e.g. for schema issues' "
03686                      "SYNTAX OMsBoolean )", NULL, NULL },
03687        { "pcacheOffline", "TRUE|FALSE",
03688               2, 2, 0, ARG_ON_OFF|ARG_MAGIC|PC_OFFLINE, pc_cf_gen,
03689               "( OLcfgOvAt:2.8 NAME 'olcPcacheOffline' "
03690                      "DESC 'Set cache to offline mode and disable expiration' "
03691                      "SYNTAX OMsBoolean )", NULL, NULL },
03692        { "pcacheBind", "filter> <attrset-index> <TTR> <scope> <base",
03693               6, 6, 0, ARG_MAGIC|PC_BIND, pc_cf_gen,
03694               "( OLcfgOvAt:2.9 NAME 'olcPcacheBind' "
03695                      "DESC 'Parameters for caching Binds' "
03696                      "SYNTAX OMsDirectoryString )", NULL, NULL },
03697        { "pcache-", "private database args",
03698               1, 0, STRLENOF("pcache-"), ARG_MAGIC|PC_PRIVATE_DB, pc_cf_gen,
03699               NULL, NULL, NULL },
03700 
03701        /* Legacy keywords */
03702        { "proxycache", "backend> <max_entries> <numattrsets> <entry limit> "
03703                             "<cycle_time",
03704               6, 6, 0, ARG_MAGIC|ARG_NO_DELETE|PC_MAIN, pc_cf_gen,
03705               NULL, NULL, NULL },
03706        { "proxyattrset", "index> <attributes...",
03707               2, 0, 0, ARG_MAGIC|PC_ATTR, pc_cf_gen,
03708               NULL, NULL, NULL },
03709        { "proxytemplate", "filter> <attrset-index> <TTL> <negTTL",
03710               4, 7, 0, ARG_MAGIC|PC_TEMP, pc_cf_gen,
03711               NULL, NULL, NULL },
03712        { "response-callback", "head|tail(default)",
03713               2, 2, 0, ARG_MAGIC|PC_RESP, pc_cf_gen,
03714               NULL, NULL, NULL },
03715        { "proxyCacheQueries", "queries",
03716               2, 2, 0, ARG_INT|ARG_MAGIC|PC_QUERIES, pc_cf_gen,
03717               NULL, NULL, NULL },
03718        { "proxySaveQueries", "TRUE|FALSE",
03719               2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, save_queries),
03720               NULL, NULL, NULL },
03721        { "proxyCheckCacheability", "TRUE|FALSE",
03722               2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, check_cacheability),
03723               NULL, NULL, NULL },
03724 
03725        { NULL, NULL, 0, 0, 0, ARG_IGNORED }
03726 };
03727 
03728 static ConfigOCs pcocs[] = {
03729        { "( OLcfgOvOc:2.1 "
03730               "NAME 'olcPcacheConfig' "
03731               "DESC 'ProxyCache configuration' "
03732               "SUP olcOverlayConfig "
03733               "MUST ( olcPcache $ olcPcacheAttrset $ olcPcacheTemplate ) "
03734               "MAY ( olcPcachePosition $ olcPcacheMaxQueries $ olcPcachePersist $ "
03735                      "olcPcacheValidate $ olcPcacheOffline $ olcPcacheBind ) )",
03736               Cft_Overlay, pccfg, NULL, pc_cfadd },
03737        { "( OLcfgOvOc:2.2 "
03738               "NAME 'olcPcacheDatabase' "
03739               "DESC 'Cache database configuration' "
03740               "AUXILIARY )", Cft_Misc, olcDatabaseDummy, pc_ldadd },
03741        { NULL, 0, NULL }
03742 };
03743 
03744 static int pcache_db_open2( slap_overinst *on, ConfigReply *cr );
03745 
03746 static int
03747 pc_ldadd_cleanup( ConfigArgs *c )
03748 {
03749        slap_overinst *on = c->ca_private;
03750        return pcache_db_open2( on, &c->reply );
03751 }
03752 
03753 static int
03754 pc_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
03755 {
03756        slap_overinst *on;
03757        cache_manager *cm;
03758 
03759        if ( p->ce_type != Cft_Overlay || !p->ce_bi ||
03760               p->ce_bi->bi_cf_ocs != pcocs )
03761               return LDAP_CONSTRAINT_VIOLATION;
03762 
03763        on = (slap_overinst *)p->ce_bi;
03764        cm = on->on_bi.bi_private;
03765        ca->be = &cm->db;
03766        /* Defer open if this is an LDAPadd */
03767        if ( CONFIG_ONLINE_ADD( ca ))
03768               ca->cleanup = pc_ldadd_cleanup;
03769        else
03770               cm->defer_db_open = 0;
03771        ca->ca_private = on;
03772        return LDAP_SUCCESS;
03773 }
03774 
03775 static int
03776 pc_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
03777 {
03778        CfEntryInfo *pe = p->e_private;
03779        slap_overinst *on = (slap_overinst *)pe->ce_bi;
03780        cache_manager *cm = on->on_bi.bi_private;
03781        struct berval bv;
03782 
03783        /* FIXME: should not hardcode "olcDatabase" here */
03784        bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ),
03785               "olcDatabase=" SLAP_X_ORDERED_FMT "%s",
03786               0, cm->db.bd_info->bi_type );
03787        if ( bv.bv_len >= sizeof( ca->cr_msg ) ) {
03788               return -1;
03789        }
03790        bv.bv_val = ca->cr_msg;
03791        ca->be = &cm->db;
03792        cm->defer_db_open = 0;
03793 
03794        /* We can only create this entry if the database is table-driven
03795         */
03796        if ( cm->db.bd_info->bi_cf_ocs )
03797               config_build_entry( op, rs, pe, ca, &bv, cm->db.bd_info->bi_cf_ocs,
03798                      &pcocs[1] );
03799 
03800        return 0;
03801 }
03802 
03803 static int
03804 pc_cf_gen( ConfigArgs *c )
03805 {
03806        slap_overinst *on = (slap_overinst *)c->bi;
03807        cache_manager*       cm = on->on_bi.bi_private;
03808        query_manager*  qm = cm->qm;
03809        QueryTemplate*       temp;
03810        AttributeName*  attr_name;
03811        AttributeName*       attrarray;
03812        const char*   text=NULL;
03813        int           i, num, rc = 0;
03814        char          *ptr;
03815        unsigned long t;
03816 
03817        if ( c->op == SLAP_CONFIG_EMIT ) {
03818               struct berval bv;
03819               switch( c->type ) {
03820               case PC_MAIN:
03821                      bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s %d %d %d %ld",
03822                             cm->db.bd_info->bi_type, cm->max_entries, cm->numattrsets,
03823                             cm->num_entries_limit, cm->cc_period );
03824                      bv.bv_val = c->cr_msg;
03825                      value_add_one( &c->rvalue_vals, &bv );
03826                      break;
03827               case PC_ATTR:
03828                      for (i=0; i<cm->numattrsets; i++) {
03829                             if ( !qm->attr_sets[i].count ) continue;
03830 
03831                             bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), "%d", i );
03832 
03833                             /* count the attr length */
03834                             for ( attr_name = qm->attr_sets[i].attrs;
03835                                    attr_name->an_name.bv_val; attr_name++ )
03836                             {
03837                                    bv.bv_len += attr_name->an_name.bv_len + 1;
03838                                    if ( attr_name->an_desc &&
03839                                                  ( attr_name->an_desc->ad_flags & SLAP_DESC_TEMPORARY ) ) {
03840                                           bv.bv_len += STRLENOF("undef:");
03841                                    }
03842                             }
03843 
03844                             bv.bv_val = ch_malloc( bv.bv_len+1 );
03845                             ptr = lutil_strcopy( bv.bv_val, c->cr_msg );
03846                             for ( attr_name = qm->attr_sets[i].attrs;
03847                                    attr_name->an_name.bv_val; attr_name++ ) {
03848                                    *ptr++ = ' ';
03849                                    if ( attr_name->an_desc &&
03850                                                  ( attr_name->an_desc->ad_flags & SLAP_DESC_TEMPORARY ) ) {
03851                                           ptr = lutil_strcopy( ptr, "undef:" );
03852                                    }
03853                                    ptr = lutil_strcopy( ptr, attr_name->an_name.bv_val );
03854                             }
03855                             ber_bvarray_add( &c->rvalue_vals, &bv );
03856                      }
03857                      if ( !c->rvalue_vals )
03858                             rc = 1;
03859                      break;
03860               case PC_TEMP:
03861                      for (temp=qm->templates; temp; temp=temp->qmnext) {
03862                             /* HEADS-UP: always print all;
03863                              * if optional == 0, ignore */
03864                             bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ),
03865                                    " %d %ld %ld %ld %ld",
03866                                    temp->attr_set_index,
03867                                    temp->ttl,
03868                                    temp->negttl,
03869                                    temp->limitttl,
03870                                    temp->ttr );
03871                             bv.bv_len += temp->querystr.bv_len + 2;
03872                             bv.bv_val = ch_malloc( bv.bv_len+1 );
03873                             ptr = bv.bv_val;
03874                             *ptr++ = '"';
03875                             ptr = lutil_strcopy( ptr, temp->querystr.bv_val );
03876                             *ptr++ = '"';
03877                             strcpy( ptr, c->cr_msg );
03878                             ber_bvarray_add( &c->rvalue_vals, &bv );
03879                      }
03880                      if ( !c->rvalue_vals )
03881                             rc = 1;
03882                      break;
03883               case PC_BIND:
03884                      for (temp=qm->templates; temp; temp=temp->qmnext) {
03885                             if ( !temp->bindttr ) continue;
03886                             bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ),
03887                                    " %d %ld %s ",
03888                                    temp->attr_set_index,
03889                                    temp->bindttr,
03890                                    ldap_pvt_scope2str( temp->bindscope ));
03891                             bv.bv_len += temp->bindbase.bv_len + temp->bindftemp.bv_len + 4;
03892                             bv.bv_val = ch_malloc( bv.bv_len + 1 );
03893                             ptr = bv.bv_val;
03894                             *ptr++ = '"';
03895                             ptr = lutil_strcopy( ptr, temp->bindftemp.bv_val );
03896                             *ptr++ = '"';
03897                             ptr = lutil_strcopy( ptr, c->cr_msg );
03898                             *ptr++ = '"';
03899                             ptr = lutil_strcopy( ptr, temp->bindbase.bv_val );
03900                             *ptr++ = '"';
03901                             *ptr = '\0';
03902                             ber_bvarray_add( &c->rvalue_vals, &bv );
03903                      }
03904                      if ( !c->rvalue_vals )
03905                             rc = 1;
03906                      break;
03907               case PC_RESP:
03908                      if ( cm->response_cb == PCACHE_RESPONSE_CB_HEAD ) {
03909                             BER_BVSTR( &bv, "head" );
03910                      } else {
03911                             BER_BVSTR( &bv, "tail" );
03912                      }
03913                      value_add_one( &c->rvalue_vals, &bv );
03914                      break;
03915               case PC_QUERIES:
03916                      c->value_int = cm->max_queries;
03917                      break;
03918               case PC_OFFLINE:
03919                      c->value_int = (cm->cc_paused & PCACHE_CC_OFFLINE) != 0;
03920                      break;
03921               }
03922               return rc;
03923        } else if ( c->op == LDAP_MOD_DELETE ) {
03924               rc = 1;
03925               switch( c->type ) {
03926               case PC_ATTR: /* FIXME */
03927               case PC_TEMP:
03928               case PC_BIND:
03929                      break;
03930               case PC_OFFLINE:
03931                      cm->cc_paused &= ~PCACHE_CC_OFFLINE;
03932                      /* If there were cached queries when we went offline,
03933                       * restart the checker now.
03934                       */
03935                      if ( cm->num_cached_queries ) {
03936                             ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
03937                             cm->cc_paused = 0;
03938                             ldap_pvt_runqueue_resched( &slapd_rq, cm->cc_arg, 0 );
03939                             ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
03940                      }
03941                      rc = 0;
03942                      break;
03943               }
03944               return rc;
03945        }
03946 
03947        switch( c->type ) {
03948        case PC_MAIN:
03949               if ( cm->numattrsets > 0 ) {
03950                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive already provided" );
03951                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
03952                      return( 1 );
03953               }
03954 
03955               if ( lutil_atoi( &cm->numattrsets, c->argv[3] ) != 0 ) {
03956                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse num attrsets=\"%s\" (arg #3)",
03957                             c->argv[3] );
03958                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
03959                      return( 1 );
03960               }
03961               if ( cm->numattrsets <= 0 ) {
03962                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "numattrsets (arg #3) must be positive" );
03963                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
03964                      return( 1 );
03965               }
03966               if ( cm->numattrsets > MAX_ATTR_SETS ) {
03967                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "numattrsets (arg #3) must be <= %d", MAX_ATTR_SETS );
03968                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
03969                      return( 1 );
03970               }
03971 
03972               if ( !backend_db_init( c->argv[1], &cm->db, -1, NULL )) {
03973                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "unknown backend type (arg #1)" );
03974                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
03975                      return( 1 );
03976               }
03977 
03978               if ( lutil_atoi( &cm->max_entries, c->argv[2] ) != 0 ) {
03979                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse max entries=\"%s\" (arg #2)",
03980                             c->argv[2] );
03981                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
03982                      return( 1 );
03983               }
03984               if ( cm->max_entries <= 0 ) {
03985                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "max entries (arg #2) must be positive.\n" );
03986                      Debug( LDAP_DEBUG_CONFIG, "%s: %s\n", c->log, c->cr_msg, 0 );
03987                      return( 1 );
03988               }
03989 
03990               if ( lutil_atoi( &cm->num_entries_limit, c->argv[4] ) != 0 ) {
03991                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse entry limit=\"%s\" (arg #4)",
03992                             c->argv[4] );
03993                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
03994                      return( 1 );
03995               }
03996               if ( cm->num_entries_limit <= 0 ) {
03997                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "entry limit (arg #4) must be positive" );
03998                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
03999                      return( 1 );
04000               }
04001               if ( cm->num_entries_limit > cm->max_entries ) {
04002                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "entry limit (arg #4) must be less than max entries %d (arg #2)", cm->max_entries );
04003                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04004                      return( 1 );
04005               }
04006 
04007               if ( lutil_parse_time( c->argv[5], &t ) != 0 ) {
04008                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse period=\"%s\" (arg #5)",
04009                             c->argv[5] );
04010                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04011                      return( 1 );
04012               }
04013 
04014               cm->cc_period = (time_t)t;
04015               Debug( pcache_debug,
04016                             "Total # of attribute sets to be cached = %d.\n",
04017                             cm->numattrsets, 0, 0 );
04018               qm->attr_sets = ( struct attr_set * )ch_calloc( cm->numattrsets,
04019                                           sizeof( struct attr_set ) );
04020               break;
04021        case PC_ATTR:
04022               if ( cm->numattrsets == 0 ) {
04023                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive not provided yet" );
04024                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04025                      return( 1 );
04026               }
04027               if ( lutil_atoi( &num, c->argv[1] ) != 0 ) {
04028                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse attrset #=\"%s\"",
04029                             c->argv[1] );
04030                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04031                      return( 1 );
04032               }
04033 
04034               if ( num < 0 || num >= cm->numattrsets ) {
04035                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "attrset index %d out of bounds (must be %s%d)",
04036                             num, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 );
04037                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04038                      return 1;
04039               }
04040               qm->attr_sets[num].flags |= PC_CONFIGURED;
04041               if ( c->argc == 2 ) {
04042                      /* assume "1.1" */
04043                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
04044                             "need an explicit attr in attrlist; use \"*\" to indicate all attrs" );
04045                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04046                      return 1;
04047 
04048               } else if ( c->argc == 3 ) {
04049                      if ( strcmp( c->argv[2], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) {
04050                             qm->attr_sets[num].count = 1;
04051                             qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 2,
04052                                    sizeof( AttributeName ) );
04053                             BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_USER_ATTRIBUTES );
04054                             break;
04055 
04056                      } else if ( strcmp( c->argv[2], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 ) {
04057                             qm->attr_sets[num].count = 1;
04058                             qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 2,
04059                                    sizeof( AttributeName ) );
04060                             BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
04061                             break;
04062 
04063                      } else if ( strcmp( c->argv[2], LDAP_NO_ATTRS ) == 0 ) {
04064                             break;
04065                      }
04066                      /* else: fallthru */
04067 
04068               } else if ( c->argc == 4 ) {
04069                      if ( ( strcmp( c->argv[2], LDAP_ALL_USER_ATTRIBUTES ) == 0 && strcmp( c->argv[3], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 )
04070                             || ( strcmp( c->argv[2], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 && strcmp( c->argv[3], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) )
04071                      {
04072                             qm->attr_sets[num].count = 2;
04073                             qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 3,
04074                                    sizeof( AttributeName ) );
04075                             BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_USER_ATTRIBUTES );
04076                             BER_BVSTR( &qm->attr_sets[num].attrs[1].an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
04077                             break;
04078                      }
04079                      /* else: fallthru */
04080               }
04081 
04082               if ( c->argc > 2 ) {
04083                      int all_user = 0, all_op = 0;
04084 
04085                      qm->attr_sets[num].count = c->argc - 2;
04086                      qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( c->argc - 1,
04087                             sizeof( AttributeName ) );
04088                      attr_name = qm->attr_sets[num].attrs;
04089                      for ( i = 2; i < c->argc; i++ ) {
04090                             attr_name->an_desc = NULL;
04091                             if ( strcmp( c->argv[i], LDAP_NO_ATTRS ) == 0 ) {
04092                                    snprintf( c->cr_msg, sizeof( c->cr_msg ),
04093                                           "invalid attr #%d \"%s\" in attrlist",
04094                                           i - 2, c->argv[i] );
04095                                    Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04096                                    ch_free( qm->attr_sets[num].attrs );
04097                                    qm->attr_sets[num].attrs = NULL;
04098                                    qm->attr_sets[num].count = 0;
04099                                    return 1;
04100                             }
04101                             if ( strcmp( c->argv[i], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) {
04102                                    all_user = 1;
04103                                    BER_BVSTR( &attr_name->an_name, LDAP_ALL_USER_ATTRIBUTES );
04104                             } else if ( strcmp( c->argv[i], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 ) {
04105                                    all_op = 1;
04106                                    BER_BVSTR( &attr_name->an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
04107                             } else {
04108                                    if ( strncasecmp( c->argv[i], "undef:", STRLENOF("undef:") ) == 0 ) {
04109                                           struct berval bv;
04110                                           ber_str2bv( c->argv[i] + STRLENOF("undef:"), 0, 0, &bv );
04111                                           attr_name->an_desc = slap_bv2tmp_ad( &bv, NULL );
04112 
04113                                    } else if ( slap_str2ad( c->argv[i], &attr_name->an_desc, &text ) ) {
04114                                           strcpy( c->cr_msg, text );
04115                                           Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04116                                           ch_free( qm->attr_sets[num].attrs );
04117                                           qm->attr_sets[num].attrs = NULL;
04118                                           qm->attr_sets[num].count = 0;
04119                                           return 1;
04120                                    }
04121                                    attr_name->an_name = attr_name->an_desc->ad_cname;
04122                             }
04123                             attr_name->an_oc = NULL;
04124                             attr_name->an_flags = 0;
04125                             if ( attr_name->an_desc == slap_schema.si_ad_objectClass )
04126                                    qm->attr_sets[num].flags |= PC_GOT_OC;
04127                             attr_name++;
04128                             BER_BVZERO( &attr_name->an_name );
04129                      }
04130 
04131                      /* warn if list contains both "*" and "+" */
04132                      if ( i > 4 && all_user && all_op ) {
04133                             snprintf( c->cr_msg, sizeof( c->cr_msg ),
04134                                    "warning: attribute list contains \"*\" and \"+\"" );
04135                             Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04136                      }
04137               }
04138               break;
04139        case PC_TEMP:
04140               if ( cm->numattrsets == 0 ) {
04141                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive not provided yet" );
04142                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04143                      return( 1 );
04144               }
04145               if ( lutil_atoi( &i, c->argv[2] ) != 0 ) {
04146                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template #=\"%s\"",
04147                             c->argv[2] );
04148                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04149                      return( 1 );
04150               }
04151 
04152               if ( i < 0 || i >= cm->numattrsets || 
04153                      !(qm->attr_sets[i].flags & PC_CONFIGURED )) {
04154                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "template index %d invalid (%s%d)",
04155                             i, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 );
04156                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04157                      return 1;
04158               }
04159               {
04160                      AttributeName *attrs;
04161                      int cnt;
04162                      cnt = template_attrs( c->argv[1], &qm->attr_sets[i], &attrs, &text );
04163                      if ( cnt < 0 ) {
04164                             snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template: %s",
04165                                    text );
04166                             Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04167                             return 1;
04168                      }
04169                      temp = ch_calloc( 1, sizeof( QueryTemplate ));
04170                      temp->qmnext = qm->templates;
04171                      qm->templates = temp;
04172                      temp->t_attrs.attrs = attrs;
04173                      temp->t_attrs.count = cnt;
04174               }
04175               ldap_pvt_thread_rdwr_init( &temp->t_rwlock );
04176               temp->query = temp->query_last = NULL;
04177               if ( lutil_parse_time( c->argv[3], &t ) != 0 ) {
04178                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
04179                             "unable to parse template ttl=\"%s\"",
04180                             c->argv[3] );
04181                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04182 pc_temp_fail:
04183                      ch_free( temp->t_attrs.attrs );
04184                      ch_free( temp );
04185                      return( 1 );
04186               }
04187               temp->ttl = (time_t)t;
04188               temp->negttl = (time_t)0;
04189               temp->limitttl = (time_t)0;
04190               temp->ttr = (time_t)0;
04191               switch ( c->argc ) {
04192               case 7:
04193                      if ( lutil_parse_time( c->argv[6], &t ) != 0 ) {
04194                             snprintf( c->cr_msg, sizeof( c->cr_msg ),
04195                                    "unable to parse template ttr=\"%s\"",
04196                                    c->argv[6] );
04197                             Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04198                             goto pc_temp_fail;
04199                      }
04200                      temp->ttr = (time_t)t;
04201                      /* fallthru */
04202 
04203               case 6:
04204                      if ( lutil_parse_time( c->argv[5], &t ) != 0 ) {
04205                             snprintf( c->cr_msg, sizeof( c->cr_msg ),
04206                                    "unable to parse template sizelimit ttl=\"%s\"",
04207                                    c->argv[5] );
04208                             Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04209                             goto pc_temp_fail;
04210                      }
04211                      temp->limitttl = (time_t)t;
04212                      /* fallthru */
04213 
04214               case 5:
04215                      if ( lutil_parse_time( c->argv[4], &t ) != 0 ) {
04216                             snprintf( c->cr_msg, sizeof( c->cr_msg ),
04217                                    "unable to parse template negative ttl=\"%s\"",
04218                                    c->argv[4] );
04219                             Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04220                             goto pc_temp_fail;
04221                      }
04222                      temp->negttl = (time_t)t;
04223                      break;
04224               }
04225 
04226               temp->no_of_queries = 0;
04227 
04228               ber_str2bv( c->argv[1], 0, 1, &temp->querystr );
04229               Debug( pcache_debug, "Template:\n", 0, 0, 0 );
04230               Debug( pcache_debug, "  query template: %s\n",
04231                             temp->querystr.bv_val, 0, 0 );
04232               temp->attr_set_index = i;
04233               qm->attr_sets[i].flags |= PC_REFERENCED;
04234               temp->qtnext = qm->attr_sets[i].templates;
04235               qm->attr_sets[i].templates = temp;
04236               Debug( pcache_debug, "  attributes: \n", 0, 0, 0 );
04237               if ( ( attrarray = qm->attr_sets[i].attrs ) != NULL ) {
04238                      for ( i=0; attrarray[i].an_name.bv_val; i++ )
04239                             Debug( pcache_debug, "\t%s\n",
04240                                    attrarray[i].an_name.bv_val, 0, 0 );
04241               }
04242               break;
04243        case PC_BIND:
04244               if ( !qm->templates ) {
04245                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcacheTemplate\" directive not provided yet" );
04246                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04247                      return( 1 );
04248               }
04249               if ( lutil_atoi( &i, c->argv[2] ) != 0 ) {
04250                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse Bind index #=\"%s\"",
04251                             c->argv[2] );
04252                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04253                      return( 1 );
04254               }
04255 
04256               if ( i < 0 || i >= cm->numattrsets || 
04257                      !(qm->attr_sets[i].flags & PC_CONFIGURED )) {
04258                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "Bind index %d invalid (%s%d)",
04259                             i, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 );
04260                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04261                      return 1;
04262               }
04263               {      struct berval bv, tempbv;
04264                      AttributeDescription **descs;
04265                      int ndescs;
04266                      ber_str2bv( c->argv[1], 0, 0, &bv );
04267                      ndescs = ftemp_attrs( &bv, &tempbv, &descs, &text );
04268                      if ( ndescs < 0 ) {
04269                             snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template: %s",
04270                                    text );
04271                             Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04272                             return 1;
04273                      }
04274                      for ( temp = qm->templates; temp; temp=temp->qmnext ) {
04275                             if ( temp->attr_set_index == i && bvmatch( &tempbv,
04276                                    &temp->querystr ))
04277                                    break;
04278                      }
04279                      ch_free( tempbv.bv_val );
04280                      if ( !temp ) {
04281                             ch_free( descs );
04282                             snprintf( c->cr_msg, sizeof( c->cr_msg ), "Bind template %s %d invalid",
04283                                    c->argv[1], i );
04284                             Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04285                             return 1;
04286                      }
04287                      ber_dupbv( &temp->bindftemp, &bv );
04288                      temp->bindfattrs = descs;
04289                      temp->bindnattrs = ndescs;
04290               }
04291               if ( lutil_parse_time( c->argv[3], &t ) != 0 ) {
04292                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
04293                             "unable to parse bind ttr=\"%s\"",
04294                             c->argv[3] );
04295                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04296 pc_bind_fail:
04297                      ch_free( temp->bindfattrs );
04298                      temp->bindfattrs = NULL;
04299                      ch_free( temp->bindftemp.bv_val );
04300                      BER_BVZERO( &temp->bindftemp );
04301                      return( 1 );
04302               }
04303               num = ldap_pvt_str2scope( c->argv[4] );
04304               if ( num < 0 ) {
04305                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
04306                             "unable to parse bind scope=\"%s\"",
04307                             c->argv[4] );
04308                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04309                      goto pc_bind_fail;
04310               }
04311               {
04312                      struct berval dn, ndn;
04313                      ber_str2bv( c->argv[5], 0, 0, &dn );
04314                      rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
04315                      if ( rc ) {
04316                             snprintf( c->cr_msg, sizeof( c->cr_msg ),
04317                                    "invalid bind baseDN=\"%s\"",
04318                                    c->argv[5] );
04319                             Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04320                             goto pc_bind_fail;
04321                      }
04322                      if ( temp->bindbase.bv_val )
04323                             ch_free( temp->bindbase.bv_val );
04324                      temp->bindbase = ndn;
04325               }
04326               {
04327                      /* convert the template into dummy filter */
04328                      struct berval bv;
04329                      char *eq = temp->bindftemp.bv_val, *e2;
04330                      Filter *f;
04331                      i = 0;
04332                      while ((eq = strchr(eq, '=' ))) {
04333                             eq++;
04334                             if ( eq[0] == ')' )
04335                                    i++;
04336                      }
04337                      bv.bv_len = temp->bindftemp.bv_len + i;
04338                      bv.bv_val = ch_malloc( bv.bv_len + 1 );
04339                      for ( e2 = bv.bv_val, eq = temp->bindftemp.bv_val;
04340                             *eq; eq++ ) {
04341                             if ( *eq == '=' ) {
04342                                    *e2++ = '=';
04343                                    if ( eq[1] == ')' )
04344                                           *e2++ = '*';
04345                             } else {
04346                                    *e2++ = *eq;
04347                             }
04348                      }
04349                      *e2 = '\0';
04350                      f = str2filter( bv.bv_val );
04351                      if ( !f ) {
04352                             ch_free( bv.bv_val );
04353                             snprintf( c->cr_msg, sizeof( c->cr_msg ),
04354                                    "unable to parse bindfilter=\"%s\"", bv.bv_val );
04355                             Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04356                             ch_free( temp->bindbase.bv_val );
04357                             BER_BVZERO( &temp->bindbase );
04358                             goto pc_bind_fail;
04359                      }
04360                      if ( temp->bindfilter )
04361                             filter_free( temp->bindfilter );
04362                      if ( temp->bindfilterstr.bv_val )
04363                             ch_free( temp->bindfilterstr.bv_val );
04364                      temp->bindfilterstr = bv;
04365                      temp->bindfilter = f;
04366               }
04367               temp->bindttr = (time_t)t;
04368               temp->bindscope = num;
04369               cm->cache_binds = 1;
04370               break;
04371 
04372        case PC_RESP:
04373               if ( strcasecmp( c->argv[1], "head" ) == 0 ) {
04374                      cm->response_cb = PCACHE_RESPONSE_CB_HEAD;
04375 
04376               } else if ( strcasecmp( c->argv[1], "tail" ) == 0 ) {
04377                      cm->response_cb = PCACHE_RESPONSE_CB_TAIL;
04378 
04379               } else {
04380                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "unknown specifier" );
04381                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04382                      return 1;
04383               }
04384               break;
04385        case PC_QUERIES:
04386               if ( c->value_int <= 0 ) {
04387                      snprintf( c->cr_msg, sizeof( c->cr_msg ), "max queries must be positive" );
04388                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04389                      return( 1 );
04390               }
04391               cm->max_queries = c->value_int;
04392               break;
04393        case PC_OFFLINE:
04394               if ( c->value_int )
04395                      cm->cc_paused |= PCACHE_CC_OFFLINE;
04396               else
04397                      cm->cc_paused &= ~PCACHE_CC_OFFLINE;
04398               break;
04399        case PC_PRIVATE_DB:
04400               if ( cm->db.be_private == NULL ) {
04401                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
04402                             "private database must be defined before setting database specific options" );
04403                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04404                      return( 1 );
04405               }
04406 
04407               if ( cm->db.bd_info->bi_cf_ocs ) {
04408                      ConfigTable   *ct;
04409                      ConfigArgs    c2 = *c;
04410                      char          *argv0 = c->argv[ 0 ];
04411 
04412                      c->argv[ 0 ] = &argv0[ STRLENOF( "pcache-" ) ];
04413 
04414                      ct = config_find_keyword( cm->db.bd_info->bi_cf_ocs->co_table, c );
04415                      if ( ct == NULL ) {
04416                             snprintf( c->cr_msg, sizeof( c->cr_msg ),
04417                                    "private database does not recognize specific option '%s'",
04418                                    c->argv[ 0 ] );
04419                             Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04420                             rc = 1;
04421 
04422                      } else {
04423                             c->table = cm->db.bd_info->bi_cf_ocs->co_type;
04424                             c->be = &cm->db;
04425                             c->bi = c->be->bd_info;
04426 
04427                             rc = config_add_vals( ct, c );
04428 
04429                             c->bi = c2.bi;
04430                             c->be = c2.be;
04431                             c->table = c2.table;
04432                      }
04433 
04434                      c->argv[ 0 ] = argv0;
04435 
04436               } else if ( cm->db.be_config != NULL ) {
04437                      char   *argv0 = c->argv[ 0 ];
04438 
04439                      c->argv[ 0 ] = &argv0[ STRLENOF( "pcache-" ) ];
04440                      rc = cm->db.be_config( &cm->db, c->fname, c->lineno, c->argc, c->argv );
04441                      c->argv[ 0 ] = argv0;
04442 
04443               } else {
04444                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
04445                             "no means to set private database specific options" );
04446                      Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
04447                      return 1;
04448               }
04449               break;
04450        default:
04451               rc = SLAP_CONF_UNKNOWN;
04452               break;
04453        }
04454 
04455        return rc;
04456 }
04457 
04458 static int
04459 pcache_db_config(
04460        BackendDB     *be,
04461        const char    *fname,
04462        int           lineno,
04463        int           argc,
04464        char          **argv
04465 )
04466 {
04467        slap_overinst *on = (slap_overinst *)be->bd_info;
04468        cache_manager*       cm = on->on_bi.bi_private;
04469 
04470        /* Something for the cache database? */
04471        if ( cm->db.bd_info && cm->db.bd_info->bi_db_config )
04472               return cm->db.bd_info->bi_db_config( &cm->db, fname, lineno,
04473                      argc, argv );
04474        return SLAP_CONF_UNKNOWN;
04475 }
04476 
04477 static int
04478 pcache_db_init(
04479        BackendDB *be,
04480        ConfigReply *cr)
04481 {
04482        slap_overinst *on = (slap_overinst *)be->bd_info;
04483        cache_manager *cm;
04484        query_manager *qm;
04485 
04486        cm = (cache_manager *)ch_malloc(sizeof(cache_manager));
04487        on->on_bi.bi_private = cm;
04488 
04489        qm = (query_manager*)ch_malloc(sizeof(query_manager));
04490 
04491        cm->db = *be;
04492        SLAP_DBFLAGS(&cm->db) |= SLAP_DBFLAG_NO_SCHEMA_CHECK;
04493        cm->db.be_private = NULL;
04494        cm->db.bd_self = &cm->db;
04495        cm->qm = qm;
04496        cm->numattrsets = 0;
04497        cm->num_entries_limit = 5;
04498        cm->num_cached_queries = 0;
04499        cm->max_entries = 0;
04500        cm->cur_entries = 0;
04501        cm->max_queries = 10000;
04502        cm->save_queries = 0;
04503        cm->check_cacheability = 0;
04504        cm->response_cb = PCACHE_RESPONSE_CB_TAIL;
04505        cm->defer_db_open = 1;
04506        cm->cache_binds = 0;
04507        cm->cc_period = 1000;
04508        cm->cc_paused = 0;
04509        cm->cc_arg = NULL;
04510 #ifdef PCACHE_MONITOR
04511        cm->monitor_cb = NULL;
04512 #endif /* PCACHE_MONITOR */
04513 
04514        qm->attr_sets = NULL;
04515        qm->templates = NULL;
04516        qm->lru_top = NULL;
04517        qm->lru_bottom = NULL;
04518 
04519        qm->qcfunc = query_containment;
04520        qm->crfunc = cache_replacement;
04521        qm->addfunc = add_query;
04522        ldap_pvt_thread_mutex_init(&qm->lru_mutex);
04523 
04524        ldap_pvt_thread_mutex_init(&cm->cache_mutex);
04525 
04526 #ifndef PCACHE_MONITOR
04527        return 0;
04528 #else /* PCACHE_MONITOR */
04529        return pcache_monitor_db_init( be );
04530 #endif /* PCACHE_MONITOR */
04531 }
04532 
04533 static int
04534 pcache_cachedquery_open_cb( Operation *op, SlapReply *rs )
04535 {
04536        assert( op->o_tag == LDAP_REQ_SEARCH );
04537 
04538        if ( rs->sr_type == REP_SEARCH ) {
04539               Attribute     *a;
04540 
04541               a = attr_find( rs->sr_entry->e_attrs, ad_cachedQueryURL );
04542               if ( a != NULL ) {
04543                      BerVarray     *valsp;
04544 
04545                      assert( a->a_nvals != NULL );
04546 
04547                      valsp = op->o_callback->sc_private;
04548                      assert( *valsp == NULL );
04549 
04550                      ber_bvarray_dup_x( valsp, a->a_nvals, op->o_tmpmemctx );
04551               }
04552        }
04553 
04554        return 0;
04555 }
04556 
04557 static int
04558 pcache_cachedquery_count_cb( Operation *op, SlapReply *rs )
04559 {
04560        assert( op->o_tag == LDAP_REQ_SEARCH );
04561 
04562        if ( rs->sr_type == REP_SEARCH ) {
04563               int    *countp = (int *)op->o_callback->sc_private;
04564 
04565               (*countp)++;
04566        }
04567 
04568        return 0;
04569 }
04570 
04571 static int
04572 pcache_db_open2(
04573        slap_overinst *on,
04574        ConfigReply *cr )
04575 {
04576        cache_manager *cm = on->on_bi.bi_private;
04577        query_manager*  qm = cm->qm;
04578        int rc;
04579 
04580        rc = backend_startup_one( &cm->db, cr );
04581        if ( rc == 0 ) {
04582               cm->defer_db_open = 0;
04583        }
04584 
04585        /* There is no runqueue in TOOL mode */
04586        if (( slapMode & SLAP_SERVER_MODE ) && rc == 0 ) {
04587               ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
04588               ldap_pvt_runqueue_insert( &slapd_rq, cm->cc_period,
04589                      consistency_check, on,
04590                      "pcache_consistency", cm->db.be_suffix[0].bv_val );
04591               ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
04592 
04593               /* Cached database must have the rootdn */
04594               if ( BER_BVISNULL( &cm->db.be_rootndn )
04595                             || BER_BVISEMPTY( &cm->db.be_rootndn ) )
04596               {
04597                      Debug( LDAP_DEBUG_ANY, "pcache_db_open(): "
04598                             "underlying database of type \"%s\"\n"
04599                             "    serving naming context \"%s\"\n"
04600                             "    has no \"rootdn\", required by \"pcache\".\n",
04601                             on->on_info->oi_orig->bi_type,
04602                             cm->db.be_suffix[0].bv_val, 0 );
04603                      return 1;
04604               }
04605 
04606               if ( cm->save_queries ) {
04607                      void          *thrctx = ldap_pvt_thread_pool_context();
04608                      Connection    conn = { 0 };
04609                      OperationBuffer      opbuf;
04610                      Operation     *op;
04611                      slap_callback cb = { 0 };
04612                      SlapReply     rs = { REP_RESULT };
04613                      BerVarray     vals = NULL;
04614                      Filter        f = { 0 }, f2 = { 0 };
04615                      AttributeAssertion   ava = ATTRIBUTEASSERTION_INIT;
04616                      AttributeName attrs[ 2 ] = {{{ 0 }}};
04617 
04618                      connection_fake_init2( &conn, &opbuf, thrctx, 0 );
04619                      op = &opbuf.ob_op;
04620 
04621                      op->o_bd = &cm->db;
04622 
04623                      op->o_tag = LDAP_REQ_SEARCH;
04624                      op->o_protocol = LDAP_VERSION3;
04625                      cb.sc_response = pcache_cachedquery_open_cb;
04626                      cb.sc_private = &vals;
04627                      op->o_callback = &cb;
04628                      op->o_time = slap_get_time();
04629                      op->o_do_not_cache = 1;
04630                      op->o_managedsait = SLAP_CONTROL_CRITICAL;
04631 
04632                      op->o_dn = cm->db.be_rootdn;
04633                      op->o_ndn = cm->db.be_rootndn;
04634                      op->o_req_dn = cm->db.be_suffix[ 0 ];
04635                      op->o_req_ndn = cm->db.be_nsuffix[ 0 ];
04636 
04637                      op->ors_scope = LDAP_SCOPE_BASE;
04638                      op->ors_deref = LDAP_DEREF_NEVER;
04639                      op->ors_slimit = 1;
04640                      op->ors_tlimit = SLAP_NO_LIMIT;
04641                      op->ors_limit = NULL;
04642                      ber_str2bv( "(pcacheQueryURL=*)", 0, 0, &op->ors_filterstr );
04643                      f.f_choice = LDAP_FILTER_PRESENT;
04644                      f.f_desc = ad_cachedQueryURL;
04645                      op->ors_filter = &f;
04646                      attrs[ 0 ].an_desc = ad_cachedQueryURL;
04647                      attrs[ 0 ].an_name = ad_cachedQueryURL->ad_cname;
04648                      op->ors_attrs = attrs;
04649                      op->ors_attrsonly = 0;
04650 
04651                      rc = op->o_bd->be_search( op, &rs );
04652                      if ( rc == LDAP_SUCCESS && vals != NULL ) {
04653                             int    i;
04654 
04655                             for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
04656                                    if ( url2query( vals[ i ].bv_val, op, qm ) == 0 ) {
04657                                           cm->num_cached_queries++;
04658                                    }
04659                             }
04660 
04661                             ber_bvarray_free_x( vals, op->o_tmpmemctx );
04662                      }
04663 
04664                      /* count cached entries */
04665                      f.f_choice = LDAP_FILTER_NOT;
04666                      f.f_not = &f2;
04667                      f2.f_choice = LDAP_FILTER_EQUALITY;
04668                      f2.f_ava = &ava;
04669                      f2.f_av_desc = slap_schema.si_ad_objectClass;
04670                      BER_BVSTR( &f2.f_av_value, "glue" );
04671                      ber_str2bv( "(!(objectClass=glue))", 0, 0, &op->ors_filterstr );
04672 
04673                      op->ors_slimit = SLAP_NO_LIMIT;
04674                      op->ors_scope = LDAP_SCOPE_SUBTREE;
04675                      op->ors_attrs = slap_anlist_no_attrs;
04676 
04677                      rs_reinit( &rs, REP_RESULT );
04678                      op->o_callback->sc_response = pcache_cachedquery_count_cb;
04679                      op->o_callback->sc_private = &rs.sr_nentries;
04680 
04681                      rc = op->o_bd->be_search( op, &rs );
04682 
04683                      cm->cur_entries = rs.sr_nentries;
04684 
04685                      /* ignore errors */
04686                      rc = 0;
04687               }
04688        }
04689        return rc;
04690 }
04691 
04692 static int
04693 pcache_db_open(
04694        BackendDB *be,
04695        ConfigReply *cr )
04696 {
04697        slap_overinst *on = (slap_overinst *)be->bd_info;
04698        cache_manager *cm = on->on_bi.bi_private;
04699        query_manager*  qm = cm->qm;
04700        int           i, ncf = 0, rf = 0, nrf = 0, rc = 0;
04701 
04702        /* check attr sets */
04703        for ( i = 0; i < cm->numattrsets; i++) {
04704               if ( !( qm->attr_sets[i].flags & PC_CONFIGURED ) ) {
04705                      if ( qm->attr_sets[i].flags & PC_REFERENCED ) {
04706                             Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d not configured but referenced.\n", i, 0, 0 );
04707                             rf++;
04708 
04709                      } else {
04710                             Debug( LDAP_DEBUG_CONFIG, "pcache: warning, attr set #%d not configured.\n", i, 0, 0 );
04711                      }
04712                      ncf++;
04713 
04714               } else if ( !( qm->attr_sets[i].flags & PC_REFERENCED ) ) {
04715                      Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d configured but not referenced.\n", i, 0, 0 );
04716                      nrf++;
04717               }
04718        }
04719 
04720        if ( ncf || rf || nrf ) {
04721               Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets configured but not referenced.\n", nrf, 0, 0 );
04722               Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets not configured.\n", ncf, 0, 0 );
04723               Debug( LDAP_DEBUG_CONFIG, "pcache: %d attr sets not configured but referenced.\n", rf, 0, 0 );
04724 
04725               if ( rf > 0 ) {
04726                      return 1;
04727               }
04728        }
04729 
04730        /* need to inherit something from the original database... */
04731        cm->db.be_def_limit = be->be_def_limit;
04732        cm->db.be_limits = be->be_limits;
04733        cm->db.be_acl = be->be_acl;
04734        cm->db.be_dfltaccess = be->be_dfltaccess;
04735 
04736        if ( SLAP_DBMONITORING( be ) ) {
04737               SLAP_DBFLAGS( &cm->db ) |= SLAP_DBFLAG_MONITORING;
04738 
04739        } else {
04740               SLAP_DBFLAGS( &cm->db ) &= ~SLAP_DBFLAG_MONITORING;
04741        }
04742 
04743        if ( !cm->defer_db_open ) {
04744               rc = pcache_db_open2( on, cr );
04745        }
04746 
04747 #ifdef PCACHE_MONITOR
04748        if ( rc == LDAP_SUCCESS ) {
04749               rc = pcache_monitor_db_open( be );
04750        }
04751 #endif /* PCACHE_MONITOR */
04752 
04753        return rc;
04754 }
04755 
04756 static void
04757 pcache_free_qbase( void *v )
04758 {
04759        Qbase *qb = v;
04760        int i;
04761 
04762        for (i=0; i<3; i++)
04763               tavl_free( qb->scopes[i], NULL );
04764        ch_free( qb );
04765 }
04766 
04767 static int
04768 pcache_db_close(
04769        BackendDB *be,
04770        ConfigReply *cr
04771 )
04772 {
04773        slap_overinst *on = (slap_overinst *)be->bd_info;
04774        cache_manager *cm = on->on_bi.bi_private;
04775        query_manager *qm = cm->qm;
04776        QueryTemplate *tm;
04777        int i, rc = 0;
04778 
04779        /* stop the thread ... */
04780        if ( cm->cc_arg ) {
04781               ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
04782               if ( ldap_pvt_runqueue_isrunning( &slapd_rq, cm->cc_arg ) ) {
04783                      ldap_pvt_runqueue_stoptask( &slapd_rq, cm->cc_arg );
04784               }
04785               ldap_pvt_runqueue_remove( &slapd_rq, cm->cc_arg );
04786               ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
04787        }
04788 
04789        if ( cm->save_queries ) {
04790               CachedQuery   *qc;
04791               BerVarray     vals = NULL;
04792 
04793               void          *thrctx;
04794               Connection    conn = { 0 };
04795               OperationBuffer      opbuf;
04796               Operation     *op;
04797               slap_callback cb = { 0 };
04798 
04799               SlapReply     rs = { REP_RESULT };
04800               Modifications mod = {{ 0 }};
04801 
04802               thrctx = ldap_pvt_thread_pool_context();
04803 
04804               connection_fake_init2( &conn, &opbuf, thrctx, 0 );
04805               op = &opbuf.ob_op;
04806 
04807                 mod.sml_numvals = 0;
04808               if ( qm->templates != NULL ) {
04809                      for ( tm = qm->templates; tm != NULL; tm = tm->qmnext ) {
04810                             for ( qc = tm->query; qc; qc = qc->next ) {
04811                                    struct berval bv;
04812 
04813                                    if ( query2url( op, qc, &bv, 0 ) == 0 ) {
04814                                           ber_bvarray_add_x( &vals, &bv, op->o_tmpmemctx );
04815                                           mod.sml_numvals++;
04816                                    }
04817                             }
04818                      }
04819               }
04820 
04821               op->o_bd = &cm->db;
04822               op->o_dn = cm->db.be_rootdn;
04823               op->o_ndn = cm->db.be_rootndn;
04824 
04825               op->o_tag = LDAP_REQ_MODIFY;
04826               op->o_protocol = LDAP_VERSION3;
04827               cb.sc_response = slap_null_cb;
04828               op->o_callback = &cb;
04829               op->o_time = slap_get_time();
04830               op->o_do_not_cache = 1;
04831               op->o_managedsait = SLAP_CONTROL_CRITICAL;
04832 
04833               op->o_req_dn = op->o_bd->be_suffix[0];
04834               op->o_req_ndn = op->o_bd->be_nsuffix[0];
04835 
04836               mod.sml_op = LDAP_MOD_REPLACE;
04837               mod.sml_flags = 0;
04838               mod.sml_desc = ad_cachedQueryURL;
04839               mod.sml_type = ad_cachedQueryURL->ad_cname;
04840               mod.sml_values = vals;
04841               mod.sml_nvalues = NULL;
04842               mod.sml_next = NULL;
04843               Debug( pcache_debug,
04844                      "%sSETTING CACHED QUERY URLS\n",
04845                      vals == NULL ? "RE" : "", 0, 0 );
04846 
04847               op->orm_modlist = &mod;
04848 
04849               op->o_bd->be_modify( op, &rs );
04850 
04851               ber_bvarray_free_x( vals, op->o_tmpmemctx );
04852        }
04853 
04854        /* cleanup stuff inherited from the original database... */
04855        cm->db.be_limits = NULL;
04856        cm->db.be_acl = NULL;
04857 
04858 
04859        if ( cm->db.bd_info->bi_db_close ) {
04860               rc = cm->db.bd_info->bi_db_close( &cm->db, NULL );
04861        }
04862        while ( (tm = qm->templates) != NULL ) {
04863               CachedQuery *qc, *qn;
04864               qm->templates = tm->qmnext;
04865               for ( qc = tm->query; qc; qc = qn ) {
04866                      qn = qc->next;
04867                      free_query( qc );
04868               }
04869               avl_free( tm->qbase, pcache_free_qbase );
04870               free( tm->querystr.bv_val );
04871               free( tm->bindfattrs );
04872               free( tm->bindftemp.bv_val );
04873               free( tm->bindfilterstr.bv_val );
04874               free( tm->bindbase.bv_val );
04875               filter_free( tm->bindfilter );
04876               ldap_pvt_thread_rdwr_destroy( &tm->t_rwlock );
04877               free( tm->t_attrs.attrs );
04878               free( tm );
04879        }
04880 
04881        for ( i = 0; i < cm->numattrsets; i++ ) {
04882               int j;
04883 
04884               /* Account of LDAP_NO_ATTRS */
04885               if ( !qm->attr_sets[i].count ) continue;
04886 
04887               for ( j = 0; !BER_BVISNULL( &qm->attr_sets[i].attrs[j].an_name ); j++ ) {
04888                      if ( qm->attr_sets[i].attrs[j].an_desc &&
04889                                    ( qm->attr_sets[i].attrs[j].an_desc->ad_flags &
04890                                      SLAP_DESC_TEMPORARY ) ) {
04891                             slap_sl_mfuncs.bmf_free( qm->attr_sets[i].attrs[j].an_desc, NULL );
04892                      }
04893               }
04894               free( qm->attr_sets[i].attrs );
04895        }
04896        free( qm->attr_sets );
04897        qm->attr_sets = NULL;
04898 
04899 #ifdef PCACHE_MONITOR
04900        if ( rc == LDAP_SUCCESS ) {
04901               rc = pcache_monitor_db_close( be );
04902        }
04903 #endif /* PCACHE_MONITOR */
04904 
04905        return rc;
04906 }
04907 
04908 static int
04909 pcache_db_destroy(
04910        BackendDB *be,
04911        ConfigReply *cr
04912 )
04913 {
04914        slap_overinst *on = (slap_overinst *)be->bd_info;
04915        cache_manager *cm = on->on_bi.bi_private;
04916        query_manager *qm = cm->qm;
04917 
04918        if ( cm->db.be_private != NULL ) {
04919               backend_stopdown_one( &cm->db );
04920        }
04921 
04922        ldap_pvt_thread_mutex_destroy( &qm->lru_mutex );
04923        ldap_pvt_thread_mutex_destroy( &cm->cache_mutex );
04924        free( qm );
04925        free( cm );
04926 
04927 #ifdef PCACHE_MONITOR
04928        pcache_monitor_db_destroy( be );
04929 #endif /* PCACHE_MONITOR */
04930 
04931        return 0;
04932 }
04933 
04934 #ifdef PCACHE_CONTROL_PRIVDB
04935 /*
04936         Control ::= SEQUENCE {
04937              controlType             LDAPOID,
04938              criticality             BOOLEAN DEFAULT FALSE,
04939              controlValue            OCTET STRING OPTIONAL }
04940 
04941         controlType ::= 1.3.6.1.4.1.4203.666.11.9.5.1
04942 
04943  * criticality must be TRUE; controlValue must be absent.
04944  */
04945 static int
04946 parse_privdb_ctrl(
04947        Operation     *op,
04948        SlapReply     *rs,
04949        LDAPControl   *ctrl )
04950 {
04951        if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_NONE ) {
04952               rs->sr_text = "privateDB control specified multiple times";
04953               return LDAP_PROTOCOL_ERROR;
04954        }
04955 
04956        if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
04957               rs->sr_text = "privateDB control value not absent";
04958               return LDAP_PROTOCOL_ERROR;
04959        }
04960 
04961        if ( !ctrl->ldctl_iscritical ) {
04962               rs->sr_text = "privateDB control criticality required";
04963               return LDAP_PROTOCOL_ERROR;
04964        }
04965 
04966        op->o_ctrlflag[ privDB_cid ] = SLAP_CONTROL_CRITICAL;
04967 
04968        return LDAP_SUCCESS;
04969 }
04970 
04971 static char *extops[] = {
04972        LDAP_EXOP_MODIFY_PASSWD,
04973        NULL
04974 };
04975 #endif /* PCACHE_CONTROL_PRIVDB */
04976 
04977 static struct berval pcache_exop_MODIFY_PASSWD = BER_BVC( LDAP_EXOP_MODIFY_PASSWD );
04978 #ifdef PCACHE_EXOP_QUERY_DELETE
04979 static struct berval pcache_exop_QUERY_DELETE = BER_BVC( PCACHE_EXOP_QUERY_DELETE );
04980 
04981 #define       LDAP_TAG_EXOP_QUERY_DELETE_BASE    ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 0)
04982 #define       LDAP_TAG_EXOP_QUERY_DELETE_DN      ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 1)
04983 #define       LDAP_TAG_EXOP_QUERY_DELETE_UUID    ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 2)
04984 
04985 /*
04986         ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
04987              requestName      [0] LDAPOID,
04988              requestValue     [1] OCTET STRING OPTIONAL }
04989 
04990         requestName ::= 1.3.6.1.4.1.4203.666.11.9.6.1
04991 
04992         requestValue ::= SEQUENCE { CHOICE {
04993                   baseDN           [0] LDAPDN
04994                   entryDN          [1] LDAPDN },
04995              queryID          [2] OCTET STRING (SIZE(16))
04996                   -- constrained to UUID }
04997 
04998  * Either baseDN or entryDN must be present, to allow database selection.
04999  *
05000  * 1. if baseDN and queryID are present, then the query corresponding
05001  *    to queryID is deleted;
05002  * 2. if baseDN is present and queryID is absent, then all queries
05003  *    are deleted;
05004  * 3. if entryDN is present and queryID is absent, then all queries
05005  *    corresponding to the queryID values present in entryDN are deleted;
05006  * 4. if entryDN and queryID are present, then all queries
05007  *    corresponding to the queryID values present in entryDN are deleted,
05008  *    but only if the value of queryID is contained in the entry;
05009  *
05010  * Currently, only 1, 3 and 4 are implemented.  2 can be obtained by either
05011  * recursively deleting the database (ldapdelete -r) with PRIVDB control,
05012  * or by removing the database files.
05013 
05014         ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
05015              COMPONENTS OF LDAPResult,
05016              responseName     [10] LDAPOID OPTIONAL,
05017              responseValue    [11] OCTET STRING OPTIONAL }
05018 
05019  * responseName and responseValue must be absent.
05020  */
05021 
05022 /*
05023  * - on success, *tagp is either LDAP_TAG_EXOP_QUERY_DELETE_BASE
05024  *   or LDAP_TAG_EXOP_QUERY_DELETE_DN.
05025  * - if ndn != NULL, it is set to the normalized DN in the request
05026  *   corresponding to either the baseDN or the entryDN, according
05027  *   to *tagp; memory is malloc'ed on the Operation's slab, and must
05028  *   be freed by the caller.
05029  * - if uuid != NULL, it is set to point to the normalized UUID;
05030  *   memory is malloc'ed on the Operation's slab, and must
05031  *   be freed by the caller.
05032  */
05033 static int
05034 pcache_parse_query_delete(
05035        struct berval *in,
05036        ber_tag_t     *tagp,
05037        struct berval *ndn,
05038        struct berval *uuid,
05039        const char    **text,
05040        void          *ctx )
05041 {
05042        int                  rc = LDAP_SUCCESS;
05043        ber_tag_t            tag;
05044        ber_len_t            len = -1;
05045        BerElementBuffer     berbuf;
05046        BerElement           *ber = (BerElement *)&berbuf;
05047        struct berval        reqdata = BER_BVNULL;
05048 
05049        *text = NULL;
05050 
05051        if ( ndn ) {
05052               BER_BVZERO( ndn );
05053        }
05054 
05055        if ( uuid ) {
05056               BER_BVZERO( uuid );
05057        }
05058 
05059        if ( in == NULL || in->bv_len == 0 ) {
05060               *text = "empty request data field in queryDelete exop";
05061               return LDAP_PROTOCOL_ERROR;
05062        }
05063 
05064        ber_dupbv_x( &reqdata, in, ctx );
05065 
05066        /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
05067        ber_init2( ber, &reqdata, 0 );
05068 
05069        tag = ber_scanf( ber, "{" /*}*/ );
05070 
05071        if ( tag == LBER_ERROR ) {
05072               Debug( LDAP_DEBUG_TRACE,
05073                      "pcache_parse_query_delete: decoding error.\n",
05074                      0, 0, 0 );
05075               goto decoding_error;
05076        }
05077 
05078        tag = ber_peek_tag( ber, &len );
05079        if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_BASE
05080               || tag == LDAP_TAG_EXOP_QUERY_DELETE_DN )
05081        {
05082               *tagp = tag;
05083 
05084               if ( ndn != NULL ) {
05085                      struct berval dn;
05086 
05087                      tag = ber_scanf( ber, "m", &dn );
05088                      if ( tag == LBER_ERROR ) {
05089                             Debug( LDAP_DEBUG_TRACE,
05090                                    "pcache_parse_query_delete: DN parse failed.\n",
05091                                    0, 0, 0 );
05092                             goto decoding_error;
05093                      }
05094 
05095                      rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx );
05096                      if ( rc != LDAP_SUCCESS ) {
05097                             *text = "invalid DN in queryDelete exop request data";
05098                             goto done;
05099                      }
05100 
05101               } else {
05102                      tag = ber_scanf( ber, "x" /* "m" */ );
05103                      if ( tag == LBER_DEFAULT ) {
05104                             goto decoding_error;
05105                      }
05106               }
05107 
05108               tag = ber_peek_tag( ber, &len );
05109        }
05110 
05111        if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_UUID ) {
05112               if ( uuid != NULL ) {
05113                      struct berval bv;
05114                      char          uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
05115 
05116                      tag = ber_scanf( ber, "m", &bv );
05117                      if ( tag == LBER_ERROR ) {
05118                             Debug( LDAP_DEBUG_TRACE,
05119                                    "pcache_parse_query_delete: UUID parse failed.\n",
05120                                    0, 0, 0 );
05121                             goto decoding_error;
05122                      }
05123 
05124                      if ( bv.bv_len != 16 ) {
05125                             Debug( LDAP_DEBUG_TRACE,
05126                                    "pcache_parse_query_delete: invalid UUID length %lu.\n",
05127                                    (unsigned long)bv.bv_len, 0, 0 );
05128                             goto decoding_error;
05129                      }
05130 
05131                      rc = lutil_uuidstr_from_normalized(
05132                             bv.bv_val, bv.bv_len,
05133                             uuidbuf, sizeof( uuidbuf ) );
05134                      if ( rc == -1 ) {
05135                             goto decoding_error;
05136                      }
05137                      ber_str2bv( uuidbuf, rc, 1, uuid );
05138                      rc = LDAP_SUCCESS;
05139 
05140               } else {
05141                      tag = ber_skip_tag( ber, &len );
05142                      if ( tag == LBER_DEFAULT ) {
05143                             goto decoding_error;
05144                      }
05145 
05146                      if ( len != 16 ) {
05147                             Debug( LDAP_DEBUG_TRACE,
05148                                    "pcache_parse_query_delete: invalid UUID length %lu.\n",
05149                                    (unsigned long)len, 0, 0 );
05150                             goto decoding_error;
05151                      }
05152               }
05153 
05154               tag = ber_peek_tag( ber, &len );
05155        }
05156 
05157        if ( tag != LBER_DEFAULT || len != 0 ) {
05158 decoding_error:;
05159               Debug( LDAP_DEBUG_TRACE,
05160                      "pcache_parse_query_delete: decoding error\n",
05161                      0, 0, 0 );
05162               rc = LDAP_PROTOCOL_ERROR;
05163               *text = "queryDelete data decoding error";
05164 
05165 done:;
05166               if ( ndn && !BER_BVISNULL( ndn ) ) {
05167                      slap_sl_free( ndn->bv_val, ctx );
05168                      BER_BVZERO( ndn );
05169               }
05170 
05171               if ( uuid && !BER_BVISNULL( uuid ) ) {
05172                      slap_sl_free( uuid->bv_val, ctx );
05173                      BER_BVZERO( uuid );
05174               }
05175        }
05176 
05177        if ( !BER_BVISNULL( &reqdata ) ) {
05178               ber_memfree_x( reqdata.bv_val, ctx );
05179        }
05180 
05181        return rc;
05182 }
05183 
05184 static int
05185 pcache_exop_query_delete(
05186        Operation     *op,
05187        SlapReply     *rs )
05188 {
05189        BackendDB     *bd = op->o_bd;
05190 
05191        struct berval uuid = BER_BVNULL,
05192                      *uuidp = NULL;
05193        char          buf[ SLAP_TEXT_BUFLEN ];
05194        unsigned      len;
05195        ber_tag_t     tag = LBER_DEFAULT;
05196 
05197        if ( LogTest( LDAP_DEBUG_STATS ) ) {
05198               uuidp = &uuid;
05199        }
05200 
05201        rs->sr_err = pcache_parse_query_delete( op->ore_reqdata,
05202               &tag, &op->o_req_ndn, uuidp,
05203               &rs->sr_text, op->o_tmpmemctx );
05204        if ( rs->sr_err != LDAP_SUCCESS ) {
05205               return rs->sr_err;
05206        }
05207 
05208        if ( LogTest( LDAP_DEBUG_STATS ) ) {
05209               assert( !BER_BVISNULL( &op->o_req_ndn ) );
05210               len = snprintf( buf, sizeof( buf ), " dn=\"%s\"", op->o_req_ndn.bv_val );
05211 
05212               if ( !BER_BVISNULL( &uuid ) && len < sizeof( buf ) ) {
05213                      snprintf( &buf[ len ], sizeof( buf ) - len, " pcacheQueryId=\"%s\"", uuid.bv_val );
05214               }
05215 
05216               Debug( LDAP_DEBUG_STATS, "%s QUERY DELETE%s\n",
05217                      op->o_log_prefix, buf, 0 );
05218        }
05219        op->o_req_dn = op->o_req_ndn;
05220 
05221        op->o_bd = select_backend( &op->o_req_ndn, 0 );
05222        if ( op->o_bd == NULL ) {
05223               send_ldap_error( op, rs, LDAP_NO_SUCH_OBJECT,
05224                      "no global superior knowledge" );
05225        }
05226        rs->sr_err = backend_check_restrictions( op, rs,
05227               (struct berval *)&pcache_exop_QUERY_DELETE );
05228        if ( rs->sr_err != LDAP_SUCCESS ) {
05229               goto done;
05230        }
05231 
05232        if ( op->o_bd->be_extended == NULL ) {
05233               send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
05234                      "backend does not support extended operations" );
05235               goto done;
05236        }
05237 
05238        op->o_bd->be_extended( op, rs );
05239 
05240 done:;
05241        if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
05242               op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
05243               BER_BVZERO( &op->o_req_ndn );
05244               BER_BVZERO( &op->o_req_dn );
05245        }
05246 
05247        if ( !BER_BVISNULL( &uuid ) ) {
05248               op->o_tmpfree( uuid.bv_val, op->o_tmpmemctx );
05249        }
05250 
05251        op->o_bd = bd;
05252 
05253         return rs->sr_err;
05254 }
05255 #endif /* PCACHE_EXOP_QUERY_DELETE */
05256 
05257 static int
05258 pcache_op_extended( Operation *op, SlapReply *rs )
05259 {
05260        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
05261        cache_manager *cm = on->on_bi.bi_private;
05262 
05263 #ifdef PCACHE_CONTROL_PRIVDB
05264        if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) {
05265               return pcache_op_privdb( op, rs );
05266        }
05267 #endif /* PCACHE_CONTROL_PRIVDB */
05268 
05269 #ifdef PCACHE_EXOP_QUERY_DELETE
05270        if ( bvmatch( &op->ore_reqoid, &pcache_exop_QUERY_DELETE ) ) {
05271               struct berval uuid = BER_BVNULL;
05272               ber_tag_t     tag = LBER_DEFAULT;
05273 
05274               rs->sr_err = pcache_parse_query_delete( op->ore_reqdata,
05275                      &tag, NULL, &uuid, &rs->sr_text, op->o_tmpmemctx );
05276               assert( rs->sr_err == LDAP_SUCCESS );
05277 
05278               if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_DN ) {
05279                      /* remove all queries related to the selected entry */
05280                      rs->sr_err = pcache_remove_entry_queries_from_cache( op,
05281                             cm, &op->o_req_ndn, &uuid );
05282 
05283               } else if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_BASE ) {
05284                      if ( !BER_BVISNULL( &uuid ) ) {
05285                             /* remove the selected query */
05286                             rs->sr_err = pcache_remove_query_from_cache( op,
05287                                    cm, &uuid );
05288 
05289                      } else {
05290                             /* TODO: remove all queries */
05291                             rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
05292                             rs->sr_text = "deletion of all queries not implemented";
05293                      }
05294               }
05295 
05296               op->o_tmpfree( uuid.bv_val, op->o_tmpmemctx );
05297               return rs->sr_err;
05298        }
05299 #endif /* PCACHE_EXOP_QUERY_DELETE */
05300 
05301        /* We only care if we're configured for Bind caching */
05302        if ( bvmatch( &op->ore_reqoid, &pcache_exop_MODIFY_PASSWD ) &&
05303               cm->cache_binds ) {
05304               /* See if the local entry exists and has a password.
05305                * It's too much work to find the matching query, so
05306                * we just see if there's a hashed password to update.
05307                */
05308               Operation op2 = *op;
05309               Entry *e = NULL;
05310               int rc;
05311               int doit = 0;
05312 
05313               op2.o_bd = &cm->db;
05314               op2.o_dn = op->o_bd->be_rootdn;
05315               op2.o_ndn = op->o_bd->be_rootndn;
05316               rc = be_entry_get_rw( &op2, &op->o_req_ndn, NULL,
05317                      slap_schema.si_ad_userPassword, 0, &e );
05318               if ( rc == LDAP_SUCCESS && e ) {
05319                      /* See if a recognized password is hashed here */
05320                      Attribute *a = attr_find( e->e_attrs,
05321                             slap_schema.si_ad_userPassword );
05322                      if ( a && a->a_vals[0].bv_val[0] == '{' &&
05323                             lutil_passwd_scheme( a->a_vals[0].bv_val )) {
05324                             doit = 1;
05325                      }
05326                      be_entry_release_r( &op2, e );
05327               }
05328 
05329               if ( doit ) {
05330                      rc = overlay_op_walk( op, rs, op_extended, on->on_info,
05331                             on->on_next );
05332                      if ( rc == LDAP_SUCCESS ) {
05333                             req_pwdexop_s *qpw = &op->oq_pwdexop;
05334 
05335                             /* We don't care if it succeeds or not */
05336                             pc_setpw( &op2, &qpw->rs_new, cm );
05337                      }
05338                      return rc;
05339               }
05340        }
05341        return SLAP_CB_CONTINUE;
05342 }
05343 
05344 static int
05345 pcache_entry_release( Operation  *op, Entry *e, int rw )
05346 {
05347        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
05348        cache_manager *cm = on->on_bi.bi_private;
05349        BackendDB *db = op->o_bd;
05350        int rc;
05351 
05352        op->o_bd = &cm->db;
05353        rc = be_entry_release_rw( op, e, rw );
05354        op->o_bd = db;
05355        return rc;
05356 }
05357 
05358 #ifdef PCACHE_MONITOR
05359 
05360 static int
05361 pcache_monitor_update(
05362        Operation     *op,
05363        SlapReply     *rs,
05364        Entry         *e,
05365        void          *priv )
05366 {
05367        cache_manager *cm = (cache_manager *) priv;
05368        query_manager *qm = cm->qm;
05369 
05370        CachedQuery   *qc;
05371        BerVarray     vals = NULL;
05372 
05373        attr_delete( &e->e_attrs, ad_cachedQueryURL );
05374        if ( ( SLAP_OPATTRS( rs->sr_attr_flags ) || ad_inlist( ad_cachedQueryURL, rs->sr_attrs ) )
05375               && qm->templates != NULL )
05376        {
05377               QueryTemplate *tm;
05378 
05379               for ( tm = qm->templates; tm != NULL; tm = tm->qmnext ) {
05380                      for ( qc = tm->query; qc; qc = qc->next ) {
05381                             struct berval bv;
05382 
05383                             if ( query2url( op, qc, &bv, 1 ) == 0 ) {
05384                                    ber_bvarray_add_x( &vals, &bv, op->o_tmpmemctx );
05385                             }
05386                      }
05387               }
05388 
05389 
05390               if ( vals != NULL ) {
05391                      attr_merge_normalize( e, ad_cachedQueryURL, vals, NULL );
05392                      ber_bvarray_free_x( vals, op->o_tmpmemctx );
05393               }
05394        }
05395 
05396        {
05397               Attribute     *a;
05398               char          buf[ SLAP_TEXT_BUFLEN ];
05399               struct berval bv;
05400 
05401               /* number of cached queries */
05402               a = attr_find( e->e_attrs, ad_numQueries );
05403               assert( a != NULL );
05404 
05405               bv.bv_val = buf;
05406               bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", cm->num_cached_queries );
05407 
05408               if ( a->a_nvals != a->a_vals ) {
05409                      ber_bvreplace( &a->a_nvals[ 0 ], &bv );
05410               }
05411               ber_bvreplace( &a->a_vals[ 0 ], &bv );
05412 
05413               /* number of cached entries */
05414               a = attr_find( e->e_attrs, ad_numEntries );
05415               assert( a != NULL );
05416 
05417               bv.bv_val = buf;
05418               bv.bv_len = snprintf( buf, sizeof( buf ), "%d", cm->cur_entries );
05419 
05420               if ( a->a_nvals != a->a_vals ) {
05421                      ber_bvreplace( &a->a_nvals[ 0 ], &bv );
05422               }
05423               ber_bvreplace( &a->a_vals[ 0 ], &bv );
05424        }
05425 
05426        return SLAP_CB_CONTINUE;
05427 }
05428 
05429 static int
05430 pcache_monitor_free(
05431        Entry         *e,
05432        void          **priv )
05433 {
05434        struct berval values[ 2 ];
05435        Modification  mod = { 0 };
05436 
05437        const char    *text;
05438        char          textbuf[ SLAP_TEXT_BUFLEN ];
05439 
05440        int           rc;
05441 
05442        /* NOTE: if slap_shutdown != 0, priv might have already been freed */
05443        *priv = NULL;
05444 
05445        /* Remove objectClass */
05446        mod.sm_op = LDAP_MOD_DELETE;
05447        mod.sm_desc = slap_schema.si_ad_objectClass;
05448        mod.sm_values = values;
05449        mod.sm_numvals = 1;
05450        values[ 0 ] = oc_olmPCache->soc_cname;
05451        BER_BVZERO( &values[ 1 ] );
05452 
05453        rc = modify_delete_values( e, &mod, 1, &text,
05454               textbuf, sizeof( textbuf ) );
05455        /* don't care too much about return code... */
05456 
05457        /* remove attrs */
05458        mod.sm_values = NULL;
05459        mod.sm_desc = ad_cachedQueryURL;
05460        mod.sm_numvals = 0;
05461        rc = modify_delete_values( e, &mod, 1, &text,
05462               textbuf, sizeof( textbuf ) );
05463        /* don't care too much about return code... */
05464 
05465        /* remove attrs */
05466        mod.sm_values = NULL;
05467        mod.sm_desc = ad_numQueries;
05468        mod.sm_numvals = 0;
05469        rc = modify_delete_values( e, &mod, 1, &text,
05470               textbuf, sizeof( textbuf ) );
05471        /* don't care too much about return code... */
05472 
05473        /* remove attrs */
05474        mod.sm_values = NULL;
05475        mod.sm_desc = ad_numEntries;
05476        mod.sm_numvals = 0;
05477        rc = modify_delete_values( e, &mod, 1, &text,
05478               textbuf, sizeof( textbuf ) );
05479        /* don't care too much about return code... */
05480 
05481        return SLAP_CB_CONTINUE;
05482 }
05483 
05484 /*
05485  * call from within pcache_initialize()
05486  */
05487 static int
05488 pcache_monitor_initialize( void )
05489 {
05490        static int    pcache_monitor_initialized = 0;
05491 
05492        if ( backend_info( "monitor" ) == NULL ) {
05493               return -1;
05494        }
05495 
05496        if ( pcache_monitor_initialized++ ) {
05497               return 0;
05498        }
05499 
05500        return 0;
05501 }
05502 
05503 static int
05504 pcache_monitor_db_init( BackendDB *be )
05505 {
05506        if ( pcache_monitor_initialize() == LDAP_SUCCESS ) {
05507               SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_MONITORING;
05508        }
05509 
05510        return 0;
05511 }
05512 
05513 static int
05514 pcache_monitor_db_open( BackendDB *be )
05515 {
05516        slap_overinst        *on = (slap_overinst *)be->bd_info;
05517        cache_manager        *cm = on->on_bi.bi_private;
05518        Attribute            *a, *next;
05519        monitor_callback_t   *cb = NULL;
05520        int                  rc = 0;
05521        BackendInfo          *mi;
05522        monitor_extra_t             *mbe;
05523        struct berval        dummy = BER_BVC( "" );
05524 
05525        if ( !SLAP_DBMONITORING( be ) ) {
05526               return 0;
05527        }
05528 
05529        mi = backend_info( "monitor" );
05530        if ( !mi || !mi->bi_extra ) {
05531               SLAP_DBFLAGS( be ) ^= SLAP_DBFLAG_MONITORING;
05532               return 0;
05533        }
05534        mbe = mi->bi_extra;
05535 
05536        /* don't bother if monitor is not configured */
05537        if ( !mbe->is_configured() ) {
05538               static int warning = 0;
05539 
05540               if ( warning++ == 0 ) {
05541                      Debug( LDAP_DEBUG_ANY, "pcache_monitor_db_open: "
05542                             "monitoring disabled; "
05543                             "configure monitor database to enable\n",
05544                             0, 0, 0 );
05545               }
05546 
05547               return 0;
05548        }
05549 
05550        /* alloc as many as required (plus 1 for objectClass) */
05551        a = attrs_alloc( 1 + 2 );
05552        if ( a == NULL ) {
05553               rc = 1;
05554               goto cleanup;
05555        }
05556 
05557        a->a_desc = slap_schema.si_ad_objectClass;
05558        attr_valadd( a, &oc_olmPCache->soc_cname, NULL, 1 );
05559        next = a->a_next;
05560 
05561        {
05562               struct berval bv = BER_BVC( "0" );
05563 
05564               next->a_desc = ad_numQueries;
05565               attr_valadd( next, &bv, NULL, 1 );
05566               next = next->a_next;
05567 
05568               next->a_desc = ad_numEntries;
05569               attr_valadd( next, &bv, NULL, 1 );
05570               next = next->a_next;
05571        }
05572 
05573        cb = ch_calloc( sizeof( monitor_callback_t ), 1 );
05574        cb->mc_update = pcache_monitor_update;
05575        cb->mc_free = pcache_monitor_free;
05576        cb->mc_private = (void *)cm;
05577 
05578        /* make sure the database is registered; then add monitor attributes */
05579        BER_BVZERO( &cm->monitor_ndn );
05580        rc = mbe->register_overlay( be, on, &cm->monitor_ndn );
05581        if ( rc == 0 ) {
05582               rc = mbe->register_entry_attrs( &cm->monitor_ndn, a, cb,
05583                      &dummy, -1, &dummy);
05584        }
05585 
05586 cleanup:;
05587        if ( rc != 0 ) {
05588               if ( cb != NULL ) {
05589                      ch_free( cb );
05590                      cb = NULL;
05591               }
05592 
05593               if ( a != NULL ) {
05594                      attrs_free( a );
05595                      a = NULL;
05596               }
05597        }
05598 
05599        /* store for cleanup */
05600        cm->monitor_cb = (void *)cb;
05601 
05602        /* we don't need to keep track of the attributes, because
05603         * bdb_monitor_free() takes care of everything */
05604        if ( a != NULL ) {
05605               attrs_free( a );
05606        }
05607 
05608        return rc;
05609 }
05610 
05611 static int
05612 pcache_monitor_db_close( BackendDB *be )
05613 {
05614        slap_overinst *on = (slap_overinst *)be->bd_info;
05615        cache_manager *cm = on->on_bi.bi_private;
05616 
05617        if ( cm->monitor_cb != NULL ) {
05618               BackendInfo          *mi = backend_info( "monitor" );
05619               monitor_extra_t             *mbe;
05620 
05621               if ( mi && &mi->bi_extra ) {
05622                      mbe = mi->bi_extra;
05623                      mbe->unregister_entry_callback( &cm->monitor_ndn,
05624                             (monitor_callback_t *)cm->monitor_cb,
05625                             NULL, 0, NULL );
05626               }
05627        }
05628 
05629        return 0;
05630 }
05631 
05632 static int
05633 pcache_monitor_db_destroy( BackendDB *be )
05634 {
05635        return 0;
05636 }
05637 
05638 #endif /* PCACHE_MONITOR */
05639 
05640 static slap_overinst pcache;
05641 
05642 static char *obsolete_names[] = {
05643        "proxycache",
05644        NULL
05645 };
05646 
05647 #if SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC
05648 static
05649 #endif /* SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC */
05650 int
05651 pcache_initialize()
05652 {
05653        int i, code;
05654        struct berval debugbv = BER_BVC("pcache");
05655        ConfigArgs c;
05656        char *argv[ 4 ];
05657 
05658        code = slap_loglevel_get( &debugbv, &pcache_debug );
05659        if ( code ) {
05660               return code;
05661        }
05662 
05663 #ifdef PCACHE_CONTROL_PRIVDB
05664        code = register_supported_control( PCACHE_CONTROL_PRIVDB,
05665               SLAP_CTRL_BIND|SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, extops,
05666               parse_privdb_ctrl, &privDB_cid );
05667        if ( code != LDAP_SUCCESS ) {
05668               Debug( LDAP_DEBUG_ANY,
05669                      "pcache_initialize: failed to register control %s (%d)\n",
05670                      PCACHE_CONTROL_PRIVDB, code, 0 );
05671               return code;
05672        }
05673 #endif /* PCACHE_CONTROL_PRIVDB */
05674 
05675 #ifdef PCACHE_EXOP_QUERY_DELETE
05676        code = load_extop2( (struct berval *)&pcache_exop_QUERY_DELETE,
05677               SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, pcache_exop_query_delete,
05678               0 );
05679        if ( code != LDAP_SUCCESS ) {
05680               Debug( LDAP_DEBUG_ANY,
05681                      "pcache_initialize: unable to register queryDelete exop: %d.\n",
05682                      code, 0, 0 );
05683               return code;
05684        }
05685 #endif /* PCACHE_EXOP_QUERY_DELETE */
05686 
05687        argv[ 0 ] = "back-bdb/back-hdb monitor";
05688        c.argv = argv;
05689        c.argc = 3;
05690        c.fname = argv[0];
05691 
05692        for ( i = 0; s_oid[ i ].name; i++ ) {
05693               c.lineno = i;
05694               argv[ 1 ] = s_oid[ i ].name;
05695               argv[ 2 ] = s_oid[ i ].oid;
05696 
05697               if ( parse_oidm( &c, 0, NULL ) != 0 ) {
05698                      Debug( LDAP_DEBUG_ANY, "pcache_initialize: "
05699                             "unable to add objectIdentifier \"%s=%s\"\n",
05700                             s_oid[ i ].name, s_oid[ i ].oid, 0 );
05701                      return 1;
05702               }
05703        }
05704 
05705        for ( i = 0; s_ad[i].desc != NULL; i++ ) {
05706               code = register_at( s_ad[i].desc, s_ad[i].adp, 0 );
05707               if ( code ) {
05708                      Debug( LDAP_DEBUG_ANY,
05709                             "pcache_initialize: register_at #%d failed\n", i, 0, 0 );
05710                      return code;
05711               }
05712               (*s_ad[i].adp)->ad_type->sat_flags |= SLAP_AT_HIDE;
05713        }
05714 
05715        for ( i = 0; s_oc[i].desc != NULL; i++ ) {
05716               code = register_oc( s_oc[i].desc, s_oc[i].ocp, 0 );
05717               if ( code ) {
05718                      Debug( LDAP_DEBUG_ANY,
05719                             "pcache_initialize: register_oc #%d failed\n", i, 0, 0 );
05720                      return code;
05721               }
05722               (*s_oc[i].ocp)->soc_flags |= SLAP_OC_HIDE;
05723        }
05724 
05725        pcache.on_bi.bi_type = "pcache";
05726        pcache.on_bi.bi_obsolete_names = obsolete_names;
05727        pcache.on_bi.bi_db_init = pcache_db_init;
05728        pcache.on_bi.bi_db_config = pcache_db_config;
05729        pcache.on_bi.bi_db_open = pcache_db_open;
05730        pcache.on_bi.bi_db_close = pcache_db_close;
05731        pcache.on_bi.bi_db_destroy = pcache_db_destroy;
05732 
05733        pcache.on_bi.bi_op_search = pcache_op_search;
05734        pcache.on_bi.bi_op_bind = pcache_op_bind;
05735 #ifdef PCACHE_CONTROL_PRIVDB
05736        pcache.on_bi.bi_op_compare = pcache_op_privdb;
05737        pcache.on_bi.bi_op_modrdn = pcache_op_privdb;
05738        pcache.on_bi.bi_op_modify = pcache_op_privdb;
05739        pcache.on_bi.bi_op_add = pcache_op_privdb;
05740        pcache.on_bi.bi_op_delete = pcache_op_privdb;
05741 #endif /* PCACHE_CONTROL_PRIVDB */
05742        pcache.on_bi.bi_extended = pcache_op_extended;
05743 
05744        pcache.on_bi.bi_entry_release_rw = pcache_entry_release;
05745        pcache.on_bi.bi_chk_controls = pcache_chk_controls;
05746 
05747        pcache.on_bi.bi_cf_ocs = pcocs;
05748 
05749        code = config_register_schema( pccfg, pcocs );
05750        if ( code ) return code;
05751 
05752        return overlay_register( &pcache );
05753 }
05754 
05755 #if SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC
05756 int init_module(int argc, char *argv[]) {
05757        return pcache_initialize();
05758 }
05759 #endif
05760 
05761 #endif /* defined(SLAPD_OVER_PROXYCACHE) */