Ensure RT incremental update not applied early

This change ensures that a full table has been provided by the
route generator (route manager) prior to RMR accepting any
incremental updates.

The change also captures the initial value of the RMR_SEED
environment variable such that it cannot be changed once it has
been used.

Issue-ID: RIC-329

Signed-off-by: E. Scott Daniels <daniels@att.com>
Change-Id: I168cb42905e41641590130de724965e8dbca67e6
diff --git a/CHANGES_CORE.txt b/CHANGES_CORE.txt
index 51c254c..cfa9ea3 100644
--- a/CHANGES_CORE.txt
+++ b/CHANGES_CORE.txt
@@ -5,6 +5,13 @@
 # API and build change  and fix summaries. Doc corrections
 # and/or changes are not mentioned here; see the commit messages.
 
+2021 April 9; version 4.7.2
+	Ensure that route table update received from route generator is not
+	applied before a full route table is received.
+
+2021 April 2; version 4.7.1
+	Correct issues found during static code analysis.
+
 2021 March 31; version 4.7.0
 	The route table collector thread will capture the current "offering"
 	from the Route Manager (table generator) if the RMR_SEED_RT environment
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a8220b4..a0d1b7f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -42,7 +42,7 @@
 
 set( major_version "4" )		# should be automatically populated from git tag later, but until CI process sets a tag we use this
 set( minor_version "7" )
-set( patch_level "1" )
+set( patch_level "2" )
 
 set( install_root "${CMAKE_INSTALL_PREFIX}" )
 set( install_inc "include/rmr" )
diff --git a/docs/rel-notes.rst b/docs/rel-notes.rst
index 7f29300..dadc465 100644
--- a/docs/rel-notes.rst
+++ b/docs/rel-notes.rst
@@ -22,6 +22,21 @@
 version 4.0.0, the RMR versions should no longer skip.
 
 
+2021 April 9; version 4.7.2
+---------------------------
+
+Ensure that route table update received from route generator
+is not applied before a full route table is received.
+
+
+
+2021 April 2; version 4.7.1
+---------------------------
+
+Correct issues found during static code analysis.
+
+
+
 2021 March 31; version 4.7.0
 ----------------------------
 
diff --git a/src/rmr/common/include/rmr_agnostic.h b/src/rmr/common/include/rmr_agnostic.h
index 7e6fdda..0858fde 100644
--- a/src/rmr/common/include/rmr_agnostic.h
+++ b/src/rmr/common/include/rmr_agnostic.h
@@ -83,11 +83,11 @@
 									// internal flags, must be > than UFLAG_MASK
 //#define IFL_....
 
+									// context flags
 #define CFL_MTC_ENABLED	0x01		// multi-threaded call is enabled
 #define CFL_NO_RTACK	0x02		// no route table ack needed when end received
-
-									// context flags
-#define CTXFL_WARN		0x01		// ok to warn on stderr for some things that shouldn't happen
+#define CFL_WARN		0x04		// ok to warn on stderr for some things that shouldn't happen
+#define CFL_FULLRT		0x08		// set when we have received an initial full route table (prevent updates before one arrives)
 
 									// msg buffer flags
 #define MFL_ZEROCOPY	0x01		// the message is an allocated zero copy message and can be sent.
diff --git a/src/rmr/common/src/rt_generic_static.c b/src/rmr/common/src/rt_generic_static.c
index 2e4b711..dcedb49 100644
--- a/src/rmr/common/src/rt_generic_static.c
+++ b/src/rmr/common/src/rt_generic_static.c
@@ -852,7 +852,7 @@
 		return;
 	}
 
-	if( (snarf_fname = getenv(  ENV_STASH_RT )) == NULL ) {			// specific place to stash the rt not given
+	if( (snarf_fname = getenv(  ENV_STASH_RT )) == NULL ) {				// specific place to stash the rt not given
 		if( (seed_fname = getenv( ENV_SEED_RT )) != NULL ) {			// no seed, we leave in the default file
 			memset( wfname, 0, sizeof( wfname ) );
 			snprintf( wfname, sizeof( wfname ) - 1, "%s.stash", seed_fname );
@@ -861,6 +861,7 @@
 	}
 
 	if( snarf_fname == NULL ) {
+		rmr_vlog( RMR_VL_DEBUG, "cycle_snarf: no file to save in" );
 		return;
 	}
 
@@ -1018,6 +1019,7 @@
 
 						send_rt_ack( pctx, mbuf, ctx->table_id, RMR_OK, NULL );
 						ctx->rtable_ready = 1;							// route based sends can now happen
+						ctx->flags |= CFL_FULLRT;						// indicate we have seen a complete route table
 					} else {
 						if( DEBUG > 1 ) rmr_vlog_force( RMR_VL_DEBUG, "end of route table noticed, but one was not started!\n" );
 						ctx->new_rtable = NULL;
@@ -1079,6 +1081,11 @@
 				break;
 
 			case 'u':												// update current table, not a total replacement
+				if( ! ctx->flags & CFL_FULLRT ) {					// we cannot update until we have a full table from route generator
+					rmr_vlog( RMR_VL_WARN, "route table update ignored: full table not previously recevied" );
+					break;
+				}
+
 				tokens[1] = clip( tokens[1] );
 				if( strcmp( tokens[1], "end" ) == 0 ) {				// wrap up the table we were building
 					if( ctx->new_rtable == NULL ) {					// update table not in progress
@@ -1158,8 +1165,13 @@
 	char*	eor;				// end of the record
 	int		rcount = 0;			// record count for debug
 
-	if( (fname = getenv( ENV_SEED_RT )) == NULL ) {
-		return;
+	if( (fname = ctx->seed_rt_fname) == NULL ) {
+		if( (fname = getenv( ENV_SEED_RT )) == NULL ) {
+			return;
+		}
+
+		ctx->seed_rt_fname = strdup( fname );
+		fname = ctx->seed_rt_fname;
 	}
 
 	if( (fbuf = ensure_nlterm( uta_fib( fname ) ) ) == NULL ) {			// read file into a single buffer (nil terminated string)
diff --git a/src/rmr/common/src/rtc_static.c b/src/rmr/common/src/rtc_static.c
index 96f8330..10bc2a7 100644
--- a/src/rmr/common/src/rtc_static.c
+++ b/src/rmr/common/src/rtc_static.c
@@ -310,6 +310,7 @@
 	ctx->flags |= CFL_NO_RTACK;				// don't ack when reading from a file
 	read_static_rt( ctx, vlevel );			// seed the route table if one provided
 	ctx->flags &= ~CFL_NO_RTACK;
+	ctx->flags &= ~CFL_FULLRT;				// even though rmr-ready goes true, the seed doesn't count as a full RT from route generator
 
 
 	my_port = getenv( ENV_CTL_PORT );				// default port to listen on (likely 4561)
diff --git a/src/rmr/si/include/rmr_si_private.h b/src/rmr/si/include/rmr_si_private.h
index ee71409..414c649 100644
--- a/src/rmr/si/include/rmr_si_private.h
+++ b/src/rmr/si/include/rmr_si_private.h
@@ -122,7 +122,7 @@
 	int	shutdown;				// thread notification if we need to tell them to stop
 	int max_mlen;				// max message length payload+header
 	int	max_plen;				// max payload length
-	int	flags;					// CTXFL_ constants
+	int	flags;					// CFL_ constants
 	int nrtele;					// number of elements in the routing table
 	int send_retries;			// number of retries send_msg() should attempt if eagain/timeout indicated by nng
 	int	trace_data_len;			// number of bytes to allocate in header for trace data
@@ -132,6 +132,7 @@
 	int rtable_ready;			// set to true when rt is received or loaded
 	int snarf_rt_fd;			// the file des where we save the last rt from RM
 	int dcount;					// drop counter when app is slow
+	char*	seed_rt_fname;		// the static/seed route table; name captured at start
 	route_table_t* rtable;		// the active route table
 	route_table_t* old_rtable;	// the previously used rt, sits here to allow for draining
 	route_table_t* new_rtable;	// route table under construction
diff --git a/src/rmr/si/src/rmr_si.c b/src/rmr/si/src/rmr_si.c
index b8bb2a0..8f88cf8 100644
--- a/src/rmr/si/src/rmr_si.c
+++ b/src/rmr/si/src/rmr_si.c
@@ -733,7 +733,7 @@
 
 	if( (tok = getenv( ENV_WARNINGS )) != NULL ) {
 		if( *tok == '1' ) {
-			ctx->flags |= CTXFL_WARN;					// turn on some warnings (not all, just ones that shouldn't impact performance)
+			ctx->flags |= CFL_WARN;					// turn on some warnings (not all, just ones that shouldn't impact performance)
 		}
 	}
 
@@ -884,6 +884,10 @@
 		return;
 	}
 
+	if( ctx->seed_rt_fname != NULL ) {
+		free( ctx->seed_rt_fname );
+	}
+
 	ctx->shutdown = 1;
 
 	SItp_stats( ctx->si_ctx );			// dump some interesting stats
diff --git a/test/rt_static_test.c b/test/rt_static_test.c
index 7a51bca..3bdd312 100644
--- a/test/rt_static_test.c
+++ b/test/rt_static_test.c
@@ -1,3 +1,4 @@
+
 // : vi ts=4 sw=4 noet :
 /*
 ==================================================================================
@@ -500,6 +501,10 @@
 		errors += fail_if_nil( ctx->rtable, "edge case route table didn't generate a pointer into the context" );
 
 		unsetenv( "RMR_SEED_RT" );			// remove for next read try
+		if( ctx && ctx->seed_rt_fname != NULL ) {
+			free( ctx->seed_rt_fname );
+			ctx->seed_rt_fname = NULL;
+		}
 		read_static_rt( ctx, 0 );			// drive for not there coverage
 	}
 
@@ -571,6 +576,10 @@
 	ctx->rtable = NULL;
 	ctx->my_name = strdup( "my_host_name" );		// set up to load a rtable
 	ctx->my_ip = strdup( "192.168.1.30" );
+	if( ctx && ctx->seed_rt_fname != NULL ) {
+		free( ctx->seed_rt_fname );
+		ctx->seed_rt_fname = NULL;
+	}
 	gen_rt( ctx );									// generate a route table with meid entries and hang off ctx
 
 	mbuf = rmr_alloc_msg( ctx, 2048 );               //  buffer to play with
diff --git a/test/test_ctx_support.c b/test/test_ctx_support.c
index d15239a..cd3f14e 100644
--- a/test/test_ctx_support.c
+++ b/test/test_ctx_support.c
@@ -45,6 +45,7 @@
 	}
 
 	memset( ctx, 0, sizeof( *ctx ) );
+	ctx->snarf_rt_fd = -1;
 
 	return ctx;
 }
@@ -70,6 +71,7 @@
 	ctx->si_ctx = malloc( 1024 );
 	ctx->my_name = strdup( "hostname1" );
 	ctx->my_ip = strdup( "123.45.67.89" );
+	ctx->snarf_rt_fd = -1;
 
 	ctx->rtgate = (pthread_mutex_t *) malloc( sizeof( *ctx->rtgate ) );
     if( ctx->rtgate != NULL ) {
diff --git a/test/test_gen_rt.c b/test/test_gen_rt.c
index 79b413f..5748ca1 100644
--- a/test/test_gen_rt.c
+++ b/test/test_gen_rt.c
@@ -45,6 +45,7 @@
 	int		fd;
 	char* 	rt_stuff;		// strings for the route table
 
+ctx->flags |= 0x08;
 	fd = open( "utesting.rt", O_WRONLY | O_CREAT, 0600 );
 	if( fd < 0 ) {
 		fprintf( stderr, "<BUGGERED> unable to open file for testing route table gen\n" );