From fef4ce0ad2269271d519a9496755c08a79d6c62f Mon Sep 17 00:00:00 2001
From: "Thierry bordaz (tbordaz)" <tbordaz@redhat.com>
Date: Tue, 14 Oct 2014 10:44:09 +0200
Subject: [PATCH] Ticket 47901: After total init, nsds5replicaLastInitStatus
 can report an erroneous error status (like 'Referral')

Bug Description:
	Total update may fails for several reasons:
		- consumer ldap server returning an error to the extended op
		- replication problem (replica not acquired, replication disabled)
		- connection timeout
	When the replica is acquired and the total update starts, each
	entry is sent by send_entry. send_entry returns a conn_Result not a ldap error

Fix Description:
	Change agmt_set_last_init_status to that the 3 errors (ldap, replication, connection)
	are reported.

http://fedorahosted.org/389/ticket/47901

Reviewed by: ?

Platforms tested: F17

Flag Day: no

Doc impact: no
---
 ldap/servers/plugins/replication/repl5.h           |  9 ++++--
 ldap/servers/plugins/replication/repl5_agmt.c      | 24 +++++++++++----
 ldap/servers/plugins/replication/repl5_agmtlist.c  |  2 +-
 .../servers/plugins/replication/repl5_connection.c | 31 ++++++++++++++++++++
 ldap/servers/plugins/replication/repl5_protocol.c  |  2 +-
 .../plugins/replication/repl5_tot_protocol.c       | 34 ++++++++++++----------
 .../plugins/replication/windows_tot_protocol.c     | 12 ++++----
 7 files changed, 82 insertions(+), 32 deletions(-)

diff --git a/ldap/servers/plugins/replication/repl5.h b/ldap/servers/plugins/replication/repl5.h
index 86c77ce..8352b32 100644
--- a/ldap/servers/plugins/replication/repl5.h
+++ b/ldap/servers/plugins/replication/repl5.h
@@ -378,7 +378,7 @@ void agmt_set_update_in_progress (Repl_Agmt *ra, PRBool in_progress);
 PRBool agmt_get_update_in_progress (const Repl_Agmt *ra);
 void agmt_set_last_init_start (Repl_Agmt *ra, time_t start_time);
 void agmt_set_last_init_end (Repl_Agmt *ra, time_t end_time);
-void agmt_set_last_init_status (Repl_Agmt *ra, int ldaprc, int replrc, const char *msg);
+void agmt_set_last_init_status (Repl_Agmt *ra, int ldaprc, int replrc, int connrc, const char *msg);
 void agmt_inc_last_update_changecount (Repl_Agmt *ra, ReplicaId rid, int skipped);
 void agmt_get_changecount_string (Repl_Agmt *ra, char *buf, int bufsize);
 int agmt_set_replicated_attributes_from_entry(Repl_Agmt *ra, const Slapi_Entry *e);
@@ -425,7 +425,9 @@ time_t backoff_step(Backoff_Timer *bt);
 int backoff_expired(Backoff_Timer *bt, int margin);
 void backoff_delete(Backoff_Timer **btp);
 
-/* In repl5_connection.c */
+/* In repl5_connection.c 
+ * keep in sync with conn_result2string
+ */
 typedef struct repl_connection Repl_Connection;
 typedef enum
 {   
@@ -450,8 +452,9 @@ typedef enum
 	CONN_NOT_WIN2K3,
 	CONN_SUPPORTS_DS90_REPL,
 	CONN_DOES_NOT_SUPPORT_DS90_REPL
-} ConnResult;  
+} ConnResult;
 
+char *conn_result2string (int result);
 Repl_Connection *conn_new(Repl_Agmt *agmt);
 ConnResult conn_connect(Repl_Connection *conn);
 void conn_disconnect(Repl_Connection *conn);
diff --git a/ldap/servers/plugins/replication/repl5_agmt.c b/ldap/servers/plugins/replication/repl5_agmt.c
index 7c5c37c..480431d 100644
--- a/ldap/servers/plugins/replication/repl5_agmt.c
+++ b/ldap/servers/plugins/replication/repl5_agmt.c
@@ -2264,15 +2264,19 @@ agmt_set_last_update_status (Repl_Agmt *ra, int ldaprc, int replrc, const char *
 }
 
 void
-agmt_set_last_init_status (Repl_Agmt *ra, int ldaprc, int replrc, const char *message)
+agmt_set_last_init_status (Repl_Agmt *ra, int ldaprc, int replrc, int connrc, const char *message)
 {
+    char *connmsg = NULL;
+    
+    connmsg = conn_result2string(connrc);
+    
 	PR_ASSERT(NULL != ra);
 	if (NULL != ra)
 	{
 		if (ldaprc != LDAP_SUCCESS)
 		{
 			char *replmsg = NULL;
-
+            
 			if ( replrc ) {
 				replmsg = protocol_response2string(replrc);
 				/* Do not mix the unknown replication error with the known ldap one */
@@ -2280,9 +2284,10 @@ agmt_set_last_init_status (Repl_Agmt *ra, int ldaprc, int replrc, const char *me
 					replmsg = NULL;
 				}
 			}
-			PR_snprintf(ra->last_init_status, STATUS_LEN, "%d %s%sLDAP error: %s%s%s",
+			PR_snprintf(ra->last_init_status, STATUS_LEN, "%d %s%sLDAP error: %s%s%s%s%s",
 				ldaprc, message?message:"",message?"":" - ",
-				slapi_err2string(ldaprc), replmsg ? " - " : "", replmsg ? replmsg : "");
+				slapi_err2string(ldaprc), replmsg ? " - " : "", replmsg ? replmsg : "",
+                connmsg ? " - " : "", connmsg ? connmsg : "");
 		}
 		/* ldaprc == LDAP_SUCCESS */
 		else if (replrc != 0)
@@ -2316,11 +2321,18 @@ agmt_set_last_init_status (Repl_Agmt *ra, int ldaprc, int replrc, const char *me
 			else
 			{
 				PR_snprintf(ra->last_init_status, STATUS_LEN,
-					"%d Replication error acquiring replica: %s%s%s",
+					"%d Replication error acquiring replica: %s%s%s%s%s",
 					replrc, protocol_response2string(replrc),
-					message?" - ":"",message?message:"");
+					message?" - ":"",message?message:"",
+                    connmsg?" - ":"",connmsg?connmsg:"");
 			}
 		}
+        else if (connrc != CONN_OPERATION_SUCCESS) {
+            PR_snprintf(ra->last_init_status, STATUS_LEN,
+					"%d connection error: %s%s%s",
+					connrc, connmsg,
+					message?" - ":"",message?message:"");
+        }
 		else if (message != NULL) /* replrc == NSDS50_REPL_REPLICA_READY == 0 */
 		{
 			PR_snprintf(ra->last_init_status, STATUS_LEN,
diff --git a/ldap/servers/plugins/replication/repl5_agmtlist.c b/ldap/servers/plugins/replication/repl5_agmtlist.c
index 8a70055..81cd937 100644
--- a/ldap/servers/plugins/replication/repl5_agmtlist.c
+++ b/ldap/servers/plugins/replication/repl5_agmtlist.c
@@ -567,7 +567,7 @@ agmtlist_modify_callback(Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry
     {
         if (agmt_initialize_replica(agmt) != 0) {
             /* The suffix/repl agmt is disabled */
-            agmt_set_last_init_status(agmt, 0, NSDS50_REPL_DISABLED, NULL);
+            agmt_set_last_init_status(agmt, 0, NSDS50_REPL_DISABLED, 0, NULL);
             if(agmt_is_enabled(agmt)){
                 PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, "Suffix is disabled");
             } else {
diff --git a/ldap/servers/plugins/replication/repl5_connection.c b/ldap/servers/plugins/replication/repl5_connection.c
index c004bfb..ae21f1c 100644
--- a/ldap/servers/plugins/replication/repl5_connection.c
+++ b/ldap/servers/plugins/replication/repl5_connection.c
@@ -145,6 +145,37 @@ static void repl5_debug_timeout_callback(time_t when, void *arg);
 static void close_connection_internal(Repl_Connection *conn);
 static void conn_delete_internal(Repl_Connection *conn);
 
+
+/* converts consumer's response to a string */
+char *
+conn_result2string (int result)
+{
+    switch (result)
+    {
+        case CONN_OPERATION_SUCCESS:            return "operation success";
+        case CONN_OPERATION_FAILED:             return "operation failure";
+        case CONN_NOT_CONNECTED:                return "not connected";
+        case CONN_SUPPORTS_DS5_REPL:            return "consumer supports all DS5 extop";
+        case CONN_DOES_NOT_SUPPORT_DS5_REPL:    return "consumer does not support all DS5 extop";
+        case CONN_SCHEMA_UPDATED:               return "consumer schema updated";
+        case CONN_SCHEMA_NO_UPDATE_NEEDED:      return "consumer schema up to date";
+        case CONN_LOCAL_ERROR:                  return "local error";
+        case CONN_BUSY:                         return "consumer is busy";
+        case CONN_SSL_NOT_ENABLED:              return "NSS not initialized";
+        case CONN_TIMEOUT:                      return "time out";
+        case CONN_SUPPORTS_DS71_REPL:           return "consumer supports all DS71 extop";
+        case CONN_DOES_NOT_SUPPORT_DS71_REPL:   return "consumer does not support all DS7.1 extop";
+        case CONN_IS_READONLY:                  return "consumer is read only";  
+        case CONN_IS_NOT_READONLY:              return "consumer is not read only";
+        case CONN_SUPPORTS_DIRSYNC:             return "consumer supports DIRSYNC control";
+        case CONN_DOES_NOT_SUPPORT_DIRSYNC:     return "consumer does not support DIRSYNC control";
+        case CONN_IS_WIN2K3:                    return "consumer is W2K3 or after";
+        case CONN_NOT_WIN2K3:                   return "consumer is before W2K3";
+        case CONN_SUPPORTS_DS90_REPL:           return "consumer supports all DS90 extop";
+        case CONN_DOES_NOT_SUPPORT_DS90_REPL:   return "consumer does not support all DS90 extop";
+        default:                                return NULL;
+    }
+}
 /*
  * Create a new connection object. Returns a pointer to the object, or
  * NULL if an error occurs.
diff --git a/ldap/servers/plugins/replication/repl5_protocol.c b/ldap/servers/plugins/replication/repl5_protocol.c
index 0e9668d..e8ec16a 100644
--- a/ldap/servers/plugins/replication/repl5_protocol.c
+++ b/ldap/servers/plugins/replication/repl5_protocol.c
@@ -244,7 +244,7 @@ prot_initialize_replica(Repl_Protocol *rp)
 	rp->next_state = STATE_PERFORMING_TOTAL_UPDATE;
 	/* Stop the incremental protocol, if running */
 	rp->prp_incremental->stop(rp->prp_incremental);
-	if (rp->prp_total) agmt_set_last_init_status(rp->prp_total->agmt, 0, 0, NULL);
+	if (rp->prp_total) agmt_set_last_init_status(rp->prp_total->agmt, 0, 0, 0, NULL);
     PR_Unlock(rp->lock);
 }
 
diff --git a/ldap/servers/plugins/replication/repl5_tot_protocol.c b/ldap/servers/plugins/replication/repl5_tot_protocol.c
index d4f0fcc..b0cb137 100644
--- a/ldap/servers/plugins/replication/repl5_tot_protocol.c
+++ b/ldap/servers/plugins/replication/repl5_tot_protocol.c
@@ -368,7 +368,7 @@ repl5_tot_run(Private_Repl_Protocol *prp)
 		int optype, ldaprc;
 		conn_get_error(prp->conn, &optype, &ldaprc);
 		agmt_set_last_init_status(prp->agmt, ldaprc,
-				  prp->last_acquire_response_code, NULL);
+				  prp->last_acquire_response_code, 0, NULL);
         goto done;
     }
 	else if (prp->terminate)
@@ -380,7 +380,7 @@ repl5_tot_run(Private_Repl_Protocol *prp)
 	hostname = agmt_get_hostname(prp->agmt);
 	portnum = agmt_get_port(prp->agmt);
 
-    agmt_set_last_init_status(prp->agmt, 0, 0, "Total schema update in progress");
+    agmt_set_last_init_status(prp->agmt, 0, 0, 0, "Total schema update in progress");
 	remote_schema_csn = agmt_get_consumer_schema_csn ( prp->agmt );
 	rc = conn_push_schema(prp->conn, &remote_schema_csn);
 
@@ -395,11 +395,11 @@ repl5_tot_run(Private_Repl_Protocol *prp)
 						"total update session.\n",
 						hostname, portnum);
 		/* But keep going */
-		agmt_set_last_init_status(prp->agmt, 0, rc, "Total schema update failed");
+		agmt_set_last_init_status(prp->agmt, 0, rc, 0, "Total schema update failed");
 	}
 	else
 	{
-		agmt_set_last_init_status(prp->agmt, 0, 0, "Total schema update succeeded");
+		agmt_set_last_init_status(prp->agmt, 0, 0, 0, "Total schema update succeeded");
 	}
 
     /* ONREPL - big assumption here is that entries a returned in the id order
@@ -408,7 +408,7 @@ repl5_tot_run(Private_Repl_Protocol *prp)
        properly updated because bulk import at the moment skips orphand entries. */
 	/* XXXggood above assumption may not be valid if orphaned entry moved???? */
 
-    agmt_set_last_init_status(prp->agmt, 0, 0, "Total update in progress");
+    agmt_set_last_init_status(prp->agmt, 0, 0, 0, "Total update in progress");
 
     slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Beginning total update of replica "
 		"\"%s\".\n", agmt_get_long_name(prp->agmt));
@@ -452,7 +452,9 @@ repl5_tot_run(Private_Repl_Protocol *prp)
 	}
 
     /* this search get all the entries from the replicated area including tombstones
-       and referrals */
+       and referrals 
+       Note that cb_data.rc contains values from ConnResult
+     */
     slapi_search_internal_callback_pb (pb, &cb_data /* callback data */,
                                        get_result /* result callback */,
                                        send_entry /* entry callback */,
@@ -467,7 +469,7 @@ repl5_tot_run(Private_Repl_Protocol *prp)
 
 	if (!prp->repl50consumer) 
 	{
-		if(cb_data.rc == LDAP_SUCCESS){ /* no need to wait if we already failed */
+		if(cb_data.rc == CONN_OPERATION_SUCCESS){ /* no need to wait if we already failed */
 			repl5_tot_waitfor_async_results(&cb_data);
 		}
 		rc = repl5_tot_destroy_async_result_thread(&cb_data);
@@ -491,16 +493,16 @@ repl5_tot_run(Private_Repl_Protocol *prp)
 	agmt_update_done(prp->agmt, 1);
 	release_replica(prp);
 	
-	if (rc != LDAP_SUCCESS)
+	if (rc != CONN_OPERATION_SUCCESS)
 	{
 		slapi_log_error (SLAPI_LOG_FATAL, repl_plugin_name, "Total update failed for replica \"%s\", "
 				         "error (%d)\n", agmt_get_long_name(prp->agmt), rc);
-		agmt_set_last_init_status(prp->agmt, rc, 0, "Total update aborted");
+		agmt_set_last_init_status(prp->agmt, 0, 0, rc, "Total update aborted");
 	} else {
 		slapi_log_error (SLAPI_LOG_FATAL, repl_plugin_name, "Finished total update of replica "
 		                 "\"%s\". Sent %lu entries.\n",
 		                 agmt_get_long_name(prp->agmt), cb_data.num_entries);
-		agmt_set_last_init_status(prp->agmt, 0, 0, "Total update succeeded");
+		agmt_set_last_init_status(prp->agmt, 0, 0, 0, "Total update succeeded");
 	}
 
 done:
@@ -776,12 +778,14 @@ int send_entry (Slapi_Entry *e, void *cb_data)
 	if (CONN_NOT_CONNECTED == rc) {
 		((callback_data*)cb_data)->rc = -2;
 		retval = -1;
-	} else if (CONN_OPERATION_SUCCESS == rc) {
-		retval = 0;
 	} else {
-		((callback_data*)cb_data)->rc = rc;
-		retval = -1;
-	}
+        ((callback_data*) cb_data)->rc = rc;
+        if (CONN_OPERATION_SUCCESS == rc) {
+            retval = 0;
+        } else {
+            retval = -1;
+        }
+    }
 error:
 	return retval;
 }
diff --git a/ldap/servers/plugins/replication/windows_tot_protocol.c b/ldap/servers/plugins/replication/windows_tot_protocol.c
index c81974b..f739944 100644
--- a/ldap/servers/plugins/replication/windows_tot_protocol.c
+++ b/ldap/servers/plugins/replication/windows_tot_protocol.c
@@ -182,7 +182,7 @@ windows_tot_run(Private_Repl_Protocol *prp)
 		int optype, ldaprc;
 		windows_conn_get_error(prp->conn, &optype, &ldaprc);
 		agmt_set_last_init_status(prp->agmt, ldaprc,
-				  prp->last_acquire_response_code, NULL);
+				  prp->last_acquire_response_code, 0, NULL);
 		goto done;
 	}
 	else if (prp->terminate)
@@ -192,9 +192,9 @@ windows_tot_run(Private_Repl_Protocol *prp)
 		goto done;    
 	}
 
-	agmt_set_last_init_status(prp->agmt, 0, 0, "Total schema update in progress");
+	agmt_set_last_init_status(prp->agmt, 0, 0, 0, "Total schema update in progress");
 
-	agmt_set_last_init_status(prp->agmt, 0, 0, "Total update in progress");
+	agmt_set_last_init_status(prp->agmt, 0, 0, 0, "Total update in progress");
 
 	agmt_set_update_in_progress(prp->agmt, PR_TRUE);
 
@@ -253,15 +253,15 @@ windows_tot_run(Private_Repl_Protocol *prp)
 	rc = cb_data.rc;
 	windows_release_replica(prp);
 		
-	if (rc != LDAP_SUCCESS) {
+	if (rc != CONN_OPERATION_SUCCESS) {
 		slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name, "%s: windows_tot_run: "
 		                "failed to obtain data to send to the consumer; LDAP error - %d\n", 
 		                agmt_get_long_name(prp->agmt), rc);
-		agmt_set_last_init_status(prp->agmt, rc, 0, "Total update aborted");
+		agmt_set_last_init_status(prp->agmt, 0, 0, rc, "Total update aborted");
 	} else {
 		slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name, "Finished total update of replica "
 						"\"%s\". Sent %lu entries.\n", agmt_get_long_name(prp->agmt), cb_data.num_entries);
-		agmt_set_last_init_status(prp->agmt, 0, 0, "Total update succeeded");
+		agmt_set_last_init_status(prp->agmt, 0, 0, 0, "Total update succeeded");
 		/* Now update our consumer RUV for this agreement.
 		 * This ensures that future incrememental updates work.
 		 */
-- 
1.7.11.7

