2 ==================================================================================
3 Copyright (c) 2019 Nokia
4 Copyright (c) 2018-2019 AT&T Intellectual Property.
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 ==================================================================================
21 Mnemonic: test_nng_em.c
22 Abstract: A nano/NNG message emulator for testing without needing to
23 actually have nanomsg, nng, or external processes.
24 We also emulate the epoll_wait() function for controlled
27 This module must be directly included to be used.
28 Date: 11 February 2019
29 Author: E. Scott Daniels
33 #include "rmr.h" // we use some of rmr defs in building dummy messages, so we need these
34 #include "rmr_agnostic.h"
36 // ---------------------- emulated nng functions ---------------------------
44 static int em_send_failures = 0; // test programme can set this to emulate eagain send failures
45 static int em_timeout = -1; // set by set socket option
46 static int em_mtc_msgs = 0; // set to generate 'received' messages with mt-call header data
47 static int return_value = 0; // functions should return this value
48 static int rcv_count = 0; // receive counter for transaction id to allow test to rest
49 static int rcv_delay = 0; // forced delay before call to rcvmsg starts to work
51 static int gates_ok = 0;
52 static pthread_mutex_t rcv_gate;
53 static int em_gen_long_hostname = 0; // if set the emulated hostname generates a longer name (>40 char)
56 // ----------- gethostname emulation ---------------------------------------
57 #define gethostname em_gethostname
58 static int em_gethostname( char* buf, size_t len ) {
64 if( em_gen_long_hostname ) {
65 snprintf( buf, len, "hostname-which-is-long-a860430b890219-dfw82" );
67 snprintf( buf, len, "em-hostname" );
73 static int em_set_long_hostname( int v ) {
74 em_gen_long_hostname = !!v;
77 // ----------- epoll emulation ---------------------------------------------
79 // CAUTION: sys/epoll.h must be included before these define and function will properly compile.
80 #define epoll_wait em_wait
81 #define epoll_ctl em_ep_ctl
82 #define epoll_create em_ep_create
85 Every other call returns 1 ready; alternate calls return 0 ready.
86 Mostly for testing the timeout receive call. First call should return
87 something ready and the second should return nothing ready so we can
90 static int em_wait( int fd, void* events, int n, int to ) {
97 int em_ep_ctl( int epfd, int op, int fd, struct epoll_event *event ) {
101 int em_ep_create( int size ) {
108 Simulated v1 message for receive to return. This needs to match the RMr header
109 so that we can fill in length, type and xaction id things.
112 int32_t mtype; // message type ("long" network integer)
113 int32_t plen; // payload length
114 int32_t rmr_ver; // our internal message version number
115 unsigned char xid[32]; // space for user transaction id or somesuch
116 unsigned char sid[32]; // sender ID for return to sender needs
117 unsigned char src[16]; // name of the sender (source)
118 unsigned char meid[32]; // managed element id.
119 struct timespec ts; // timestamp ???
124 v2 message; should be able to use it for everything that is set up here as
125 we don't add a payload even if setting a v1 type.
127 #define ALT_MSG_VER 1 // alternate every so often
128 #define MSG_VER 3 // default version to insert
130 int32_t mtype; // message type ("long" network integer)
131 int32_t plen; // payload length
132 int32_t rmr_ver; // our internal message version number
133 unsigned char xid[32]; // space for user transaction id or somesuch
134 unsigned char sid[32]; // sender ID for return to sender needs
135 unsigned char src[64]; // name of the sender (source)
136 unsigned char meid[32]; // managed element id.
137 struct timespec ts; // timestamp ???
140 int32_t flags; // HFL_* constants
141 int32_t len0; // length of the RMr header data
142 int32_t len1; // length of the tracing data
143 int32_t len2; // length of data 1 (d1)
144 int32_t len3; // length of data 2 (d2)
145 int32_t sub_id; // subscription id (-1 invalid)
148 unsigned char srcip[64]; // sender ID for return to sender needs
153 // -- emulation control functions ------------------------------------------------------
156 Test app can call this to have all emulated functions return failure instead
159 static void en_set_return( int rv ) {
165 static int em_nng_foo() {
166 fprintf( stderr, "emulated functions in play" );
171 Turns on/off the generation of multi-threaded call messages
173 static int em_set_mtc_msgs( int state ) {
178 Returns the size of the header we inserted
180 static int em_hdr_size() {
182 return (int) sizeof( struct em_msg ) + 4;
185 return (int) sizeof( struct em_msg );
188 static void em_set_rcvcount( int v ) {
192 static void em_set_rcvdelay( int v ) {
196 static void em_start() {
198 pthread_mutex_init( &rcv_gate, NULL );
203 //--------------------------------------------------------------------------
211 Receive message must allocate a new buffer and return the pointer into *m.
212 Every 9 messages or so we'll simulate an old version message
214 If em_mtc_msgs is set, then we add a non-zero d1 field with
215 the call-id set to 2, and alternate the call flag
217 static int em_nng_recvmsg( nng_socket s, nng_msg ** m, int i ) {
218 static int call_flag = 0;
226 if( rcv_delay > 0 ) {
235 b = (void *) malloc( 2048 );
236 memset( b, 0, 2048 );
239 msg = (struct em_msg *) b;
240 if( ! em_mtc_msgs && (rcv_count % 10) == 9 ) {
241 msg->rmr_ver = ALT_MSG_VER; // allow emulation the bug in RMr v1
243 msg->rmr_ver = htonl( MSG_VER );
246 msg->mtype = htonl( 1 );
247 msg->plen = htonl( 220 );
248 msg->len0 = htonl( sizeof( struct em_msg ) );
249 msg->len1 = htonl( trace_size );
250 msg->len2 = htonl( d1_size );
251 msg->len3 = htonl( 0 );
253 pthread_mutex_lock( &rcv_gate ); // hold lock to update counter/flag
255 d1 = DATA1_ADDR( msg );
256 d1[0] = 2; // simulated msgs always on chute 2
259 msg->flags |= HFL_CALL_MSG;
261 if( rcv_delay > 0 ) {
262 fprintf( stderr, "<EM> count=%d flag=%d %02x \n", rcv_count, call_flag, msg->flags );
264 call_flag = !call_flag;
268 pthread_mutex_unlock( &rcv_gate );
269 snprintf( msg->xid, 32, "%015d", rcv_count ); // simple transaction id so we can test receive specific and ring stuff
270 snprintf( msg->src, 64, "localhost:4562" ); // set src id (unrealistic) so that rts() can be tested
271 snprintf( msg->srcip, 64, "89.2.19.19:4562" ); // set src ip for rts testing
273 //fprintf( stderr, ">>> simulated received message: %s %s p=%p len0=%d\n", msg->src, msg->srcip, msg, (int) ntohl( msg->len0 ) );
275 fprintf( stderr, "<WARN> em: simulated receive no msg pointer provided\n" );
281 static void* em_msg_body( nng_msg* msg ) {
282 return (void *) msg; // we don't manage a real msg, so body is just the buffer we allocated
285 static size_t em_msg_len( const nng_msg* msg ) {
294 static int em_nng_pull_open(nng_socket * s ) {
297 static int em_nng_pull0_open(nng_socket * s ) {
300 static int em_nng_listen(nng_socket s, const char * c, nng_listener * l, int i ) {
303 static int em_nng_close(nng_socket s ) {
306 static int em_nng_push0_open(nng_socket * s ) {
309 static int em_nng_dial(nng_socket s, const char * c, nng_dialer * d, int i ) {
310 //fprintf( stderr, "<info> === simulated dialing: %s\n", c );
313 static int em_nng_setopt(nng_socket s, const char * c, const void * p, size_t t ) {
316 static int em_nng_sub_open(nng_socket * s ) {
319 static int em_nng_sub0_open(nng_socket * s ) {
322 static int em_nng_recv(nng_socket s, void * v, size_t * t, int i ) {
326 static int em_nng_send( nng_socket s, void* m, int l, int f ) {
331 Emulate sending a message. If the global em_send_failures is set,
332 then every so often we fail with an EAGAIN to drive that part
335 static int em_sendmsg( nng_socket s, nng_msg* m, int i ) {
336 static int count = 0;
338 if( em_send_failures && (count++ % 15 == 14) ) {
339 //fprintf( stderr, ">>>> failing send\n\n" );
346 static void* em_nng_alloc( size_t len ) {
347 return malloc( len );
350 static int em_nng_msg_alloc( nng_msg** mp, size_t l ) {
353 if( !mp || return_value != 0 ) {
357 p = (void *) malloc( sizeof( char ) * l );
364 We just free the buffer here as it was a simple malloc.
366 static void em_nng_free( void* p, size_t l ) {
368 //fprintf( stderr, ">>>>> not freed: %p\n", p );
372 static void em_nng_msg_free( void* p ) {
374 //fprintf( stderr, ">>>>> not freed: %p\n", p );
379 static int em_dialer_create( void* d, nng_socket s, char* stuff ) {
380 //fprintf( stderr, ">>>> emulated dialer create\n\n" );
384 static int em_dialer_start( nng_dialer d, int i ) {
385 //fprintf( stderr, ">>>> emulated dialer start\n\n" );
390 static int em_dialer_setopt_ms( nng_dialer dialer, void* option, int ms ) {
394 static int em_nng_getopt_int( nng_socket s, void* con, int* target ) {
403 // nng redefines some of these to point directly to various 'versions' of the function (ugg, function versions, really?)
407 #undef nng_pull0_open
410 #undef nng_getopt_int
411 #undef nng_push0_open
419 #define nng_msg_alloc em_nng_msg_alloc
420 #define nng_recvmsg em_nng_recvmsg
421 #define nng_free em_nng_free
422 #define nng_free em_nng_free
423 #define nng_msg_free em_nng_msg_free
424 #define nng_pull_open em_nng_pull_open
425 #define nng_pull0_open em_nng_pull0_open
426 #define nng_listen em_nng_listen
427 #define nng_close em_nng_close
428 #define nng_getopt_int em_nng_getopt_int
429 #define nng_push0_open em_nng_push0_open
430 #define nng_dial em_nng_dial
431 #define nng_setopt em_nng_setopt
432 #define nng_sub_open em_nng_sub_open
433 #define nng_sub0_open em_nng_sub0_open
434 #define nng_recv em_nng_recv
435 #define nng_send em_nng_send
436 #define nng_sendmsg em_sendmsg
437 #define nng_alloc em_nng_alloc
438 #define nng_free em_nng_free
439 #define nng_dialer_setopt_ms em_dialer_setopt_ms
440 #define nng_dialer_start em_dialer_start
441 #define nng_dialer_create em_dialer_create
442 #define nng_msg_body em_msg_body
443 #define nng_msg_len em_msg_len
449 // ----------------------- emulated nano functions --------------------------
450 struct em_nn_msghdr {
454 static int em_nn_socket (int domain, int protocol ) {
460 static int em_nn_close (int s ) {
464 //static int em_nn_setsockopt (int s, int level, int option, const void *optval, size_t optvallen ) {
468 static int em_nn_getsockopt (int s, int level, int option, void *optval, size_t *optvallen ) {
472 static int em_nn_bind (int s, const char *addr ) {
473 // fprintf( stderr, ">>> ===== emulated bind called ====\n" );
477 static int em_nn_connect (int s, const char *addr ) {
481 static int em_nn_shutdown (int s, int how ) {
485 static int em_nn_send (int s, const void *buf, size_t len, int flags ) {
489 static int em_nn_recv (int s, void *m, size_t len, int flags ) {
492 static int count = 0; // we'll simulate a message going in by dropping an rmr-ish msg with transaction id only
494 static int counter = 0; // if timeout value is set; we return timeout (eagain) every 3 calls
497 if( em_timeout > 0 ) {
499 if( counter % 3 == 0 ) {
508 b = (void *) malloc( 2048 );
509 if( m != NULL ) { // blindly we assume this is 2k or bigger
510 memset( m, 0, 2048 );
511 msg = (struct em_msg *) m;
512 if( count % 10 == 9 ) {
513 //msg->rmr_ver = htonl( MSG_VER );
514 msg->rmr_ver = ALT_MSG_VER; // emulate the bug in RMr v1
516 msg->rmr_ver = htonl( MSG_VER );
518 msg->mtype = htonl( 1 );
519 msg->plen = htonl( 220 );
520 msg->len0 = htonl( sizeof( struct em_msg ) );
521 msg->len1 = htonl( trace_size );
522 msg->len2 = htonl( d1_size );
523 msg->len3 = htonl( 0 );
524 snprintf( msg->xid, 32, "%015d", count++ ); // simple transaction id so we can test receive specific and ring stuff
525 snprintf( msg->src, 64, "localhost:4562" ); // set src id (unrealistic) so that rts() can be tested
526 snprintf( msg->srcip, 64, "89.2.19.19:4562" ); // set src ip for rts testing
527 //fprintf( stderr, "<EM> returning message len=%d\n\n", ntohl( msg->plen ) );
529 fprintf( stderr, "<EM> message was nil\n\n" );
532 //fprintf( stderr, ">>> simulated received message: %s %s len=%d p=%p\n", msg->src, msg->srcip, ntohl( msg->plen ), m );
536 static int em_sendmsg (int s, const struct em_nn_msghdr *msghdr, int flags ) {
540 static int em_nn_recvmsg (int s, struct nn_msghdr *msghdr, int flags ) {
544 static void em_nn_freemsg( void* ptr ) {
549 Hacky implementation of set sock opt. We assume value is a pointer to int and ignore size.
551 static int em_setsockopt( int sock, int foo, int action, int* value, int size ) {
552 if( action == NN_RCVTIMEO ) {
559 #define nn_socket em_nn_socket
560 #define nn_close em_nn_close
561 //#define nn_setsockopt em_nn_setsockopt
562 #define nn_getsockopt em_nn_getsockopt
563 #define nn_bind em_nn_bind
564 #define nn_connect em_nn_connect
565 #define nn_shutdown em_nn_shutdown
566 #define nn_send em_nn_send
567 #define nn_recv em_nn_recv
568 #define nn_sendmsg em_nn_sendmsg
569 #define nn_recvmsg em_nn_recvmsg
570 #define nn_setsockopt em_setsockopt
571 #define nn_freemsg em_nn_freemsg