Rename health_ck support binary to rmr_probe
[ric-plt/lib/rmr.git] / src / support / rmr_probe.c
1 // :vim ts=4 sw=4 noet:
2 /*
3 ==================================================================================
4         Copyright (c) 2020 Nokia
5         Copyright (c) 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:       rmr_probe.c
23         Abstract:       This sends probe messages to the indicated applications. The only
24                                 use currently is to send health check messages and wait for a 
25                                 response. It might be extended later and would cause a bit of
26                                 a redesign, but from the outside the switch to the name rmr_probe
27                                 makes sense. 
28
29                                 Original abstract which stands until the probe does more:
30                                 This is a generic, and very simplistic, health check application
31                                 which will send n RMR messages to an application (localhost:4560
32                                 by default) and expect to receive n responses.  Messages are sent
33                                 with the message type RIC_HEALTH_CHECK_REQ and responses are 
34                                 expected to have a message type RIC_HEALTH_CHECK_RESP (defined in
35                                 the RIC message type header file).
36
37                                 Responses are expected to have a payload containing ASCII data.
38                                 The first, space separated token, is expected to be one of:
39                                         OK
40                                         ERR
41
42                                 to indicate the state of the response. The ERR token may optionally
43                                 be followed with a text string; when present it will be written on
44                                 standard error as an aide to problem determination if needed. 
45
46                                 The programme exit code will be 0 on success (all received messages
47                                 had the OK token), or 1 to indicate failure. A failure reason will
48                                 also be written to standard error.
49
50                                 Command line options and parameters:
51                                         [-h host:port]          target application to "ping"
52                                         [-n num-msgs]           total number of "pings" to send
53                                         [-p port]                       specific port to open and use for responses
54                                         [-t seconds]            max timeout (default 3 seconds)
55
56                                 Route table:  While we don't need a route table to do wormhole sends we
57                                 do need for RMR to initialise an empty one. To avoid having to have a
58                                 dummy table on disk somewhere, we'll create one and "point" RMR at it.
59
60         Date:           26 February 2020
61         Author:         E. Scott Daniels
62 */
63
64 #include <unistd.h>
65 #include <errno.h>
66 #include <string.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <sys/epoll.h>
70 #include <time.h>
71 #include <fcntl.h>
72
73 /*
74    CAUTION:
75                 This is built as a part of the RMR project by CMake, not as a standalone binary.
76                 As such the includes do NOT have the leading 'rmr/' as a normal application
77                 might have.  (there is no such thing as /usr/local/include/rmr while building
78                 the project!)
79 */
80 #include <rmr.h>
81 #include <RIC_message_types.h>
82
83 #define MAX_MSG_SZ      2048            // max size we'll expect back
84 #define PAYLOAD_SZ      1024            // size of payload we will send
85
86 #define VERBOSE(...) if(verbose){ fprintf( stderr,  "[INFO] " __VA_ARGS__);}
87
88 /*
89         we can compute the latency.
90 typedef struct trace_data {
91         char    id[10];                                 // allows us to verify that it's valid
92         struct  timespec out_ts;                // time this payload was sent
93 } trace_data_t;
94 */
95
96 // ---------------------------------------------------------------------------
97
98 /*
99         Compute the elapsed time between ts1 and ts2.
100         Returns mu-seconds.
101 */
102 static int elapsed( struct timespec* start_ts, struct timespec* end_ts ) {
103         long long start;
104         long long end;
105         int bin;
106
107         start = ( start_ts->tv_sec * 1000000000) + start_ts->tv_nsec;
108         end = ( end_ts->tv_sec * 1000000000) + end_ts->tv_nsec;
109
110         bin = (end - start) / 1000;                     // to mu-sec
111         //bin = (end - start);
112
113         return bin;
114 }
115
116 static void usage( char* arg0 ) {
117         
118         fprintf( stderr,        "version 1.0.0\n"
119                                                 "usage: %s [-h host:port] [-n msg-count] [-r] [-t seconds] [-v]\n"
120                                                 "\thost:port may be ip-address:port or name:port of the application\n"
121                                                 "\tmsg-count is the number of health check requests sent; default is 1\n"
122                                                 "\t-r causes a random listen port NOT to be used; 43086 is used instead\n"
123                                                 "\t-v enables some amount of extra verbose output to stderr\n", arg0 );
124 }
125
126 int main( int argc, char** argv ) {
127         int             ai = 1;                                                 // arg index
128         int             i;
129
130         void* mrc;                                                      // msg router context
131         rmr_mbuf_t*             mbuf;                                   // message buffer
132         char*   payload;                                                // direct reference to msg payload
133         long    expiry;                                                 // point at which we give up (expire)
134         long    now;                                                    // current time
135         long    max_timeout = 3;                                // (seconds) -t to overrride
136         char*   target = "localhost:4560";              // address of target to ping
137         char*   listen_port = NULL;                             // the port we open for "backhaul" connections (somewhat random by default)
138         int             rand_port = 1;                                  // -r turns off and we use a default port value
139         char*   tok;                                                    // pointer at token in a buffer
140         char    wbuf[MAX_MSG_SZ];                               // work buffer
141         char*   rbuf;                                                   // spot to work on the response
142         int             whid;                                                   // id of wormhole
143         int             num2send = 1;                                   // number of messages to send
144         int             count = 0;
145         int             good = 0;                                               // num good messages back
146         int             verbose = 0;
147         int             et;                                                             // elapsed time
148         struct  timespec out_ts;                                // time we sent the message
149         struct  timespec in_ts;                                 // time we got response
150
151         // ---- simple arg parsing ------
152         while( ai < argc ) {
153                 if( *argv[ai] == '-' ) {
154                         switch( argv[ai][1] ) {
155                                 case 'h':                                       // host:port
156                                         ai++;
157                                         target = strdup( argv[ai] );
158                                         break;
159
160                                 case 'n':                                       // num to send
161                                         ai++;
162                                         num2send = atoi( argv[ai] );
163                                         break;
164
165                                 case 'p':                                       // num to send
166                                         ai++;
167                                         listen_port = strdup( argv[ai] );
168                                         break;
169
170                                 case 'r':                                       // generate random listen port
171                                         rand_port = 0;
172                                         ;;
173
174                                 case 't':                                       // timeout
175                                         ai++;
176                                         max_timeout = atoi( argv[ai] );
177                                         break;
178
179                                 case 'v':
180                                         verbose = 1;
181                                         break;
182
183                                 case '?':       usage( argv[0] );
184                                                         exit( 0 );
185
186                                 default:
187                                         fprintf( stderr, "[FAIL] unrecognised option: %s\n", argv[ai] );
188                                         usage( argv[0] );
189                                         exit( 1 );
190                         }
191
192                         ai++;
193                 } else {
194                         break;          // not an option, leave with a1 @ first positional parm
195                 }
196         }
197
198         
199         if( listen_port == NULL ) {
200                 if( rand_port ) {                               // generate a somewhat random listen port (RMR needs)
201                         srand( time( NULL ) );
202                         snprintf( wbuf, sizeof( wbuf ), "%d", 43000 + (rand() % 1000) );
203                         listen_port = strdup( wbuf );
204                 } else {
205                         listen_port = "43086";          // -r given to disable random; go with static value
206                 }
207         }
208
209
210         VERBOSE( "listen port: %s; sending %d messages\n", listen_port, num2send );
211
212         if( (mrc = rmr_init( listen_port, MAX_MSG_SZ, RMRFL_NOTHREAD )) == NULL ) {             // start without route table listener thread
213                 fprintf( stderr, "[FAIL] unable to initialise RMR\n" );
214                 exit( 1 );
215         }
216         while( ! rmr_ready( mrc ) ) {
217                 VERBOSE( "waiting for RMR to show ready\n" );
218                 sleep( 1 );
219         }
220         VERBOSE( "RMR initialised\n" );
221
222         mbuf = rmr_alloc_msg( mrc, MAX_MSG_SZ );                // enough room for us, and provide app with extra room for response
223
224         VERBOSE( "starting session with %s, starting to send\n", target );
225         whid = rmr_wh_open( mrc, target );                                                              // open a wormhole directly to the target
226         if( whid < 0 ) {
227                 fprintf( stderr, "[FAIL] unable to connect to %s\n", target );
228                 exit( 1 );
229         }
230
231         VERBOSE( "connected to %s, sending %d pings\n", target, num2send );
232
233         now = time( NULL );
234         expiry =  now + max_timeout;                                            // when we can end this madness
235
236         while( count < num2send && now < expiry ) {                     // until we've sent and recevied n things, or we expire
237                 if( !mbuf ) {
238                         fprintf( stderr, "[FAIL] internal mishap: mbuf is nil?\n" );
239                         exit( 1 );
240                 }
241
242                 payload = mbuf->payload;
243                 snprintf( payload, PAYLOAD_SZ-1, "health check request prev=%d <eom>", count );
244
245                 mbuf->mtype = RIC_HEALTH_CHECK_REQ;
246                 mbuf->sub_id = -1;
247                 mbuf->len =  PAYLOAD_SZ;                // yes; we're sending more than we filled so xapp has good size for response data if needed
248                 mbuf->state = 0;
249
250                 VERBOSE( "sending message: %s\n", payload );
251                 clock_gettime( CLOCK_MONOTONIC, &out_ts );              // mark time out
252                 mbuf = rmr_wh_send_msg( mrc, whid, mbuf );
253
254                 if( mbuf->state == RMR_OK ) {                                                   // good send, wait for response
255                         do {
256                                 mbuf = rmr_torcv_msg( mrc, mbuf, 250 );                                 // wait for a response, but break often to check for timeout
257                                 clock_gettime( CLOCK_MONOTONIC, &in_ts );                               // mark time in (assuming there is a message)
258                                 now = time( NULL );
259                         } while( mbuf->state == RMR_ERR_TIMEOUT && now < expiry );
260
261                         if( mbuf->state == RMR_OK ) {
262                                 if( mbuf->mtype == RIC_HEALTH_CHECK_RESP ) {
263                                         payload = mbuf->payload;
264                                         memset( wbuf, 0, sizeof( wbuf ) );
265                                         memcpy( wbuf, payload, mbuf->len > sizeof( wbuf ) ? sizeof(wbuf)-1 : mbuf->len );
266                                         VERBOSE( "got: (%s) state=%d\n", wbuf, mbuf->state );
267
268                                         if( strncmp( payload, "OK", 2 ) == 0 ) {
269                                                 good++;
270                                                 et = elapsed( &out_ts, &in_ts );
271                                                 VERBOSE( "good response received; elapsed time = %d mu-sec\n", et );
272                                         } else {
273                                                 fprintf( stderr, "[WARN] xAPP response: %s\n", wbuf );
274                                         }
275                                 } else {
276                                         fprintf( stderr, "[WARN] invalid message type received: %d\n", mbuf->mtype );
277                                 }
278
279                                 count++;
280                         } else {
281                                 fprintf( stderr, "[FAIL] too few messages recevied during timeout window: wanted %d got %d\n", num2send, count );
282                                 break;
283                         }
284                 } else {
285                         fprintf( stderr, "[FAIL] send failed: %d %d %s\n", mbuf->state, errno, strerror( errno ) );
286                         break;
287                 }
288
289                 now = time( NULL );
290         }
291
292         rmr_wh_close( mrc, whid );
293
294         return good == 0;                       // if none were good, then exit 1
295 }
296