]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#10080 Fix refreshAndPersist synchronization problem with glue + rwm
authorHiroyuki Homma <homma@allworks.co.jp>
Wed, 10 Jan 2024 06:06:49 +0000 (15:06 +0900)
committerQuanah Gibson-Mount <quanah@openldap.org>
Thu, 11 Jan 2024 18:02:07 +0000 (18:02 +0000)
servers/slapd/backglue.c
servers/slapd/overlays/rwm.c
servers/slapd/overlays/syncprov.c
tests/scripts/test088-syncprov-glue-rwm [new file with mode: 0755]

index a0d4dad60dee4498eec9ee7e353c07c140380f40..00a672d4cd8838b6e1b49c778349f27ff146bef5 100644 (file)
@@ -369,6 +369,8 @@ glue_sub_search( Operation *op, SlapReply *rs, BackendDB *b0,
                BackendInfo *bi = op->o_bd->bd_info;
                int rc = SLAP_CB_CONTINUE;
                for ( on=on->on_next; on; on=on->on_next ) {
+                       if ( on->on_bi.bi_flags & SLAPO_BFLAG_DISABLED )
+                               continue;
                        op->o_bd->bd_info = (BackendInfo *)on;
                        if ( on->on_bi.bi_op_search ) {
                                rc = on->on_bi.bi_op_search( op, rs );
@@ -550,6 +552,11 @@ glue_op_search ( Operation *op, SlapReply *rs )
                                rs->sr_err = glue_sub_search( op, rs, b0, on );
                        }
 
+                       if ( rs->sr_err == SLAPD_NO_REPLY ) {
+                               gs.err = rs->sr_err;
+                               break;
+                       }
+
                        switch ( gs.err ) {
 
                        /*
@@ -716,7 +723,8 @@ end_of_loop:;
        }
        rs->sr_ctrls = gs.ctrls;
 
-       send_ldap_result( op, rs );
+       if ( rs->sr_err != SLAPD_NO_REPLY )
+               send_ldap_result( op, rs );
 
        op->o_bd = b0;
        op->o_bd->bd_info = bi0;
index 43043df63046ea86020d3a90839cdc22a702838d..af10f6df22ddab13937667c6eb9f89cfa4aca946 100644 (file)
@@ -1815,6 +1815,16 @@ rwm_response( Operation *op, SlapReply *rs )
                break;
        }
 
+       if ( op->o_tag == LDAP_REQ_ADD && op->ora_e ) {
+               /*
+                * Rewrite back the dn and attributes of the added entry op->ora_e
+                */
+               SlapReply rs2 = *rs;
+               rs2.sr_entry = op->ora_e;
+               rs2.sr_flags |= REP_ENTRY_MODIFIABLE;
+               return rwm_send_entry( op, &rs2 );
+       }
+
        return SLAP_CB_CONTINUE;
 }
 
index 3c2290d1aab9d296e12935af5fb02e94bbf2461e..f208846fa94c0c539e6fbfb8fbc9bc49e655e536 100644 (file)
@@ -698,6 +698,7 @@ syncprov_findcsn( Operation *op, find_csn_t mode, struct berval *csn )
                                "FIND_PRESENT",
                csn ? csn->bv_val : "" );
 
+again:
        fop = *op;
        fop.o_sync_mode &= SLAP_CONTROL_MASK;   /* turn off sync_mode */
        /* We want pure entries, not referrals */
@@ -714,7 +715,6 @@ syncprov_findcsn( Operation *op, find_csn_t mode, struct berval *csn )
        fop.ors_filter = &cf;
        fop.ors_filterstr.bv_val = buf;
 
-again:
        switch( mode ) {
        case FIND_MAXCSN:
                cf.f_choice = LDAP_FILTER_GE;
@@ -1319,17 +1319,9 @@ syncprov_matchops( Operation *op, opcookie *opc, int saveit )
        Entry *e = NULL;
        Attribute *a;
        int rc, gonext;
-       struct berval newdn;
-       int freefdn = 0;
        BackendDB *b0 = op->o_bd, db;
 
-       fc.fdn = &op->o_req_ndn;
-       /* compute new DN */
-       if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
-               ber_dupbv_x( &newdn, &op->orr_nnewDN, op->o_tmpmemctx );
-               fc.fdn = &newdn;
-               freefdn = 1;
-       }
+       fc.fdn = saveit ? &op->o_req_ndn : &opc->sndn;
        if ( !saveit && op->o_tag == LDAP_REQ_DELETE ) {
                /* Delete succeeded, there is no entry */
        } else if ( op->o_tag != LDAP_REQ_ADD ) {
@@ -1362,8 +1354,13 @@ syncprov_matchops( Operation *op, opcookie *opc, int saveit )
        }
 
        if ( saveit || op->o_tag == LDAP_REQ_ADD ) {
-               ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
-               ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
+               if ( op->o_tag == LDAP_REQ_MODRDN ) {
+                       ber_dupbv_x( &opc->sdn, &op->orr_newDN, op->o_tmpmemctx );
+                       ber_dupbv_x( &opc->sndn, &op->orr_nnewDN, op->o_tmpmemctx );
+               } else {
+                       ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
+                       ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
+               }
                opc->sreference = is_entry_referral( e );
                a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
                if ( a )
@@ -1371,11 +1368,6 @@ syncprov_matchops( Operation *op, opcookie *opc, int saveit )
                Debug( LDAP_DEBUG_SYNC, "%s syncprov_matchops: "
                        "%srecording uuid for dn=%s on opc=%p\n",
                        op->o_log_prefix, a ? "" : "not ", opc->sdn.bv_val, opc );
-       } else if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
-               op->o_tmpfree( opc->sndn.bv_val, op->o_tmpmemctx );
-               op->o_tmpfree( opc->sdn.bv_val, op->o_tmpmemctx );
-               ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
-               ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
        }
 
        ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
@@ -1520,9 +1512,6 @@ syncprov_matchops( Operation *op, opcookie *opc, int saveit )
                else if ( opc->se )
                        entry_free( opc->se );
        }
-       if ( freefdn ) {
-               op->o_tmpfree( fc.fdn->bv_val, op->o_tmpmemctx );
-       }
        op->o_bd = b0;
 }
 
@@ -2955,6 +2944,7 @@ syncprov_search_response( Operation *op, SlapReply *rs )
 {
        searchstate *ss = op->o_callback->sc_private;
        slap_overinst *on = ss->ss_on;
+       syncops *so = ss->ss_so;
        syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
        sync_control *srs = op->o_controls[slap_cids.sc_LDAPsync];
 
@@ -2983,7 +2973,7 @@ syncprov_search_response( Operation *op, SlapReply *rs )
                        sid = slap_parse_csn_sid( &a->a_nvals[0] );
 
                        /* If not a persistent search */
-                       if ( !ss->ss_so ) {
+                       if ( !so ) {
                                /* Make sure entry is less than the snapshot'd contextCSN */
                                for ( i=0; i<ss->ss_numcsns; i++ ) {
                                        if ( sid == ss->ss_sids[i] && ber_bvcmp( &a->a_nvals[0],
@@ -3048,7 +3038,7 @@ syncprov_search_response( Operation *op, SlapReply *rs )
                /* Is this a regular refresh?
                 * Note: refresh never gets here if there were no changes
                 */
-               if ( !ss->ss_so ) {
+               if ( !so ) {
                        rs->sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2,
                                op->o_tmpmemctx );
                        rs->sr_ctrls[1] = NULL;
@@ -3059,6 +3049,7 @@ syncprov_search_response( Operation *op, SlapReply *rs )
                        op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
                } else {
                /* It's RefreshAndPersist, transition to Persist phase */
+                       rs->sr_err = SLAPD_NO_REPLY;
                        syncprov_sendinfo( op, rs, ( ss->ss_flags & SS_PRESENT ) ?
                                LDAP_TAG_SYNC_REFRESH_PRESENT : LDAP_TAG_SYNC_REFRESH_DELETE,
                                ( ss->ss_flags & SS_CHANGED ) ? &cookie : NULL,
@@ -3076,21 +3067,21 @@ syncprov_search_response( Operation *op, SlapReply *rs )
                                return SLAPD_ABANDON;
 
                        } else {
-                               ldap_pvt_thread_mutex_lock( &ss->ss_so->s_mutex );
+                               ldap_pvt_thread_mutex_lock( &so->s_mutex );
                                /* Turn off the refreshing flag */
-                               ss->ss_so->s_flags ^= PS_IS_REFRESHING;
+                               so->s_flags ^= PS_IS_REFRESHING;
 
                                Debug( LDAP_DEBUG_SYNC, "%s syncprov_search_response: "
                                        "detaching op\n", op->o_log_prefix );
-                               syncprov_detach_op( op, ss->ss_so, on );
+                               syncprov_detach_op( op, so, on );
 
                                ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
 
                                /* If there are queued responses, fire them off */
-                               if ( ss->ss_so->s_res )
-                                       syncprov_qstart( ss->ss_so );
-                               ldap_pvt_thread_mutex_unlock( &ss->ss_so->s_mutex );
-                               return SLAPD_NO_REPLY;
+                               if ( so->s_res )
+                                       syncprov_qstart( so );
+                               ldap_pvt_thread_mutex_unlock( &so->s_mutex );
+                               return rs->sr_err;
                        }
                }
        }
@@ -3098,6 +3089,33 @@ syncprov_search_response( Operation *op, SlapReply *rs )
        return SLAP_CB_CONTINUE;
 }
 
+static int
+syncprov_search_cb( Operation *op, SlapReply *rs )
+{
+       /*
+        * Prevent the glue overlay from processing subordinates when it is
+        * configured (explicitly or implicitly) below the syncprov overlay.
+        */
+       if ( rs->sr_type == REP_RESULT )
+               op->o_no_subordinate_glue = 1;
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+syncprov_search_cleanup( Operation *op, SlapReply *rs )
+{
+       if ( rs->sr_type == REP_RESULT || rs->sr_type == REP_INTERMEDIATE ||
+               rs->sr_err == SLAPD_ABANDON || op->o_abandon ) {
+               searchstate *ss = op->o_callback->sc_private;
+               if ( ss && ss->ss_numcsns ) {
+                       ber_bvarray_free_x( ss->ss_ctxcsn, op->o_tmpmemctx );
+                       op->o_tmpfree( ss->ss_sids, op->o_tmpmemctx );
+               }
+               slap_freeself_cb( op, rs );
+       }
+       return SLAP_CB_CONTINUE;
+}
+
 static int
 syncprov_op_search( Operation *op, SlapReply *rs )
 {
@@ -3114,6 +3132,14 @@ syncprov_op_search( Operation *op, SlapReply *rs )
        int minsid, maxsid;
        int dirty = 0;
 
+       if ( op->o_sync > SLAP_CONTROL_IGNORED ) {
+               cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
+               cb->sc_response = syncprov_search_cb;
+               cb->sc_cleanup = syncprov_search_cleanup;
+               cb->sc_next = op->o_callback;
+               op->o_callback = cb;
+       }
+
        if ( !(op->o_sync_mode & SLAP_SYNC_REFRESH) ) return SLAP_CB_CONTINUE;
 
        if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
@@ -3320,6 +3346,10 @@ bailout:
                                }
                                rs->sr_ctrls = NULL;
                                send_ldap_result( op, rs );
+                               if ( numcsns ) {
+                                       ber_bvarray_free_x( ctxcsn, op->o_tmpmemctx );
+                                       op->o_tmpfree( sids, op->o_tmpmemctx );
+                               }
                                return rs->sr_err;
                        }
                }
@@ -3342,6 +3372,10 @@ no_change:       if ( !(op->o_sync_mode & SLAP_SYNC_PERSIST) ) {
                                rs->sr_err = LDAP_SUCCESS;
                                send_ldap_result( op, rs );
                                rs->sr_ctrls = NULL;
+                               if ( numcsns ) {
+                                       ber_bvarray_free_x( ctxcsn, op->o_tmpmemctx );
+                                       op->o_tmpfree( sids, op->o_tmpmemctx );
+                               }
                                return rs->sr_err;
                        }
                        Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
@@ -3429,10 +3463,6 @@ no_change:       if ( !(op->o_sync_mode & SLAP_SYNC_PERSIST) ) {
                        /* No, so a reload is required */
                        /* the 2.2 consumer doesn't send this hint */
                        if ( si->si_usehint && srs->sr_rhint == 0 ) {
-                               if ( ctxcsn )
-                                       ber_bvarray_free_x( ctxcsn, op->o_tmpmemctx );
-                               if ( sids )
-                                       op->o_tmpfree( sids, op->o_tmpmemctx );
                                rs->sr_err = LDAP_SYNC_REFRESH_REQUIRED;
                                rs->sr_text = "sync cookie is stale";
                                goto bailout;
@@ -3452,13 +3482,8 @@ no_change:       if ( !(op->o_sync_mode & SLAP_SYNC_PERSIST) ) {
                } else {
                        gotstate = 1;
                        /* If changed and doing Present lookup, send Present UUIDs */
-                       if ( syncprov_findcsn( op, FIND_PRESENT, 0 ) != LDAP_SUCCESS ) {
-                               if ( ctxcsn )
-                                       ber_bvarray_free_x( ctxcsn, op->o_tmpmemctx );
-                               if ( sids )
-                                       op->o_tmpfree( sids, op->o_tmpmemctx );
+                       if ( syncprov_findcsn( op, FIND_PRESENT, 0 ) != LDAP_SUCCESS )
                                goto bailout;
-                       }
                }
        } else {
                /* The consumer knows nothing, we know nothing. OK. */
@@ -3516,6 +3541,7 @@ shortcut:
        ss->ss_numcsns = numcsns;
        ss->ss_sids = sids;
        cb->sc_response = syncprov_search_response;
+       cb->sc_cleanup = syncprov_search_cleanup;
        cb->sc_private = ss;
        cb->sc_next = op->o_callback;
        op->o_callback = cb;
diff --git a/tests/scripts/test088-syncprov-glue-rwm b/tests/scripts/test088-syncprov-glue-rwm
new file mode 100755 (executable)
index 0000000..435a04a
--- /dev/null
@@ -0,0 +1,448 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $BACKLDAP = ldapno; then
+       echo "LDAP backend not available, test skipped"
+       exit 0
+fi
+
+if test $RWM = rwmno; then
+       echo "rwm (rewrite/remap) overlay not available, test skipped"
+       exit 0
+fi
+
+if test $SYNCPROV = syncprovno; then 
+       echo "Syncrepl provider overlay not available, test skipped"
+       exit 0
+fi 
+
+RMTSUFFIX="dc=remote,$BASEDN"
+RMTROOTDN="cn=Manager,$RMTSUFFIX"
+
+RMTDIR=$TESTDIR/remote
+PR1DIR=$TESTDIR/provider1
+PR2DIR=$TESTDIR/provider2
+RMTCONF=$RMTDIR/slapd.d
+PR1CONF=$PR1DIR/slapd.d
+PR2CONF=$PR2DIR/slapd.d
+
+ENTRIES=$TESTDIR/entries.ldif
+SYNC1OUT=$TESTDIR/syncrepl1.out
+SYNC2OUT=$TESTDIR/syncrepl2.out
+
+mkdir -p $RMTDIR $RMTCONF $RMTDIR/db
+mkdir -p $PR1DIR $PR1CONF $PR1DIR/db
+mkdir -p $PR2DIR $PR2CONF $PR2DIR/db
+
+cd $TESTDIR
+
+KILLPIDS=
+
+$SLAPPASSWD -g -n > $CONFIGPWF
+
+cat <<EOF > $CONFLDIF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://$CONFIGPWF
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://$ABS_SCHEMADIR/core.ldif
+include: file://$ABS_SCHEMADIR/cosine.ldif
+include: file://$ABS_SCHEMADIR/nis.ldif
+include: file://$ABS_SCHEMADIR/inetorgperson.ldif
+
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+EOF
+
+[ "$BACKENDTYPE" = mod ] && echo "olcModuleLoad: $TESTWD/../servers/slapd/back-$BACKEND/back_$BACKEND.la" >> $CONFLDIF
+
+echo "Initializing remote configurations..."
+cat $CONFLDIF - <<EOF | $SLAPADD -F $RMTCONF -n 0
+
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: $RMTDIR/db
+olcSuffix: $RMTSUFFIX
+olcRootDN: $RMTROOTDN
+olcRootPW: $PASSWD
+EOF
+RC=$?
+if test $RC != 0 ; then
+       echo "slapadd failed ($RC)!"
+       exit $RC
+fi
+
+[ "$BACKLDAP" = ldapmod ] && echo "olcModuleLoad: $TESTWD/../servers/slapd/back-ldap/back_ldap.la" >> $CONFLDIF
+[ "$RWM" = rwmmod ] && echo "olcModuleLoad: rwm.la" >> $CONFLDIF
+[ "$SYNCPROV" = syncprovmod ] && echo "olcModuleLoad: syncprov.la" >> $CONFLDIF
+cat <<EOF >> $CONFLDIF
+
+dn: olcDatabase={1}ldap,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcLDAPConfig
+olcDatabase: {1}ldap
+olcSuffix: ou=remote,ou=users,$BASEDN
+olcSubordinate: TRUE
+olcDbURI: $URI1
+olcDbIDAssertBind: bindmethod=simple
+  binddn="$RMTROOTDN"
+  credentials=$PASSWD
+  mode=none
+olcDbIDAssertAuthzFrom: dn.exact:$MANAGERDN
+olcRootDN: $MANAGERDN
+
+dn: olcOverlay={0}rwm,olcDatabase={1}ldap,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcRwmConfig
+olcOverlay: {0}rwm
+olcRwmRewrite: rwm-suffixmassage "ou=users,$RMTSUFFIX"
+EOF
+
+echo "Initializing provider1 configurations..."
+cat $CONFLDIF - <<EOF | $SLAPADD -F $PR1CONF -n 0
+
+dn: olcDatabase={2}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {2}$BACKEND
+${nullExclude}olcDbDirectory: $PR1DIR/db
+olcSuffix: $BASEDN
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+
+dn: olcOverlay={0}syncprov,olcDatabase={2}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+EOF
+RC=$?
+if test $RC != 0 ; then
+       echo "slapadd failed ($RC)!"
+       exit $RC
+fi
+
+echo "Initializing provider2 configurations..."
+cat $CONFLDIF - <<EOF | $SLAPADD -F $PR2CONF -n 0
+
+dn: olcDatabase={2}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {2}$BACKEND
+${nullExclude}olcDbDirectory: $PR2DIR/db
+olcSuffix: $BASEDN
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+
+dn: olcOverlay={0}glue,olcDatabase={2}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+olcOverlay: {0}glue
+
+dn: olcOverlay={1}syncprov,olcDatabase={2}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: {1}syncprov
+EOF
+RC=$?
+if test $RC != 0 ; then
+       echo "slapadd failed ($RC)!"
+       exit $RC
+fi
+
+echo "Starting remote slapd on TCP/IP port $PORT1..."
+cd $RMTDIR
+$SLAPD -F slapd.d -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+    echo PID $PID
+    read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+cd $TESTWD
+sleep 1
+echo "Using ldapsearch to check that remote slapd is running..."
+for i in 0 1 2 3 4 5; do
+       $LDAPSEARCH -s base -b "" -H $URI1 \
+               'objectclass=*' > /dev/null 2>&1
+       RC=$?
+       if test $RC = 0 ; then
+               break
+       fi
+       echo "Waiting 5 seconds for slapd to start..."
+       sleep 5
+done
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Starting provider1 slapd on TCP/IP port $PORT2..."
+cd $PR1DIR
+$SLAPD -F slapd.d -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+    echo PID $PID
+    read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+cd $TESTWD
+sleep 1
+echo "Using ldapsearch to check that provider1 slapd is running..."
+for i in 0 1 2 3 4 5; do
+       $LDAPSEARCH -s base -b "" -H $URI2 \
+               'objectclass=*' > /dev/null 2>&1
+       RC=$?
+       if test $RC = 0 ; then
+               break
+       fi
+       echo "Waiting 5 seconds for slapd to start..."
+       sleep 5
+done
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Starting provider2 slapd on TCP/IP port $PORT3..."
+cd $PR2DIR
+$SLAPD -F slapd.d -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+    echo PID $PID
+    read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+cd $TESTWD
+sleep 1
+echo "Using ldapsearch to check that provider2 slapd is running..."
+for i in 0 1 2 3 4 5; do
+       $LDAPSEARCH -s base -b "" -H $URI3 \
+               'objectclass=*' > /dev/null 2>&1
+       RC=$?
+       if test $RC = 0 ; then
+               break
+       fi
+       echo "Waiting 5 seconds for slapd to start..."
+       sleep 5
+done
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Populating remote database entries..."
+$LDAPADD -D "$RMTROOTDN" -H $URI1 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: $RMTSUFFIX
+objectClass: dcObject
+objectClass: organization
+dc: `echo $RMTSUFFIX | sed 's/^dc=\([^,]*\),.*/\1/'`
+o: Example, Inc
+
+dn: ou=users,$RMTSUFFIX
+objectClass: organizationalUnit
+ou: users
+
+dn: cn=remote_user,ou=users,$RMTSUFFIX
+objectClass: person
+cn: remote_user
+sn: remote_user
+userPassword: $PASSWD
+EOF
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapadd failed to populate remote database entries ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+cat <<EOF > $ENTRIES
+dn: $BASEDN
+objectClass: dcObject
+objectClass: organization
+dc: example
+o: Example, Inc
+
+dn: ou=users,$BASEDN
+objectClass: organizationalUnit
+ou: users
+
+dn: ou=local,ou=users,$BASEDN
+objectClass: organizationalUnit
+ou: local
+
+dn: cn=local_user,ou=local,ou=users,$BASEDN
+objectClass: person
+cn: local_user
+sn: local_user
+userPassword: $PASSWD
+EOF
+
+echo "Populating provider1 database entries..."
+$LDAPADD -D "$MANAGERDN" -H $URI2 -w $PASSWD < $ENTRIES >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapadd failed to populate provider1 database entries ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Populating provider2 database entries..."
+$LDAPADD -D "$MANAGERDN" -H $URI3 -w $PASSWD < $ENTRIES >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapadd failed to populate provider2 database entries ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Starting refreshAndPersist search on provider1..."
+$LDAPRSEARCH -D $MANAGERDN -H $URI2 -w $PASSWD -MM -E sync=rp -b $BASEDN '*' + 2>&1 > $SYNC1OUT &
+PID=$!
+RC=32
+for i in 0 1 2 3 4 5; do
+       echo "Waiting for refreshDone message..."
+       sleep $SLEEP0
+       if grep '^# refresh done, switching to persist stage' $SYNC1OUT; then
+               awk '/^result:/{print; exit $2}' $SYNC1OUT
+               RC=$?
+               break
+       fi
+done
+if test $RC != 0 ; then
+       echo "refresh failed ($RC)!"
+       kill $PID
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Using ldapmodify to modify local entry on provider1..."
+$LDAPMODIFY -D $MANAGERDN -H $URI2 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: cn=local_user,ou=local,ou=users,$BASEDN
+changeType: modify
+replace: description
+description: Syncrepl local_user
+EOF
+RC=32
+for i in 0 1 2 3 4 5; do
+       echo "Waiting for syncrepl to receive changes..."
+       sleep $SLEEP0
+       if grep -q '^description: Syncrepl local_user' $SYNC1OUT; then
+               RC=0
+               break
+       fi
+done
+kill $PID
+if test $RC != 0 ; then
+       echo "syncrepl failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Check that remote entries are NOT replicated..."
+if grep 'ou=remote,' $SYNC1OUT; then
+       echo "remote entries were unexpectedly replicated!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit 1
+fi
+
+echo "Starting refreshAndPersist search on provider2..."
+$LDAPRSEARCH -D $MANAGERDN -H $URI3 -w $PASSWD -MM -E sync=rp -b $BASEDN '*' + 2>&1 > $SYNC2OUT &
+PID=$!
+RC=32
+for i in 0 1 2 3 4 5; do
+       echo "Waiting for refreshDone message..."
+       sleep $SLEEP0
+       if grep '^# refresh done, switching to persist stage' $SYNC2OUT; then
+               awk '/^result:/{print; exit $2}' $SYNC2OUT
+               RC=$?
+               break
+       fi
+done
+if test $RC != 0 ; then
+       echo "refresh failed ($RC)!"
+       kill $PID
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Using ldapmodify to modify local entry on privider2..."
+$LDAPMODIFY -D $MANAGERDN -H $URI3 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: cn=local_user,ou=local,ou=users,$BASEDN
+changeType: modify
+replace: description
+description: Syncrepl local_user
+EOF
+RC=32
+for i in 0 1 2 3 4 5; do
+       echo "Waiting for syncrepl to receive changes..."
+       sleep $SLEEP0
+       if grep -q '^description: Syncrepl local_user' $SYNC2OUT; then
+               RC=0
+               break
+       fi
+done
+if test $RC != 0 ; then
+       echo "syncrepl failed ($RC)!"
+       kill $PID
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Using ldapmodify to modify remote entry on privider2..."
+$LDAPMODIFY -D $MANAGERDN -H $URI3 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: cn=remote_user,ou=remote,ou=users,$BASEDN
+changeType: modify
+replace: description
+description: Syncrepl remote_user
+EOF
+RC=32
+for i in 0 1 2 3 4 5; do
+       echo "Waiting for syncrepl to receive changes..."
+       sleep $SLEEP0
+       if grep -q '^description: Syncrepl remote_user' $SYNC2OUT; then
+               RC=0
+               break
+       fi
+done
+kill $PID
+if test $RC != 0 ; then
+       echo "syncrepl failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+exit 0