Correct table clear bug in route table ready
[ric-plt/lib/rmr.git] / src / rmr / common / src / rt_generic_static.c
1 // :vi sw=4 ts=4 noet:
2 /*
3 ==================================================================================
4         Copyright (c) 2019-2020 Nokia
5         Copyright (c) 2018-2020 AT&T Intellectual Property.
6
7    Licensed under the Apache License, Version 2.0 (the "License") ;
8    you may not use this file except in compliance with the License.
9    You may obtain a copy of the License at
10
11            http://www.apache.org/licenses/LICENSE-2.0
12
13    Unless required by applicable law or agreed to in writing, software
14    distributed under the License is distributed on an "AS IS" BASIS,
15    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    See the License for the specific language governing permissions and
17    limitations under the License.
18 ==================================================================================
19 */
20
21 /*
22         Mnemonic:       rt_generic_static.c
23         Abstract:       These are route table functions which are not specific to the
24                                 underlying protocol.  rtable_static, and rtable_nng_static
25                                 have transport provider specific code.
26
27                                 This file must be included before the nng/nano specific file as
28                                 it defines types.
29
30         Author:         E. Scott Daniels
31         Date:           5  February 2019
32 */
33
34 #ifndef rt_generic_static_c
35 #define rt_generic_static_c
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <netdb.h>
40 #include <errno.h>
41 #include <string.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <unistd.h>
47 #include <netdb.h>
48 #include <pthread.h>
49
50 #include <RIC_message_types.h>          // needed for route manager messages
51
52 #define ALL 1
53 #define SOME 0
54
55 /*
56         Passed to a symtab foreach callback to construct a list of pointers from
57         a current symtab.
58 */
59 typedef struct thing_list {
60         int nalloc;
61         int nused;
62         void** things;
63         const char** names;
64 } thing_list_t;
65
66 // ---- debugging/testing -------------------------------------------------------------------------
67
68 /*
69         Dump some stats for an endpoint in the RT. This is generally called to
70         verify endpoints after a table load/change.
71
72         This is called by the for-each mechanism of the symtab and the prototype is
73         fixe; we don't really use some of the parms, but have dummy references to
74         keep sonar from complaining.
75 */
76 static void ep_stats( void* st, void* entry, char const* name, void* thing, void* vcounter ) {
77         int*    counter;
78         endpoint_t* ep;
79
80         if( (ep = (endpoint_t *) thing) == NULL ) {
81                 return;
82         }
83
84         if( (counter = (int *) vcounter) != NULL ) {
85                 (*counter)++;
86         } else {
87                 rmr_vlog( RMR_VL_DEBUG, "ep_stas: nil counter %p %p %p", st, entry, name );     // dummy refs
88         }
89
90         rmr_vlog_force( RMR_VL_DEBUG, "rt endpoint: target=%s open=%d\n", ep->name, ep->open );
91 }
92
93 /*
94         Called to count meid entries in the table. The meid points to an 'owning' endpoint
95         so we can list what we find
96
97         See note in ep_stats about dummy refs.
98 */
99 static void meid_stats( void* st, void* entry, char const* name, void* thing, void* vcounter ) {
100         int*    counter;
101         endpoint_t* ep;
102
103         if( (ep = (endpoint_t *) thing) == NULL ) {
104                 return;
105         }
106
107         if( (counter = (int *) vcounter) != NULL ) {
108                 (*counter)++;
109         } else {
110                 rmr_vlog( RMR_VL_DEBUG, "meid_stas: nil counter %p %p %p", st, entry, name );   // dummy refs
111         }
112
113         rmr_vlog_force( RMR_VL_DEBUG, "meid=%s owner=%s open=%d\n", name, ep->name, ep->open );
114 }
115
116 /*
117         Dump counts for an endpoint in the RT. The vid parm is assumed to point to
118         the 'source' information and is added to each message.
119
120         See note above about dummy references.
121 */
122 static void ep_counts( void* st, void* entry, char const* name, void* thing, void* vid ) {
123         endpoint_t* ep;
124         char*   id;
125
126         if( (ep = (endpoint_t *) thing) == NULL ) {
127                 rmr_vlog( RMR_VL_DEBUG, "ep_counts: nil thing %p %p %p", st, entry, name );     // dummy refs
128                 return;
129         }
130
131         if( (id = (char *) vid) == NULL ) {
132                 id = "missing";
133         }
134
135         rmr_vlog_force( RMR_VL_INFO, "sends: ts=%lld src=%s target=%s open=%d succ=%lld fail=%lld (hard=%lld soft=%lld)\n",
136                 (long long) time( NULL ),
137                 id,
138                 ep->name,
139                 ep->open,
140                 ep->scounts[EPSC_GOOD],
141                 ep->scounts[EPSC_FAIL] + ep->scounts[EPSC_TRANS],
142                 ep->scounts[EPSC_FAIL],
143                 ep->scounts[EPSC_TRANS]   );
144 }
145
146 /*
147         Dump stats for a route entry in the table.
148 */
149 static void rte_stats( void* st, void* entry, char const* name, void* thing, void* vcounter ) {
150         int*    counter;
151         rtable_ent_t const* rte;                // thing is really an rte
152         int             mtype;
153         int             sid;
154
155         if( (rte = (rtable_ent_t *) thing) == NULL ) {
156                 rmr_vlog( RMR_VL_DEBUG, "rte_stats: nil thing %p %p %p", st, entry, name );     // dummy refs
157                 return;
158         }
159
160         if( (counter = (int *) vcounter) != NULL ) {
161                 (*counter)++;
162         }
163
164         mtype = rte->key & 0xffff;
165         sid = (int) (rte->key >> 32);
166
167         rmr_vlog_force( RMR_VL_DEBUG, "rte: key=%016lx mtype=%4d sid=%4d nrrg=%2d refs=%d\n", rte->key, mtype, sid, rte->nrrgroups, rte->refs );
168 }
169
170 /*
171         Given a route table, cause some stats to be spit out.
172 */
173 static void  rt_stats( route_table_t* rt ) {
174         int* counter;
175
176         if( rt == NULL ) {
177                 rmr_vlog_force( RMR_VL_DEBUG, "rtstats: nil table\n" );
178                 return;
179         }
180
181         counter = (int *) malloc( sizeof( int ) );
182         *counter = 0;
183         rmr_vlog_force( RMR_VL_DEBUG, "route table stats:\n" );
184         rmr_vlog_force( RMR_VL_DEBUG, "route table endpoints:\n" );
185         rmr_sym_foreach_class( rt->ephash, RT_NAME_SPACE, ep_stats, counter );          // run endpoints (names) in the active table
186         rmr_vlog_force( RMR_VL_DEBUG, "rtable: %d known endpoints\n", *counter );
187
188         rmr_vlog_force( RMR_VL_DEBUG, "route table entries:\n" );
189         *counter = 0;
190         rmr_sym_foreach_class( rt->hash, RT_MT_SPACE, rte_stats, counter );                     // run message type entries
191         rmr_vlog_force( RMR_VL_DEBUG, "rtable: %d mt entries in table\n", *counter );
192
193         rmr_vlog_force( RMR_VL_DEBUG, "route table meid map:\n" );
194         *counter = 0;
195         rmr_sym_foreach_class( rt->hash, RT_ME_SPACE, meid_stats, counter );            // run meid space
196         rmr_vlog_force( RMR_VL_DEBUG, "rtable: %d meids in map\n", *counter );
197
198         free( counter );
199 }
200
201 /*
202         Given a route table, cause endpoint counters to be written to stderr. The id
203         parm is written as the "source" in the output.
204 */
205 static void  rt_epcounts( route_table_t* rt, char* id ) {
206         if( rt == NULL ) {
207                 rmr_vlog_force( RMR_VL_INFO, "endpoint: no counts: empty table\n" );
208                 return;
209         }
210
211         rmr_sym_foreach_class( rt->hash, 1, ep_counts, id );            // run endpoints in the active table
212 }
213
214 // ------------ route manager communication -------------------------------------------------
215 /*
216         Send a request for a table update to the route manager. Updates come in
217         async, so send and go.
218
219         pctx is the private context for the thread; ctx is the application context
220         that we need to be able to send the application ID in case rt mgr needs to
221         use it to idenfity us.
222
223         Returns 0 if we were not able to send a request.
224 */
225 static int send_update_req( uta_ctx_t* pctx, uta_ctx_t* ctx ) {
226         rmr_mbuf_t*     smsg;
227         int     state = 0;
228
229         if( ctx->rtg_whid < 0 ) {
230                 return state;
231         }
232
233         smsg = rmr_alloc_msg( pctx, 1024 );
234         if( smsg != NULL ) {
235                 smsg->mtype = RMRRM_REQ_TABLE;
236                 smsg->sub_id = 0;
237                 snprintf( smsg->payload, 1024, "%s ts=%ld\n", ctx->my_name, time( NULL ) );
238                 rmr_vlog( RMR_VL_INFO, "rmr_rtc: requesting table: (%s) whid=%d\n", smsg->payload, ctx->rtg_whid );
239                 smsg->len = strlen( smsg->payload ) + 1;
240
241                 smsg = rmr_wh_send_msg( pctx, ctx->rtg_whid, smsg );
242                 if( (state = smsg->state) != RMR_OK ) {
243                         rmr_vlog( RMR_VL_INFO, "rmr_rtc: send failed: %d whid=%d\n", smsg->state, ctx->rtg_whid );
244                         rmr_wh_close( ctx, ctx->rtg_whid );                                     // send failed, assume connection lost
245                         ctx->rtg_whid = -1;
246                 }
247
248                 rmr_free_msg( smsg );
249         }
250
251         return state;
252 }
253
254 /*
255         Send an ack to the route table manager for a table ID that we are
256         processing.      State is 1 for OK, and 0 for failed. Reason might
257         be populated if we know why there was a failure.
258
259         Context should be the PRIVATE context that we use for messages
260         to route manger and NOT the user's context.
261
262         If a message buffere is passed we use that and use return to sender
263         assuming that this might be a response to a call and that is needed
264         to send back to the proper calling thread. If msg is nil, we allocate
265         and use it.
266 */
267 static void send_rt_ack( uta_ctx_t* ctx, rmr_mbuf_t* smsg, char* table_id, int state, char* reason ) {
268         int             use_rts = 1;
269         int             payload_size = 1024;
270
271         if( ctx == NULL || ctx->rtg_whid < 0 ) {
272                 return;
273         }
274
275         if( ctx->flags & CFL_NO_RTACK ) {               // don't ack if reading from file etc
276                 return;
277         }
278
279         if( smsg != NULL ) {
280                 smsg = rmr_realloc_payload( smsg, payload_size, FALSE, FALSE );         // ensure it's large enough to send a response
281         } else {
282                 use_rts = 0;
283                 smsg = rmr_alloc_msg( ctx, payload_size );
284         }
285
286         if( smsg != NULL ) {
287                 smsg->mtype = RMRRM_TABLE_STATE;
288                 smsg->sub_id = -1;
289                 snprintf( smsg->payload, payload_size-1, "%s %s %s\n", state == RMR_OK ? "OK" : "ERR",
290                         table_id == NULL ? "<id-missing>" : table_id, reason == NULL ? "" : reason );
291
292                 smsg->len = strlen( smsg->payload ) + 1;
293
294                 rmr_vlog( RMR_VL_INFO, "rmr_rtc: sending table state: (%s) state=%d whid=%d table=%s\n", smsg->payload, state, ctx->rtg_whid, table_id );
295                 if( use_rts ) {
296                         smsg = rmr_rts_msg( ctx, smsg );
297                 } else {
298                         smsg = rmr_wh_send_msg( ctx, ctx->rtg_whid, smsg );
299                 }
300                 if( (state = smsg->state) != RMR_OK ) {
301                         rmr_vlog( RMR_VL_WARN, "unable to send table state: %d\n", smsg->state );
302                         rmr_wh_close( ctx, ctx->rtg_whid );                                     // send failed, assume connection lost
303                         ctx->rtg_whid = -1;
304                 }
305
306                 if( ! use_rts ) {
307                         rmr_free_msg( smsg );                   // if not our message we must free the leftovers
308                 }
309         }
310 }
311
312 // ---- utility -----------------------------------------------------------------------------------
313 /*
314         Little diddy to trim whitespace and trailing comments. Like shell, trailing comments
315         must be at the start of a word (i.e. must be immediatly preceeded by whitespace).
316 */
317 static char* clip( char* buf ) {
318         char*   tok;
319
320         while( *buf && isspace( *buf ) ) {                                                      // skip leading whitespace
321                 buf++;
322         }
323
324         if( (tok = strchr( buf, '#' )) != NULL ) {
325                 if( tok == buf ) {
326                         return buf;                                     // just push back; leading comment sym handled there
327                 }
328
329                 if( isspace( *(tok-1) ) ) {
330                         *tok = 0;
331                 }
332         }
333
334         for( tok = buf + (strlen( buf ) - 1); tok > buf && isspace( *tok ); tok-- );    // trim trailing spaces too
335         *(tok+1) = 0;
336
337         return buf;
338 }
339
340 /*
341         This accepts a pointer to a nil terminated string, and ensures that there is a
342         newline as the last character. If there is not, a new buffer is allocated and
343         the newline is added.  If a new buffer is allocated, the buffer passed in is
344         freed.  The function returns a pointer which the caller should use, and must
345         free.  In the event of an error, a nil pointer is returned.
346 */
347 static char* ensure_nlterm( char* buf ) {
348         char*   nb = NULL;
349         int             len = 0;
350
351         if( buf != NULL ) {
352                 len = strlen( buf );
353         }
354
355         nb = buf;                                                       // default to returning original as is
356         switch( len ) {
357                 case 0:
358                         nb = strdup( "\n" );
359                         break;
360
361                 case 1:
362                         if( *buf != '\n' ) {            // not a newline; realloc
363                                 rmr_vlog( RMR_VL_WARN, "rmr buf_check: input buffer was not newline terminated (file missing final \\n?)\n" );
364                                 nb = strdup( " \n" );
365                                 *nb = *buf;
366                                 free( buf );
367                         }
368                         break;
369
370                 default:
371                         if( buf[len-1] != '\n' ) {              // not newline terminated, realloc
372                                 rmr_vlog( RMR_VL_WARN, "rmr buf_check: input buffer was not newline terminated (file missing final \\n?)\n" );
373                                 if( (nb = (char *) malloc( sizeof( char ) * (len + 2) )) != NULL ) {
374                                         memcpy( nb, buf, len );
375                                         *(nb+len) = '\n';                       // insert \n and nil into the two extra bytes we allocated
376                                         *(nb+len+1) = 0;
377                                         free( buf );
378                                 }
379                         }
380                         break;
381         }
382
383         return nb;
384 }
385
386 /*
387         Roll the new table into the active and the active into the old table. We
388         must have the lock on the active table to do this. It's possible that there
389         is no active table (first load), so we have to account for that (no locking).
390 */
391 static void roll_tables( uta_ctx_t* ctx ) {
392
393         if( ctx->rtable != NULL ) {                                                     // initially there isn't one, so must check!
394                 pthread_mutex_lock( ctx->rtgate );                              // must hold lock to move to active
395                 ctx->old_rtable = ctx->rtable;                                  // currently active becomes old and allowed to 'drain'
396                 ctx->rtable = ctx->new_rtable;                                  // one we've been adding to becomes active
397                 pthread_mutex_unlock( ctx->rtgate );
398         } else {
399                 ctx->old_rtable = NULL;                                         // ensure there isn't an old reference
400                 ctx->rtable = ctx->new_rtable;                          // make new the active one
401         }
402
403         ctx->new_rtable = NULL;
404 }
405
406 // ------------ entry update functions ---------------------------------------------------------------
407 /*
408         Given a message type create a route table entry and add to the hash keyed on the
409         message type.  Once in the hash, endpoints can be added with uta_add_ep. Size
410         is the number of group slots to allocate in the entry.
411 */
412 static rtable_ent_t* uta_add_rte( route_table_t* rt, uint64_t key, int nrrgroups ) {
413         rtable_ent_t* rte;
414         rtable_ent_t* old_rte;          // entry which was already in the table for the key
415
416         if( rt == NULL ) {
417                 return NULL;
418         }
419
420         if( (rte = (rtable_ent_t *) malloc( sizeof( *rte ) )) == NULL ) {
421                 rmr_vlog( RMR_VL_ERR, "rmr_add_rte: malloc failed for entry\n" );
422                 return NULL;
423         }
424         memset( rte, 0, sizeof( *rte ) );
425         rte->refs = 1;
426         rte->key = key;
427
428         if( nrrgroups < 0 ) {           // zero is allowed as %meid entries have no groups
429                 nrrgroups = 10;
430         }
431
432         if( nrrgroups ) {
433                 if( (rte->rrgroups = (rrgroup_t **) malloc( sizeof( rrgroup_t * ) * nrrgroups )) == NULL ) {
434                         free( rte );
435                         return NULL;
436                 }
437                 memset( rte->rrgroups, 0, sizeof( rrgroup_t *) * nrrgroups );
438         } else {
439                 rte->rrgroups = NULL;
440         }
441
442         rte->nrrgroups = nrrgroups;
443
444         if( (old_rte = rmr_sym_pull( rt->hash, key )) != NULL ) {
445                 del_rte( NULL, NULL, NULL, old_rte, NULL );                             // dec the ref counter and trash if unreferenced
446         }
447
448         rmr_sym_map( rt->hash, key, rte );                                                      // add to hash using numeric mtype as key
449
450         if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, "route table entry created: k=%llx groups=%d\n", (long long) key, nrrgroups );
451         return rte;
452 }
453
454 /*
455         This accepts partially parsed information from an rte or mse record sent by route manager or read from
456         a file such that:
457                 ts_field is the msg-type,sender field
458                 subid is the integer subscription id
459                 rr_field is the endpoint information for round robening message over
460
461         If all goes well, this will add an RTE to the table under construction.
462
463         The ts_field is checked to see if we should ingest this record. We ingest if one of
464         these is true:
465                 there is no sender info (a generic entry for all)
466                 there is sender and our host:port matches one of the senders
467                 the sender info is an IP address that matches one of our IP addresses
468 */
469 static void build_entry( uta_ctx_t* ctx, char* ts_field, uint32_t subid, char* rr_field, int vlevel ) {
470         rtable_ent_t*   rte;            // route table entry added
471         char const*     tok;
472         int             ntoks;
473         uint64_t key = 0;                       // the symtab key will be mtype or sub_id+mtype
474         char*   tokens[128];
475         char*   gtokens[64];
476         int             i;
477         int             ngtoks;                         // number of tokens in the group list
478         int             grp;                            // index into group list
479
480         ts_field = clip( ts_field );                            // ditch extra whitespace and trailing comments
481         rr_field = clip( rr_field );
482
483         if( ((tok = strchr( ts_field, ',' )) == NULL ) ||                                       // no sender names (generic entry for all)
484                 (uta_has_str( ts_field,  ctx->my_name, ',', 127) >= 0) ||               // our name is in the list
485                 has_myip( ts_field, ctx->ip_list, ',', 127 ) ) {                                // the list has one of our IP addresses
486
487                 key = build_rt_key( subid, atoi( ts_field ) );
488
489                 if( DEBUG > 1 || (vlevel > 1) ) rmr_vlog_force( RMR_VL_DEBUG, "create rte for mtype=%s subid=%d key=%lx\n", ts_field, subid, key );
490
491                 if( (ngtoks = uta_tokenise( rr_field, gtokens, 64, ';' )) > 0 ) {                                       // split round robin groups
492                         if( strcmp( gtokens[0], "%meid" ) == 0 ) {
493                                 ngtoks = 0;                                                                                                                                     // special indicator that uses meid to find endpoint, no rrobin
494                         }
495                         rte = uta_add_rte( ctx->new_rtable, key, ngtoks );                                                              // get/create entry for this key
496                         rte->mtype = atoi( ts_field );                                                                                                  // capture mtype for debugging
497
498                         for( grp = 0; grp < ngtoks; grp++ ) {
499                                 if( (ntoks = uta_rmip_tokenise( gtokens[grp], ctx->ip_list, tokens, 64, ',' )) > 0 ) {          // remove any referneces to our ip addrs
500                                         for( i = 0; i < ntoks; i++ ) {
501                                                 if( strcmp( tokens[i], ctx->my_name ) != 0 ) {                                  // don't add if it is us -- cannot send to ourself
502                                                         if( DEBUG > 1  || (vlevel > 1)) rmr_vlog_force( RMR_VL_DEBUG, "add endpoint  ts=%s %s\n", ts_field, tokens[i] );
503                                                         uta_add_ep( ctx->new_rtable, rte, tokens[i], grp );
504                                                 }
505                                         }
506                                 }
507                         }
508                 }
509         } else {
510                 if( DEBUG || (vlevel > 2) ) {
511                         rmr_vlog_force( RMR_VL_DEBUG, "build entry: ts_entry not of form msg-type,sender: %s\n", ts_field );
512                 }
513         }
514 }
515
516 /*
517         Trash_entry takes a partially parsed record from the input and
518         will delete the entry if the sender,mtype matches us or it's a
519         generic mtype. The refernce in the new table is removed and the
520         refcounter for the actual rte is decreased. If that ref count is
521         0 then the memory is freed (handled byh the del_rte call).
522 */
523 static void trash_entry( uta_ctx_t* ctx, char* ts_field, uint32_t subid, int vlevel ) {
524         rtable_ent_t*   rte;            // route table entry to be 'deleted'
525         char const*     tok;
526         int             ntoks;
527         uint64_t key = 0;                       // the symtab key will be mtype or sub_id+mtype
528         char*   tokens[128];
529
530         if( ctx == NULL || ctx->new_rtable == NULL || ctx->new_rtable->hash == NULL ) {
531                 return;
532         }
533
534         ts_field = clip( ts_field );                            // ditch extra whitespace and trailing comments
535
536         if( ((tok = strchr( ts_field, ',' )) == NULL ) ||                                       // no sender names (generic entry for all)
537                 (uta_has_str( ts_field,  ctx->my_name, ',', 127) >= 0) ||               // our name is in the list
538                 has_myip( ts_field, ctx->ip_list, ',', 127 ) ) {                                // the list has one of our IP addresses
539
540                 key = build_rt_key( subid, atoi( ts_field ) );
541                 rte = rmr_sym_pull( ctx->new_rtable->hash, key );                       // get it
542                 if( rte != NULL ) {
543                         if( DEBUG || (vlevel > 1) ) {
544                                  rmr_vlog_force( RMR_VL_DEBUG, "delete rte for mtype=%s subid=%d key=%08lx\n", ts_field, subid, key );
545                         }
546                         rmr_sym_ndel( ctx->new_rtable->hash, key );                     // clear from the new table
547                         del_rte( NULL, NULL, NULL, rte, NULL );                         // clean up the memory: reduce ref and free if ref == 0
548                 } else {
549                         if( DEBUG || (vlevel > 1) ) {
550                                 rmr_vlog_force( RMR_VL_DEBUG, "delete could not find rte for mtype=%s subid=%d key=%lx\n", ts_field, subid, key );
551                         }
552                 }
553         } else {
554                 if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, "delete rte skipped: %s\n", ts_field );
555         }
556 }
557
558 // -------------------------- parse functions --------------------------------------------------
559
560 /*
561         Given the tokens from an mme_ar (meid add/replace) entry, add the entries.
562         the 'owner' which should be the dns name or IP address of an enpoint
563         the meid_list is a space separated list of me IDs
564
565         This function assumes the caller has vetted the pointers as needed.
566
567         For each meid in the list, an entry is pushed into the hash which references the owner
568         endpoint such that when the meid is used to route a message it references the endpoint
569         to send messages to.
570 */
571 static void parse_meid_ar( route_table_t* rtab, char* owner, char* meid_list, int vlevel ) {
572         char const*     tok;
573         int             ntoks;
574         char*   tokens[128];
575         int             i;
576         int             state;
577         endpoint_t*     ep;                                             // endpoint struct for the owner
578
579         owner = clip( owner );                          // ditch extra whitespace and trailing comments
580         meid_list = clip( meid_list );
581
582         ntoks = uta_tokenise( meid_list, tokens, 128, ' ' );
583         for( i = 0; i < ntoks; i++ ) {
584                 if( (ep = rt_ensure_ep( rtab, owner )) != NULL ) {
585                         state = rmr_sym_put( rtab->hash, tokens[i], RT_ME_SPACE, ep );                                          // slam this one in if new; replace if there
586                         if( DEBUG || (vlevel > 1) ) rmr_vlog_force( RMR_VL_DEBUG, "parse_meid_ar: add/replace meid: %s owned by: %s state=%d\n", tokens[i], owner, state );
587                 } else {
588                         rmr_vlog( RMR_VL_WARN, "rmr parse_meid_ar: unable to create an endpoint for owner: %s", owner );
589                 }
590         }
591 }
592
593 /*
594         Given the tokens from an mme_del, delete the listed meid entries from the new
595         table. The list is a space separated list of meids.
596
597         The meids in the hash reference endpoints which are never deleted and so
598         the only thing that we need to do here is to remove the meid from the hash.
599
600         This function assumes the caller has vetted the pointers as needed.
601 */
602 static void parse_meid_del( route_table_t* rtab, char* meid_list, int vlevel ) {
603         char const*     tok;
604         int             ntoks;
605         char*   tokens[128];
606         int             i;
607
608         if( rtab->hash == NULL ) {
609                 return;
610         }
611
612         meid_list = clip( meid_list );
613
614         ntoks = uta_tokenise( meid_list, tokens, 128, ' ' );
615         for( i = 0; i < ntoks; i++ ) {
616                 rmr_sym_del( rtab->hash, tokens[i], RT_ME_SPACE );                                              // and it only took my little finger to blow it away!
617                 if( DEBUG || (vlevel > 1) ) rmr_vlog_force( RMR_VL_DEBUG, "parse_meid_del: meid deleted: %s\n", tokens[i] );
618         }
619 }
620
621 /*
622         Parse a partially parsed meid record. Tokens[0] should be one of:
623                 meid_map, mme_ar, mme_del.
624
625         pctx is the private context needed to return an ack/nack using the provided
626         message buffer with the route managers address info.
627 */
628 static void meid_parser( uta_ctx_t* ctx, uta_ctx_t* pctx, rmr_mbuf_t* mbuf, char** tokens, int ntoks, int vlevel ) {
629         char wbuf[1024];
630
631         if( tokens == NULL || ntoks < 1 ) {
632                 return;                                                 // silent but should never happen
633         }
634
635         if( ntoks < 2 ) {                                       // must have at least two for any valid request record
636                 rmr_vlog( RMR_VL_ERR, "meid_parse: not enough tokens on %s record\n", tokens[0] );
637                 return;
638         }
639
640         if( strcmp( tokens[0], "meid_map" ) == 0 ) {                                    // start or end of the meid map update
641                 tokens[1] = clip( tokens[1] );
642                 if( *(tokens[1]) == 's' ) {
643                         if( ctx->new_rtable != NULL ) {                                 // one in progress?  this forces it out
644                                 if( DEBUG > 1 || (vlevel > 1) ) rmr_vlog_force( RMR_VL_DEBUG, "meid map start: dropping incomplete table\n" );
645                                 uta_rt_drop( ctx->new_rtable );
646                                 ctx->new_rtable = NULL;
647                                 send_rt_ack( pctx, mbuf, ctx->table_id, !RMR_OK, "table not complete" );        // nack the one that was pending as and never made it
648                         }
649
650                         if( ctx->table_id != NULL ) {
651                                 free( ctx->table_id );
652                         }
653                         if( ntoks > 2 ) {
654                                 ctx->table_id = strdup( clip( tokens[2] ) );
655                         } else {
656                                 ctx->table_id = NULL;
657                         }
658
659                         ctx->new_rtable = prep_new_rt( ctx, ALL );                                      // start with a clone of everything (mtype, endpoint refs and meid)
660                         ctx->new_rtable->mupdates = 0;
661
662                         if( DEBUG || (vlevel > 1)  ) rmr_vlog_force( RMR_VL_DEBUG, "meid_parse: meid map start found\n" );
663                 } else {
664                         if( strcmp( tokens[1], "end" ) == 0 ) {                                                         // wrap up the table we were building
665                                 if( ntoks > 2 ) {                                                                                               // meid_map | end | <count> |??? given
666                                         if( ctx->new_rtable->mupdates != atoi( tokens[2] ) ) {          // count they added didn't match what we received
667                                                 rmr_vlog( RMR_VL_ERR, "meid_parse: meid map update had wrong number of records: received %d expected %s\n",
668                                                                 ctx->new_rtable->mupdates, tokens[2] );
669                                                 snprintf( wbuf, sizeof( wbuf ), "missing table records: expected %s got %d\n", tokens[2], ctx->new_rtable->updates );
670                                                 send_rt_ack( pctx, mbuf, ctx->table_id, !RMR_OK, wbuf );
671                                                 uta_rt_drop( ctx->new_rtable );
672                                                 ctx->new_rtable = NULL;
673                                                 return;
674                                         }
675
676                                         if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, "meid_parse: meid map update ended; found expected number of entries: %s\n", tokens[2] );
677                                 }
678
679                                 if( ctx->new_rtable ) {
680                                         roll_tables( ctx );                                             // roll active to old, and new to active with proper locking
681                                         if( DEBUG > 1 || (vlevel > 1) ) rmr_vlog_force( RMR_VL_DEBUG, "end of meid map noticed\n" );
682                                         send_rt_ack( pctx, mbuf, ctx->table_id, RMR_OK, NULL );
683
684                                         if( vlevel > 0 ) {
685                                                 if( ctx->old_rtable != NULL ) {
686                                                         rmr_vlog_force( RMR_VL_DEBUG, "old route table: (ref_count=%d)\n", ctx->old_rtable->ref_count );
687                                                         rt_stats( ctx->old_rtable );
688                                                 } else {
689                                                         rmr_vlog_force( RMR_VL_DEBUG, "old route table was empty\n" );
690                                                 }
691                                                 rmr_vlog_force( RMR_VL_DEBUG, "new route table:\n" );
692                                                 rt_stats( ctx->rtable );
693                                         }
694                                 } else {
695                                         if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, "end of meid map noticed, but one was not started!\n" );
696                                         ctx->new_rtable = NULL;
697                                 }
698                         }
699                 }
700
701                 return;
702         }
703
704         if( ! ctx->new_rtable ) {                       // for any other mmap entries, there must be a table in progress or we punt
705                 if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, "meid update/delte (%s) encountered, but table update not started\n", tokens[0] );
706                 return;
707         }
708
709         if( strcmp( tokens[0], "mme_ar" ) == 0 ) {
710                 if( ntoks < 3  || tokens[1] == NULL || tokens[2] == NULL ) {
711                         rmr_vlog( RMR_VL_ERR, "meid_parse: mme_ar record didn't have enough tokens found %d\n", ntoks );
712                         return;
713                 }
714                 parse_meid_ar( ctx->new_rtable,  tokens[1], tokens[2], vlevel );
715                 ctx->new_rtable->mupdates++;
716                 return;
717         }
718
719         if( strcmp( tokens[0], "mme_del" ) == 0 ) {                                             // ntoks < 2 already validated
720                 parse_meid_del( ctx->new_rtable,  tokens[1], vlevel );
721                 ctx->new_rtable->mupdates++;
722                 return;
723         }
724 }
725
726 /*
727         Parse a single record recevied from the route table generator, or read
728         from a static route table file.  Start records cause a new table to
729         be started (if a partial table was received it is discarded. Table
730         entry records are added to the currenly 'in progress' table, and an
731         end record causes the in progress table to be finalised and the
732         currently active table is replaced.
733
734         The updated table will be activated when the *|end record is encountered.
735         However, to allow for a "double" update, where both the meid map and the
736         route table must be updated at the same time, the end indication on a
737         route table (new or update) may specifiy "hold" which indicates that meid
738         map entries are to follow and the updated route table should be held as
739         pending until the end of the meid map is received and validated.
740
741         CAUTION:  we are assuming that there is a single route/meid map generator
742                 and as such only one type of update is received at a time; in other
743                 words, the sender cannot mix update records and if there is more than
744                 one sender process they must synchronise to avoid issues.
745
746
747         For a RT update, we expect:
748                 newrt | start | <table-id>
749                 newrt | end | <count>
750                 rte|<mtype>[,sender]|<endpoint-grp>[;<endpoint-grp>,...]
751                 mse|<mtype>[,sender]|<sub-id>|<endpoint-grp>[;<endpoint-grp>,...]
752                 mse| <mtype>[,sender] | <sub-id> | %meid
753
754
755         For a meid map update we expect:
756                 meid_map | start | <table-id>
757                 meid_map | end | <count> | <md5-hash>
758                 mme_ar | <e2term-id> | <meid0> <meid1>...<meidn>
759                 mme_del | <meid0> <meid1>...<meidn>
760
761
762         The pctx is our private context that must be used to send acks/status
763         messages back to the route manager.  The regular ctx is the ctx that
764         the user has been given and thus that's where we have to hang the route
765         table we're working with.
766
767         If mbuf is given, and we need to ack, then we ack using the mbuf and a
768         return to sender call (allows route manager to use wh_call() to send
769         an update and rts is required to get that back to the right thread).
770         If mbuf is nil, then one will be allocated (in ack) and a normal wh_send
771         will be used.
772 */
773 static void parse_rt_rec( uta_ctx_t* ctx,  uta_ctx_t* pctx, char* buf, int vlevel, rmr_mbuf_t* mbuf ) {
774         int i;
775         int ntoks;                                                      // number of tokens found in something
776         int ngtoks;
777         int     grp;                                                    // group number
778         rtable_ent_t const*     rte;                    // route table entry added
779         char*   tokens[128];
780         char*   tok;                                            // pointer into a token or string
781         char    wbuf[1024];
782
783         if( ! buf ) {
784                 return;
785         }
786
787         while( *buf && isspace( *buf ) ) {                                                      // skip leading whitespace
788                 buf++;
789         }
790         for( tok = buf + (strlen( buf ) - 1); tok > buf && isspace( *tok ); tok-- );    // trim trailing spaces too
791         *(tok+1) = 0;
792
793         memset( tokens, 0, sizeof( tokens ) );
794         if( (ntoks = uta_tokenise( buf, tokens, 128, '|' )) > 0 ) {
795                 tokens[0] = clip( tokens[0] );
796                 switch( *(tokens[0]) ) {
797                         case 0:                                                                                                 // ignore blanks
798                                 // fallthrough
799                         case '#':                                                                                               // and comment lines
800                                 break;
801
802                         case 'd':                                                                                               // del | [sender,]mtype | sub-id
803                                 if( ! ctx->new_rtable ) {                       // bad sequence, or malloc issue earlier; ignore siliently
804                                         break;
805                                 }
806
807                                 if( ntoks < 3 ) {
808                                         if( DEBUG ) rmr_vlog( RMR_VL_WARN, "rmr_rtc: del record had too few fields: %d instead of 3\n", ntoks );
809                                         break;
810                                 }
811
812                                 trash_entry( ctx, tokens[1], atoi( tokens[2] ), vlevel );
813                                 ctx->new_rtable->updates++;
814                                 break;
815
816                         case 'n':                                                                                               // newrt|{start|end}
817                                 tokens[1] = clip( tokens[1] );
818                                 if( strcmp( tokens[1], "end" ) == 0 ) {                         // wrap up the table we were building
819                                         if( ntoks >2 ) {
820                                                 if( ctx->new_rtable->updates != atoi( tokens[2] ) ) {   // count they added didn't match what we received
821                                                         rmr_vlog( RMR_VL_ERR, "rmr_rtc: RT update had wrong number of records: received %d expected %s\n",
822                                                                 ctx->new_rtable->updates, tokens[2] );
823                                                         snprintf( wbuf, sizeof( wbuf ), "missing table records: expected %s got %d\n", tokens[2], ctx->new_rtable->updates );
824                                                         send_rt_ack( pctx, mbuf, ctx->table_id, !RMR_OK, wbuf );
825                                                         uta_rt_drop( ctx->new_rtable );
826                                                         ctx->new_rtable = NULL;
827                                                         break;
828                                                 }
829                                         }
830
831                                         if( ctx->new_rtable ) {
832                                                 if( DEBUG > 1 || (vlevel > 1) ) rmr_vlog( RMR_VL_DEBUG, "end of route table noticed\n" );
833                                                 roll_tables( ctx );                                             // roll active to old, and new to active with proper locking
834
835                                                 if( vlevel > 0 ) {
836                                                         if( ctx->old_rtable != NULL ) {
837                                                                 rmr_vlog_force( RMR_VL_DEBUG, "old route table: (ref_count=%d)\n", ctx->old_rtable->ref_count );
838                                                                 rt_stats( ctx->old_rtable );
839                                                         } else {
840                                                                 rmr_vlog_force( RMR_VL_DEBUG, "old route table was empty\n" );
841                                                         }
842                                                         rmr_vlog_force( RMR_VL_DEBUG, "new route table:\n" );
843                                                         rt_stats( ctx->rtable );
844                                                 }
845
846                                                 send_rt_ack( pctx, mbuf, ctx->table_id, RMR_OK, NULL );
847                                                 ctx->rtable_ready = 1;                                                  // route based sends can now happen
848                                         } else {
849                                                 if( DEBUG > 1 ) rmr_vlog_force( RMR_VL_DEBUG, "end of route table noticed, but one was not started!\n" );
850                                                 ctx->new_rtable = NULL;
851                                         }
852                                 } else {                                                                                                                        // start a new table.
853                                         if( ctx->new_rtable != NULL ) {                                                                 // one in progress?  this forces it out
854                                                 send_rt_ack( pctx, mbuf, ctx->table_id, !RMR_OK, "table not complete" );                        // nack the one that was pending as end never made it
855
856                                                 if( DEBUG > 1 || (vlevel > 1) ) rmr_vlog_force( RMR_VL_DEBUG, "new table; dropping incomplete table\n" );
857                                                 uta_rt_drop( ctx->new_rtable );
858                                                 ctx->new_rtable = NULL;
859                                         }
860
861                                         if( ctx->table_id != NULL ) {
862                                                 free( ctx->table_id );
863                                         }
864                                         if( ntoks >2 ) {
865                                                 ctx->table_id = strdup( clip( tokens[2] ) );
866                                         } else {
867                                                 ctx->table_id = NULL;
868                                         }
869
870                                         ctx->new_rtable = prep_new_rt( ctx, SOME );                     // wait for old table to drain and shift it back to new
871                                         ctx->new_rtable->updates = 0;                                           // init count of entries received
872
873                                         if( DEBUG > 1 || (vlevel > 1)  ) rmr_vlog_force( RMR_VL_DEBUG, "start of route table noticed\n" );
874                                 }
875                                 break;
876
877                         case 'm':                                                                       // mse entry or one of the meid_ records
878                                 if( strcmp( tokens[0], "mse" ) == 0 ) {
879                                         if( ! ctx->new_rtable ) {                       // bad sequence, or malloc issue earlier; ignore siliently
880                                                 break;
881                                         }
882
883                                         if( ntoks < 4 ) {
884                                                 if( DEBUG ) rmr_vlog( RMR_VL_WARN, "rmr_rtc: mse record had too few fields: %d instead of 4\n", ntoks );
885                                                 break;
886                                         }
887
888                                         build_entry( ctx, tokens[1], atoi( tokens[2] ), tokens[3], vlevel );
889                                         ctx->new_rtable->updates++;
890                                 } else {
891                                         meid_parser( ctx, pctx, mbuf, tokens, ntoks, vlevel );
892                                 }
893                                 break;
894
895                         case 'r':                                       // assume rt entry
896                                 if( ! ctx->new_rtable ) {                       // bad sequence, or malloc issue earlier; ignore siliently
897                                         break;
898                                 }
899
900                                 ctx->new_rtable->updates++;
901                                 if( ntoks > 3 ) {                                                                                                       // assume new entry with subid last
902                                         build_entry( ctx, tokens[1], atoi( tokens[3] ), tokens[2], vlevel );
903                                 } else {
904                                         build_entry( ctx, tokens[1], UNSET_SUBID, tokens[2], vlevel );                  // old school entry has no sub id
905                                 }
906                                 break;
907
908                         case 'u':                                                                                               // update current table, not a total replacement
909                                 tokens[1] = clip( tokens[1] );
910                                 if( strcmp( tokens[1], "end" ) == 0 ) {                         // wrap up the table we were building
911                                         if( ctx->new_rtable == NULL ) {                                 // update table not in progress
912                                                 break;
913                                         }
914
915                                         if( ntoks >2 ) {
916                                                 if( ctx->new_rtable->updates != atoi( tokens[2] ) ) {   // count they added didn't match what we received
917                                                         rmr_vlog( RMR_VL_ERR, "rmr_rtc: RT update had wrong number of records: received %d expected %s\n",
918                                                                 ctx->new_rtable->updates, tokens[2] );
919                                                         send_rt_ack( pctx, mbuf, ctx->table_id, !RMR_OK, wbuf );
920                                                         uta_rt_drop( ctx->new_rtable );
921                                                         ctx->new_rtable = NULL;
922                                                         break;
923                                                 }
924                                         }
925
926                                         if( ctx->new_rtable ) {
927                                                 roll_tables( ctx );                                             // roll active to old, and new to active with proper locking
928                                                 if( DEBUG > 1 || (vlevel > 1) ) rmr_vlog_force( RMR_VL_DEBUG, "end of rt update noticed\n" );
929
930                                                 if( vlevel > 0 ) {
931                                                         if( ctx->old_rtable != NULL ) {
932                                                                 rmr_vlog_force( RMR_VL_DEBUG, "old route table:  (ref_count=%d)\n", ctx->old_rtable->ref_count );
933                                                                 rt_stats( ctx->old_rtable );
934                                                         } else {
935                                                                 rmr_vlog_force( RMR_VL_DEBUG, "old route table was empty\n" );
936                                                         }
937                                                         rmr_vlog_force( RMR_VL_DEBUG, "updated route table:\n" );
938                                                         rt_stats( ctx->rtable );
939                                                 }
940
941                                                 send_rt_ack( pctx, mbuf, ctx->table_id, RMR_OK, NULL );
942                                                 ctx->rtable_ready = 1;                                                  // route based sends can now happen
943                                         } else {
944                                                 if( DEBUG > 1 ) rmr_vlog_force( RMR_VL_DEBUG, "end of rt update noticed, but one was not started!\n" );
945                                                 ctx->new_rtable = NULL;
946                                         }
947                                 } else {                                                                                        // start a new table.
948                                         if( ctx->new_rtable != NULL ) {                                 // one in progress?  this forces it out
949                                                 if( DEBUG > 1 || (vlevel > 1) ) rmr_vlog_force( RMR_VL_DEBUG, "new table; dropping incomplete table\n" );
950                                                 send_rt_ack( pctx, mbuf, ctx->table_id, !RMR_OK, "table not complete" );                        // nack the one that was pending as end never made it
951                                                 uta_rt_drop( ctx->new_rtable );
952                                                 ctx->new_rtable = NULL;
953                                         }
954
955                                         if( ntoks > 2 ) {
956                                                 if( ctx->table_id != NULL ) {
957                                                         free( ctx->table_id );
958                                                 }
959                                                 ctx->table_id = strdup( clip( tokens[2] ) );
960                                         }
961
962                                         ctx->new_rtable = prep_new_rt( ctx, ALL );                              // start with a copy of everything in the live table
963                                         ctx->new_rtable->updates = 0;                                                   // init count of updates received
964
965                                         if( DEBUG > 1 || (vlevel > 1)  ) rmr_vlog_force( RMR_VL_DEBUG, "start of rt update noticed\n" );
966                                 }
967                                 break;
968
969                         default:
970                                 if( DEBUG ) rmr_vlog( RMR_VL_WARN, "rmr_rtc: unrecognised request: %s\n", tokens[0] );
971                                 break;
972                 }
973         }
974 }
975
976 /*
977         This function attempts to open a static route table in order to create a 'seed'
978         table during initialisation.  The environment variable RMR_SEED_RT is expected
979         to contain the necessary path to the file. If missing, or if the file is empty,
980         no route table will be available until one is received from the generator.
981
982         This function is probably most useful for testing situations, or extreme
983         cases where the routes are static.
984 */
985 static void read_static_rt( uta_ctx_t* ctx, int vlevel ) {
986         int             i;
987         char*   fname;
988         char*   fbuf;                           // buffer with file contents
989         char*   rec;                            // start of the record
990         char*   eor;                            // end of the record
991         int             rcount = 0;                     // record count for debug
992
993         if( (fname = getenv( ENV_SEED_RT )) == NULL ) {
994                 return;
995         }
996
997         if( (fbuf = ensure_nlterm( uta_fib( fname ) ) ) == NULL ) {                     // read file into a single buffer (nil terminated string)
998                 rmr_vlog( RMR_VL_WARN, "rmr read_static: seed route table could not be opened: %s: %s\n", fname, strerror( errno ) );
999                 return;
1000         }
1001
1002         if( DEBUG ) rmr_vlog_force( RMR_VL_DEBUG, "seed route table successfully opened: %s\n", fname );
1003         for( eor = fbuf; *eor; eor++ ) {                                        // fix broken systems that use \r or \r\n to terminate records
1004                 if( *eor == '\r' ) {
1005                         *eor = '\n';                                                            // will look like a blank line which is ok
1006                 }
1007         }
1008
1009         rec = fbuf;
1010         while( rec && *rec ) {
1011                 rcount++;
1012                 if( (eor = strchr( rec, '\n' )) != NULL ) {
1013                         *eor = 0;
1014                 } else {
1015                         rmr_vlog( RMR_VL_WARN, "rmr read_static: seed route table had malformed records (missing newline): %s\n", fname );
1016                         rmr_vlog( RMR_VL_WARN, "rmr read_static: seed route table not used: %s\n", fname );
1017                         free( fbuf );
1018                         return;
1019                 }
1020
1021                 parse_rt_rec( ctx, NULL, rec, vlevel, NULL );           // no pvt context as we can't ack
1022
1023                 rec = eor+1;
1024         }
1025
1026         if( DEBUG ) rmr_vlog_force( RMR_VL_DEBUG, "rmr_read_static:  seed route table successfully parsed: %d records\n", rcount );
1027         free( fbuf );
1028 }
1029
1030 /*
1031         Callback driven for each named thing in a symtab. We collect the pointers to those
1032         things for later use (cloning).
1033 */
1034 static void collect_things( void* st, void* entry, char const* name, void* thing, void* vthing_list ) {
1035         thing_list_t*   tl;
1036
1037         if( (tl = (thing_list_t *) vthing_list) == NULL ) {
1038                 return;
1039         }
1040
1041         if( thing == NULL ) {
1042                 rmr_vlog_force( RMR_VL_DEBUG, "collect things given nil thing: %p %p %p\n", st, entry, name );  // dummy ref for sonar
1043                 return;
1044         }
1045
1046         tl->names[tl->nused] = name;                    // the name/key
1047         tl->things[tl->nused++] = thing;                // save a reference to the thing
1048 }
1049
1050 /*
1051         Called to delete a route table entry struct. We delete the array of endpoint
1052         pointers, but NOT the endpoints referenced as those are referenced from
1053         multiple entries.
1054
1055         Route table entries can be concurrently referenced by multiple symtabs, so
1056         the actual delete happens only if decrementing the rte's ref count takes it
1057         to 0. Thus, it is safe to call this function across a symtab when cleaning up
1058         the symtab, or overlaying an entry.
1059
1060         This function uses ONLY the pointer to the rte (thing) and ignores the other
1061         information that symtab foreach function passes (st, entry, and data) which
1062         means that it _can_ safetly be used outside of the foreach setting. If
1063         the function is changed to depend on any of these three, then a stand-alone
1064         rte_cleanup() function should be added and referenced by this, and refererences
1065         to this outside of the foreach world should be changed.
1066 */
1067 static void del_rte( void* st, void* entry, char const* name, void* thing, void* data ) {
1068         rtable_ent_t*   rte;
1069         int i;
1070
1071         if( (rte = (rtable_ent_t *) thing) == NULL ) {
1072                 rmr_vlog_force( RMR_VL_DEBUG, "delrte given nil table: %p %p %p\n", st, entry, name );  // dummy ref for sonar
1073                 return;
1074         }
1075
1076         rte->refs--;
1077         if( rte->refs > 0 ) {                   // something still referencing, so it lives
1078                 return;
1079         }
1080
1081         if( rte->rrgroups ) {                                                                   // clean up the round robin groups
1082                 for( i = 0; i < rte->nrrgroups; i++ ) {
1083                         if( rte->rrgroups[i] ) {
1084                                 free( rte->rrgroups[i]->epts );                 // ditch list of endpoint pointers (end points are reused; don't trash them)
1085                         }
1086                 }
1087
1088                 free( rte->rrgroups );
1089         }
1090
1091         free( rte );                                                                                    // finally, drop the potato
1092 }
1093
1094 /*
1095         Read an entire file into a buffer. We assume for route table files
1096         they will be smallish and so this won't be a problem.
1097         Returns a pointer to the buffer, or nil. Caller must free.
1098         Terminates the buffer with a nil character for string processing.
1099
1100         If we cannot stat the file, we assume it's empty or missing and return
1101         an empty buffer, as opposed to a nil, so the caller can generate defaults
1102         or error if an empty/missing file isn't tolerated.
1103 */
1104 static char* uta_fib( char const* fname ) {
1105         struct stat     stats;
1106         off_t           fsize = 8192;   // size of the file
1107         off_t           nread;                  // number of bytes read
1108         int                     fd;
1109         char*           buf;                    // input buffer
1110
1111         if( (fd = open( fname, O_RDONLY )) >= 0 ) {
1112                 if( fstat( fd, &stats ) >= 0 ) {
1113                         if( stats.st_size <= 0 ) {                                      // empty file
1114                                 close( fd );
1115                                 fd = -1;
1116                         } else {
1117                                 fsize = stats.st_size;                                          // stat ok, save the file size
1118                         }
1119                 } else {
1120                         fsize = 8192;                                                           // stat failed, we'll leave the file open and try to read a default max of 8k
1121                 }
1122         }
1123
1124         if( fd < 0 ) {                                                                                  // didn't open or empty
1125                 if( (buf = (char *) malloc( sizeof( char ) * 1 )) == NULL ) {
1126                         return NULL;
1127                 }
1128
1129                 *buf = 0;
1130                 return buf;
1131         }
1132
1133         // add a size limit check here
1134
1135         if( (buf = (char *) malloc( sizeof( char ) * fsize + 2 )) == NULL ) {           // enough to add nil char to make string
1136                 close( fd );
1137                 errno = ENOMEM;
1138                 return NULL;
1139         }
1140
1141         nread = read( fd, buf, fsize );
1142         if( nread < 0 || nread > fsize ) {                                                      // failure of some kind
1143                 free( buf );
1144                 errno = EFBIG;                                                                                  // likely too much to handle
1145                 close( fd );
1146                 return NULL;
1147         }
1148
1149         buf[nread] = 0;
1150
1151         close( fd );
1152         return buf;
1153 }
1154
1155 // --------------------- initialisation/creation ---------------------------------------------
1156 /*
1157         Create and initialise a route table; Returns a pointer to the table struct.
1158 */
1159 static route_table_t* uta_rt_init( uta_ctx_t* ctx ) {
1160         route_table_t*  rt;
1161
1162         if( ctx == NULL ) {
1163                 return NULL;
1164         }
1165         if( (rt = (route_table_t *) malloc( sizeof( route_table_t ) )) == NULL ) {
1166                 return NULL;
1167         }
1168
1169         memset( rt, 0, sizeof( *rt ) );
1170
1171         if( (rt->hash = rmr_sym_alloc( RT_SIZE )) == NULL ) {
1172                 free( rt );
1173                 return NULL;
1174         }
1175
1176         rt->gate = ctx->rtgate;                                         // single mutex needed for all route tables
1177         rt->ephash = ctx->ephash;                                       // all route tables share a common endpoint hash
1178         pthread_mutex_init( rt->gate, NULL );
1179
1180         return rt;
1181 }
1182
1183 /*
1184         Clones one of the spaces in the given table.
1185         Srt is the source route table, Nrt is the new route table; if nil, we allocate it.
1186         Space is the space in the old table to copy. Space 0 uses an integer key and
1187         references rte structs. All other spaces use a string key and reference endpoints.
1188 */
1189 static route_table_t* rt_clone_space( uta_ctx_t* ctx, route_table_t* srt, route_table_t* nrt, int space ) {
1190         endpoint_t*     ep;                     // an endpoint (ignore sonar complaint about const*)
1191         rtable_ent_t*   rte;    // a route table entry  (ignore sonar complaint about const*)
1192         void*   sst;                    // source symtab
1193         void*   nst;                    // new symtab
1194         thing_list_t things;    // things from the space to copy
1195         int             i;
1196         int             free_on_err = 0;
1197
1198         if( ctx == NULL ) {
1199                 return NULL;
1200         }
1201         if( nrt == NULL ) {                             // make a new table if needed
1202                 free_on_err = 1;
1203                 nrt = uta_rt_init( ctx );
1204                 if( nrt == NULL ) {
1205                         return NULL;
1206                 }
1207         }
1208
1209         if( srt == NULL ) {             // source was nil, just give back the new table
1210                 return nrt;
1211         }
1212
1213         things.nalloc = 2048;
1214         things.nused = 0;
1215         things.things = (void **) malloc( sizeof( void * ) * things.nalloc );
1216         memset( things.things, 0, sizeof( sizeof( void * ) * things.nalloc ) );
1217         things.names = (const char **) malloc( sizeof( char * ) * things.nalloc );
1218         memset( things.names, 0, sizeof( char * ) * things.nalloc );
1219         if( things.things == NULL ) {
1220                 if( free_on_err ) {
1221                         rmr_sym_free( nrt->hash );
1222                         free( nrt );
1223                         nrt = NULL;
1224                 }
1225
1226                 return nrt;
1227         }
1228
1229         sst = srt->hash;                                                                                        // convenience pointers (src symtab)
1230         nst = nrt->hash;
1231
1232         rmr_sym_foreach_class( sst, space, collect_things, &things );           // collect things from this space
1233
1234         if( DEBUG ) rmr_vlog_force( RMR_VL_DEBUG, "clone space cloned %d things in space %d\n",  things.nused, space );
1235         for( i = 0; i < things.nused; i++ ) {
1236                 if( space ) {                                                                                           // string key, epoint reference
1237                         ep = (endpoint_t *) things.things[i];
1238                         rmr_sym_put( nst, things.names[i], space, ep );                                 // slam this one into the new table
1239                 } else {
1240                         rte = (rtable_ent_t *) things.things[i];
1241                         rte->refs++;                                                                                    // rtes can be removed, so we track references
1242                         rmr_sym_map( nst, rte->key, rte );                                              // add to hash using numeric mtype/sub-id as key (default to space 0)
1243                 }
1244         }
1245
1246         free( things.things );
1247         free( (void *) things.names );
1248         return nrt;
1249 }
1250
1251 /*
1252         Given a destination route table (drt), clone from the source (srt) into it.
1253         If drt is nil, alloc a new one. If srt is nil, then nothing is done (except to
1254         allocate the drt if that was nil too). If all is true (1), then we will clone both
1255         the MT and the ME spaces; otherwise only the ME space is cloned.
1256 */
1257 static route_table_t* uta_rt_clone( uta_ctx_t* ctx, route_table_t* srt, route_table_t* drt, int all ) {
1258         endpoint_t*             ep;                             // an endpoint
1259         rtable_ent_t*   rte;                    // a route table entry
1260         int i;
1261
1262         if( ctx == NULL ) {
1263                 return NULL;
1264         }
1265         if( drt == NULL ) {
1266                 drt = uta_rt_init( ctx );
1267         }
1268         if( srt == NULL ) {
1269                 return drt;
1270         }
1271
1272         drt->ephash = ctx->ephash;                                              // all rts reference the same EP symtab
1273         rt_clone_space( ctx, srt, drt, RT_ME_SPACE );
1274         if( all ) {
1275                 rt_clone_space( ctx, srt, drt, RT_MT_SPACE );
1276         }
1277
1278         return drt;
1279 }
1280
1281 /*
1282         Prepares the "new" route table for populating. If the old_rtable is not nil, then
1283         we wait for it's use count to reach 0. Then the table is cleared, and moved on the
1284         context to be referenced by the new pointer; the old pointer is set to nil.
1285
1286         If the old table doesn't exist, then a new table is created and the new pointer is
1287         set to reference it.
1288 */
1289 static route_table_t* prep_new_rt( uta_ctx_t* ctx, int all ) {
1290         int counter = 0;
1291         route_table_t*  rt;
1292
1293         if( ctx == NULL ) {
1294                 return NULL;
1295         }
1296
1297         if( (rt = ctx->old_rtable) != NULL ) {
1298                 ctx->old_rtable = NULL;
1299                 while( rt->ref_count > 0 ) {                    // wait for all who are using to stop
1300                         if( counter++ > 1000 ) {
1301                                 rmr_vlog( RMR_VL_WARN, "rt_prep_newrt:  internal mishap, ref count on table seems wedged" );
1302                                 break;
1303                         }
1304
1305                         usleep( 1000 );                                         // small sleep to yield the processer if that is needed
1306                 }
1307
1308                 if( rt->hash != NULL ) {
1309                         rmr_sym_foreach_class( rt->hash, 0, del_rte, NULL );            // deref and drop if needed
1310                         rmr_sym_clear( rt->hash );                                                                      // clear all entries from the old table
1311                 }
1312         } else {
1313                 rt = NULL;
1314         }
1315
1316         rt = uta_rt_clone( ctx, ctx->rtable, rt, all );         // also sets the ephash pointer
1317         rt->ref_count = 0;                                                                      // take no chances; ensure it's 0!
1318
1319         return rt;
1320 }
1321
1322
1323 /*
1324         Given a name, find the endpoint struct in the provided route table.
1325 */
1326 static endpoint_t* uta_get_ep( route_table_t* rt, char const* ep_name ) {
1327
1328         if( rt == NULL || rt->ephash == NULL || ep_name == NULL || *ep_name == 0 ) {
1329                 return NULL;
1330         }
1331
1332         return rmr_sym_get( rt->ephash, ep_name, 1 );
1333 }
1334
1335 /*
1336         Drop the given route table. Purge all type 0 entries, then drop the symtab itself.
1337         Does NOT destroy the gate as it's a common gate for ALL route tables.
1338 */
1339 static void uta_rt_drop( route_table_t* rt ) {
1340         if( rt == NULL ) {
1341                 return;
1342         }
1343
1344         rmr_sym_foreach_class( rt->hash, 0, del_rte, NULL );            // free each rte referenced by the hash, but NOT the endpoints
1345         rmr_sym_free( rt->hash );                                                                       // free all of the hash related data
1346         free( rt );
1347 }
1348
1349 /*
1350         Look up and return the pointer to the endpoint stuct matching the given name.
1351         If not in the hash, a new endpoint is created, added to the hash. Should always
1352         return a pointer.
1353 */
1354 static endpoint_t* rt_ensure_ep( route_table_t* rt, char const* ep_name ) {
1355         endpoint_t*     ep;
1356
1357         if( !rt || !ep_name || ! *ep_name ) {
1358                 rmr_vlog( RMR_VL_WARN, "rt_ensure:  internal mishap, something undefined rt=%p ep_name=%p\n", rt, ep_name );
1359                 errno = EINVAL;
1360                 return NULL;
1361         }
1362
1363         if( (ep = uta_get_ep( rt, ep_name )) == NULL ) {                                        // not there yet, make
1364                 if( (ep = (endpoint_t *) malloc( sizeof( *ep ) )) == NULL ) {
1365                         rmr_vlog( RMR_VL_WARN, "rt_ensure:  malloc failed for endpoint creation: %s\n", ep_name );
1366                         errno = ENOMEM;
1367                         return NULL;
1368                 }
1369
1370                 ep->notify = 1;                                                         // show notification on first connection failure
1371                 ep->open = 0;                                                           // not connected
1372                 ep->addr = uta_h2ip( ep_name );
1373                 ep->name = strdup( ep_name );
1374                 pthread_mutex_init( &ep->gate, NULL );          // init with default attrs
1375                 memset( &ep->scounts[0], 0, sizeof( ep->scounts ) );
1376
1377                 rmr_sym_put( rt->ephash, ep_name, 1, ep );
1378         }
1379
1380         return ep;
1381 }
1382
1383
1384 /*
1385         Given a session id and message type build a key that can be used to look up the rte in the route
1386         table hash. Sub_id is expected to be -1 if there is no session id associated with the entry.
1387 */
1388 static inline uint64_t build_rt_key( int32_t sub_id, int32_t mtype ) {
1389         uint64_t key;
1390
1391         if( sub_id == UNSET_SUBID ) {
1392                 key = 0xffffffff00000000 | mtype;
1393         } else {
1394                 key = (((uint64_t) sub_id) << 32) | (mtype & 0xffffffff);
1395         }
1396
1397         return key;
1398 }
1399
1400 /*
1401         Given a route table and meid string, find the owner (if known). Returns a pointer to
1402         the endpoint struct or nil.
1403 */
1404 static inline endpoint_t*  get_meid_owner( route_table_t *rt, char const* meid ) {
1405         endpoint_t const* ep;           // the ep we found in the hash
1406
1407         if( rt == NULL || rt->hash == NULL || meid == NULL || *meid == 0 ) {
1408                 return NULL;
1409         }
1410
1411         return (endpoint_t *) rmr_sym_get( rt->hash, meid, RT_ME_SPACE );
1412 }
1413
1414 /*
1415         This returns a pointer to the currently active route table and ups
1416         the reference count so that the route table is not freed while it
1417         is being used. The caller MUST call release_rt() when finished
1418         with the pointer.
1419
1420         Care must be taken: the ctx->rtable pointer _could_ change during the time
1421         between the release of the lock and the return. Therefore we MUST grab
1422         the current pointer when we have the lock so that if it does we don't
1423         return a pointer to the wrong table.
1424
1425         This will return NULL if there is no active table.
1426 */
1427 static inline route_table_t* get_rt( uta_ctx_t* ctx ) {
1428         route_table_t*  rrt;                    // return value
1429
1430         if( ctx == NULL || ctx->rtable == NULL ) {
1431                 return NULL;
1432         }
1433
1434         pthread_mutex_lock( ctx->rtgate );                              // must hold lock to bump use
1435         rrt = ctx->rtable;                                                              // must stash the pointer while we hold lock
1436         rrt->ref_count++;
1437         pthread_mutex_unlock( ctx->rtgate );
1438
1439         return rrt;                                                                             // pointer we upped the count with
1440 }
1441
1442 /*
1443         This will "release" the route table by reducing the use counter
1444         in the table. The table may not be freed until the counter reaches
1445         0, so it's imparative that the pointer be "released" when it is
1446         fetched by get_rt().  Once the caller has released the table it
1447         may not safely use the pointer that it had.
1448 */
1449 static inline void release_rt( uta_ctx_t* ctx, route_table_t* rt ) {
1450         if( ctx == NULL || rt == NULL ) {
1451                 return;
1452         }
1453
1454         pthread_mutex_lock( ctx->rtgate );                              // must hold lock
1455         if( rt->ref_count > 0 ) {                                               // something smells if it's already 0, don't do antyhing if it is
1456                 rt->ref_count--;
1457         }
1458         pthread_mutex_unlock( ctx->rtgate );
1459 }
1460 #endif