]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/overlays/pcache.c
fix length
[openldap] / servers / slapd / overlays / pcache.c
index 6a20a1efc14e91c1df2da8759c4181980530547e..81d195aa243bba624337494570865a4de03bb0c5 100644 (file)
@@ -1,7 +1,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2003-2007 The OpenLDAP Foundation.
+ * Copyright 2003-2008 The OpenLDAP Foundation.
  * Portions Copyright 2003 IBM Corporation.
  * Portions Copyright 2003 Symas Corporation.
  * All rights reserved.
@@ -182,18 +182,20 @@ typedef struct cache_manager_s {
        unsigned long   num_cached_queries;             /* total number of cached queries */
        unsigned long   max_queries;                    /* upper bound on # of cached queries */
        int             save_queries;                   /* save cached queries across restarts */
+       int     check_cacheability;             /* check whether a query is cacheable */
        int     numattrsets;                    /* number of attribute sets */
        int     cur_entries;                    /* current number of entries cached */
        int     max_entries;                    /* max number of entries cached */
-        int     num_entries_limit;             /* max # of entries in a cacheable query */
+       int     num_entries_limit;              /* max # of entries in a cacheable query */
 
        char    response_cb;                    /* install the response callback
                                                 * at the tail of the callback list */
 #define PCACHE_RESPONSE_CB_HEAD        0
 #define PCACHE_RESPONSE_CB_TAIL        1
+       char    defer_db_open;                  /* defer open for online add */
 
        time_t  cc_period;              /* interval between successive consistency checks (sec) */
-       int     cc_paused;
+       int     cc_paused;
        void    *cc_arg;
 
        ldap_pvt_thread_mutex_t         cache_mutex;
@@ -263,17 +265,17 @@ query2url( Operation *op, CachedQuery *q, struct berval *urlbv )
 {
        struct berval   bv_scope,
                        bv_filter;
-       char            attrset_buf[ 32 ],
-                       expiry_buf[ 32 ],
+       char            attrset_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
+                       expiry_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
                        *ptr;
        ber_len_t       attrset_len,
                        expiry_len;
 
        ldap_pvt_scope2bv( q->scope, &bv_scope );
        filter2bv_x( op, q->filter, &bv_filter );
-       attrset_len = snprintf( attrset_buf, sizeof( attrset_buf ),
+       attrset_len = sprintf( attrset_buf,
                "%lu", (unsigned long)q->qtemp->attr_set_index );
-       expiry_len = snprintf( expiry_buf, sizeof( expiry_buf ),
+       expiry_len = sprintf( expiry_buf,
                "%lu", (unsigned long)q->expiry_time );
 
        urlbv->bv_len = STRLENOF( "ldap:///" )
@@ -1592,6 +1594,7 @@ struct search_info {
        Query query;
        QueryTemplate *qtemp;
        AttributeName*  save_attrs;     /* original attributes, saved for response */
+       int swap_saved_attrs;
        int max;
        int over;
        int count;
@@ -1694,7 +1697,7 @@ pcache_remove_entries_from_cache(
        Filter          f = { 0 };
        char            filtbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(entryUUID=)" ) ];
        AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
-       AttributeName   attrs[ 2 ] = { 0 };
+       AttributeName   attrs[ 2 ] = {{{ 0 }}};
        int             s, rc;
 
        if ( op == NULL ) {
@@ -1809,7 +1812,7 @@ pcache_remove_entry_queries_from_cache(
        Filter                  f = { 0 };
        char                    filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(queryId=)" ) ];
        AttributeAssertion      ava = ATTRIBUTEASSERTION_INIT;
-       AttributeName           attrs[ 2 ] = { 0 };
+       AttributeName           attrs[ 2 ] = {{{ 0 }}};
        int                     rc;
 
        BerVarray               vals = NULL;
@@ -1900,14 +1903,19 @@ cache_entries(
        Entry           *e;
        struct berval   crp_uuid;
        char            uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
-       Operation op_tmp = *op;
+       Operation       *op_tmp;
+       Connection      conn = {0};
+       OperationBuffer opbuf;
+       void            *thrctx = ldap_pvt_thread_pool_context();
 
        query_uuid->bv_len = lutil_uuidstr(uuidbuf, sizeof(uuidbuf));
        ber_str2bv(uuidbuf, query_uuid->bv_len, 1, query_uuid);
 
-       op_tmp.o_bd = &cm->db;
-       op_tmp.o_dn = cm->db.be_rootdn;
-       op_tmp.o_ndn = cm->db.be_rootndn;
+       connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+       op_tmp = &opbuf.ob_op;
+       op_tmp->o_bd = &cm->db;
+       op_tmp->o_dn = cm->db.be_rootdn;
+       op_tmp->o_ndn = cm->db.be_rootndn;
 
        Debug( pcache_debug, "UUID for query being added = %s\n",
                        uuidbuf, 0, 0 );
@@ -1917,10 +1925,10 @@ cache_entries(
                e->e_private = NULL;
                while ( cm->cur_entries > (cm->max_entries) ) {
                        BER_BVZERO( &crp_uuid );
-                       remove_query_and_data( &op_tmp, rs, cm, &crp_uuid );
+                       remove_query_and_data( op_tmp, rs, cm, &crp_uuid );
                }
 
-               return_val = merge_entry(&op_tmp, e, query_uuid);
+               return_val = merge_entry(op_tmp, e, query_uuid);
                ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
                cm->cur_entries += return_val;
                Debug( pcache_debug,
@@ -1954,6 +1962,14 @@ pcache_op_cleanup( Operation *op, SlapReply *rs ) {
                 * limit, empty the chain and ignore the rest.
                 */
                if ( !si->over ) {
+                       /* check if the entry contains undefined
+                        * attributes/objectClasses (ITS#5680) */
+                       if ( cm->check_cacheability && test_filter( op, rs->sr_entry, si->query.filter ) != LDAP_COMPARE_TRUE ) {
+                               Debug( pcache_debug, "%s: query not cacheable because of schema issues in DN \"%s\"\n",
+                                       op->o_log_prefix, rs->sr_entry->e_name.bv_val, 0 );
+                               goto over;
+                       }
+
                        if ( si->count < si->max ) {
                                si->count++;
                                e = entry_dup( rs->sr_entry );
@@ -1962,6 +1978,7 @@ pcache_op_cleanup( Operation *op, SlapReply *rs ) {
                                si->tail = e;
 
                        } else {
+over:;
                                si->over = 1;
                                si->count = 0;
                                for (;si->head; si->head=e) {
@@ -1978,11 +1995,12 @@ pcache_op_cleanup( Operation *op, SlapReply *rs ) {
        if ( rs->sr_type == REP_RESULT || 
                op->o_abandon || rs->sr_err == SLAPD_ABANDON )
        {
-               if ( si->save_attrs != NULL ) {
+               if ( si->swap_saved_attrs ) {
                        rs->sr_attrs = si->save_attrs;
                        op->ors_attrs = si->save_attrs;
                }
-               if ( op->o_abandon || rs->sr_err == SLAPD_ABANDON ) {
+               if ( (op->o_abandon || rs->sr_err == SLAPD_ABANDON) && 
+                               si->caching_reason == PC_IGNORE ) {
                        filter_free( si->query.filter );
                        if ( si->count ) {
                                /* duplicate query, free it */
@@ -2008,6 +2026,13 @@ pcache_op_cleanup( Operation *op, SlapReply *rs ) {
                                case PC_SIZELIMIT:
                                        qc->q_sizelimit = rs->sr_nentries;
                                        break;
+
+                               case PC_NEGATIVE:
+                                       break;
+
+                               default:
+                                       assert( 0 );
+                                       break;
                                }
                                ldap_pvt_thread_rdwr_wunlock(&qc->rwlock);
                                ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
@@ -2053,7 +2078,7 @@ pcache_response(
 {
        struct search_info *si = op->o_callback->sc_private;
 
-       if ( si->save_attrs != NULL ) {
+       if ( si->swap_saved_attrs ) {
                rs->sr_attrs = si->save_attrs;
                op->ors_attrs = si->save_attrs;
        }
@@ -2150,7 +2175,7 @@ add_filter_attrs(
        }
        BER_BVZERO( &(*new_attrs)[j].an_name );
 
-       return count;
+       return j;
 }
 
 /* NOTE: this is a quick workaround to let pcache minimally interact
@@ -2205,6 +2230,13 @@ pcache_op_privdb(
                return SLAP_CB_CONTINUE;
        }
 
+       /* The cache DB isn't open yet */
+       if ( cm->defer_db_open ) {
+               send_ldap_error( op, rs, LDAP_UNAVAILABLE,
+                       "pcachePrivDB: cacheDB not available" );
+               return rs->sr_err;
+       }
+
        /* FIXME: might be a little bit exaggerated... */
        if ( !be_isroot( op ) ) {
                save_cb = op->o_callback;
@@ -2277,6 +2309,16 @@ pcache_op_search(
        }
 #endif /* PCACHE_CONTROL_PRIVDB */
 
+       /* The cache DB isn't open yet */
+       if ( cm->defer_db_open ) {
+               send_ldap_error( op, rs, LDAP_UNAVAILABLE,
+                       "pcachePrivDB: cacheDB not available" );
+               return rs->sr_err;
+       }
+
+       /* pickup runtime ACL changes */
+       cm->db.be_acl = op->o_bd->be_acl;
+
        tempstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len+1, op->o_tmpmemctx );
        tempstr.bv_len = 0;
        if ( filter2template( op, op->ors_filter, &tempstr, &filter_attrs,
@@ -2388,6 +2430,7 @@ pcache_op_search(
                }
                si->head = NULL;
                si->tail = NULL;
+               si->swap_saved_attrs = 1;
                si->save_attrs = op->ors_attrs;
 
                op->ors_attrs = qtemp->t_attrs.attrs;
@@ -2499,7 +2542,7 @@ consistency_check(
        op->o_dn = cm->db.be_rootdn;
        op->o_ndn = cm->db.be_rootndn;
 
-       cm->cc_arg = arg;
+       cm->cc_arg = arg;
 
        for (templ = qm->templates; templ; templ=templ->qmnext) {
                query = templ->query_last;
@@ -2606,6 +2649,11 @@ static ConfigTable pccfg[] = {
                "( OLcfgOvAt:2.6 NAME 'olcProxySaveQueries' "
                        "DESC 'Save cached queries for hot restart' "
                        "SYNTAX OMsBoolean )", NULL, NULL },
+       { "proxyCheckCacheability", "TRUE|FALSE",
+               2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, check_cacheability),
+               "( OLcfgOvAt:2.7 NAME 'olcProxyCheckCacheability' "
+                       "DESC 'Check whether the results of a query are cacheable, e.g. for schema issues' "
+                       "SYNTAX OMsBoolean )", NULL, NULL },
 
        { NULL, NULL, 0, 0, 0, ARG_IGNORED }
 };
@@ -2616,15 +2664,24 @@ static ConfigOCs pcocs[] = {
                "DESC 'ProxyCache configuration' "
                "SUP olcOverlayConfig "
                "MUST ( olcProxyCache $ olcProxyAttrset $ olcProxyTemplate ) "
-               "MAY ( olcProxyResponseCB $ olcProxyCacheQueries $ olcProxySaveQueries ) )",
+               "MAY ( olcProxyResponseCB $ olcProxyCacheQueries $ olcProxySaveQueries $ olcProxyCheckCacheability ) )",
                Cft_Overlay, pccfg, NULL, pc_cfadd },
        { "( OLcfgOvOc:2.2 "
                "NAME 'olcPcacheDatabase' "
                "DESC 'Cache database configuration' "
-               "AUXILIARY )", Cft_Misc, pccfg, pc_ldadd },
+               "AUXILIARY )", Cft_Misc, olcDatabaseDummy, pc_ldadd },
        { NULL, 0, NULL }
 };
 
+static int pcache_db_open2( slap_overinst *on, ConfigReply *cr );
+
+static int
+pc_ldadd_cleanup( ConfigArgs *c )
+{
+       slap_overinst *on = c->ca_private;
+       return pcache_db_open2( on, &c->reply );
+}
+
 static int
 pc_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
 {
@@ -2638,6 +2695,12 @@ pc_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
        on = (slap_overinst *)p->ce_bi;
        cm = on->on_bi.bi_private;
        ca->be = &cm->db;
+       /* Defer open if this is an LDAPadd */
+       if ( CONFIG_ONLINE_ADD( ca ))
+               ca->cleanup = pc_ldadd_cleanup;
+       else
+               cm->defer_db_open = 0;
+       ca->ca_private = on;
        return LDAP_SUCCESS;
 }
 
@@ -2652,11 +2715,12 @@ pc_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
        /* FIXME: should not hardcode "olcDatabase" here */
        bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ),
                "olcDatabase=%s", cm->db.bd_info->bi_type );
-       if ( bv.bv_len < 0 || bv.bv_len >= sizeof( ca->cr_msg ) ) {
+       if ( bv.bv_len >= sizeof( ca->cr_msg ) ) {
                return -1;
        }
        bv.bv_val = ca->cr_msg;
        ca->be = &cm->db;
+       cm->defer_db_open = 0;
 
        /* We can only create this entry if the database is table-driven
         */
@@ -2826,6 +2890,7 @@ pc_cf_gen( ConfigArgs *c )
                        Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
                        return( 1 );
                }
+
                cm->cc_period = (time_t)t;
                Debug( pcache_debug,
                                "Total # of attribute sets to be cached = %d.\n",
@@ -3083,7 +3148,7 @@ pcache_db_init(
        cm->db = *be;
        SLAP_DBFLAGS(&cm->db) |= SLAP_DBFLAG_NO_SCHEMA_CHECK;
        cm->db.be_private = NULL;
-       cm->db.be_pcl_mutexp = &cm->db.be_pcl_mutex;
+       cm->db.bd_self = &cm->db;
        cm->qm = qm;
        cm->numattrsets = 0;
        cm->num_entries_limit = 5;
@@ -3092,9 +3157,12 @@ pcache_db_init(
        cm->cur_entries = 0;
        cm->max_queries = 10000;
        cm->save_queries = 0;
+       cm->check_cacheability = 0;
        cm->response_cb = PCACHE_RESPONSE_CB_TAIL;
+       cm->defer_db_open = 1;
        cm->cc_period = 1000;
        cm->cc_paused = 0;
+       cm->cc_arg = NULL;
 
        qm->attr_sets = NULL;
        qm->templates = NULL;
@@ -3149,64 +3217,25 @@ pcache_cachedquery_count_cb( Operation *op, SlapReply *rs )
 }
 
 static int
-pcache_db_open(
-       BackendDB *be,
+pcache_db_open2(
+       slap_overinst *on,
        ConfigReply *cr )
 {
-       slap_overinst   *on = (slap_overinst *)be->bd_info;
        cache_manager   *cm = on->on_bi.bi_private;
        query_manager*  qm = cm->qm;
-       int             i, ncf = 0, rf = 0, nrf = 0, rc = 0;
-
-       /* check attr sets */
-       for ( i = 0; i < cm->numattrsets; i++) {
-               if ( !( qm->attr_sets[i].flags & PC_CONFIGURED ) ) {
-                       if ( qm->attr_sets[i].flags & PC_REFERENCED ) {
-                               Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d not configured but referenced.\n", i, 0, 0 );
-                               rf++;
-
-                       } else {
-                               Debug( LDAP_DEBUG_CONFIG, "pcache: warning, attr set #%d not configured.\n", i, 0, 0 );
-                       }
-                       ncf++;
-
-               } else if ( !( qm->attr_sets[i].flags & PC_REFERENCED ) ) {
-                       Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d configured but not referenced.\n", i, 0, 0 );
-                       nrf++;
-               }
-       }
-
-       if ( ncf || rf || nrf ) {
-               Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets configured but not referenced.\n", nrf, 0, 0 );
-               Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets not configured.\n", ncf, 0, 0 );
-               Debug( LDAP_DEBUG_CONFIG, "pcache: %d attr sets not configured but referenced.\n", rf, 0, 0 );
-
-               if ( rf > 0 ) {
-                       return 1;
-               }
-       }
-
-       /* need to inherit something from the original database... */
-       cm->db.be_def_limit = be->be_def_limit;
-       cm->db.be_limits = be->be_limits;
-       cm->db.be_acl = be->be_acl;
-       cm->db.be_dfltaccess = be->be_dfltaccess;
-
-       if ( SLAP_DBMONITORING( be ) ) {
-               SLAP_DBFLAGS( &cm->db ) |= SLAP_DBFLAG_MONITORING;
+       int rc;
 
-       } else {
-               SLAP_DBFLAGS( &cm->db ) &= ~SLAP_DBFLAG_MONITORING;
+       rc = backend_startup_one( &cm->db, cr );
+       if ( rc == 0 ) {
+               cm->defer_db_open = 0;
        }
 
-       rc = backend_startup_one( &cm->db, NULL );
-
        /* There is no runqueue in TOOL mode */
-       if ( slapMode & SLAP_SERVER_MODE ) {
+       if (( slapMode & SLAP_SERVER_MODE ) && rc == 0 ) {
                ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
                ldap_pvt_runqueue_insert( &slapd_rq, cm->cc_period,
                        consistency_check, on,
-                       "pcache_consistency", be->be_suffix[0].bv_val );
+                       "pcache_consistency", cm->db.be_suffix[0].bv_val );
                ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
 
                /* Cached database must have the rootdn */
@@ -3232,7 +3261,7 @@ pcache_db_open(
                        BerVarray       vals = NULL;
                        Filter          f = { 0 }, f2 = { 0 };
                        AttributeAssertion      ava = ATTRIBUTEASSERTION_INIT;
-                       AttributeName   attrs[ 2 ] = { 0 };
+                       AttributeName   attrs[ 2 ] = {{{ 0 }}};
 
                        connection_fake_init( &conn, &opbuf, thrctx );
                        op = &opbuf.ob_op;
@@ -3304,6 +3333,62 @@ pcache_db_open(
                        rc = 0;
                }
        }
+       return rc;
+}
+
+static int
+pcache_db_open(
+       BackendDB *be,
+       ConfigReply *cr )
+{
+       slap_overinst   *on = (slap_overinst *)be->bd_info;
+       cache_manager   *cm = on->on_bi.bi_private;
+       query_manager*  qm = cm->qm;
+       int             i, ncf = 0, rf = 0, nrf = 0, rc = 0;
+
+       /* check attr sets */
+       for ( i = 0; i < cm->numattrsets; i++) {
+               if ( !( qm->attr_sets[i].flags & PC_CONFIGURED ) ) {
+                       if ( qm->attr_sets[i].flags & PC_REFERENCED ) {
+                               Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d not configured but referenced.\n", i, 0, 0 );
+                               rf++;
+
+                       } else {
+                               Debug( LDAP_DEBUG_CONFIG, "pcache: warning, attr set #%d not configured.\n", i, 0, 0 );
+                       }
+                       ncf++;
+
+               } else if ( !( qm->attr_sets[i].flags & PC_REFERENCED ) ) {
+                       Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d configured but not referenced.\n", i, 0, 0 );
+                       nrf++;
+               }
+       }
+
+       if ( ncf || rf || nrf ) {
+               Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets configured but not referenced.\n", nrf, 0, 0 );
+               Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets not configured.\n", ncf, 0, 0 );
+               Debug( LDAP_DEBUG_CONFIG, "pcache: %d attr sets not configured but referenced.\n", rf, 0, 0 );
+
+               if ( rf > 0 ) {
+                       return 1;
+               }
+       }
+
+       /* need to inherit something from the original database... */
+       cm->db.be_def_limit = be->be_def_limit;
+       cm->db.be_limits = be->be_limits;
+       cm->db.be_acl = be->be_acl;
+       cm->db.be_dfltaccess = be->be_dfltaccess;
+
+       if ( SLAP_DBMONITORING( be ) ) {
+               SLAP_DBFLAGS( &cm->db ) |= SLAP_DBFLAG_MONITORING;
+
+       } else {
+               SLAP_DBFLAGS( &cm->db ) &= ~SLAP_DBFLAG_MONITORING;
+       }
+
+       if ( !cm->defer_db_open )
+               rc = pcache_db_open2( on, cr );
 
        return rc;
 }
@@ -3342,7 +3427,7 @@ pcache_db_close(
                slap_callback   cb = { 0 };
 
                SlapReply       rs = { REP_RESULT };
-               Modifications   mod = { 0 };
+               Modifications   mod = {{ 0 }};
 
                thrctx = ldap_pvt_thread_pool_context();
 
@@ -3715,8 +3800,8 @@ pcache_exop_query_delete(
 
        struct berval   uuid = BER_BVNULL,
                        *uuidp = NULL;
-       char            buf[ SLAP_TEXT_BUFLEN ] = { '\0' };
-       int             len = 0;
+       char            buf[ SLAP_TEXT_BUFLEN ];
+       unsigned        len;
        ber_tag_t       tag = LBER_DEFAULT;
 
        if ( LogTest( LDAP_DEBUG_STATS ) ) {
@@ -3734,7 +3819,7 @@ pcache_exop_query_delete(
                assert( !BER_BVISNULL( &op->o_req_ndn ) );
                len = snprintf( buf, sizeof( buf ), " dn=\"%s\"", op->o_req_ndn.bv_val );
 
-               if ( !BER_BVISNULL( &uuid ) ) {
+               if ( !BER_BVISNULL( &uuid ) && len < sizeof( buf ) ) {
                        snprintf( &buf[ len ], sizeof( buf ) - len, " queryId=\"%s\"", uuid.bv_val );
                }