From 804c1aeb8ea66c67d2ccf55494e0da6542448455 Mon Sep 17 00:00:00 2001
From: "Thierry bordaz (tbordaz)" <tbordaz@redhat.com>
Date: Thu, 2 May 2013 15:24:13 +0200
Subject: [PATCH] Ticket 47350 - Allow search to look up 'in memory RUV'

Bug Description:

	On a running instance and for a given replica the RUV is an in memory data structure.
	This data structure is writen to disk at each update and there it is named database RUV.
	With ticket https://fedorahosted.org/389/ticket/564, the update of the database RUV would be done
	asynchronously. The consequence is that the database RUV would be late compare to the current
	update status.
	It is important for monitoring reason to look up the current update status and so to be able to
	read the in memory RUV

Fix Description:

	The fix consists to return the in memory RUV if attributes "nsds50ruv" and/or "nsruvReplicaLastModified" are
	requested when reading the entry (replica_config_search):
		cn=replica,cn=\"<suffix>\",cn=mapping tree,cn=config

	In addition to provide backward compatibility, if the database ruv is lookup
	(i.e. nsuniqueid=ffffffff-ffffffff-ffffffff-ffffffff,<suffix>), it actually returns the in memory ruv
	(i.e. cn=replica,cn=\"<suffix>\",cn=mapping tree,cn=config). This is achieved with a registered MMR preop callback
	(multimaster_preop_init), that is called when send the result entry (SLAPI_PLUGIN_PRE_ENTRY_FN).

https://fedorahosted.org/389/ticket/47350

Reviewed by: Rich Meggison

Platforms tested: F17

Flag Day: no

Doc impact: no
---
 ldap/servers/plugins/replication/repl5.h           |  2 +
 ldap/servers/plugins/replication/repl5_init.c      |  3 +-
 ldap/servers/plugins/replication/repl5_plugins.c   | 31 +++++++++
 ldap/servers/plugins/replication/repl5_replica.c   | 23 +++++--
 .../plugins/replication/repl5_replica_config.c     | 76 ++++++++++++++++++++++
 ldap/servers/plugins/replication/repl5_ruv.c       | 76 ++++++++++++++++++++++
 ldap/servers/plugins/replication/repl5_ruv.h       |  2 +
 7 files changed, 208 insertions(+), 5 deletions(-)

diff --git a/ldap/servers/plugins/replication/repl5.h b/ldap/servers/plugins/replication/repl5.h
index c7639e3..8da2d3b 100644
--- a/ldap/servers/plugins/replication/repl5.h
+++ b/ldap/servers/plugins/replication/repl5.h
@@ -212,6 +212,7 @@ int multimaster_preop_modify (Slapi_PBlock *pb);
 int multimaster_preop_modrdn (Slapi_PBlock *pb);
 int multimaster_preop_search (Slapi_PBlock *pb);
 int multimaster_preop_compare (Slapi_PBlock *pb);
+int multimaster_ruv_search(Slapi_PBlock *pb);
 int multimaster_bepreop_add (Slapi_PBlock *pb);
 int multimaster_bepreop_delete (Slapi_PBlock *pb);
 int multimaster_bepreop_modify (Slapi_PBlock *pb);
@@ -591,6 +592,7 @@ void replica_set_tombstone_reap_interval (Replica *r, long interval);
 void replica_update_ruv_consumer (Replica *r, RUV *supplier_ruv);
 void replica_set_ruv_dirty (Replica *r);
 void replica_write_ruv (Replica *r);
+Slapi_Entry *get_in_memory_ruv(Slapi_DN *suffix_sdn);
 char *replica_get_dn(Replica *r);
 void replica_check_for_tasks(Replica*r, Slapi_Entry *e);
 void replica_update_state (time_t when, void *arg);
diff --git a/ldap/servers/plugins/replication/repl5_init.c b/ldap/servers/plugins/replication/repl5_init.c
index 8471f00..1533db9 100644
--- a/ldap/servers/plugins/replication/repl5_init.c
+++ b/ldap/servers/plugins/replication/repl5_init.c
@@ -259,7 +259,8 @@ multimaster_preop_init( Slapi_PBlock *pb )
 		slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_MODIFY_FN, (void *) multimaster_preop_modify ) != 0 ||
 	    slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_MODRDN_FN, (void *) multimaster_preop_modrdn ) != 0 ||
 	    slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_SEARCH_FN, (void *) multimaster_preop_search ) != 0 ||
-	    slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_COMPARE_FN, (void *) multimaster_preop_compare ) != 0)
+	    slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_COMPARE_FN, (void *) multimaster_preop_compare ) != 0 ||
+            slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_ENTRY_FN, (void *) multimaster_ruv_search ) != 0)
 	{
 		slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "multimaster_preop_init failed\n" );
 		rc= -1;
diff --git a/ldap/servers/plugins/replication/repl5_plugins.c b/ldap/servers/plugins/replication/repl5_plugins.c
index 43c1596..cb5fe5c 100644
--- a/ldap/servers/plugins/replication/repl5_plugins.c
+++ b/ldap/servers/plugins/replication/repl5_plugins.c
@@ -640,6 +640,37 @@ multimaster_preop_compare (Slapi_PBlock *pb)
     return 0;
 }
 
+int
+multimaster_ruv_search(Slapi_PBlock *pb)
+{
+        Slapi_Entry *e, *e_alt;
+        Slapi_DN *suffix_sdn;
+
+        slapi_pblock_get(pb, SLAPI_SEARCH_ENTRY_ORIG, &e);
+
+        if (e == NULL)
+                return 0;
+
+        if (is_ruv_tombstone_entry(e)) {
+                /* We are about to send back the database RUV, we need to return
+                 * in memory RUV instead
+                 */
+
+                /* Retrieve the suffix DN from the RUV entry */
+                suffix_sdn = slapi_sdn_new();
+                slapi_sdn_get_parent(slapi_entry_get_sdn(e), suffix_sdn);
+
+                /* Now set the in memory RUV into the pblock */
+                if ((e_alt = get_in_memory_ruv(suffix_sdn)) != NULL) {
+                        slapi_pblock_set(pb, SLAPI_SEARCH_ENTRY_COPY, e_alt);
+                }
+
+                slapi_sdn_free(&suffix_sdn);
+        }
+        
+        return 0;
+}
+
 static void
 purge_entry_state_information (Slapi_PBlock *pb)
 {
diff --git a/ldap/servers/plugins/replication/repl5_replica.c b/ldap/servers/plugins/replication/repl5_replica.c
index 5a19833..6c73d05 100644
--- a/ldap/servers/plugins/replication/repl5_replica.c
+++ b/ldap/servers/plugins/replication/repl5_replica.c
@@ -104,7 +104,7 @@ typedef struct reap_callback_data
 
 
 /* Forward declarations of helper functions*/
-static Slapi_Entry* _replica_get_config_entry (const Slapi_DN *root);
+static Slapi_Entry* _replica_get_config_entry (const Slapi_DN *root, char **attrs);
 static int _replica_check_validity (const Replica *r);
 static int _replica_init_from_config (Replica *r, Slapi_Entry *e, char *errortext);
 static int _replica_update_entry (Replica *r, Slapi_Entry *e, char *errortext);
@@ -139,7 +139,7 @@ replica_new(const Slapi_DN *root)
 	PR_ASSERT (root);
 
 	/* check if there is a replica associated with the tree */
-	e = _replica_get_config_entry (root);
+	e = _replica_get_config_entry (root, NULL);
 	if (e)
 	{
 		errorbuf[0] = '\0';
@@ -1556,7 +1556,7 @@ int replica_check_for_data_reload (Replica *r, void *arg)
    mapping tree node for the replica's backend */
 
 static Slapi_Entry* 
-_replica_get_config_entry (const Slapi_DN *root)
+_replica_get_config_entry (const Slapi_DN *root, char **attrs)
 {
 	int rc = 0;
 	char *dn = NULL;
@@ -1573,7 +1573,7 @@ _replica_get_config_entry (const Slapi_DN *root)
 	}
 	pb = slapi_pblock_new ();
 
-	slapi_search_internal_set_pb (pb, dn, LDAP_SCOPE_BASE, "objectclass=*", NULL, 0, NULL,
+	slapi_search_internal_set_pb (pb, dn, LDAP_SCOPE_BASE, "objectclass=*", attrs, 0, NULL,
 								  NULL, repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
 	slapi_search_internal_pb (pb);	
 	slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
@@ -1590,6 +1590,21 @@ _replica_get_config_entry (const Slapi_DN *root)
 	return e; 
 }
 
+/* It does an internal search to read the in memory RUV
+ * of the provided suffix
+  */
+Slapi_Entry *
+get_in_memory_ruv(Slapi_DN *suffix_sdn)
+{
+        char *attrs[3];
+        
+        /* these two attributes needs to be asked when reading the RUV */
+        attrs[0] = type_ruvElement;
+        attrs[1] = type_ruvElementUpdatetime;
+        attrs[2] = NULL;
+        return(_replica_get_config_entry(suffix_sdn, attrs));
+}
+
 char *
 replica_get_dn(Replica *r)
 {
diff --git a/ldap/servers/plugins/replication/repl5_replica_config.c b/ldap/servers/plugins/replication/repl5_replica_config.c
index 77f748f..b0e65cf 100644
--- a/ldap/servers/plugins/replication/repl5_replica_config.c
+++ b/ldap/servers/plugins/replication/repl5_replica_config.c
@@ -704,6 +704,70 @@ replica_config_delete (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter
 	return SLAPI_DSE_CALLBACK_OK;
 }
 
+static void
+replica_config_search_last_modified(Slapi_PBlock *pb, Slapi_Entry* e, Replica *replica) 
+{
+        Object *ruv_obj = NULL;
+        RUV *ruv = NULL;
+        Slapi_Value **values;
+
+        if (replica == NULL)
+                return;
+        ruv_obj = replica_get_ruv(replica);
+        ruv = object_get_data(ruv_obj);
+
+        if ((values = ruv_last_modified_to_valuearray(ruv)) != NULL) {
+                slapi_entry_add_values_sv(e, type_ruvElementUpdatetime, values);
+                valuearray_free(&values);
+        }
+
+        object_release(ruv_obj);
+
+}
+
+static void
+replica_config_search_ruv(Slapi_PBlock *pb, Slapi_Entry* e, Replica *replica) 
+{
+        Object *ruv_obj = NULL;
+        RUV *ruv = NULL;
+        Slapi_Value **values;
+
+        if (replica == NULL)
+                return;
+        ruv_obj = replica_get_ruv(replica);
+        ruv = object_get_data(ruv_obj);
+
+        if ((values = ruv_to_valuearray(ruv)) != NULL) {
+                slapi_entry_add_values_sv(e, type_ruvElement, values);
+                valuearray_free(&values);
+        }
+
+        object_release(ruv_obj);
+
+}
+
+/* Returns PR_TRUE if 'attr' is present in the search requested attributes
+ * else it returns PR_FALSE
+ */
+static PRBool
+search_requested_attr(Slapi_PBlock *pb, char *attr) 
+{
+        char **attrs = NULL;
+        int i;
+
+        slapi_pblock_get(pb, SLAPI_SEARCH_ATTRS, &attrs);
+        if ((attr == NULL) || (attrs == NULL))
+                return PR_FALSE;
+
+        for (i = 0; attrs[i] != NULL; i++) {
+                if (strncasecmp(attrs[i], attr, strlen(attr)) == 0) {
+                        return PR_TRUE;
+                }
+        }
+        
+        return PR_FALSE;
+}
+
 static int
 replica_config_search (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode,
                        char *returntext, void *arg)
@@ -730,6 +794,18 @@ replica_config_search (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter
 		if (replica) {
 			reapActive = replica_get_tombstone_reap_active(replica);
 		}
+                
+                /* Check if the in memory ruv is requested */
+                if (search_requested_attr(pb, type_ruvElement)) {
+                        replica_config_search_ruv(pb, e, replica);
+                }
+                
+                /* Check if the last update time is requested */
+                if (search_requested_attr(pb, type_ruvElementUpdatetime)) {
+                        replica_config_search_last_modified(pb, e, replica);
+                }
+                
+                
 		object_release (mtnode_ext->replica);
 	}
 
diff --git a/ldap/servers/plugins/replication/repl5_ruv.c b/ldap/servers/plugins/replication/repl5_ruv.c
index 9ce9768..9742e48 100644
--- a/ldap/servers/plugins/replication/repl5_ruv.c
+++ b/ldap/servers/plugins/replication/repl5_ruv.c
@@ -1161,6 +1161,82 @@ ruv_get_cleaned_rids(RUV *ruv, ReplicaId *rids)
     }
 }
 
+/* 
+ * This routine dump the ruv (last modified time) into a value array
+ * the call must free the returned value array
+ */
+Slapi_Value **
+ruv_last_modified_to_valuearray(RUV *ruv)
+{
+        RUVElement *ruv_e;
+        int cookie;
+        Slapi_Value *value;
+        Slapi_Value **values = NULL;
+        char *buffer;
+
+
+        /* Acquire the ruv lock */
+        slapi_rwlock_rdlock(ruv->lock);
+
+        /* Now loop for each RUVElement and store its string value into the valueset*/
+        for (ruv_e = dl_get_first(ruv->elements, &cookie);
+                NULL != ruv_e;
+                ruv_e = dl_get_next(ruv->elements, &cookie)) {
+                			
+                buffer = slapi_ch_smprintf("%s%d%s%s} %08lx", prefix_ruvcsn, ruv_e->rid,
+			    ruv_e->replica_purl == NULL ? "" : " ",
+			    ruv_e->replica_purl == NULL ? "" : ruv_e->replica_purl,
+			    ruv_e->last_modified);
+                value = slapi_value_new_string_passin(buffer);
+                valuearray_add_value(&values, value);
+                slapi_value_free(&value);
+        }
+        
+        slapi_rwlock_unlock(ruv->lock);
+
+        return (values);
+}
+
+/* 
+ * This routine dump the ruv (replicageneration and ruvElements) into a value array
+ * the call must free the returned value array
+ */
+Slapi_Value **
+ruv_to_valuearray(RUV *ruv)
+{
+        RUVElement *ruv_e;
+        int cookie;
+        Slapi_Value *value;
+        Slapi_Value **values = NULL;
+        struct berval bv;
+        char *buffer; 
+
+        /* Acquire the ruv lock */
+        slapi_rwlock_rdlock(ruv->lock);
+        
+        /* dump the replicageneration */
+        buffer = slapi_ch_smprintf("%s %s", prefix_replicageneration, ruv->replGen);
+        value = slapi_value_new_string_passin(buffer);
+        valuearray_add_value(&values, value);
+        slapi_value_free(&value);
+
+        /* Now loop for each RUVElement and store its string value into the valueset*/
+        for (ruv_e = dl_get_first(ruv->elements, &cookie);
+                NULL != ruv_e;
+                ruv_e = dl_get_next(ruv->elements, &cookie)) {
+
+                ruv_element_to_string(ruv_e, &bv, NULL, 0);
+                value = slapi_value_new_berval(&bv);
+                slapi_ber_bvdone(&bv);
+                valuearray_add_value(&values, value);
+                slapi_value_free(&value);
+        }
+        
+        slapi_rwlock_unlock(ruv->lock);
+
+        return (values);
+}
+
 int
 ruv_to_smod(const RUV *ruv, Slapi_Mod *smod)
 {
diff --git a/ldap/servers/plugins/replication/repl5_ruv.h b/ldap/servers/plugins/replication/repl5_ruv.h
index 036951f..c9b85ac 100644
--- a/ldap/servers/plugins/replication/repl5_ruv.h
+++ b/ldap/servers/plugins/replication/repl5_ruv.h
@@ -125,6 +125,8 @@ int ruv_get_min_csn(const RUV *ruv, CSN **csn);
 int ruv_get_max_csn(const RUV *ruv, CSN **csn);
 int ruv_get_rid_max_csn(const RUV *ruv, CSN **csn, ReplicaId rid);
 int ruv_enumerate_elements (const RUV *ruv, FNEnumRUV fn, void *arg);
+Slapi_Value **ruv_last_modified_to_valuearray(RUV *ruv);
+Slapi_Value **ruv_to_valuearray(RUV *ruv);
 int ruv_to_smod(const RUV *ruv, Slapi_Mod *smod);
 int ruv_last_modified_to_smod(const RUV *ruv, Slapi_Mod *smod);
 int ruv_to_bervals(const RUV *ruv, struct berval ***bvals);
-- 
1.7.11.7

