enhance(rtc): Tolerate missing newline in rtable 72/272/1
authorE. Scott Daniels <daniels@research.att.com>
Thu, 6 Jun 2019 16:21:24 +0000 (16:21 +0000)
committerE. Scott Daniels <daniels@research.att.com>
Thu, 6 Jun 2019 16:21:24 +0000 (16:21 +0000)
If the final record in a static route table does not have
a final newline (\n) this will be tolerated and the table
will be parsed as if the newline was present.  On some
systems it is not possible for the user to control when a
final newline is added (or not).

Signed-off-by: E. Scott Daniels <daniels@research.att.com>
Change-Id: I5627bd2a0b59a628a20757428df7960149c8229b

src/rmr/common/src/rt_generic_static.c
test/sr_nano_static_test.c

index 253e7ab..9b01100 100644 (file)
@@ -153,6 +153,38 @@ static char* clip( char* buf ) {
        return buf;
 }
 
+/*
+       This accepts a pointer to a nil terminated string, and ensures that there is a
+       newline as the last character. If there is not, a new buffer is allocated and
+       the newline is added.  If a new buffer is allocated, the buffer passed in is
+       freed.  The function returns a pointer which the caller should use, and must
+       free.  In the event of an error, a nil pointer is returned.
+*/
+static char* ensure_nlterm( char* buf ) {
+       char*   nb = NULL;
+       int             len = 1;
+       
+
+       nb = buf;
+       if( buf == NULL || (len = strlen( buf )) < 2 ) {
+               if( (nb = (char *) malloc( sizeof( char ) * 2 )) != NULL ) {
+                       *nb = '\n';
+                       *(nb+1) = 0;
+               }
+       } else {
+               if( buf[len-1] != '\n' ) {
+                       fprintf( stderr, "[WARN] rmr buf_check: input buffer was not newline terminated (file missing final \\n?)\n" );
+                       if( (nb = (char *) malloc( sizeof( char ) * (len + 2) )) != NULL ) {
+                               memcpy( nb, buf, len );
+                               *(nb+len) = '\n';                       // insert \n and nil into the two extra bytes we allocated
+                               *(nb+len+1) = 0;
+                               free( buf );
+                       }       
+               }
+       }
+
+       return nb;
+}
 
 /*
        Given a message type create a route table entry and add to the hash keyed on the
@@ -489,7 +521,7 @@ static void read_static_rt( uta_ctx_t* ctx, int vlevel ) {
                return;
        }
 
-       if( (fbuf = uta_fib( fname ) ) == NULL ) {                      // read file into a single buffer (nil terminated string)
+       if( (fbuf = ensure_nlterm( uta_fib( fname ) ) ) == NULL ) {                     // read file into a single buffer (nil terminated string)
                fprintf( stderr, "[WRN] rmr read_static: seed route table could not be opened: %s: %s\n", fname, strerror( errno ) );
                return;
        }
@@ -507,7 +539,7 @@ static void read_static_rt( uta_ctx_t* ctx, int vlevel ) {
                        *eor = 0;
                } else {
                        fprintf( stderr, "[WARN] rmr read_static: seed route table had malformed records (missing newline): %s\n", fname );
-                       fprintf( stderr, "[WARN] rmr read_static: seed route table not used%s\n", fname );
+                       fprintf( stderr, "[WARN] rmr read_static: seed route table not used%s\n", fname );
                        free( fbuf );
                        return;
                }
index ef6e496..efeba23 100644 (file)
@@ -82,6 +82,75 @@ static void gen_rt( uta_ctx_t* ctx ) {
        unlink( "utesting.rt" );
 }
 
+/*
+       Generates a legitimate table but with a missing newline on the last record.
+*/
+static void gen_mlnl_rt( uta_ctx_t* ctx ) {
+       int             fd;
+       char*   rt_stuff;               // strings for the route table
+
+       rt_stuff =
+               "newrt|start\n"
+           "rte|0|localhost:4560,localhost:4562\n"                                     // these are legitimate entries for our testing
+           "rte|1|localhost:4562;localhost:4561,localhost:4569\n"
+           "rte|2|localhost:4562| 10\n"                                                                // new subid at end
+           "mse|4|10|localhost:4561\n"                                                                 // new msg/subid specifier rec
+           "mse|4|localhost:4561\n"                                                                    // new mse entry with less than needed fields
+               "   rte|   5   |localhost:4563    #garbage comment\n"           // tests white space cleanup
+           "rte|6|localhost:4562\n"
+               "newrt|end";                                                                                            // should not affect the loader
+
+       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" );
+               return;
+       }
+
+       setenv( "RMR_SEED_RT", "utesting.rt", 1 );
+       write( fd, rt_stuff, strlen( rt_stuff ) );
+       close( fd );
+       read_static_rt( ctx, 0 );
+       unlink( "utesting.rt" );
+}
+
+/*
+       Generate an empty route table to test edge case.
+*/
+static void gen_empty_rt( uta_ctx_t* ctx ) {
+       int             fd;
+
+       fd = open( "utesting.rt", O_WRONLY | O_CREAT | O_TRUNC, 0600 );
+       if( fd < 0 ) {
+               fprintf( stderr, "<BUGGERED> unable to open file for testing route table gen\n" );
+               return;
+       }
+
+       setenv( "RMR_SEED_RT", "utesting.rt", 1 );
+       //write( fd, "", 0 );
+       close( fd );
+       read_static_rt( ctx, 0 );
+       unlink( "utesting.rt" );
+}
+
+/*
+       Generate an single byte route table to drive an edge handling case.
+*/
+static void gen_sb_rt( uta_ctx_t* ctx ) {
+       int             fd;
+
+       fd = open( "utesting.rt", O_WRONLY | O_CREAT | O_TRUNC, 0600 );
+       if( fd < 0 ) {
+               fprintf( stderr, "<BUGGERED> unable to open file for testing route table gen\n" );
+               return;
+       }
+
+       setenv( "RMR_SEED_RT", "utesting.rt", 1 );
+       write( fd, " ", 1 );
+       close( fd );
+       read_static_rt( ctx, 0 );
+       unlink( "utesting.rt" );
+}
+
 
 /*
        Drive the send and receive functions.  We also drive as much of the route
@@ -118,8 +187,23 @@ static int sr_nano_test() {
        ctx->my_ip = strdup( "30.4.19.86:1111" );
        uta_lookup_rtg( ctx );
 
+       ctx->rtable = NULL;
+       gen_sb_rt( ctx );                                                       // generate and read a file with a sinle byte to test edge case
+       errors += fail_not_nil( ctx->rtable, "read single byte route table produced a table" );
+
+       ctx->rtable = NULL;
+       gen_empty_rt( ctx );                                            // generate and read an empty rt file to test edge case
+       errors += fail_not_nil( ctx->rtable, "read empty route table file produced a table" );
+
+       ctx->rtable = NULL;
+       gen_mlnl_rt( ctx );                                             // ensure that a file with missing last new line does not trip us up
+       errors += fail_if_nil( ctx->rtable, "read  route table file with missing last newline did not produce a table" );
+       
+       ctx->rtable = NULL;
        gen_rt( ctx );                                                          // forces a static load with some known info since we don't start the rtc()
+       errors += fail_if_nil( ctx->rtable, "read  multi test route table file did not produce a table" );
        gen_rt( ctx );                                                          // force a second load to test cloning
+       errors += fail_if_nil( ctx->rtable, "read  multi test route table file to test clone did not produce a table" );
 
        p = rt_ensure_ep( NULL, "foo" );                                // drive for coverage
        errors += fail_not_nil( p,  "rt_ensure_ep did not return nil when given nil route table" );