API and build change and fix summaries. Doc correctsions
and/or changes are not mentioned here; see the commit messages.
-2019 November 14; version 1.11.1
+2019 December 6; version 1.13.0
+ Add ability to route messages based on the MEID in a message combined
+ with the message type/subscription-ID.
+
+
+2019 November 14; version 1.11.1 (Amber)
Fix bug in payload reallocation function; correct length of payload
was not always copied.
2019 November 13; version 1.12.1
New message type constants added to support A1.
-2019 November 4; version 1.11.0
+2019 November 4; version 1.11.0 (Amber)
Version bump to move away from the 1.10.* to distinguish between
release A and the trial.
cmake_minimum_required( VERSION 3.5 )
set( major_version "1" ) # should be automatically populated from git tag later, but until CI process sets a tag we use this
-set( minor_version "12" )
-set( patch_level "1" )
+set( minor_version "13" )
+set( patch_level "0" )
set( install_root "${CMAKE_INSTALL_PREFIX}" )
set( install_inc "include/rmr" )
.dv beg_dlist .bd $1 $2
.dv end_dlist .ed
- .dv ditem .cc 2 .di $1 ^:
- .dv di .cc 2 .di $1 ^:
- .dv li .cc 2 .li
+ .dv ditem .cc 2 .sp .5 .di $1 ^:
+ .dv di .cc 2 .sp .5 .di $1 ^:
+ .dv li .cc 2 .sp .5 .li
.dv item .cc 2 .li
.dv proto_start .sp 1 .cc .5i .st 9 .sf Courier-bold .nf
&h3(The Route Table)
-The library is supplied with a route table which maps message numbers to
+The library must be given a route table which maps message numbers to
endpoint groups such that each time a message of type T is sent, the message
is delivered to one member of each group associated with T.
For example, message type 2 might route to two different groups where
Once understood, the route table generator publishes a table that is ingested
by RMr and used for mapping messages to end points.
+.sp
+The following is a simple route table which causes message types 0 through 9 to
+be routed to specific applications:
+
+&ex_start
+newrt|start
+ mse|0|-1| %meid
+ mse|1|-1|app10:4560,app11:4560
+ mse|2|-1|app12:4560
+ mse|3|-1|app14:4560
+ mse|4|-1|app18:4560
+ mse|5|-1|app01:4560
+ mse|6|-1|app02:4560
+ mse|7|-1|app03:4560
+ mse|8|-1|app04:4560
+ mse|9|-1|app05:4560
+newrt|end
+&ex_end
+&space
+The special endpoint "%meid" indicates that the message type (0 in this case) is
+to be routed to the endpoint which has been listed as the "owner" for the meid
+appearing in the message.
+MEID ownership is communicated to RMR using the same Route Table Manager interface
+and by supplying a "table" such as the one below:
+
+&ex_start
+meid_map | start
+ mme_ar | control1 | meid000 meid001 meid002 meid003 meid004 meid005
+ mme_ar | control2 | meid100 meid101 meid102 meid103
+meid_map | end | 2
+&ex_end
+
+This table indicates that the application (endpoint) &ital(control1) "owns" 6 MEIDs
+and &ital(control2) owns 4.
+When message type 0 is sent, the MEID in the message will be used to select the
+endpoint via this table.
+
+&space
+The MEID table will update the existing owner relationships, and add new ones; it
+is necessary to send only the changes with the add/replace (mme_ar) entries in
+the table.
+When necessary, MEIDs can be deleted by adding an &cw(mme_del) record to the table.
+The following example illustrates how this might look:
+
+&ex_start
+meid_map | start
+ mme_ar | control1 | meid000 meid001 meid002 meid003 meid004 meid005
+ mme_ar | control2 | meid100 meid101 meid102 meid103
+ mme_del| meid200 meid401
+meid_map | end | 1
+&ex_end
+
+&h3(Route Table Syntax)
+The following illustrates the syntax for both the route table.
+
+&space
+&ex_start
+newrt | start
+mse | <message-type>[,<sender-endpoint>] | <sub-id> <roud-robin-grp>[;<round-robin-grp>]...
+newrt | end
+&ex_end
+&space
+A round robin group is one or more endpoints from which one will be selected to receive
+the message.
+When multiple endpoints are given in a group, they must be separated with a comma.
+An endpoint is the IP address and port (e.g. 192.158.4.30:8219) or DNS name and port of the
+application that should receive the message type.
+If multiple round-robin groups are given, they must be separated by a semicolon, and
+
+&h3(MEID Map Syntax)
+The MEID map is similar to the route table.
+Entries are used to add or replace the ownership of one or more MEIDs (mme_ar) or to
+delete one or more MEIDs (mme_del).
+The following is the syntax for the MEID map.
+
+&space
+&ex_start
+meid_map | start
+mme_ar | <owner-endpoint> | <meid> [<meid>...]
+mme_del | <meid> [<meid>...]
+meid_map | end | <count>
+&ex_end
+
+&space
+The <count> on the end record indicates the number of mme_ar and mme_del records
+which were sent; if the count does not match the whole map is refused and dropped.
+The <owner-endpoint> is the endpoint which should receive the message when a message
+is routed based on the MEID it contains.
+A MEID may be "owned" by only one endpoint, and if supplied multiple times, the last
+observed relationship is used.
+Each of the lists of MEIDs are blank separated.
+
+
&h3(Environment)
To enable configuration of the library behaviour outside of direct user application
control, RMr supports a number of environment variables which provide information
The following is a list of the various environment variables, what they control
and the defaults which RMr uses if undefined.
+&space
.** the list of environment vars supported
.im &{lib}/man/env_var_list.im
the repo and committed with changes to the source.
The command 'make all' should be all that is needed to build the
-rtd documentation.
+rtd documentation. Follow that with 'make publish' to actually move
+the .rst files into the docs directory at the root; only the changed
+files are moved.
Rationale
Documentation is just code, and by maintaining the documentation as
used for debugging, testing, or if no route table
generator process is being used to supply the route table.
If not defined, no static table is used and RMr will not
- report *ready* until a table is received.
+ report *ready* until a table is received. The static route
+ table may contain both the route table (between newrt
+ start and end records), and the MEID map (between meid_map
+ start and end records)
RMR_SRC_ID
The Route Table
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The library is supplied with a route table which maps message
+The library must be given a route table which maps message
numbers to endpoint groups such that each time a message of
type T is sent, the message is delivered to one member of
each group associated with T. For example, message type 2
generator publishes a table that is ingested by RMr and used
for mapping messages to end points.
+The following is a simple route table which causes message
+types 0 through 9 to be routed to specific applications:
+
+::
+
+ newrt|start
+ mse|0|-1| %meid
+ mse|1|-1|app10:4560,app11:4560
+ mse|2|-1|app12:4560
+ mse|3|-1|app14:4560
+ mse|4|-1|app18:4560
+ mse|5|-1|app01:4560
+ mse|6|-1|app02:4560
+ mse|7|-1|app03:4560
+ mse|8|-1|app04:4560
+ mse|9|-1|app05:4560
+ newrt|end
+
+
+
+The special endpoint "%meid" indicates that the message type
+(0 in this case) is to be routed to the endpoint which has
+been listed as the "owner" for the meid appearing in the
+message. MEID ownership is communicated to RMR using the same
+Route Table Manager interface and by supplying a "table" such
+as the one below:
+
+::
+
+ meid_map | start
+ mme_ar | control1 | meid000 meid001 meid002 meid003 meid004 meid005
+ mme_ar | control2 | meid100 meid101 meid102 meid103
+ meid_map | end | 2
+
+
+This table indicates that the application (endpoint)
+*control1* "owns" 6 MEIDs and *control2* owns 4. When message
+type 0 is sent, the MEID in the message will be used to
+select the endpoint via this table.
+
+The MEID table will update the existing owner relationships,
+and add new ones; it is necessary to send only the changes
+with the add/replace (mme_ar) entries in the table. When
+necessary, MEIDs can be deleted by adding an mme_del record
+to the table. The following example illustrates how this
+might look:
+
+::
+
+ meid_map | start
+ mme_ar | control1 | meid000 meid001 meid002 meid003 meid004 meid005
+ mme_ar | control2 | meid100 meid101 meid102 meid103
+ mme_del| meid200 meid401
+ meid_map | end | 1
+
+
+
+Route Table Syntax
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The following illustrates the syntax for both the route
+table.
+
+
+::
+
+ newrt | start
+ mse | <message-type>[,<sender-endpoint>] | <sub-id> <roud-robin-grp>[;<round-robin-grp>]...
+ newrt | end
+
+
+
+A round robin group is one or more endpoints from which one
+will be selected to receive the message. When multiple
+endpoints are given in a group, they must be separated with a
+comma. An endpoint is the IP address and port (e.g.
+192.158.4.30:8219) or DNS name and port of the application
+that should receive the message type. If multiple round-robin
+groups are given, they must be separated by a semicolon, and
+
+MEID Map Syntax
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The MEID map is similar to the route table. Entries are used
+to add or replace the ownership of one or more MEIDs (mme_ar)
+or to delete one or more MEIDs (mme_del). The following is
+the syntax for the MEID map.
+
+
+::
+
+ meid_map | start
+ mme_ar | <owner-endpoint> | <meid> [<meid>...]
+ mme_del | <meid> [<meid>...]
+ meid_map | end | <count>
+
+
+
+The <count> on the end record indicates the number of mme_ar
+and mme_del records which were sent; if the count does not
+match the whole map is refused and dropped. The
+<owner-endpoint> is the endpoint which should receive the
+message when a message is routed based on the MEID it
+contains. A MEID may be "owned" by only one endpoint, and if
+supplied multiple times, the last observed relationship is
+used. Each of the lists of MEIDs are blank separated.
+
Environment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if undefined.
+
RMR_ASYNC_CONN
Allows the asynch connection mode to be turned off (by
used for debugging, testing, or if no route table
generator process is being used to supply the route table.
If not defined, no static table is used and RMr will not
- report *ready* until a table is received.
+ report *ready* until a table is received. The static route
+ table may contain both the route table (between newrt
+ start and end records), and the MEID map (between meid_map
+ start and end records)
RMR_SRC_ID
#define QUOTE_DEF(a) QUOTE(a) // allow a #define value to be quoted (e.g. QUOTE(MAJOR_VERSION) )
+#define RT_SIZE 10009 // primary entries in route table (prime) meids hash through this so larger than expected # meids
+ // space deginations in the hash table
+#define RT_MT_SPACE 0 // (integer) message type as the key
+#define RT_NAME_SPACE 1 // enpoint name/address is the key
+#define RT_ME_SPACE 2 // message id is the key
+
#define RMR_MSG_VER 3 // message version this code was designed to handle
// environment variable names we'll suss out
#define ENV_BIND_IF "RMR_BIND_IF" // the interface to bind to for both normal comma and RTG (0.0.0.0 if missing)
#define ENV_RTG_PORT "RMR_RTG_SVC" // the port we'll listen on for rtg connections
#define ENV_SEED_RT "RMR_SEED_RT" // where we expect to find the name of the seed route table
+#define ENV_SEED_MEMAP "RMR_SEED_MEMAP" // where we expect to find the name of the seed route table
#define ENV_RTG_RAW "RMR_RTG_ISRAW" // if > 0 we expect route table gen messages as raw (not sent from an RMr application)
#define ENV_VERBOSE_FILE "RMR_VCTL_FILE" // file where vlevel may be managed for some (non-time critical) functions
#define ENV_NAME_ONLY "RMR_SRC_NAMEONLY" // src in message is name only
uint64_t key; // key used to reinsert this entry into a new symtab
int refs; // number of symtabs which reference the entry
int mtype; // the message type for this list
- int nrrgroups; // number of rr groups to send to
+ int nrrgroups; // number of rr groups to send to (if 0, the meid in a message determines endpoint)
rrgroup_t** rrgroups; // one or more set of endpoints to round robin messages to
} rtable_ent_t;
typedef struct {
void* hash; // hash table.
int updates; // counter of update records received
+ int mupdates; // counter of meid update records received
} route_table_t;
/*
EINVAL id poitner, buf or buf header are bad.
EOVERFLOW if the bytes given would have overrun
+ We have been told that the meid will be a string, so we enforce that even if
+ the user is copying in bytes. We will add a 0 byte at len+1 when len is less
+ than the field size, or as the last byte (doing damage to their string) if
+ the caller didn't play by the rules. If they pass a non-nil terminated set
+ of bytes which are the field length, we'll indicate truncation.
+
*/
extern int rmr_bytes2meid( rmr_mbuf_t* mbuf, unsigned char const* src, int len ) {
uta_mhdr_t* hdr;
if( len > RMR_MAX_MEID ) {
len = RMR_MAX_MEID;
errno = EOVERFLOW;
- }
+ }
hdr = (uta_mhdr_t *) mbuf->header;
memcpy( hdr->meid, src, len );
+ if( len == RMR_MAX_MEID ) {
+ if( *(hdr->meid+len-1) != 0 ) {
+ *(hdr->meid+len-1) = 0;
+ errno = EOVERFLOW;
+ }
+ } else {
+ *(hdr->meid+len) = 0;
+ }
+
return len;
}
int nalloc;
int nused;
void** things;
+ const char** names;
} thing_list_t;
// ---- debugging/testing -------------------------------------------------------------------------
(*counter)++;
}
- fprintf( stderr, "[DBUG] RMR sends: target=%s open=%d\n", ep->name, ep->open );
+ fprintf( stderr, "[DBUG] RMR rt endpoint: target=%s open=%d\n", ep->name, ep->open );
+}
+
+/*
+ Called to count meid entries in the table. The meid points to an 'owning' endpoint
+ so we can list what we find
+*/
+static void meid_stats( void* st, void* entry, char const* name, void* thing, void* vcounter ) {
+ int* counter;
+ endpoint_t* ep;
+
+ if( (ep = (endpoint_t *) thing) == NULL ) {
+ return;
+ }
+
+ if( (counter = (int *) vcounter) != NULL ) {
+ (*counter)++;
+ }
+
+ fprintf( stderr, "[DBUG] RMR meid=%s owner=%s open=%d\n", name, ep->name, ep->open );
}
/*
id = "missing";
}
- fprintf( stderr, "[INFO] RMR sends: ts=%lld src=%s target=%s open=%d succ=%lld fail=%lld (hard=%lld soft=%lld)\n",
- (long long) time( NULL ),
- id,
- ep->name,
- ep->open,
- ep->scounts[EPSC_GOOD],
- ep->scounts[EPSC_FAIL] + ep->scounts[EPSC_TRANS],
- ep->scounts[EPSC_FAIL],
+ fprintf( stderr, "[INFO] RMR sends: ts=%lld src=%s target=%s open=%d succ=%lld fail=%lld (hard=%lld soft=%lld)\n",
+ (long long) time( NULL ),
+ id,
+ ep->name,
+ ep->open,
+ ep->scounts[EPSC_GOOD],
+ ep->scounts[EPSC_FAIL] + ep->scounts[EPSC_TRANS],
+ ep->scounts[EPSC_FAIL],
ep->scounts[EPSC_TRANS] );
}
mtype = rte->key & 0xffff;
sid = (int) (rte->key >> 32);
- fprintf( stderr, "[DBUG] rte: key=%016lx mtype=%4d sid=%4d nrrg=%2d refs=%d\n", rte->key, mtype, sid, rte->nrrgroups, rte->refs );
+ fprintf( stderr, "[DBUG] RMR rte: key=%016lx mtype=%4d sid=%4d nrrg=%2d refs=%d\n", rte->key, mtype, sid, rte->nrrgroups, rte->refs );
}
/*
counter = (int *) malloc( sizeof( int ) );
*counter = 0;
- fprintf( stderr, "[DBUG] rtstats:\n" );
- rmr_sym_foreach_class( rt->hash, 1, ep_stats, counter ); // run endpoints in the active table
- fprintf( stderr, "[DBUG] %d endpoints\n", *counter );
+ fprintf( stderr, "[DBUG] RMR route table stats:\n" );
+ fprintf( stderr, "[DBUG] RMR route table endpoints:\n" );
+ rmr_sym_foreach_class( rt->hash, RT_NAME_SPACE, ep_stats, counter ); // run endpoints (names) in the active table
+ fprintf( stderr, "[DBUG] RMR rtable: %d known endpoints\n", *counter );
+
+ fprintf( stderr, "[DBUG] RMR route table entries:\n" );
+ *counter = 0;
+ rmr_sym_foreach_class( rt->hash, RT_MT_SPACE, rte_stats, counter ); // run message type entries
+ fprintf( stderr, "[DBUG] RMR rtable: %d mt entries in table\n", *counter );
+ fprintf( stderr, "[DBUG] RMR route table meid map:\n" );
*counter = 0;
- rmr_sym_foreach_class( rt->hash, 0, rte_stats, counter ); // run entries
- fprintf( stderr, "[DBUG] %d entries\n", *counter );
+ rmr_sym_foreach_class( rt->hash, RT_ME_SPACE, meid_stats, counter ); // run meid space
+ fprintf( stderr, "[DBUG] RMR rtable: %d meids in map\n", *counter );
free( counter );
}
static char* ensure_nlterm( char* buf ) {
char* nb = NULL;
int len = 1;
-
nb = buf;
if( buf == NULL || (len = strlen( buf )) < 2 ) {
memcpy( nb, buf, len );
*(nb+len) = '\n'; // insert \n and nil into the two extra bytes we allocated
*(nb+len+1) = 0;
- }
+ }
free( buf );
}
rte->refs = 1;
rte->key = key;
- if( nrrgroups <= 0 ) {
+ if( nrrgroups < 0 ) { // zero is allowed as %meid entries have no groups
nrrgroups = 10;
}
- if( (rte->rrgroups = (rrgroup_t **) malloc( sizeof( rrgroup_t * ) * nrrgroups )) == NULL ) {
- fprintf( stderr, "rmr_add_rte: malloc failed for rrgroup array\n" );
- free( rte );
- return NULL;
+ if( nrrgroups ) {
+ if( (rte->rrgroups = (rrgroup_t **) malloc( sizeof( rrgroup_t * ) * nrrgroups )) == NULL ) {
+ fprintf( stderr, "rmr_add_rte: malloc failed for rrgroup array\n" );
+ free( rte );
+ return NULL;
+ }
+ memset( rte->rrgroups, 0, sizeof( rrgroup_t *) * nrrgroups );
+ } else {
+ rte->rrgroups = NULL;
}
- memset( rte->rrgroups, 0, sizeof( rrgroup_t *) * nrrgroups );
+
rte->nrrgroups = nrrgroups;
if( (old_rte = rmr_sym_pull( rt->hash, key )) != NULL ) {
}
/*
- This accepts partially parsed information from a record sent by route manager or read from
+ This accepts partially parsed information from an rte or mse record sent by route manager or read from
a file such that:
ts_field is the msg-type,sender field
subid is the integer subscription id
(uta_has_str( ts_field, ctx->my_name, ',', 127) >= 0) || // our name is in the list
has_myip( ts_field, ctx->ip_list, ',', 127 ) ) { // the list has one of our IP addresses
- key = build_rt_key( subid, atoi( ts_field ) );
-
- if( DEBUG > 1 || (vlevel > 1) ) fprintf( stderr, "[DBUG] create rte for mtype=%s subid=%d key=%lx\n", ts_field, subid, key );
+ key = build_rt_key( subid, atoi( ts_field ) );
- if( (ngtoks = uta_tokenise( rr_field, gtokens, 64, ';' )) > 0 ) { // split round robin groups
- rte = uta_add_rte( ctx->new_rtable, key, ngtoks ); // get/create entry for this key
+ if( DEBUG > 1 || (vlevel > 1) ) fprintf( stderr, "[DBUG] create rte for mtype=%s subid=%d key=%lx\n", ts_field, subid, key );
- for( grp = 0; grp < ngtoks; grp++ ) {
- if( (ntoks = uta_rmip_tokenise( gtokens[grp], ctx->ip_list, tokens, 64, ',' )) > 0 ) { // remove any referneces to our ip addrs
- for( i = 0; i < ntoks; i++ ) {
- if( strcmp( tokens[i], ctx->my_name ) != 0 ) { // don't add if it is us -- cannot send to ourself
- if( DEBUG > 1 || (vlevel > 1)) fprintf( stderr, "[DBUG] add endpoint ts=%s %s\n", ts_field, tokens[i] );
- uta_add_ep( ctx->new_rtable, rte, tokens[i], grp );
- }
+ if( (ngtoks = uta_tokenise( rr_field, gtokens, 64, ';' )) > 0 ) { // split round robin groups
+ if( strcmp( gtokens[0], "%meid" ) == 0 ) {
+ ngtoks = 0; // special indicator that uses meid to find endpoint, no rrobin
+ }
+ rte = uta_add_rte( ctx->new_rtable, key, ngtoks ); // get/create entry for this key
+
+ for( grp = 0; grp < ngtoks; grp++ ) {
+ if( (ntoks = uta_rmip_tokenise( gtokens[grp], ctx->ip_list, tokens, 64, ',' )) > 0 ) { // remove any referneces to our ip addrs
+ for( i = 0; i < ntoks; i++ ) {
+ if( strcmp( tokens[i], ctx->my_name ) != 0 ) { // don't add if it is us -- cannot send to ourself
+ if( DEBUG > 1 || (vlevel > 1)) fprintf( stderr, "[DBUG] add endpoint ts=%s %s\n", ts_field, tokens[i] );
+ uta_add_ep( ctx->new_rtable, rte, tokens[i], grp );
}
}
}
}
- } else {
- if( DEBUG || (vlevel > 2) ) {
- fprintf( stderr, "entry not included, sender not matched: %s\n", tokens[1] );
- }
}
+ } else {
+ if( DEBUG || (vlevel > 2) ) {
+ fprintf( stderr, "entry not included, sender not matched: %s\n", tokens[1] );
+ }
+ }
}
/*
}
}
+/*
+ Given the tokens from an mme_ar (meid add/replace) entry, add the entries.
+ the 'owner' which should be the dns name or IP address of an enpoint
+ the meid_list is a space separated list of me IDs
+
+ This function assumes the caller has vetted the pointers as needed.
+
+ For each meid in the list, an entry is pushed into the hash which references the owner
+ endpoint such that when the meid is used to route a message it references the endpoint
+ to send messages to.
+*/
+static void parse_meid_ar( route_table_t* rtab, char* owner, char* meid_list, int vlevel ) {
+ char* tok;
+ int ntoks;
+ char* tokens[128];
+ int i;
+ int state;
+ endpoint_t* ep; // endpoint struct for the owner
+
+ owner = clip( owner ); // ditch extra whitespace and trailing comments
+ meid_list = clip( meid_list );
+
+ ntoks = uta_tokenise( meid_list, tokens, 128, ' ' );
+ for( i = 0; i < ntoks; i++ ) {
+ if( (ep = rt_ensure_ep( rtab, owner )) != NULL ) {
+ state = rmr_sym_put( rtab->hash, tokens[i], RT_ME_SPACE, ep ); // slam this one in if new; replace if there
+ if( DEBUG || (vlevel > 1) ) fprintf( stderr, "[DBUG] parse_meid_ar: add/replace meid: %s owned by: %s state=%d\n", tokens[i], owner, state );
+fprintf( stderr, "[DBUG] parse_meid_ar: add/replace meid: %s owned by: %s state=%d\n", tokens[i], owner, state );
+ } else {
+ fprintf( stderr, "[WRN] rmr parse_meid_ar: unable to create an endpoint for owner: %s", owner );
+ }
+ }
+}
+
+/*
+ Given the tokens from an mme_del, delete the listed meid entries from the new
+ table. The list is a space separated list of meids.
+
+ The meids in the hash reference endpoints which are never deleted and so
+ the only thing that we need to do here is to remove the meid from the hash.
+
+ This function assumes the caller has vetted the pointers as needed.
+*/
+static void parse_meid_del( route_table_t* rtab, char* meid_list, int vlevel ) {
+ char* tok;
+ int ntoks;
+ char* tokens[128];
+ int i;
+
+ if( rtab->hash == NULL ) {
+ return;
+ }
+
+ meid_list = clip( meid_list );
+
+ ntoks = uta_tokenise( meid_list, tokens, 128, ' ' );
+ for( i = 0; i < ntoks; i++ ) {
+ rmr_sym_del( rtab->hash, tokens[i], RT_ME_SPACE ); // and it only took my little finger to blow it away!
+ if( DEBUG || (vlevel > 1) ) fprintf( stderr, "[DBUG] parse_meid_del: meid deleted: %s\n", tokens[i] );
+ }
+}
+
+/*
+ Parse a partially parsed meid record. Tokens[0] should be one of:
+ meid_map, mme_ar, mme_del.
+*/
+static void meid_parser( uta_ctx_t* ctx, char** tokens, int ntoks, int vlevel ) {
+ if( tokens == NULL || ntoks < 1 ) {
+ return; // silent but should never happen
+ }
+
+ if( ntoks < 2 ) { // must have at least two for any valid request record
+ fprintf( stderr, "[ERR] meid_parse: not enough tokens on %s record\n", tokens[0] );
+ return;
+ }
+
+ if( strcmp( tokens[0], "meid_map" ) == 0 ) { // start or end of the meid map update
+ tokens[1] = clip( tokens[1] );
+ if( *(tokens[1]) == 's' ) {
+ if( ctx->new_rtable != NULL ) { // one in progress? this forces it out
+ if( DEBUG > 1 || (vlevel > 1) ) fprintf( stderr, "[DBUG] meid map start: dropping incomplete table\n" );
+ uta_rt_drop( ctx->new_rtable );
+ }
+
+ ctx->new_rtable = uta_rt_clone_all( ctx->rtable ); // start with a clone of everything (mtype, endpoint refs and meid)
+ ctx->new_rtable->mupdates = 0;
+ if( DEBUG || (vlevel > 1) ) fprintf( stderr, "[DBUG] meid_parse: meid map start found\n" );
+ } else {
+ if( strcmp( tokens[1], "end" ) == 0 ) { // wrap up the table we were building
+ if( ntoks > 2 ) { // meid_map | end | <count> |??? given
+ if( ctx->new_rtable->mupdates != atoi( tokens[2] ) ) { // count they added didn't match what we received
+ fprintf( stderr, "[ERR] meid_parse: meid map update had wrong number of records: received %d expected %s\n", ctx->new_rtable->mupdates, tokens[2] );
+ uta_rt_drop( ctx->new_rtable );
+ ctx->new_rtable = NULL;
+ return;
+ }
+
+ if( DEBUG ) fprintf( stderr, "[DBUG] meid_parse: meid map update ended; found expected number of entries: %s\n", tokens[2] );
+ }
+
+ if( ctx->new_rtable ) {
+ uta_rt_drop( ctx->old_rtable ); // time to drop one that was previously replaced
+ ctx->old_rtable = ctx->rtable; // currently active becomes old and allowed to 'drain'
+ ctx->rtable = ctx->new_rtable; // one we've been adding to becomes active
+ ctx->new_rtable = NULL;
+ if( DEBUG > 1 || (vlevel > 1) ) fprintf( stderr, "[DBUG] end of meid map noticed\n" );
+
+ if( vlevel > 0 ) {
+ fprintf( stderr, "[DBUG] old route table:\n" );
+ rt_stats( ctx->old_rtable );
+ fprintf( stderr, "[DBUG] new route table:\n" );
+ rt_stats( ctx->rtable );
+ }
+ } else {
+ if( DEBUG ) fprintf( stderr, "[DBUG] end of meid map noticed, but one was not started!\n" );
+ ctx->new_rtable = NULL;
+ }
+ }
+ }
+
+ return;
+ }
+
+ if( ! ctx->new_rtable ) { // for any other mmap entries, there must be a table in progress or we punt
+ if( DEBUG ) fprintf( stderr, "[DBUG] meid update/delte (%s) encountered, but table update not started\n", tokens[0] );
+ return;
+ }
+
+ if( strcmp( tokens[0], "mme_ar" ) == 0 ) {
+ if( ntoks < 3 || tokens[1] == NULL || tokens[2] == NULL ) {
+ fprintf( stderr, "[ERR] meid_parse: mme_ar record didn't have enough tokens found %d\n", ntoks );
+ return;
+ }
+ parse_meid_ar( ctx->new_rtable, tokens[1], tokens[2], vlevel );
+ ctx->new_rtable->mupdates++;
+ }
+
+ if( strcmp( tokens[0], "mme_del" ) == 0 ) {
+ if( ntoks < 2 ) {
+ fprintf( stderr, "[ERR] meid_parse: mme_del record didn't have enough tokens\n" );
+ return;
+ }
+ parse_meid_del( ctx->new_rtable, tokens[1], vlevel );
+ ctx->new_rtable->mupdates++;
+ }
+}
+
/*
Parse a single record recevied from the route table generator, or read
from a static route table file. Start records cause a new table to
end record causes the in progress table to be finalised and the
currently active table is replaced.
- We expect one of several types:
- newrt|{start|end}
+ The updated table will be activated when the *|end record is encountered.
+ However, to allow for a "double" update, where both the meid map and the
+ route table must be updated at the same time, the end indication on a
+ route table (new or update) may specifiy "hold" which indicates that meid
+ map entries are to follow and the updated route table should be held as
+ pending until the end of the meid map is received and validated.
+
+ CAUTION: we are assuming that there is a single route/meid map generator
+ and as such only one type of update is received at a time; in other
+ words, the sender cannot mix update records and if there is more than
+ one sender process they must synchronise to avoid issues.
+
+
+ For a RT update, we expect:
+ newrt|{start|end [hold]}
rte|<mtype>[,sender]|<endpoint-grp>[;<endpoint-grp>,...]
mse|<mtype>[,sender]|<sub-id>|<endpoint-grp>[;<endpoint-grp>,...]
+ mse| <mtype>[,sender] | <sub-id> | %meid
+
+
+ For a meid map update we expect:
+ meid_map | start
+ meid_map | end | <count> | <md5-hash>
+ mme_ar | <e2term-id> | <meid0> <meid1>...<meidn>
+ mme_del | <meid0> <meid1>...<meidn>
+
*/
static void parse_rt_rec( uta_ctx_t* ctx, char* buf, int vlevel ) {
int i;
int grp; // group number
rtable_ent_t* rte; // route table entry added
char* tokens[128];
- char* gtokens[64]; // groups
char* tok; // pointer into a token or string
if( ! buf ) {
*(tok+1) = 0;
if( (ntoks = uta_tokenise( buf, tokens, 128, '|' )) > 0 ) {
+ tokens[0] = clip( tokens[0] );
switch( *(tokens[0]) ) {
case 0: // ignore blanks
// fallthrough
uta_rt_drop( ctx->new_rtable );
}
- if( ctx->rtable ) {
- ctx->new_rtable = uta_rt_clone( ctx->rtable ); // create by cloning endpoint entries from active table
- } else {
- ctx->new_rtable = uta_rt_init( ); // don't have one yet, just crate empty
- }
+ ctx->new_rtable = NULL;
+ ctx->new_rtable = uta_rt_clone( ctx->rtable ); // create by cloning endpoint and meidtentries from active table
if( DEBUG > 1 || (vlevel > 1) ) fprintf( stderr, "[DBUG] start of route table noticed\n" );
}
break;
- case 'm': // assume mse entry
- if( ! ctx->new_rtable ) { // bad sequence, or malloc issue earlier; ignore siliently
- break;
- }
+ case 'm': // mse entry or one of the meid_ records
+ if( strcmp( tokens[0], "mse" ) == 0 ) {
+ if( ! ctx->new_rtable ) { // bad sequence, or malloc issue earlier; ignore siliently
+ break;
+ }
- if( ntoks < 4 ) {
- if( DEBUG ) fprintf( stderr, "[WRN] rmr_rtc: mse record had too few fields: %d instead of 4\n", ntoks );
- break;
- }
+ if( ntoks < 4 ) {
+ if( DEBUG ) fprintf( stderr, "[WRN] rmr_rtc: mse record had too few fields: %d instead of 4\n", ntoks );
+ break;
+ }
- build_entry( ctx, tokens[1], atoi( tokens[2] ), tokens[3], vlevel );
- ctx->new_rtable->updates++;
+ build_entry( ctx, tokens[1], atoi( tokens[2] ), tokens[3], vlevel );
+ ctx->new_rtable->updates++;
+ } else {
+ meid_parser( ctx, tokens, ntoks, vlevel );
+ }
break;
case 'r': // assume rt entry
uta_rt_drop( ctx->new_rtable );
}
- if( ctx->rtable ) {
- ctx->new_rtable = uta_rt_clone_all( ctx->rtable ); // start with a clone of everything (endpts and entries)
- } else {
- ctx->new_rtable = uta_rt_init( ); // don't have one yet, just crate empty
- }
-
+ ctx->new_rtable = uta_rt_clone_all( ctx->rtable ); // start with a clone of everything (endpts and entries)
ctx->new_rtable->updates = 0; // init count of updates received
if( DEBUG > 1 || (vlevel > 1) ) fprintf( stderr, "[DBUG] start of rt update noticed\n" );
}
parse_rt_rec( ctx, rec, vlevel );
}
- if( DEBUG ) fprintf( stderr, "[DBUG] rmr: seed route table successfully parsed: %d records\n", rcount );
+ if( DEBUG ) fprintf( stderr, "[DBUG] rmr_read_static: seed route table successfully parsed: %d records\n", rcount );
free( fbuf );
}
return;
}
+ tl->names[tl->nused] = name; // the name/key
tl->things[tl->nused++] = thing; // save a reference to the thing
}
return NULL;
}
- if( (rt->hash = rmr_sym_alloc( 509 )) == NULL ) { // modest size, prime
+ if( (rt->hash = rmr_sym_alloc( RT_SIZE )) == NULL ) {
free( rt );
return NULL;
}
}
/*
- Clone (sort of) an existing route table. This is done to preserve the endpoint
- names referenced in a table (and thus existing sessions) when a new set
- of message type to endpoint name mappings is received. A new route table
- with only endpoint name references is returned based on the active table in
- the context.
+ Clones one of the spaces in the given table.
+ Srt is the source route table, Nrt is the new route table; if nil, we allocate it.
+ Space is the space in the old table to copy. Space 0 uses an integer key and
+ references rte structs. All other spaces use a string key and reference endpoints.
*/
-static route_table_t* uta_rt_clone( route_table_t* srt ) {
+static route_table_t* rt_clone_space( route_table_t* srt, route_table_t* nrt, int space ) {
endpoint_t* ep; // an endpoint
- route_table_t* nrt; // new route table
+ rtable_ent_t* rte; // a route table entry
void* sst; // source symtab
void* nst; // new symtab
- thing_list_t things;
- int i;
-
- if( srt == NULL ) {
- return NULL;
- }
+ thing_list_t things; // things from the space to copy
+ int i;
+ int free_on_err = 0;
- if( (nrt = (route_table_t *) malloc( sizeof( *nrt ) )) == NULL ) {
- return NULL;
+ if( nrt == NULL ) { // make a new table if needed
+ free_on_err = 1;
+ nrt = uta_rt_init();
}
- if( (nrt->hash = rmr_sym_alloc( 509 )) == NULL ) { // modest size, prime
- free( nrt );
- return NULL;
+ if( srt == NULL ) { // source was nil, just give back the new table
+ return nrt;
}
things.nalloc = 2048;
things.nused = 0;
things.things = (void **) malloc( sizeof( void * ) * things.nalloc );
+ things.names = (const char **) malloc( sizeof( char * ) * things.nalloc );
if( things.things == NULL ) {
- free( nrt->hash );
- free( nrt );
- return NULL;
+ if( free_on_err ) {
+ free( nrt->hash );
+ free( nrt );
+ nrt = NULL;
+ }
+
+ return nrt;
}
sst = srt->hash; // convenience pointers (src symtab)
nst = nrt->hash;
- rmr_sym_foreach_class( sst, 1, collect_things, &things ); // collect the named endpoints in the active table
+ rmr_sym_foreach_class( sst, space, collect_things, &things ); // collect things from this space
+ if( DEBUG ) fprintf( stderr, "[DBUG] clone space cloned %d things in space %d\n", things.nused, space );
for( i = 0; i < things.nused; i++ ) {
- ep = (endpoint_t *) things.things[i];
- rmr_sym_put( nst, ep->name, 1, ep ); // slam this one into the new table
+ if( space ) { // string key, epoint reference
+ ep = (endpoint_t *) things.things[i];
+ rmr_sym_put( nst, things.names[i], space, ep ); // slam this one into the new table
+ } else {
+ rte = (rtable_ent_t *) things.things[i];
+ rte->refs++; // rtes can be removed, so we track references
+ rmr_sym_map( nst, rte->key, rte ); // add to hash using numeric mtype/sub-id as key (default to space 0)
+ }
}
free( things.things );
+ free( (void *) things.names );
return nrt;
}
/*
- Clones _all_ of the given route table (references both endpoints AND the route table
- entries. Needed to support a partial update where some route table entries will not
- be deleted if not explicitly in the update.
+ Creates a new route table and then clones the parts of the table which we must keep with each newrt|start.
+ The endpoint and meid entries in the hash must be preserved.
*/
-static route_table_t* uta_rt_clone_all( route_table_t* srt ) {
- endpoint_t* ep; // an endpoint
- rtable_ent_t* rte; // a route table entry
- route_table_t* nrt; // new route table
- void* sst; // source symtab
- void* nst; // new symtab
- thing_list_t things0; // things from space 0 (table entries)
- thing_list_t things1; // things from space 1 (end points)
+static route_table_t* uta_rt_clone( route_table_t* srt ) {
+ endpoint_t* ep; // an endpoint
+ rtable_ent_t* rte; // a route table entry
+ route_table_t* nrt = NULL; // new route table
int i;
if( srt == NULL ) {
- return NULL;
- }
-
- if( (nrt = (route_table_t *) malloc( sizeof( *nrt ) )) == NULL ) {
- return NULL;
- }
-
- if( (nrt->hash = rmr_sym_alloc( 509 )) == NULL ) { // modest size, prime
- free( nrt );
- return NULL;
+ return uta_rt_init(); // no source to clone, just return an empty table
}
- things0.nalloc = 2048;
- things0.nused = 0;
- things0.things = (void **) malloc( sizeof( void * ) * things0.nalloc );
- if( things0.things == NULL ) {
- free( nrt->hash );
- free( nrt );
- return NULL;
- }
-
- things1.nalloc = 2048;
- things1.nused = 0;
- things1.things = (void **) malloc( sizeof( void * ) * things1.nalloc );
- if( things1.things == NULL ) {
- free( nrt->hash );
- free( nrt );
- return NULL;
- }
+ nrt = rt_clone_space( srt, nrt, RT_NAME_SPACE ); // allocate a new one, add endpoint refs
+ rt_clone_space( srt, nrt, RT_ME_SPACE ); // add meid refs to new
- sst = srt->hash; // convenience pointers (src symtab)
- nst = nrt->hash;
+ return nrt;
+}
- rmr_sym_foreach_class( sst, 0, collect_things, &things0 ); // collect the rtes
- rmr_sym_foreach_class( sst, 1, collect_things, &things1 ); // collect the named endpoints in the active table
+/*
+ Creates a new route table and then clones _all_ of the given route table (references
+ both endpoints AND the route table entries. Needed to support a partial update where
+ some route table entries will not be deleted if not explicitly in the update and when
+ we are adding/replacing meid references.
+*/
+static route_table_t* uta_rt_clone_all( route_table_t* srt ) {
+ endpoint_t* ep; // an endpoint
+ rtable_ent_t* rte; // a route table entry
+ route_table_t* nrt = NULL; // new route table
+ int i;
- for( i = 0; i < things0.nused; i++ ) {
- rte = (rtable_ent_t *) things0.things[i];
- rte->refs++; // rtes can be removed, so we track references
- rmr_sym_map( nst, rte->key, rte ); // add to hash using numeric mtype/sub-id as key (default to space 0)
+ if( srt == NULL ) {
+ return uta_rt_init(); // no source to clone, just return an empty table
}
- for( i = 0; i < things1.nused; i++ ) {
- ep = (endpoint_t *) things1.things[i];
- rmr_sym_put( nst, ep->name, 1, ep ); // slam this one into the new table
- }
+ nrt = rt_clone_space( srt, nrt, RT_MT_SPACE ); // create new, clone all spaces to it
+ rt_clone_space( srt, nrt, RT_NAME_SPACE );
+ rt_clone_space( srt, nrt, RT_ME_SPACE );
- free( things0.things );
- free( things1.things );
return nrt;
}
#include <sys/stat.h>
#include <unistd.h>
+static int refresh_vlevel( int vfd ) {
+ int vlevel = 0;
+ char rbuf[128];
+
+ if( vfd >= 0 ) { // if file is open, read current value
+ rbuf[0] = 0;
+ lseek( vfd, 0, 0 );
+ read( vfd, rbuf, 10 );
+ vlevel = atoi( rbuf );
+ }
+
+ return vlevel;
+}
+
/*
Route Table Collector
A side thread which opens a socket and subscribes to a routing table generator.
Buffers received from the route table generator can contain multiple newline terminated
records, but each buffer must be less than 4K in length, and the last record in a
- buffere may NOT be split across buffers.
+ buffer may NOT be split across buffers.
Other chores:
In addition to the primary task of getting, vetting, and installing a new route table, or
if( (eptr = getenv( ENV_VERBOSE_FILE )) != NULL ) {
vfd = open( eptr, O_RDONLY );
- if( vfd >= 0 ) {
- wbuf[0] = 0;
- lseek( vfd, 0, 0 );
- read( vfd, wbuf, 10 );
- vlevel = atoi( wbuf );
- }
+ vlevel = refresh_vlevel( vfd );
}
read_static_rt( ctx, vlevel ); // seed the route table if one provided
}
if( time( NULL ) > blabber ) {
- blabber = time( NULL ) + count_delay; // set next time to blabber, then do so
- if( blabber > bump_freq ) {
- count_delay = 300;
+ vlevel = refresh_vlevel( vfd );
+ if( vlevel >= 0 ) { // allow it to be forced off with -n in verbose file
+ blabber = time( NULL ) + count_delay; // set next time to blabber, then do so
+ if( blabber > bump_freq ) {
+ count_delay = 300;
+ }
+ rt_epcounts( ctx->rtable, ctx->my_name );
}
- rt_epcounts( ctx->rtable, ctx->my_name );
}
}
- if( vfd >= 0 ) { // if file open, check for change to vlevel
- wbuf[0] = 0;
- lseek( vfd, 0, 0 );
- read( vfd, wbuf, 10 );
- vlevel = atoi( wbuf );
- }
+ vlevel = refresh_vlevel( vfd ); // ensure it's fresh when we get a message
if( msg != NULL && msg->len > 0 ) {
payload = msg->payload;
return rte;
}
+/*
+ Given a route table and meid string, find the owner (if known). Returns a pointer to
+ the endpoint struct or nil.
+*/
+static inline endpoint_t* get_meid_owner( route_table_t *rt, char* meid ) {
+ endpoint_t* ep; // the ep we found in the hash
+
+ if( rt == NULL || rt->hash == NULL || meid == NULL || *meid == 0 ) {
+ return NULL;
+ }
+
+ return (endpoint_t *) rmr_sym_get( rt->hash, meid, RT_ME_SPACE );
+}
+
/*
Return a string of count information. E.g.:
<ep-name>:<port> <good> <hard-fail> <soft-fail>
return rs;
}
+/*
+ Given a message, use the meid field to find the owner endpoint for the meid.
+ The owner ep is then used to extract the socket through which the message
+ is sent. This returns TRUE if we found a socket and it was written to the
+ nn_sock pointer; false if we didn't.
+
+ We've been told that the meid is a string, thus we count on it being a nil
+ terminated set of bytes.
+*/
+static int epsock_meid( route_table_t *rtable, rmr_mbuf_t* msg, nng_socket* nn_sock, endpoint_t** uepp ) {
+ endpoint_t* ep; // seected end point
+ int state = FALSE; // processing state
+ char* meid;
+
+
+ errno = 0;
+ if( ! nn_sock || msg == NULL || rtable == NULL ) { // missing stuff; bail fast
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ meid = ((uta_mhdr_t *) msg->header)->meid;
+
+ if( (ep = get_meid_owner( rtable, meid )) == NULL ) {
+ if( uepp != NULL ) { // caller needs refernce to endpoint too
+ *uepp = NULL;
+ }
+
+ if( DEBUG ) fprintf( stderr, "[DBUG] epsock_meid: no ep in hash for (%s)\n", meid );
+ return FALSE;
+ }
+
+ state = TRUE;
+ if( ! ep->open ) { // not connected
+ if( ep->addr == NULL ) { // name didn't resolve before, try again
+ ep->addr = strdup( ep->name ); // use the name directly; if not IP then transport will do dns lookup
+ }
+
+ if( uta_link2( ep ) ) { // find entry in table and create link
+ ep->open = TRUE;
+ *nn_sock = ep->nn_sock; // pass socket back to caller
+ } else {
+ state = FALSE;
+ }
+ if( DEBUG ) fprintf( stderr, "[DBUG] epsock_meid: connection attempted with %s: %s\n", ep->name, state ? "[OK]" : "[FAIL]" );
+ } else {
+ *nn_sock = ep->nn_sock;
+ }
+
+ return state;
+}
+
#endif
send_again = 1; // force loop entry
group = 0; // always start with group 0
while( send_again ) {
- sock_ok = uta_epsock_rr( rte, group, &send_again, &nn_sock, &ep ); // select endpt from rr group and set again if more groups
+ if( rte->nrrgroups > 0 ) { // this is a round robin entry
+ sock_ok = uta_epsock_rr( rte, group, &send_again, &nn_sock, &ep ); // select endpt from rr group and set again if more groups
+ } else {
+ sock_ok = epsock_meid( ctx->rtable, msg, &nn_sock, &ep );
+ send_again = 0;
+ }
if( DEBUG ) fprintf( stderr, "[DBUG] mtosend_msg: flgs=0x%04x type=%d again=%d group=%d len=%d sock_ok=%d\n",
msg->flags, msg->mtype, send_again, group, msg->len, sock_ok );
errors += fail_if( i > 0, "(rv) attempt to copy bytes to meid with nil message" );
errno = 0;
+ strncpy( src_buf, "a very long string that should trigger an error when stuffing it into meid", sizeof( src_buf ) ); // ensure no zero byte at field length
i = rmr_bytes2meid( mbuf, src_buf, RMR_MAX_MEID + 1 );
errors += fail_if( errno == 0, "(errno) attempt to copy bytes to meid with large source buffer" );
errors += fail_if( i != RMR_MAX_MEID, "(rv) attempt to copy bytes to meid with large source buffer" );
+
+ errno = 0;
+ i = rmr_bytes2meid( mbuf, src_buf, RMR_MAX_MEID ); // it's not 0 terminated at length expect failure
+ errors += fail_if( errno == 0, "(errno) attempt to copy non-zero termianted bytes to meid with large source buffer" );
+ errors += fail_if( i != RMR_MAX_MEID, "(rv) attempt to copy bytes to meid with large source buffer" );
errno = 0;
+ memset( src_buf, 0, RMR_MAX_MEID+2 );
+ strcpy( src_buf, "smaller bytes" );
i = rmr_bytes2meid( mbuf, src_buf, RMR_MAX_MEID );
errors += fail_if( errno != 0, "copy bytes to meid; expected errno to be ok" );
errors += fail_if( i != RMR_MAX_MEID, "copy bytes to meid; expected return value to be max meid len" );
// specific test tools in this directory
#include "test_support.c" // things like fail_if()
+#include "test_gen_rt.c"
// and finally....
#include "tools_static_test.c" // local test functions pulled directly because of static nature of things
#include "symtab_static_test.c"
char *buf;
char* seed_fname; // seed file
nng_socket nn_sock; // this is a struct in nng, so difficult to validate
+ rmr_mbuf_t* mbuf; // message for meid route testing
setenv( "ENV_VERBOSE_FILE", ".ut_rmr_verbose", 1 ); // allow for verbose code in rtc to be driven
i = open( ".ut_rmr_verbose", O_RDWR | O_CREAT, 0644 );
state = uta_epsock_rr( rte, 22, NULL, NULL, &ep );
errors += fail_if_true( state, "uta_epsock_rr returned bad (non-zero) state when given nil socket pointer" );
+
uta_rt_clone( NULL ); // verify null parms don't crash things
uta_rt_drop( NULL );
uta_epsock_rr( NULL, 0, &more, &nn_sock, &ep ); // drive null case for coverage
state = uta_link2( ep );
errors += fail_if_true( state, "link2 did not return false when given nil pointers" );
+ // ----------------- test the meid support for looking up an endpoint based on the meid in the message -----
+
+ ctx->my_name = strdup( "my_host_name" ); // set up to load a rtable
+ ctx->my_ip = strdup( "192.168.1.30" );
+ gen_rt( ctx ); // generate a route table with meid entries and hang off ctx
+
+ mbuf = rmr_alloc_msg( ctx, 2048 ); // buffer to play with
+ mbuf->len = 100;
+ rmr_str2meid( mbuf, "meid1" ); // id that we know is in the map
+
+ state = epsock_meid( ctx->rtable, mbuf, &nn_sock, &ep );
+ errors += fail_if_nil( ep, "ep was nil when looking up ep with known meid in message" );
+ errors += fail_not_equal( state, 1, "state was not true when looking up ep with known meid in message" );
+
+ rmr_str2meid( mbuf, "XXXmeid1" ); // id that we know is NOT in the map
+ state = epsock_meid( ctx->rtable, mbuf, &nn_sock, &ep );
+ errors += fail_not_nil( ep, "ep was NOT nil when looking ep up with unknown meid in message" );
+ errors += fail_not_equal( state, 0, "state was not false when looking up ep with unknown meid in message" );
return !!errors; // 1 or 0 regardless of count
}
#define COPY 1
#define NO_COPY 0
-/*
- Generate a simple route table (for all but direct route table testing).
- This gets tricky inasmuch as we generate two in one; first a whole table
- and then two update tables. The first is a table with a bad counter in the
- last record to test that we don't load that table and error. The second
- is a good update.
-*/
-static void gen_rt( uta_ctx_t* ctx ) {
- int fd;
- char* rt_stuff; // strings for the route table
-
- rt_stuff =
- "newrt|end\n" // end of table check before start of table found
- "# comment to drive full comment test\n"
- "\n" // handle blank lines
- " \n" // handle blank lines
- "mse|4|10|localhost:4561\n" // entry before start message
- "rte|4|localhost:4561\n" // entry before start message
- "newrt|start\n" // false start to drive detection
- "xxx|badentry to drive default case"
- "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\n";
-
- 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 ) ); // write in the whole table
-
- rt_stuff =
- "updatert|start\n" // this is an update to the table
- "mse|4|99|fooapp:9999,barapp:9999;logger:9999\n" // update just one entry
- "updatert|end | 3\n"; // bad count; this update should be rejected
- write( fd, rt_stuff, strlen( rt_stuff ) );
-
- rt_stuff =
- "updatert|start\n" // this is an update to the table
- "mse|4|10|fooapp:4561,barapp:4561;logger:9999\n" // update just one entry
- "del|2|-1\n" // delete an entry; not there so no action
- "del|2|10\n" // delete an entry
- "updatert|end | 3\n"; // end table; updates have a count as last field
- write( fd, rt_stuff, strlen( rt_stuff ) );
-
- close( fd );
- read_static_rt( ctx, 1 ); // force in verbose mode to see stats on tty if failure
- unlink( "utesting.rt" );
-}
-
-
/*
Drive the send and receive functions. We also drive as much of the route
table collector as is possible without a real rtg process running somewhere.
module as it tests the user facing send/receive/call/rts functions. These tests
should exercise specific cases for the internal functions as they will not
specifically be driven elsewhere.
+
+ This requires the gen_rt funcition that is in the test_gen_rt module and should
+ have been included by the test module(s) which include this.
*/
static int sr_nng_test() {
uta_ctx_t* ctx; // context needed to test load static rt
--- /dev/null
+// : vi ts=4 sw=4 noet :
+/*
+==================================================================================
+ Copyright (c) 2019 Nokia
+ Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================================
+*/
+
+/*
+ Mnemonic: test_gen_rt.c
+ Abstract: This provides the means to generate a route table to disk.
+ Author: E. Scott Daniels
+ Date: 6 January 2019
+*/
+
+#ifndef _test_gen_rt_c
+#define _test_gen_rt_c
+
+
+/*
+ Generate a simple route table (for all but direct route table testing).
+ This gets tricky inasmuch as we generate two in one; first a whole table
+ and then two update tables. The first is a table with a bad counter in the
+ last record to test that we don't load that table and error. The second
+ is a good update. The same applies to the meid map; first has a bad counter
+ and some bad records to drive coverage testing. The end should leave a good
+ meid map in the table.
+*/
+static void gen_rt( uta_ctx_t* ctx ) {
+ int fd;
+ char* rt_stuff; // strings for the route table
+
+ 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;
+ }
+
+ rt_stuff =
+ "newrt|end\n" // end of table check before start of table found
+ "# comment to drive full comment test\n"
+ "\n" // handle blank lines
+ " \n" // handle blank lines
+ "mse|4|10|localhost:4561\n" // entry before start message
+ "rte|4|localhost:4561\n" // entry before start message
+ "newrt|start\n" // false start to drive detection
+ "xxx|badentry to drive default case"
+ "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\n";
+
+ setenv( "RMR_SEED_RT", "utesting.rt", 1 );
+ write( fd, rt_stuff, strlen( rt_stuff ) ); // write in the whole table
+
+ rt_stuff = // add an meid map which will fail
+ "meid_map | start\n"
+ "mme_ar | e2t-1 | one two three four\n"
+ "mme_del | one two\n"
+ "mme_del \n" // short entries drive various checks for coverage
+ "mme_ar \n"
+ "mme_ar | e2t-0 \n"
+ "meid_map | end | 5\n"; // this will fail as the short recs don't "count"
+ write( fd, rt_stuff, strlen( rt_stuff ) );
+
+ rt_stuff =
+ "updatert|start\n" // this is an update to the table
+ "mse|4|99|fooapp:9999,barapp:9999;logger:9999\n" // update just one entry
+ "updatert|end | 3\n"; // bad count; this update should be rejected
+ write( fd, rt_stuff, strlen( rt_stuff ) );
+
+
+ rt_stuff =
+ "updatert|start\n" // this is an update to the table
+ "mse|4|10|fooapp:4561,barapp:4561;logger:9999\n" // update just one entry
+ "mse | 99 | -1 | %meid\n" // type 99 will route based on meid and not mtype
+ "del|2|-1\n" // delete an entry; not there so no action
+ "del|2|10\n" // delete an entry
+ "updatert|end | 4\n"; // end table; updates have a count as last field
+ write( fd, rt_stuff, strlen( rt_stuff ) );
+
+ rt_stuff = // this leaves an meid map in place too
+ "meid_map | start\n"
+ "mme_ar | localhost:4567 | meid1 meid2 meid3 meid4\n"
+ "mme_ar | localhost:4067 | meid11 meid12\n"
+ "meid_map | end | 2\n";
+ write( fd, rt_stuff, strlen( rt_stuff ) );
+
+ rt_stuff = // verify that we can del entries in the current table
+ "meid_map | start\n"
+ "mme_del | meid11 meid12 meid13\n" // includes a non-existant meid
+ "meid_map | end | 1\n";
+ write( fd, rt_stuff, strlen( rt_stuff ) );
+
+ close( fd );
+ read_static_rt( ctx, 1 ); // force in verbose mode to see stats on tty if failure
+ unlink( "utesting.rt" );
+}
+
+
+
+/*
+ Generate a custom route table file using the buffer passed in.
+*/
+static void gen_custom_rt( uta_ctx_t* ctx, char* buf ) {
+ int fd;
+ char* rt_stuff; // strings for the route table
+
+ 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( buf ) );
+
+ close( fd );
+ read_static_rt( ctx, 1 ); // force in verbose mode to see stats on tty if failure
+ unlink( "utesting.rt" );
+}
+
+
+#endif
if ! ./${tfile%.c} >/tmp/PID$$.log 2>&1
then
echo "[FAIL] unit test failed for: $tfile"
- if [[ -n capture_file ]]
+ if [[ -n $capture_file ]]
then
echo "all errors captured in $capture_file, listing only fail message on tty"
echo "$tfile --------------------------------------" >>$capture_file