Add alarm generation when application is slow
[ric-plt/lib/rmr.git] / src / rmr / common / src / tools_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:       tools_static.c
23         Abstract:       A small set of very simple tools to support Uta == RMR.
24                                         uta_tokenise -- simple string tokeniser
25                                         uta_rmip_tokenise -- tokenise and remove ip addresses from the list
26                                         uta_h2ip        -- look up host name and return an ip address
27                                         uta_lookup_rtg  -- looks in env for rtg host:port
28                                         uta_has_str     -- searches buffer of tokens for a string
29
30                                         uta_link2       -- establish a nanomsg connection to a host
31
32                                         uta_dump_env -- dump the environment variables to stdout that are
33                                                                 of importance to RMR.
34
35         Author:         E. Scott Daniels
36         Date:           30 November 2018
37 */
38
39 #ifndef _tools_static_c
40 #define _tools_static_c
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <netdb.h>
45 #include <errno.h>
46 #include <string.h>
47 #include <errno.h>
48 #include <pthread.h>
49 #include <ctype.h>
50
51 #include <sys/types.h>          // these are needed to suss out ip addresses from interfaces
52 #include <ifaddrs.h>
53 #include <arpa/inet.h>
54 #include <sys/socket.h>
55 #include <netdb.h>
56
57 // --- some protos needed for better organisation --------
58 static int is_this_myip( if_addrs_t* l, char* addr );
59
60
61 // ----------------------------------------------------------------------------------
62
63
64 /*
65         A strncpy() replacement that ensures the resulting dest buffer has
66         a zero (nil) terminator even if the source is longer than the dest
67         length.
68         A max of len-1 bytes are copied from src to dest. The copy stops when
69         a zero (nil) is encountered in the src, or len-1 bytes are copied.
70         The string is always nil terminated.
71         The string length is returned.
72
73         It is the responsiblity of the caller to ensure that dest is at
74         least len bytes in length.
75
76         If either src/dest is invalid (nil) a value of -1 is returned.
77 */
78 static inline int zt_buf_fill( char* dest, char const* src, int len ) {
79         char*           dp;
80         char const*     sp;
81         int                     n;              // num moved
82
83         if( dest == NULL || src == NULL ) {
84                 return -1;
85         }
86
87         dp = dest;
88         sp = src;
89         n = 0;
90         while(  *sp && n < len-1 ) {
91                 *(dp++) = *(sp++);
92                 n++;
93         }
94
95         *dp = 0;
96         return n;
97 }
98
99 /*
100         Simple tokeniser. Split a null terminated string into tokens recording the
101         pointers in the tokens array provided.  Tokens MUST be large enough. Max is
102         the max number of tokens to split into.  Returns the actual number of tokens
103         recorded in the pointer array.
104
105         CAUTION: this modifies the string passed in!!
106 */
107 static int uta_tokenise( char* buf, char** tokens, int max, char sep ) {
108         char* end;                                      // end of token
109         int     n = 0;
110
111         if( !buf || ! tokens || !(*buf) ) {
112                 return 0;
113         }
114
115         tokens[n++] = buf;
116         end = buf;
117         while( n < max && *end && (end = strchr( end, sep )) != NULL ) {
118                 *end = 0;
119                 tokens[n++] = ++end;
120         }
121
122         return n;
123 }
124
125 /*
126         Tokenise and remove matches.
127         Given a buffer of 'sep' separated tokens, and a list of things,
128         return up to max tokens with any tokens that matched things in
129         the list. Toks is the user supplied array of char* which we will
130         fill in (up to max) with pointers to tokens from buf.  This
131         damages buf, so the caller must dup the string if it must be
132         preserved for later, original, use.  The pointers returned in
133         toks will reference portions of bufs.
134
135         Returns the number of tokens referenced by toks.
136 */
137 static int uta_rmip_tokenise( char* buf, if_addrs_t* iplist, char** toks, int max, char sep ) {
138         int     ntoks = 0;                      // total toks in the original buffer
139         int             pcount = 0;                     // count after prune
140         char**  all_toks;
141         int i;
142         int j;
143
144
145         all_toks = malloc( sizeof( char * ) * max );                                    // refernce to all tokens; we'll prune
146         pcount = ntoks = uta_tokenise( buf, all_toks, max, sep );               // split them up
147         j = 0;
148         if( ntoks > 0 ) {
149                 for( i = 0; i < ntoks; i++ ) {
150                         if( is_this_myip( iplist, all_toks[i] ) ) {
151                                 pcount--;                                                                       // ours, prune
152                         } else {
153                                 toks[j++] = all_toks[i];                                        // not one of ours, keep it
154                         }
155                 }
156         }
157
158         free( all_toks );
159         return pcount;
160 }
161
162 /*
163         Xlate hostname (expected to be name:port) to an IP address that nano will tolerate.
164         We'll use the first address from the list to keep it simple. If the first character
165         of the name is a digit, we assume it's really an IP address and just return that.
166
167         Return is a string which the caller must free. Even if the string passed in is already
168         an IP address, a duplicate will be returend so that it can always be freed.
169         On error a nil pointer is returned.
170 */
171 static char* uta_h2ip( char const* hname ) {
172         char                    buf[120];
173         struct hostent* hent;
174         unsigned int    octs[4];
175         unsigned int    a;
176         int                             i;
177         char*                   tok;
178         char*                   dname;          // duplicated name for distruction
179
180         dname = strdup( hname );
181
182         if( isdigit( *dname ) || *dname == '[' ) {              // hostnames can't start with digit, or ipv6 [; assume ip address
183                 return dname;
184         }
185
186         if( (tok = strchr( dname, ':' )) != NULL ) {
187                 *(tok++) = 0;
188         }
189
190         hent = gethostbyname( dname );                  // valgrind will complain that this leaks, but we cannot free it!
191         if( hent == NULL || hent->h_addr_list == NULL ) {
192                 //rmr_vlog( RMR_VL_WARN, "h2ip: dns lookup failed for: %s\n", dname );
193                 free( dname );
194                 return NULL;
195         }
196
197         a = ntohl( *((unsigned int *)hent->h_addr_list[0]) );
198         for( i = 3; i >= 0; i-- ) {
199                 octs[i] = a & 0xff;
200                 a = a >> 8;
201         }
202
203         if( tok ) {                                                     // if :port was given, smash it back on
204                 snprintf( buf, sizeof( buf ), "%d.%d.%d.%d:%s", octs[0], octs[1], octs[2], octs[3], tok );
205         } else {
206                 snprintf( buf, sizeof( buf ), "%d.%d.%d.%d", octs[0], octs[1], octs[2], octs[3] );
207         }
208
209         free( dname );
210         return strdup( buf );
211 }
212
213
214 #ifdef RTG_PUB
215 /*
216         Looks for the environment variable RMR_RTG_SVC which we assume to be name[:port], and
217         does a dns lookup on the name. If the env does not have such a variable, we default to
218         "rtg" and a port of 5656.
219
220         Returns true (1) if lookup found something;
221
222         CAUTION:  this is ONLY used if the RTG is a pub and we are using pub/sub to get updates.
223                         There are issues with some underlying transport pub/sub implementations so this
224                         is likley NOT needed/used.
225 */
226 static int uta_lookup_rtg( uta_ctx_t* ctx ) {
227         char*   ev;                                     // pointer to the env value
228         char*   def_port = "5656";
229         char*   port = NULL;
230         char*   dstr = NULL;
231
232         if( ctx == NULL ) {
233                 return 0;
234         }
235
236
237         if( ctx->rtg_addr ) {
238                 free( ctx->rtg_addr );
239         }
240
241         if( (ev = getenv( "RMR_RTG_SVC" )) == NULL ) {
242                 ev = "rtg";
243                 port = def_port;
244         } else {
245                 dstr = strdup( ev );                    // copy so we can trash it
246                 if( (port = strchr( dstr, ':' )) == NULL ) {
247                         port = def_port;
248                 } else {
249                         *port = 0;
250                         port++;                                         // point at the first digit
251                 }
252                 ev = dstr;                                              // all references below assume ev
253         }
254
255         ctx->rtg_addr = uta_h2ip( ev );         // convert name to IP addr
256         ctx->rtg_port = atoi( port );
257         if( dstr ) {
258                 free( dstr );
259         }
260
261         return ctx->rtg_addr != NULL;
262 }
263 #endif
264
265
266 /*
267         Expects a buffer of 'sep' separated tokens and looks to see if
268         the given string is one of those tokens. Returns the token
269         index (0 - n-1) if the string is found; -1 otherwise. The max
270         parameter supplies the maximum number of tokens to search in
271         the buffer.
272
273         On failure (-1) errno will be set in cases where memory cannot
274         be alocated (is this even possible any more?). If errno is 0
275         and failure is returned, then the caller should assume that
276         the token isn't in the list, or the list had no elements.
277 */
278 static int uta_has_str( char const* buf, char const* str, char sep, int max ) {
279         char*   dbuf;                   // duplicated buf so we can trash
280         char** tokens;                  // pointer to tokens from the string
281         int             ntokens;                // number of tokens buf split into
282         int             i;
283         int             rc;                             // return code
284
285         if( max < 2 ) {
286                 return -1;
287         }
288
289         dbuf = strdup( buf );
290         if( dbuf == NULL  ) {
291                 errno = ENOMEM;
292                 return -1;
293         }
294
295         if( (tokens = (char **) malloc( sizeof( char * ) * max )) == NULL ) {
296                 errno = ENOMEM;
297                 free( dbuf );
298                 return -1;
299         }
300
301         ntokens = uta_tokenise( dbuf, tokens, max, sep );
302         errno = 0;
303         rc = -1;
304         for( i = 0; rc < 0 && i < ntokens; i++ ) {
305                 if( tokens[i] ) {
306                         if( strcmp( tokens[i], str ) == 0 ) {
307                                 rc = i;
308                         }
309                 }
310         }
311
312         free( dbuf );
313         free( tokens );
314         return rc;
315 }
316
317 /*
318         Generate a list of all IP address associated with the interfaces available.
319         For now we capture them all, but we may need to limit. The port is smashed
320         onto each IP we find so that we can do a direct compare against the addr
321         that could be in the route table.
322
323         If the environment variable which limits the binding of our listen port
324         to a single interface (ENV_BIND_IF) then ONLY that interface/address is added
325         to the list so that we don't pick up entries from the rtable that are for other
326         processes listening on different interfaces.
327
328         The ENV_BIN_IF environment variable may be either an IP address (v6 must be in
329         square braces), or an interface name (e.g. eth0).
330 */
331 static if_addrs_t*  mk_ip_list( char* port ) {
332         if_addrs_t* l;
333         struct  ifaddrs *ifs;           // pointer to head
334         struct  ifaddrs *ele;           // pointer into the list
335         char    octs[NI_MAXHOST+1];
336         char    wbuf[NI_MAXHOST+128];
337         char*   fmt = NULL;                     // address format (v4 or v6)
338         char*   envp;                           // at the environment var if there
339         char*   target_if = NULL;       // target interface supplied by ENV_BIND_IF
340         char*   tok;
341
342
343         if( (l = (if_addrs_t *) malloc( sizeof( if_addrs_t ) )) == NULL ) {
344                 return NULL;
345         }
346         memset( l, 0, sizeof( if_addrs_t ) );
347         l->addrs = (char **) malloc( sizeof( char* ) * 128 );
348         if( l->addrs == NULL ) {
349                 free( l );
350                 return NULL;
351         }
352
353         if( (envp = getenv( ENV_BIND_IF )) != NULL ) {
354                 if( isdigit( *envp ) || *envp == '[' ) {                                        // ip address given and not device name
355                         snprintf( wbuf, sizeof( wbuf ), "%s:%s", envp, port );          // smash port onto the addr as is
356                         l->addrs[l->naddrs] = strdup( wbuf );
357                         l->naddrs++;
358                         if( DEBUG ) rmr_vlog( RMR_VL_INFO, "rmr: using only specific bind interface when searching specific RT entries: %s\n", wbuf );
359                         return l;
360                 }
361
362                 target_if = envp;               // device name given, suss it out below
363         }
364
365         getifaddrs( &ifs );
366         for( ele = ifs; ele; ele = ele->ifa_next ) {
367                 memset( octs, 0, sizeof( octs ) );
368                 if( ele && strcmp( ele->ifa_name, "lo" ) &&                                                                     // do NOT capture the loopback interface address
369                         (target_if == NULL || strcmp( ele->ifa_name, target_if ) == 0 ) ) {             // no target, or matches ENV_BIND_IF target
370
371                         if( ele->ifa_addr != NULL ) {                                           // possible for some interfaces to not have an address
372                                 if( ele->ifa_addr->sa_family == AF_INET ) {
373                                         getnameinfo( ele->ifa_addr, sizeof( struct sockaddr_in ),  octs, NI_MAXHOST, NULL, 0, NI_NUMERICHOST );
374                                         fmt = "%s:%s";
375                                 } else {
376                                         if( ele->ifa_addr->sa_family == AF_INET6 ) {
377                                                 getnameinfo( ele->ifa_addr, sizeof( struct sockaddr_in6 ),  octs, NI_MAXHOST, NULL, 0, NI_NUMERICHOST );
378                                                 fmt = "[%s]:%s";
379                                         }
380                                 }
381
382                                 if( *octs  && fmt != NULL ) {                           // possible that we didn't recognise the format (v4 or v6), don't try if we didn't
383                                         if( (tok = strchr( octs, '%' )) != NULL ) {                     // for unknown reasons some ip6 addrs have %if-name appended; truncate
384                                                 *tok = 0;
385                                         }
386                                         if( l->naddrs < 128 ) {
387                                                 if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, "capture address: %s: %s\n", ele->ifa_name, octs );
388
389                                                 snprintf( wbuf, sizeof( wbuf ), fmt, octs, port );              // smash port onto the addr
390                                                 l->addrs[l->naddrs] = strdup( wbuf );
391                                                 l->naddrs++;
392                                         }
393                                 }
394                         }
395                 }
396         }
397
398         if( ifs ) {
399                 freeifaddrs( ifs );
400         }
401
402         return l;
403 }
404
405 /*
406         Check the address:port passed in and return true if it matches
407         one of the addresses we saw when we built the list. Right now
408         this isn't a speed intensive part of our processing, so we just
409         do a straight search through the list. We don't expect this to
410         ever be a higly driven functions so not bothering to optimise.
411 */
412 static int is_this_myip( if_addrs_t* l, char* addr ) {
413         int i;
414
415         if( l == NULL ) {
416                 return 0;
417         }
418
419         if( addr == NULL ) {
420                 return 0;
421         }
422
423         for( i = 0; i < l->naddrs; i++ ) {
424                 if( l->addrs[i] != NULL  &&  strcmp( addr, l->addrs[i] ) == 0 ) {
425                         return 1;
426                 }
427         }
428
429         return 0;
430 }
431
432 /*
433         Expects a buffer containing "sep" separated tokens, and a list of
434         IP addresses anchored by ip_list.  Searches the tokens to see if
435         any are an ip address:port which is in the ip list.  Returns true
436         (1) if a token is in the list, false otherwise.
437 */
438 static int has_myip( char const* buf, if_addrs_t* list, char sep, int max ) {
439         char*   dbuf;                   // duplicated buf so we can trash
440         char** tokens;                  // pointer to tokens from the string
441         int             ntokens;                // number of tokens buf split into
442         int             i;
443         int             rc = 0;                 // return code
444
445         if( max < 2 ) {
446                 return 0;
447         }
448
449         if( buf == NULL ) {
450                 return 0;
451         }
452
453         if( list == NULL ) {
454                 return 0;
455         }
456
457
458         dbuf = strdup( buf );                   // get a copy we can mess with
459         if( dbuf == NULL  ) {
460                 errno = ENOMEM;
461                 return 0;
462         }
463
464         if( (tokens = (char **) malloc( sizeof( char * ) * max )) == NULL ) {
465                 errno = ENOMEM;
466                 free( dbuf );
467                 return 0;
468         }
469
470         ntokens = uta_tokenise( dbuf, tokens, max, sep );
471         errno = 0;
472         rc = 0;
473         for( i = 0; ! rc  && i < ntokens; i++ ) {
474                 if( tokens[i] ) {
475                         if( is_this_myip( list, tokens[i] ) ) {
476                                 rc = 1;
477                                 break;
478                         }
479                 }
480         }
481
482         free( dbuf );
483         free( tokens );
484         return rc;
485 }
486
487 /*
488         Given a list manager block, return the default IP address.
489         For now, that is just the first address on the list which
490         easily could be non-deterministic and change with each restart
491         of the application if a specific interface is not provided via
492         the environment variable (ENV_BIND_IF) and if there is more than
493         one device available on the container/physical host.
494 */
495 static char* get_default_ip( if_addrs_t* iplist ) {
496
497         if( iplist != NULL  &&  iplist->naddrs > 0  &&  iplist->addrs != NULL ) {
498                 return strdup( iplist->addrs[0] );
499         }
500
501         return NULL;
502 }
503
504 /*
505         Write all environment variables that we consider to be important to stderr.
506 */
507 static void uta_dump_env( ) {
508         char* token;
509         char* elist[] = {
510                         ENV_BIND_IF,
511                         ENV_RTG_PORT,
512                         ENV_RTG_ADDR,
513                         ENV_SEED_RT,
514                         ENV_SEED_MEMAP,
515                         ENV_RTG_RAW,
516                         ENV_VERBOSE_FILE,
517                         ENV_NAME_ONLY,
518                         ENV_WARNINGS,
519                         ENV_SRC_ID,
520                         ENV_LOG_HR,
521                         ENV_LOG_VLEVEL,
522                         ENV_CTL_PORT,
523                         ENV_RTREQ_FREA
524         };
525         int i;
526
527         for( i = 0; i < sizeof( elist ) / sizeof( char *); i ++ ) {
528                 token = getenv( elist[i] );
529                 if( token != NULL ) {
530                         rmr_vlog( RMR_VL_INFO, "dump_env: %s = '%s'\n", elist[i], token );
531                 } else {
532                         rmr_vlog( RMR_VL_INFO, "dump_env: %s = <unset>\n", elist[i] );
533                 }
534         }
535 }
536
537 #endif