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