1 // :vim ts=4 sw=4 noet:
3 ==================================================================================
4 Copyright (c) 2019 Nokia
5 Copyright (c) 2018-2019 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 ==================================================================================
22 Mnemonic: health_check.c
23 Abstract: This is a simple programme which sends a 'health check' message to
24 an application and waits for a response. By default, the application
25 is assumed to be running on the local host, and listening on 4560,
26 but both host and port can be configured as needed. Connection is
27 made via a wormhole, so there is no need for a routing table.
29 The application being checked is expected to recognise the health
30 check message type, and to return the message using the RMR return
31 to sender function after changing the message type to "health response,"
32 and leaving the remainder of the payload _unchanged_.
34 A timestamp is placed into the outbound payload, and the round trip
35 latency is reported (the reason the pinged application should not modify
39 Command line options and parameters:
41 [-n num-msgs] total number to send
42 [-t seconds] max timeout per message
44 Route table: While we don't need a route table to do wormhole sends we
45 do need for RMR to initialise an empty one. To avoid having to have a
46 dummy table on disk somewhere, we'll create one and "point" RMR at it.
49 Author: E. Scott Daniels
57 #include <sys/epoll.h>
62 // include message types header
65 #define HEALTH_CHECK 100 // message types
66 #define HEALTH_RESP 101
73 char msg[512]; // message for human consumption
74 struct timespec out_ts; // time this payload was sent
77 // ---------------------------------------------------------------------------
79 Very simple checksum over a buffer.
81 static int sum( unsigned char* buf, int len ) {
88 sum += *(buf++) + i++;
95 Compute the elapsed time between ts1 and ts2.
98 static int elapsed( struct timespec* start_ts, struct timespec* end_ts ) {
103 start = ( start_ts->tv_sec * 1000000000) + start_ts->tv_nsec;
104 end = ( end_ts->tv_sec * 1000000000) + end_ts->tv_nsec;
106 bin = (end - start) / 1000; // to mu-sec
107 //bin = (end - start);
113 See if my id string is in the buffer immediately after the first >.
114 Return 1 if so, 0 if not.
116 static int vet_received( char* me, char* buf ) {
119 if( (ch = strchr( buf, '>' )) == NULL ) {
123 return strcmp( me, ch+1 ) == 0;
127 Create an empty route table and set an environment var for RMR to find.
128 This must be called before initialising RMR.
130 static void mk_rt( ) {
133 char* contents = "newrt|start\nnewrt|end\n";
135 snprintf( fnb, sizeof( fnb ), "/tmp/health_check.rt" );
136 fd = open( fnb, O_CREAT | O_WRONLY, 0664 );
138 fprintf( stderr, "[FAIL] could not create dummy route table: %s %s\n", fnb, strerror( errno ) );
142 write( fd, contents, strlen( contents ) );
143 if( (close( fd ) < 0 ) ) {
144 fprintf( stderr, "[FAIL] couldn't close dummy route table: %s: %s\n", fnb, strerror( errno ) );
148 setenv( "RMR_SEED_RT", fnb, 0 ); // set it, but don't overwrite it
151 int main( int argc, char** argv ) {
152 void* mrc; // msg router context
153 rmr_mbuf_t* mbuf; // message buffer
154 mpl_t* payload; // the payload in a message
155 int ai = 1; // arg index
157 long max_timeout = 5; // -t to overrride
158 char* target = "localhost:4560"; // address of target to ping
159 char* listen_port; // the port we open for "backhaul" connections (random)
160 char* tok; // pointer at token in a buffer
163 char me[128]; // who I am to vet rts was actually from me
164 int rand_port = 0; // -r sets and causes us to generate a random listen port
165 int whid; // id of wormhole
166 int num2send = 1; // number of messages to send
167 int ep_fd = -1; // epoll's file des (given to epoll_wait)
168 int rcv_fd; // file des that NNG tickles -- give this to epoll to listen on
169 int nready; // number of events ready for receive
172 int cksum; // computed simple checksum
173 struct timespec in_ts; // time we got response
174 struct epoll_event events[1]; // list of events to give to epoll
175 struct epoll_event epe; // event definition for event to listen to
177 // ---- simple arg parsing ------
179 if( *argv[ai] == '-' ) {
180 switch( argv[ai][1] ) {
181 case 'h': // host port
183 target = strdup( argv[ai] );
186 case 'n': // num to send
188 num2send = atoi( argv[ai] );
191 case 'r': // generate random listen port
197 max_timeout = atoi( argv[ai] );
201 fprintf( stderr, "[FAIL] unrecognised option: %s\n", argv[ai] );
207 break; // not an option, leave with a1 @ first positional parm
212 srand( time( NULL ) );
213 snprintf( wbuf, sizeof( wbuf ), "%d", 43000 + (rand() % 1000) ); // random listen port
214 listen_port = strdup( wbuf );
216 listen_port = "43086";
220 mk_rt(); // create a dummy route table so we don't have errors/hang
222 fprintf( stderr, "[INFO] listen port: %s; sending %d messages\n", listen_port, num2send );
224 if( (mrc = rmr_init( listen_port, 1400, 0 )) == NULL ) { // start without route table listener thread
225 fprintf( stderr, "[FAIL] unable to initialise RMr\n" );
228 fprintf( stderr, "[INFO] RMR initialised\n" );
230 if( (rcv_fd = rmr_get_rcvfd( mrc )) < 0 ) { // if we can't get an epoll FD, then we can't timeout; abort
231 fprintf( stderr, "[FAIL] unable to get an epoll FD\n" );
235 if( (ep_fd = epoll_create1( 0 )) < 0 ) {
236 fprintf( stderr, "[FAIL] unable to create epoll fd: %d\n", errno );
239 epe.events = EPOLLIN;
240 epe.data.fd = rcv_fd;
242 if( epoll_ctl( ep_fd, EPOLL_CTL_ADD, rcv_fd, &epe ) != 0 ) {
243 fprintf( stderr, "[FAIL] epoll_ctl status not 0 : %s\n", strerror( errno ) );
247 while( ! rmr_ready( mrc ) ) {
251 mbuf = rmr_alloc_msg( mrc, sizeof( *payload ) + 100 ); // send buffer with a bit of padding
253 fprintf( stderr, "[INFO] starting session with %s, starting to send\n", target );
254 whid = rmr_wh_open( mrc, target ); // open a wormhole directly to the target
256 fprintf( stderr, "[FAIL] unable to connect to %s\n", target );
260 fprintf( stderr, "[INFO] connected to %s, starting to send\n", target );
261 rmr_set_stimeout( mrc, 3 ); // we let rmr retry failures for up to 3 "rounds"
263 gethostname( wbuf, sizeof( wbuf ) );
264 snprintf( me, sizeof( me ), "%s-%d", wbuf, getpid( ) );
267 while( count < num2send ) { // we send n messages after the first message is successful
269 fprintf( stderr, "[FAIL] mbuf is nil?\n" );
273 payload = (mpl_t *) mbuf->payload;
275 snprintf( wbuf, sizeof( payload->msg ), "%s count=%d %d", me, count, rand() );
276 snprintf( mbuf->payload, 1024, "%d|%s", sum( wbuf , strlen( wbuf ) ), wbuf );
278 mbuf->mtype = HEALTH_CHECK;
280 mbuf->len = sizeof( *payload );
283 clock_gettime( CLOCK_REALTIME, &payload->out_ts ); // mark time out
284 mbuf = rmr_wh_send_msg( mrc, whid, mbuf );
286 if( mbuf->state == RMR_OK ) { // good send, wait for response
287 nready = epoll_wait( ep_fd, events, 1, max_timeout * 1000 );
289 clock_gettime( CLOCK_REALTIME, &in_ts ); // mark response received time
291 mbuf = rmr_rcv_msg( mrc, mbuf );
292 payload = (mpl_t *) mbuf->payload;
293 tok = strchr( payload->msg, '|' ); // find end of chksum
296 cksum = sum( tok, strlen( tok ) );
297 if( cksum != atoi( payload->msg ) ) {
298 fprintf( stderr, "[WRN] response to msg %d received, cksum mismatch; expected %d, got %d\n",
299 count+1, atoi( payload->msg ), cksum );
301 fprintf( stderr, "[INFO] response to msg %d received, %d mu-sec\n", count+1, elapsed( &payload->out_ts, &in_ts ) );
305 fprintf( stderr, "[ERR] timeout waiting for response to message %d\n", count+1 );
309 fprintf( stderr, "[ERR] send failed: %d\n", mbuf->state );
316 rmr_wh_close( mrc, whid );