1 // :vim ts=4 sw=4 noet:
3 ==================================================================================
4 Copyright (c) 2020 Nokia
5 Copyright (c) 2020 AT&T Intellectual Property.
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
11 http://www.apache.org/licenses/LICENSE-2.0
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 ==================================================================================
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
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).
37 Responses are expected to have a payload containing ASCII data.
38 The first, space separated token, is expected to be one of:
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.
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.
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)
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.
60 Date: 26 February 2020
61 Author: E. Scott Daniels
69 #include <sys/epoll.h>
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
81 #include <RIC_message_types.h>
83 #define MAX_MSG_SZ 2048 // max size we'll expect back
84 #define PAYLOAD_SZ 1024 // size of payload we will send
86 #define VERBOSE(...) if(verbose){ fprintf( stderr, "[INFO] " __VA_ARGS__);}
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
96 // ---------------------------------------------------------------------------
99 Compute the elapsed time between ts1 and ts2.
102 static int elapsed( struct timespec* start_ts, struct timespec* end_ts ) {
107 start = ( start_ts->tv_sec * 1000000000) + start_ts->tv_nsec;
108 end = ( end_ts->tv_sec * 1000000000) + end_ts->tv_nsec;
110 bin = (end - start) / 1000; // to mu-sec
111 //bin = (end - start);
116 static void usage( char* arg0 ) {
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 );
128 This validates the arg index is in range (< argc). If it is not
129 valid, the a message is issued and we abort.
131 static void vet_ai( int ai, int argc, char* arg0 ) {
132 if( ai < argc && ai > 0 ) {
136 fprintf( stderr, "abort: command line parameter(s) missing\n" );
141 int main( int argc, char** argv ) {
142 int ai = 1; // arg index
145 void* mrc; // msg router context
146 rmr_mbuf_t* mbuf; // message buffer
147 char* payload; // direct reference to msg payload
148 long expiry; // point at which we give up (expire)
149 long now; // current time
150 long max_timeout = 3; // (seconds) -t to overrride
151 char* target = "localhost:4560"; // address of target to ping
152 char* listen_port = NULL; // the port we open for "backhaul" connections (somewhat random by default)
153 int rand_port = 1; // -r turns off and we use a default port value
154 char* tok; // pointer at token in a buffer
155 char wbuf[MAX_MSG_SZ]; // work buffer
156 char* rbuf; // spot to work on the response
157 int whid; // id of wormhole
158 int num2send = 1; // number of messages to send
160 int good = 0; // num good messages back
162 int et; // elapsed time
163 struct timespec out_ts; // time we sent the message
164 struct timespec in_ts; // time we got response
166 // ---- simple arg parsing ------
168 if( *argv[ai] == '-' ) {
169 switch( argv[ai][1] ) {
170 case 'h': // host:port
172 vet_ai( ai, argc, argv[0] );
173 target = strdup( argv[ai] );
176 case 'n': // num to send
178 vet_ai( ai, argc, argv[0] );
179 num2send = atoi( argv[ai] );
182 case 'p': // num to send
184 vet_ai( ai, argc, argv[0] );
185 listen_port = strdup( argv[ai] );
188 case 'r': // generate random listen port
194 vet_ai( ai, argc, argv[0] );
195 max_timeout = atoi( argv[ai] );
202 case '?': usage( argv[0] );
206 fprintf( stderr, "[FAIL] unrecognised option: %s\n", argv[ai] );
213 break; // not an option, leave with a1 @ first positional parm
218 if( listen_port == NULL ) {
219 if( rand_port ) { // generate a somewhat random listen port (RMR needs)
220 srand( time( NULL ) );
221 snprintf( wbuf, sizeof( wbuf ), "%d", 43000 + (rand() % 1000) );
222 listen_port = strdup( wbuf );
224 listen_port = "43086"; // -r given to disable random; go with static value
229 VERBOSE( "listen port: %s; sending %d messages\n", listen_port, num2send );
231 if( (mrc = rmr_init( listen_port, MAX_MSG_SZ, RMRFL_NOTHREAD )) == NULL ) { // start without route table listener thread
232 fprintf( stderr, "[FAIL] unable to initialise RMR\n" );
235 while( ! rmr_ready( mrc ) ) {
236 VERBOSE( "waiting for RMR to show ready\n" );
239 VERBOSE( "RMR initialised\n" );
241 mbuf = rmr_alloc_msg( mrc, MAX_MSG_SZ ); // enough room for us, and provide app with extra room for response
243 VERBOSE( "starting session with %s, starting to send\n", target );
244 whid = rmr_wh_open( mrc, target ); // open a wormhole directly to the target
246 fprintf( stderr, "[FAIL] unable to connect to %s\n", target );
250 VERBOSE( "connected to %s, sending %d pings\n", target, num2send );
253 expiry = now + max_timeout; // when we can end this madness
255 while( count < num2send && now < expiry ) { // until we've sent and recevied n things, or we expire
257 fprintf( stderr, "[FAIL] internal mishap: mbuf is nil?\n" );
261 payload = mbuf->payload;
262 snprintf( payload, PAYLOAD_SZ-1, "health check request prev=%d <eom>", count );
264 mbuf->mtype = RIC_HEALTH_CHECK_REQ;
266 mbuf->len = PAYLOAD_SZ; // yes; we're sending more than we filled so xapp has good size for response data if needed
269 VERBOSE( "sending message: %s\n", payload );
270 clock_gettime( CLOCK_MONOTONIC, &out_ts ); // mark time out
271 mbuf = rmr_wh_send_msg( mrc, whid, mbuf );
273 if( mbuf->state == RMR_OK ) { // good send, wait for response
275 mbuf = rmr_torcv_msg( mrc, mbuf, 250 ); // wait for a response, but break often to check for timeout
276 clock_gettime( CLOCK_MONOTONIC, &in_ts ); // mark time in (assuming there is a message)
278 } while( mbuf->state == RMR_ERR_TIMEOUT && now < expiry );
280 if( mbuf->state == RMR_OK ) {
281 if( mbuf->mtype == RIC_HEALTH_CHECK_RESP ) {
282 payload = mbuf->payload;
283 memset( wbuf, 0, sizeof( wbuf ) );
284 memcpy( wbuf, payload, mbuf->len > sizeof( wbuf ) ? sizeof(wbuf)-1 : mbuf->len );
285 VERBOSE( "got: (%s) state=%d\n", wbuf, mbuf->state );
287 if( strncmp( payload, "OK", 2 ) == 0 ) {
289 et = elapsed( &out_ts, &in_ts );
290 VERBOSE( "good response received; elapsed time = %d mu-sec\n", et );
292 fprintf( stderr, "[WARN] xAPP response: %s\n", wbuf );
295 fprintf( stderr, "[WARN] invalid message type received: %d\n", mbuf->mtype );
300 fprintf( stderr, "[FAIL] too few messages recevied during timeout window: wanted %d got %d\n", num2send, count );
304 fprintf( stderr, "[FAIL] send failed: %d %d %s\n", mbuf->state, errno, strerror( errno ) );
311 rmr_wh_close( mrc, whid );
313 return good == 0; // if none were good, then exit 1