--- /dev/null
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+ Copyright (c) 2020 Nokia
+ Copyright (c) 2020 AT&T Intellectual Property.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================================
+*/
+
+/*
+ Mnemonic: rmr_dump.cpp
+ Abstract: This is a sample xapp which will behave somewhat like tcpdump
+ as it receives rmr messages. The list of message types which should
+ be processed are given on the command line, following any flag
+ options (e.g. -p port). Any of those messages received are reported
+ on by the application. The verbosity level may be used to increase
+ the amount of detail given for the tracked messages.
+
+ This writes to the TTY which is slow, so do not expect it to be able
+ to process and report on a high rate of messages. Also, forwarded
+ messages will reach the intended target, however if the target
+ attempts to send a response the response will come back to THIS
+ application, and not the message origination; this cannot be a bit
+ of middleware in it's current form.
+
+ Date: 25 March 2020
+ Author: E. Scott Daniels
+
+*/
+#include <stdio.h>
+#include <unistd.h>
+#include <atomic>
+
+#include "ricxfcpp/xapp.hpp"
+
+/*
+ Information that the callback needs outside
+ of what is given to it via parms on a call
+ by the framework.
+*/
+typedef struct {
+ int vlevel; // verbosity level
+ bool forward; // if true, message is forwarded
+ int stats_freq; // header/stats after n messages
+ std::atomic<long> pcount; // messages processed
+ std::atomic<long> icount; // messages ignored
+ std::atomic<int> hdr; // number of messages before next header
+} cb_info_t;
+
+// ----------------------------------------------------------
+
+/*
+ Dump bytes to tty.
+*/
+void dump( unsigned const char* buf, int len ) {
+ int i;
+ int j;
+ char cheater[17];
+
+ fprintf( stdout, "<RD> 0000 | " );
+ j = 0;
+ for( i = 0; i < len; i++ ) {
+ cheater[j++] = isprint( buf[i] ) ? buf[i] : '.';
+ fprintf( stdout, "%02x ", buf[i] );
+
+ if( j == 16 ) {
+ cheater[j] = 0;
+ fprintf( stdout, " | %s\n<RD> %04x | ", cheater, i+1 );
+ j = 0;
+ }
+ }
+
+ if( j ) {
+ i = 16 - (i % 16);
+ for( ; i > 0; i-- ) {
+ fprintf( stdout, " " );
+ }
+ cheater[j] = 0;
+ fprintf( stdout, " | %s\n", cheater );
+ }
+}
+
+/*
+ generate stats when the hdr count reaches 0. Only one active
+ thread will ever see it be exactly 0, so this is thread safe.
+*/
+void stats( cb_info_t& cbi ) {
+ int curv; // current stat trigger value
+
+ curv = cbi.hdr--;
+
+ if( curv == 0 ) { // stats when we reach 0
+ fprintf( stdout, "ignored: %ld processed: %ld\n",
+ cbi.icount.load(), cbi.pcount.load() );
+ if( cbi.vlevel > 0 ) {
+ fprintf( stdout, "\n %5s %5s %2s %5s\n",
+ "MTYPE", "SUBID", "ST", "PLLEN" );
+ }
+
+ cbi.hdr = cbi.stats_freq; // reset must be last
+ }
+}
+
+void cb1( Message& mbuf, int mtype, int subid, int len,
+ Msg_component payload, void* data ) {
+ cb_info_t* cbi;
+ long total_count;
+
+ if( (cbi = (cb_info_t *) data) == NULL ) {
+ return;
+ }
+
+ cbi->pcount++;
+ stats( *cbi ); // gen stats & header if needed
+
+ if( cbi->vlevel > 0 ) {
+ fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \n",
+ mtype, subid, mbuf.Get_state(), len );
+
+ if( cbi->vlevel > 1 ) {
+ dump( payload.get(), len > 64 ? 64 : len );
+ }
+ }
+
+ if( cbi->forward ) {
+ // forward with no change to len or payload
+ mbuf.Send_msg( Message::NO_CHANGE, NULL );
+ }
+}
+
+/*
+ registered as the default callback; it counts the
+ messages that we aren't giving details about.
+*/
+void cbd( Message& mbuf, int mtype, int subid, int len,
+ Msg_component payload, void* data ) {
+ cb_info_t* cbi;
+
+ if( (cbi = (cb_info_t *) data) == NULL ) {
+ return;
+ }
+
+ cbi->icount++;
+ stats( *cbi );
+
+ if( cbi->forward ) {
+ // forward with no change to len or payload
+ mbuf.Send_msg( Message::NO_CHANGE, NULL );
+ }
+}
+
+int main( int argc, char** argv ) {
+ std::unique_ptr<Xapp> x;
+ char* port = (char *) "4560";
+ int ai = 1; // arg processing index
+ cb_info_t* cbi;
+ int ncb = 0; // number of callbacks registered
+ int mtype;
+ int nthreads = 1;
+
+ cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
+ cbi->pcount = 0;
+ cbi->icount = 0;
+ cbi->stats_freq = 10;
+
+ ai = 1;
+ // very simple flag parsing (no error/bounds checking)
+ while( ai < argc ) {
+ if( argv[ai][0] != '-' ) { // break on first non-flag
+ break;
+ }
+
+ // very simple arg parsing; each must be separate -x -y not -xy.
+ switch( argv[ai][1] ) {
+ case 'f': // enable packet forwarding
+ cbi->forward = true;
+ break;
+
+ case 'p': // define port
+ port = argv[ai+1];
+ ai++;
+ break;
+
+ case 's': // stats frequency
+ cbi->stats_freq = atoi( argv[ai+1] );
+ if( cbi->stats_freq < 5 ) { // enforce sanity
+ cbi->stats_freq = 5;
+ }
+ ai++;
+ break;
+
+ case 't': // thread count
+ nthreads = atoi( argv[ai+1] );
+ if( nthreads < 1 ) {
+ nthreads = 1;
+ }
+ ai++;
+ break;
+
+ case 'v': // simple verbose bump
+ cbi->vlevel++;
+ break;
+
+ case 'V': // explicit verbose level
+ cbi->vlevel = atoi( argv[ai+1] );
+ ai++;
+ break;
+
+ default:
+ fprintf( stderr, "unrecognised option: %s\n", argv[ai] );
+ fprintf( stderr, "usage: %s [-f] [-p port] [-s stats-freq] [-t thread-count] [-v | -V n] msg-type1 ... msg-typen\n", argv[0] );
+ fprintf( stderr, "\tstats frequency is in number of messages received\n" );
+ fprintf( stderr, "\tverbose levels (-V) 0 counts only, 1 message info 2 payload dump\n" );
+ exit( 1 );
+ }
+
+ ai++;
+ }
+
+ cbi->hdr = cbi->stats_freq;
+ fprintf( stderr, "<RD> listening on port: %s\n", port );
+
+ // create xapp, wait for route table if forwarding
+ x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
+
+ // register callback for each type on the command line
+ while( ai < argc ) {
+ mtype = atoi( argv[ai] );
+ ai++;
+ fprintf( stderr, "<RD> capturing messages for type %d\n", mtype );
+ x->Add_msg_cb( mtype, cb1, cbi );
+ ncb++;
+ }
+
+ if( ncb < 1 ) {
+ fprintf( stderr, "<RD> no message types specified on the command line\n" );
+ exit( 1 );
+ }
+
+ x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi ); // register default cb
+
+ fprintf( stderr, "<RD> starting driver\n" );
+ x->Run( nthreads );
+
+ // return from run() is not expected, but some compilers might
+ // compilain if there isn't a return value here.
+ return 0;
+}