]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/overlays/accesslog.c
Each refint op needs a unique timestamp, must perform searches as rootdn
[openldap] / servers / slapd / overlays / accesslog.c
index 87a2aa78926b7dff6e2e11bfa46d0197b42c96af..15c512a0224c05734f34bb850bdef4141aae8161 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2005 The OpenLDAP Foundation.
+ * Copyright 2005-2006 The OpenLDAP Foundation.
  * Portions copyright 2004-2005 Symas Corporation.
  * All rights reserved.
  *
 #define LOG_OP_ALL             (LOG_OP_READS|LOG_OP_WRITES|LOG_OP_SESSION| \
        LOG_OP_EXTENDED|LOG_OP_UNKNOWN)
 
+typedef struct log_attr {
+       struct log_attr *next;
+       AttributeDescription *attr;
+} log_attr;
+
 typedef struct log_info {
        BackendDB *li_db;
        slap_mask_t li_ops;
        int li_age;
        int li_cycle;
        struct re_s *li_task;
+       Filter *li_oldf;
+       Entry *li_old;
+       log_attr *li_oldattrs;
        int li_success;
-       ldap_pvt_thread_mutex_t li_op_mutex;
+       ldap_pvt_thread_rmutex_t li_op_rmutex;
        ldap_pvt_thread_mutex_t li_log_mutex;
 } log_info;
 
@@ -68,7 +76,9 @@ enum {
        LOG_DB = 1,
        LOG_OPS,
        LOG_PURGE,
-       LOG_SUCCESS
+       LOG_SUCCESS,
+       LOG_OLD,
+       LOG_OLDATTR
 };
 
 static ConfigTable log_cfats[] = {
@@ -90,6 +100,14 @@ static ConfigTable log_cfats[] = {
                log_cf_gen, "( OLcfgOvAt:4.4 NAME 'olcAccessLogSuccess' "
                        "DESC 'Log successful ops only' "
                        "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+       { "logold", "filter", 2, 2, 0, ARG_MAGIC|LOG_OLD,
+               log_cf_gen, "( OLcfgOvAt:4.5 NAME 'olcAccessLogOld' "
+                       "DESC 'Log old values when modifying entries matching the filter' "
+                       "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+       { "logoldattr", "attrs", 2, 0, 0, ARG_MAGIC|LOG_OLDATTR,
+               log_cf_gen, "( OLcfgOvAt:4.6 NAME 'olcAccessLogOldAttr' "
+                       "DESC 'Log old values of these attributes even if unmodified' "
+                       "SYNTAX OMsDirectoryString )", NULL, NULL },
        { NULL }
 };
 
@@ -99,7 +117,8 @@ static ConfigOCs log_cfocs[] = {
                "DESC 'Access log configuration' "
                "SUP olcOverlayConfig "
                "MUST olcAccessLogDB "
-               "MAY ( olcAccessLogOps $ olcAccessLogPurge $ olcAccessLogSuccess ) )",
+               "MAY ( olcAccessLogOps $ olcAccessLogPurge $ olcAccessLogSuccess $ "
+                       "olcAccessLogOld $ olcAccessLogOldAttr ) )",
                        Cft_Overlay, log_cfats },
        { NULL }
 };
@@ -361,7 +380,7 @@ static struct {
                "DESC 'ModRDN operation' "
                "SUP auditWriteObject STRUCTURAL "
                "MUST ( reqNewRDN $ reqDeleteOldRDN ) "
-               "MAY reqNewSuperior )", &log_ocs[LOG_EN_MODRDN] },
+               "MAY ( reqNewSuperior $ reqOld ) )", &log_ocs[LOG_EN_MODRDN] },
        { "( " LOG_SCHEMA_OC ".11 NAME 'auditSearch' "
                "DESC 'Search operation' "
                "SUP auditReadObject STRUCTURAL "
@@ -396,29 +415,29 @@ log_age_parse(char *agestr)
                        return -1;
                t1 *= 24;
                gotdays = 1;
-       } else if ( *endptr != ':' ) {
-       /* No valid delimiter found, fail */
-               return -1;
+               agestr = endptr + 1;
+       } else {
+               if ( agestr[2] != ':' ) {
+                       /* No valid delimiter found, fail */
+                       return -1;
+               }
+               t1 *= 60;
+               agestr += 3;
        }
 
-       agestr += 3;
        t2 = atoi( agestr );
-
-       /* if there's a delimiter, it can only be a colon */
-       if ( agestr[2] && agestr[2] != ':' )
-               return -1;
-
-       /* If we're at the end of the string, and we started with days,
-        * fail because we expected to find minutes too.
-        */
-       if ( gotdays && !agestr[2] )
-               return -1;
-
-       t1 *= 60;
        t1 += t2;
 
-       if ( !agestr[2] )
-               return t1 * 60;
+       if ( agestr[2] ) {
+               /* if there's a delimiter, it can only be a colon */
+               if ( agestr[2] != ':' )
+                       return -1;
+       } else {
+               /* If we're at the end of the string, and we started with days,
+                * fail because we expected to find minutes too.
+                */
+               return gotdays ? -1 : t1 * 60;
+       }
 
        agestr += 3;
        t2 = atoi( agestr );
@@ -429,12 +448,15 @@ log_age_parse(char *agestr)
        t1 *= 60;
        t1 += t2;
 
-       t1 *= 60;
        if ( agestr[2] ) {
                agestr += 3;
                if ( agestr[2] )
                        return -1;
+               t1 *= 60;
                t1 += atoi( agestr );
+       } else if ( gotdays ) {
+               /* only got days+hh:mm */
+               t1 *= 60;
        }
        return t1;
 }
@@ -449,7 +471,7 @@ log_age_unparse( int age, struct berval *agebv )
        age /= 60;
        mm = age % 60;
        age /= 60;
-       hh = age % 60;
+       hh = age % 24;
        age /= 24;
        dd = age;
 
@@ -583,6 +605,14 @@ log_cf_gen(ConfigArgs *c)
        case SLAP_CONFIG_EMIT:
                switch( c->type ) {
                case LOG_DB:
+                       if ( li->li_db == NULL ) {
+                               snprintf( c->msg, sizeof( c->msg ),
+                                       "accesslog: \"logdb <suffix>\" must be specified" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+                                       c->log, c->msg, c->value_dn.bv_val );
+                               rc = 1;
+                               break;
+                       }
                        value_add( &c->rvalue_vals, li->li_db->be_suffix );
                        value_add( &c->rvalue_nvals, li->li_db->be_nsuffix );
                        break;
@@ -590,6 +620,10 @@ log_cf_gen(ConfigArgs *c)
                        rc = mask_to_verbs( logops, li->li_ops, &c->rvalue_vals );
                        break;
                case LOG_PURGE:
+                       if ( !li->li_age ) {
+                               rc = 1;
+                               break;
+                       }
                        agebv.bv_val = agebuf;
                        log_age_unparse( li->li_age, &agebv );
                        agebv.bv_val[agebv.bv_len] = ' ';
@@ -605,6 +639,24 @@ log_cf_gen(ConfigArgs *c)
                        else
                                rc = 1;
                        break;
+               case LOG_OLD:
+                       if ( li->li_oldf ) {
+                               filter2bv( li->li_oldf, &agebv );
+                               value_add_one( &c->rvalue_vals, &agebv );
+                       }
+                       else
+                               rc = 1;
+                       break;
+               case LOG_OLDATTR:
+                       if ( li->li_oldattrs ) {
+                               log_attr *la;
+
+                               for ( la = li->li_oldattrs; la; la=la->next )
+                                       value_add_one( &c->rvalue_vals, &la->attr->ad_cname );
+                       }
+                       else
+                               rc = 1;
+                       break;
                }
                break;
        case LDAP_MOD_DELETE:
@@ -635,6 +687,32 @@ log_cf_gen(ConfigArgs *c)
                case LOG_SUCCESS:
                        li->li_success = 0;
                        break;
+               case LOG_OLD:
+                       if ( li->li_oldf ) {
+                               filter_free( li->li_oldf );
+                               li->li_oldf = NULL;
+                       }
+                       break;
+               case LOG_OLDATTR:
+                       if ( c->valx < 0 ) {
+                               log_attr *la, *ln;
+
+                               for ( la = li->li_oldattrs; la; la = ln ) {
+                                       ln = la->next;
+                                       ch_free( la );
+                               }
+                       } else {
+                               log_attr *la = NULL, **lp;
+                               int i;
+
+                               for ( lp = &li->li_oldattrs, i=0; i < c->valx; i++ ) {
+                                       la = *lp;
+                                       lp = &la->next;
+                               }
+                               *lp = la->next;
+                               ch_free( la );
+                       }
+                       break;
                }
                break;
        default:
@@ -642,7 +720,15 @@ log_cf_gen(ConfigArgs *c)
                case LOG_DB:
                        li->li_db = select_backend( &c->value_ndn, 0, 0 );
                        if ( !li->li_db ) {
-                               sprintf( c->msg, "<%s> no matching backend found for suffix",
+                               snprintf( c->msg, sizeof( c->msg ),
+                                       "<%s> no matching backend found for suffix",
+                                       c->argv[0] );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+                                       c->log, c->msg, c->value_dn.bv_val );
+                               rc = 1;
+                       } else if ( BER_BVISEMPTY( &li->li_db->be_rootdn )) {
+                               snprintf( c->msg, sizeof( c->msg ),
+                                       "<%s> no rootDN was configured for suffix",
                                        c->argv[0] );
                                Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
                                        c->log, c->msg, c->value_dn.bv_val );
@@ -658,11 +744,11 @@ log_cf_gen(ConfigArgs *c)
                        break;
                case LOG_PURGE:
                        li->li_age = log_age_parse( c->argv[1] );
-                       if ( li->li_age == -1 ) {
+                       if ( li->li_age 1 ) {
                                rc = 1;
                        } else {
                                li->li_cycle = log_age_parse( c->argv[2] );
-                               if ( li->li_cycle == -1 ) {
+                               if ( li->li_cycle 1 ) {
                                        rc = 1;
                                } else if ( slapMode & SLAP_SERVER_MODE ) {
                                        struct re_s *re = li->li_task;
@@ -680,6 +766,33 @@ log_cf_gen(ConfigArgs *c)
                case LOG_SUCCESS:
                        li->li_success = c->value_int;
                        break;
+               case LOG_OLD:
+                       li->li_oldf = str2filter( c->argv[1] );
+                       if ( !li->li_oldf ) {
+                               sprintf( c->msg, "bad filter!" );
+                               rc = 1;
+                       }
+                       break;
+               case LOG_OLDATTR: {
+                       int i;
+                       AttributeDescription *ad;
+                       const char *text;
+
+                       for ( i=1; i< c->argc; i++ ) {
+                               ad = NULL;
+                               if ( slap_str2ad( c->argv[i], &ad, &text ) == LDAP_SUCCESS ) {
+                                       log_attr *la = ch_malloc( sizeof( log_attr ));
+                                       la->attr = ad;
+                                       la->next = li->li_oldattrs;
+                                       li->li_oldattrs = la;
+                               } else {
+                                       sprintf( c->msg, "%s: %s", c->argv[i], text );
+                                       rc = ARG_BAD_CONF;
+                                       break;
+                               }
+                       }
+                       }
+                       break;
                }
                break;
        }
@@ -782,6 +895,24 @@ static struct berval derefs[] = {
 
 static struct berval simple = BER_BVC("SIMPLE");
 
+static void accesslog_val2val(AttributeDescription *ad, struct berval *val,
+       char c_op, struct berval *dst) {
+       char *ptr;
+
+       dst->bv_len = ad->ad_cname.bv_len + val->bv_len + 2;
+       if ( c_op ) dst->bv_len++;
+
+       dst->bv_val = ch_malloc( dst->bv_len+1 );
+
+       ptr = lutil_strcopy( dst->bv_val, ad->ad_cname.bv_val );
+       *ptr++ = ':';
+       if ( c_op )
+               *ptr++ = c_op;
+       *ptr++ = ' ';
+       AC_MEMCPY( ptr, val->bv_val, val->bv_len );
+       dst->bv_val[dst->bv_len] = '\0';
+}
+
 static int accesslog_response(Operation *op, SlapReply *rs) {
        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
        log_info *li = on->on_bi.bi_private;
@@ -791,7 +922,7 @@ static int accesslog_response(Operation *op, SlapReply *rs) {
        int i;
        int logop;
        slap_verbmasks *lo;
-       Entry *e;
+       Entry *e = NULL, *old = NULL;
        char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE+8];
        struct berval bv;
        char *ptr;
@@ -821,7 +952,9 @@ static int accesslog_response(Operation *op, SlapReply *rs) {
 
        if ( lo->mask & LOG_OP_WRITES ) {
                ldap_pvt_thread_mutex_lock( &li->li_log_mutex );
-               ldap_pvt_thread_mutex_unlock( &li->li_op_mutex );
+               old = li->li_old;
+               li->li_old = NULL;
+               ldap_pvt_thread_rmutex_unlock( &li->li_op_rmutex, op->o_tid );
        }
 
        if ( li->li_success && rs->sr_err != LDAP_SUCCESS )
@@ -844,9 +977,22 @@ static int accesslog_response(Operation *op, SlapReply *rs) {
 
        switch( logop ) {
        case LOG_EN_ADD:
+       case LOG_EN_DELETE: {
+               char c_op;
+               Entry *e2;
+
+               if ( logop == LOG_EN_ADD ) {
+                       e2 = op->ora_e;
+                       c_op = '+';
+               } else {
+                       if ( !old )
+                               break;
+                       e2 = old;
+                       c_op = 0;
+               }
                /* count all the vals */
                i = 0;
-               for ( a=op->ora_e->e_attrs; a; a=a->a_next ) {
+               for ( a=e2->e_attrs; a; a=a->a_next ) {
                        if ( a->a_vals ) {
                                for (b=a->a_vals; !BER_BVISNULL( b ); b++) {
                                        i++;
@@ -855,32 +1001,21 @@ static int accesslog_response(Operation *op, SlapReply *rs) {
                }
                vals = ch_malloc( (i+1) * sizeof( struct berval ));
                i = 0;
-               for ( a=op->ora_e->e_attrs; a; a=a->a_next ) {
+               for ( a=e2->e_attrs; a; a=a->a_next ) {
                        if ( a->a_vals ) {
                                for (b=a->a_vals; !BER_BVISNULL( b ); b++,i++) {
-                                       vals[i].bv_len = a->a_desc->ad_cname.bv_len + b->bv_len +3;
-                                       vals[i].bv_val = ch_malloc( vals[i].bv_len+1 );
-                                       ptr = lutil_strcopy( vals[i].bv_val,
-                                               a->a_desc->ad_cname.bv_val );
-                                       *ptr++ = ':';
-                                       *ptr++ = '+';
-                                       *ptr++ = ' ';
-                                       AC_MEMCPY( ptr, b->bv_val, b->bv_len );
-                                       vals[i].bv_val[vals[i].bv_len] = '\0';
+                                       accesslog_val2val( a->a_desc, b, c_op, &vals[i] );
                                }
                        }
                }
                vals[i].bv_val = NULL;
                vals[i].bv_len = 0;
-               a = attr_alloc( ad_reqMod );
+               a = attr_alloc( logop == LOG_EN_ADD ? ad_reqMod : ad_reqOld );
                a->a_vals = vals;
                a->a_nvals = vals;
                last_attr->a_next = a;
                break;
-
-       case LOG_EN_DELETE:
-               /* needs nothing else */
-               break;
+       }
 
        case LOG_EN_MODIFY:
                /* count all the mods */
@@ -896,15 +1031,31 @@ static int accesslog_response(Operation *op, SlapReply *rs) {
                }
                vals = ch_malloc( (i+1) * sizeof( struct berval ));
                i = 0;
+
+               /* init flags on old entry */
+               if ( old ) {
+                       for ( a=old->e_attrs; a; a=a->a_next ) {
+                               log_attr *la;
+                               a->a_flags = 0;
+
+                               /* look for attrs that are always logged */
+                               for ( la=li->li_oldattrs; la; la=la->next )
+                                       if ( a->a_desc == la->attr )
+                                               a->a_flags = 1;
+                       }
+               }
+
                for ( m=op->orm_modlist; m; m=m->sml_next ) {
+                       /* Mark this attribute as modified */
+                       if ( old ) {
+                               a = attr_find( old->e_attrs, m->sml_desc );
+                               if ( a )
+                                       a->a_flags = 1;
+                       }
                        if ( m->sml_values ) {
                                for (b=m->sml_values; !BER_BVISNULL( b ); b++,i++) {
                                        char c_op;
-                                       vals[i].bv_len = m->sml_desc->ad_cname.bv_len + b->bv_len +3;
-                                       vals[i].bv_val = ch_malloc( vals[i].bv_len+1 );
-                                       ptr = lutil_strcopy( vals[i].bv_val,
-                                               m->sml_desc->ad_cname.bv_val );
-                                       *ptr++ = ':';
+
                                        switch( m->sml_op ) {
                                        case LDAP_MOD_ADD: c_op = '+'; break;
                                        case LDAP_MOD_DELETE:   c_op = '-'; break;
@@ -917,10 +1068,7 @@ static int accesslog_response(Operation *op, SlapReply *rs) {
                                         */
                                        default: c_op = '?'; break;
                                        }
-                                       *ptr++ = c_op;
-                                       *ptr++ = ' ';
-                                       AC_MEMCPY( ptr, b->bv_val, b->bv_len );
-                                       vals[i].bv_val[vals[i].bv_len] = '\0';
+                                       accesslog_val2val( m->sml_desc, b, c_op, &vals[i] );
                                }
                        } else if ( m->sml_op == LDAP_MOD_DELETE ) {
                                vals[i].bv_len = m->sml_desc->ad_cname.bv_len + 2;
@@ -939,9 +1087,71 @@ static int accesslog_response(Operation *op, SlapReply *rs) {
                a->a_vals = vals;
                a->a_nvals = vals;
                last_attr->a_next = a;
+
+               if ( old ) {
+                       last_attr = a;
+                       /* count all the vals */
+                       i = 0;
+                       for ( a=old->e_attrs; a; a=a->a_next ) {
+                               if ( a->a_vals && a->a_flags ) {
+                                       for (b=a->a_vals; !BER_BVISNULL( b ); b++) {
+                                       i++;
+                                       }
+                               }
+                       }
+                       vals = ch_malloc( (i+1) * sizeof( struct berval ));
+                       i = 0;
+                       for ( a=old->e_attrs; a; a=a->a_next ) {
+                               if ( a->a_vals && a->a_flags ) {
+                                       for (b=a->a_vals; !BER_BVISNULL( b ); b++,i++) {
+                                               accesslog_val2val( a->a_desc, b, 0, &vals[i] );
+                                       }
+                               }
+                       }
+                       vals[i].bv_val = NULL;
+                       vals[i].bv_len = 0;
+                       a = attr_alloc( ad_reqOld );
+                       a->a_vals = vals;
+                       a->a_nvals = vals;
+                       last_attr->a_next = a;
+               }
                break;
 
        case LOG_EN_MODRDN:
+               if ( old ) {
+                       /* count all the vals */
+                       i = 0;
+                       for ( a=old->e_attrs; a; a=a->a_next ) {
+                               log_attr *la;
+
+                               /* look for attrs that are always logged */
+                               for ( la=li->li_oldattrs; la; la=la->next ) {
+                                       if ( a->a_desc == la->attr ) {
+                                               for (b=a->a_vals; !BER_BVISNULL( b ); b++) {
+                                                       i++;
+                                               }
+                                       }
+                               }
+                       }
+                       vals = ch_malloc( (i+1) * sizeof( struct berval ));
+                       i = 0;
+                       for ( a=old->e_attrs; a; a=a->a_next ) {
+                               log_attr *la;
+                               for ( la=li->li_oldattrs; la; la=la->next ) {
+                                       if ( a->a_desc == la->attr ) {
+                                               for (b=a->a_vals; !BER_BVISNULL( b ); b++,i++) {
+                                                       accesslog_val2val( a->a_desc, b, 0, &vals[i] );
+                                               }
+                                       }
+                               }
+                       }
+                       vals[i].bv_val = NULL;
+                       vals[i].bv_len = 0;
+                       a = attr_alloc( ad_reqOld );
+                       a->a_vals = vals;
+                       a->a_nvals = vals;
+                       last_attr->a_next = a;
+               }
                attr_merge_one( e, ad_reqNewRDN, &op->orr_newrdn, &op->orr_nnewrdn );
                attr_merge_one( e, ad_reqDeleteOldRDN, op->orr_deleteoldrdn ?
                        (struct berval *)&slap_true_bv : (struct berval *)&slap_false_bv,
@@ -1040,10 +1250,12 @@ static int accesslog_response(Operation *op, SlapReply *rs) {
        }
 
        op2.o_bd->be_add( &op2, &rs2 );
-       entry_free( e );
 
 done:
-       ldap_pvt_thread_mutex_unlock( &li->li_log_mutex );
+       if ( lo->mask & LOG_OP_WRITES )
+               ldap_pvt_thread_mutex_unlock( &li->li_log_mutex );
+       if ( e ) entry_free( e );
+       if ( old ) entry_free( old );
        return SLAP_CB_CONTINUE;
 }
 
@@ -1095,10 +1307,22 @@ accesslog_op_mod( Operation *op, SlapReply *rs )
        log_info *li = on->on_bi.bi_private;
 
        if ( li->li_ops & LOG_OP_WRITES ) {
-               /* FIXME: this needs to be a recursive mutex to allow
-                * overlays like refint to keep working.
-                */
-               ldap_pvt_thread_mutex_lock( &li->li_op_mutex );
+               ldap_pvt_thread_rmutex_lock( &li->li_op_rmutex, op->o_tid );
+               if ( li->li_oldf && ( op->o_tag == LDAP_REQ_DELETE ||
+                       op->o_tag == LDAP_REQ_MODIFY ||
+                       ( op->o_tag == LDAP_REQ_MODRDN && li->li_oldattrs ))) {
+                       int rc;
+                       Entry *e;
+
+                       op->o_bd->bd_info = on->on_info->oi_orig;
+                       rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+                       if ( e ) {
+                               if ( test_filter( op, e, li->li_oldf ) == LDAP_COMPARE_TRUE )
+                                       li->li_old = entry_dup( e );
+                               be_entry_release_rw( op, e, 0 );
+                       }
+                       op->o_bd->bd_info = (BackendInfo *)on;
+               }
        }
        return SLAP_CB_CONTINUE;
 }
@@ -1112,7 +1336,7 @@ accesslog_unbind( Operation *op, SlapReply *rs )
        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
        if ( op->o_conn->c_authz_backend == on->on_info->oi_origdb ) {
                log_info *li = on->on_bi.bi_private;
-               Operation op2;
+               Operation op2 = {0};
                void *cids[SLAP_MAX_CIDS];
                SlapReply rs2 = {REP_RESULT};
                Entry *e;
@@ -1132,7 +1356,6 @@ accesslog_unbind( Operation *op, SlapReply *rs )
                op2.o_callback = &nullsc;
                op2.o_controls = cids;
                memset(cids, 0, sizeof( cids ));
-               memset(op2.o_ctrlflag, 0, sizeof( op2.o_ctrlflag ));
 
                op2.o_bd->be_add( &op2, &rs2 );
                entry_free( e );
@@ -1145,7 +1368,7 @@ accesslog_abandon( Operation *op, SlapReply *rs )
 {
        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
        log_info *li = on->on_bi.bi_private;
-       Operation op2;
+       Operation op2 = {0};
        void *cids[SLAP_MAX_CIDS];
        SlapReply rs2 = {REP_RESULT};
        Entry *e;
@@ -1171,7 +1394,6 @@ accesslog_abandon( Operation *op, SlapReply *rs )
        op2.o_callback = &nullsc;
        op2.o_controls = cids;
        memset(cids, 0, sizeof( cids ));
-       memset(op2.o_ctrlflag, 0, sizeof( op2.o_ctrlflag ));
 
        op2.o_bd->be_add( &op2, &rs2 );
        entry_free( e );
@@ -1190,7 +1412,7 @@ accesslog_db_init(
        log_info *li = ch_calloc(1, sizeof(log_info));
 
        on->on_bi.bi_private = li;
-       ldap_pvt_thread_mutex_init( &li->li_op_mutex );
+       ldap_pvt_thread_rmutex_init( &li->li_op_rmutex );
        ldap_pvt_thread_mutex_init( &li->li_log_mutex );
        return 0;
 }
@@ -1204,7 +1426,7 @@ accesslog_db_destroy(
        log_info *li = on->on_bi.bi_private;
 
        ldap_pvt_thread_mutex_destroy( &li->li_log_mutex );
-       ldap_pvt_thread_mutex_destroy( &li->li_op_mutex );
+       ldap_pvt_thread_rmutex_destroy( &li->li_op_rmutex );
        free( li );
        return LDAP_SUCCESS;
 }
@@ -1224,6 +1446,13 @@ accesslog_db_open(
        int rc;
        void *thrctx;
 
+       if ( li->li_db == NULL ) {
+               Debug( LDAP_DEBUG_ANY,
+                       "accesslog: \"logdb <suffix>\" must be specified.\n",
+                       0, 0, 0 );
+               return 1;
+       }
+
        if ( slapMode & SLAP_TOOL_MODE )
                return 0;
 
@@ -1243,6 +1472,8 @@ accesslog_db_open(
                char *ptr;
                AttributeDescription *ad = NULL;
                const char *text = NULL;
+               Entry *e_ctx;
+
                e = ch_calloc( 1, sizeof( Entry ));
                e->e_name = *li->li_db->be_suffix;
                e->e_nname = *li->li_db->be_nsuffix;
@@ -1268,6 +1499,25 @@ accesslog_db_open(
                nrdn.bv_val = ptr+1;
                attr_merge_one( e, ad, &rdn, &nrdn );
 
+               /* Get contextCSN from main DB */
+               op->o_bd = be;
+               op->o_bd->bd_info = on->on_info->oi_orig;
+               rc = be_entry_get_rw( op, be->be_nsuffix, NULL,
+                       slap_schema.si_ad_contextCSN, 0, &e_ctx );
+
+               if ( e_ctx ) {
+                       Attribute *a;
+
+                       a = attr_find( e_ctx->e_attrs, slap_schema.si_ad_contextCSN );
+                       if ( a ) {
+                               attr_merge( e, slap_schema.si_ad_entryCSN, a->a_vals, NULL );
+                               attr_merge( e, a->a_desc, a->a_vals, NULL );
+                       }
+                       be_entry_release_rw( op, e_ctx, 0 );
+               }
+               op->o_bd->bd_info = (BackendInfo *)on;
+               op->o_bd = li->li_db;
+
                op->ora_e = e;
                op->o_req_dn = e->e_name;
                op->o_req_ndn = e->e_nname;