f3d5858588de34d81d15dde5d308a6ab7e88d205
[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] [-p port | -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-p uses the given port instead of assigning a random port (-r ignored if given)\n"
123                                                 "\t-r causes a random listen port NOT to be used; 43086 is used instead\n"
124                                                 "\t-v enables some amount of extra verbose output to stderr\n", arg0 );
125 }
126
127 int main( int argc, char** argv ) {
128         int             ai = 1;                                                 // arg index
129         int             i;
130
131         void* mrc;                                                              // msg router context
132         rmr_mbuf_t*             mbuf;                                   // message buffer
133         char*   payload;                                                // direct reference to msg payload
134         long    expiry;                                                 // point at which we give up (expire)
135         long    now;                                                    // current time
136         long    max_timeout = 3;                                // (seconds) -t to overrride
137         char*   target = "localhost:4560";              // address of target to ping
138         char*   listen_port = NULL;                             // the port we open for "backhaul" connections (somewhat random by default)
139         int             rand_port = 1;                                  // -r turns off and we use a default port value
140         char*   tok;                                                    // pointer at token in a buffer
141         char    wbuf[MAX_MSG_SZ];                               // work buffer
142         char*   rbuf;                                                   // spot to work on the response
143         int             whid;                                                   // id of wormhole
144         int             num2send = 1;                                   // number of messages to send
145         int             count = 0;
146         int             good = 0;                                               // num good messages back
147         int             verbose = 0;
148         int             et;                                                             // elapsed time
149         struct  timespec out_ts;                                // time we sent the message
150         struct  timespec in_ts;                                 // time we got response
151
152         // ---- simple arg parsing ------
153         while( ai < argc ) {
154                 if( *argv[ai] == '-' ) {
155                         switch( argv[ai][1] ) {
156                                 case 'h':                                       // host:port
157                                         ai++;
158                                         target = strdup( argv[ai] );
159                                         break;
160
161                                 case 'n':                                       // num to send
162                                         ai++;
163                                         num2send = atoi( argv[ai] );
164                                         break;
165
166                                 case 'p':                                       // num to send
167                                         ai++;
168                                         listen_port = strdup( argv[ai] );
169                                         break;
170
171                                 case 'r':                                       // generate random listen port
172                                         rand_port = 0;
173                                         break;
174
175                                 case 't':                                       // timeout
176                                         ai++;
177                                         max_timeout = atoi( argv[ai] );
178                                         break;
179
180                                 case 'v':
181                                         verbose = 1;
182                                         break;
183
184                                 case '?':       usage( argv[0] );
185                                                         exit( 0 );
186
187                                 default:
188                                         fprintf( stderr, "[FAIL] unrecognised option: %s\n", argv[ai] );
189                                         usage( argv[0] );
190                                         exit( 1 );
191                         }
192
193                         ai++;
194                 } else {
195                         break;          // not an option, leave with a1 @ first positional parm
196                 }
197         }
198
199
200         if( listen_port == NULL ) {
201                 if( rand_port ) {                               // generate a somewhat random listen port (RMR needs)
202                         srand( time( NULL ) );
203                         snprintf( wbuf, sizeof( wbuf ), "%d", 43000 + (rand() % 1000) );
204                         listen_port = strdup( wbuf );
205                 } else {
206                         listen_port = "43086";          // -r given to disable random; go with static value
207                 }
208         }
209
210
211         VERBOSE( "listen port: %s; sending %d messages\n", listen_port, num2send );
212
213         if( (mrc = rmr_init( listen_port, MAX_MSG_SZ, RMRFL_NOTHREAD )) == NULL ) {             // start without route table listener thread
214                 fprintf( stderr, "[FAIL] unable to initialise RMR\n" );
215                 exit( 1 );
216         }
217         while( ! rmr_ready( mrc ) ) {
218                 VERBOSE( "waiting for RMR to show ready\n" );
219                 sleep( 1 );
220         }
221         VERBOSE( "RMR initialised\n" );
222
223         mbuf = rmr_alloc_msg( mrc, MAX_MSG_SZ );                // enough room for us, and provide app with extra room for response
224
225         VERBOSE( "starting session with %s, starting to send\n", target );
226         whid = rmr_wh_open( mrc, target );                                                              // open a wormhole directly to the target
227         if( whid < 0 ) {
228                 fprintf( stderr, "[FAIL] unable to connect to %s\n", target );
229                 exit( 1 );
230         }
231
232         VERBOSE( "connected to %s, sending %d pings\n", target, num2send );
233
234         now = time( NULL );
235         expiry =  now + max_timeout;                                            // when we can end this madness
236
237         while( count < num2send && now < expiry ) {                     // until we've sent and recevied n things, or we expire
238                 if( !mbuf ) {
239                         fprintf( stderr, "[FAIL] internal mishap: mbuf is nil?\n" );
240                         exit( 1 );
241                 }
242
243                 payload = mbuf->payload;
244                 snprintf( payload, PAYLOAD_SZ-1, "health check request prev=%d <eom>", count );
245
246                 mbuf->mtype = RIC_HEALTH_CHECK_REQ;
247                 mbuf->sub_id = -1;
248                 mbuf->len =  PAYLOAD_SZ;                // yes; we're sending more than we filled so xapp has good size for response data if needed
249                 mbuf->state = 0;
250
251                 VERBOSE( "sending message: %s\n", payload );
252                 clock_gettime( CLOCK_MONOTONIC, &out_ts );              // mark time out
253                 mbuf = rmr_wh_send_msg( mrc, whid, mbuf );
254
255                 if( mbuf->state == RMR_OK ) {                                                   // good send, wait for response
256                         do {
257                                 mbuf = rmr_torcv_msg( mrc, mbuf, 250 );                                 // wait for a response, but break often to check for timeout
258                                 clock_gettime( CLOCK_MONOTONIC, &in_ts );                               // mark time in (assuming there is a message)
259                                 now = time( NULL );
260                         } while( mbuf->state == RMR_ERR_TIMEOUT && now < expiry );
261
262                         if( mbuf->state == RMR_OK ) {
263                                 if( mbuf->mtype == RIC_HEALTH_CHECK_RESP ) {
264                                         payload = mbuf->payload;
265                                         memset( wbuf, 0, sizeof( wbuf ) );
266                                         memcpy( wbuf, payload, mbuf->len > sizeof( wbuf ) ? sizeof(wbuf)-1 : mbuf->len );
267                                         VERBOSE( "got: (%s) state=%d\n", wbuf, mbuf->state );
268
269                                         if( strncmp( payload, "OK", 2 ) == 0 ) {
270                                                 good++;
271                                                 et = elapsed( &out_ts, &in_ts );
272                                                 VERBOSE( "good response received; elapsed time = %d mu-sec\n", et );
273                                         } else {
274                                                 fprintf( stderr, "[WARN] xAPP response: %s\n", wbuf );
275                                         }
276                                 } else {
277                                         fprintf( stderr, "[WARN] invalid message type received: %d\n", mbuf->mtype );
278                                 }
279
280                                 count++;
281                         } else {
282                                 fprintf( stderr, "[FAIL] too few messages recevied during timeout window: wanted %d got %d\n", num2send, count );
283                                 break;
284                         }
285                 } else {
286                         fprintf( stderr, "[FAIL] send failed: %d %d %s\n", mbuf->state, errno, strerror( errno ) );
287                         break;
288                 }
289
290                 now = time( NULL );
291         }
292
293         rmr_wh_close( mrc, whid );
294
295         return good == 0;                       // if none were good, then exit 1
296 }
297