blob: 52fe23de8f5c58c3e51891a3b735225572a2c7a3 [file] [log] [blame]
// 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
Caution: This example code is pulled directly into one or more of the documents
(starting from the "start-example" tag below. Use caution with
line lengths and contents because of the requirement that this
be documentation as well as working code.
*/
// start-example
#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 based on # 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;
}