// $Id: lemon_collector.html,v 1.1 2001/05/22 07:58:08 paudley Exp $
// Time-stamp: <00/01/26 16:55:00 paudley>

#include <stl.h>
#include "stringplus.h"
#include "SNMPplus.h"
#include "instant.h"
#include "sqlfile.h"

extern "C" {
# include <unistd.h>
# include <sys/wait.h>
}


sqlfile* lemonDB = NULL;
stringplus id;
string_map *config;
stringplus SA[128]; // For decomposing OID's
string_map *V; // x -> VLAN
string_map *vInt; // int -> VLAN
string_map *ifMap;
SNMPplus *S;
SNMPquery *Q;
map<stringplus,stringplus>::iterator iter;
//bool _init_ok;
bool _reset;


void _run_id( const stringplus& t_id );
bool maybe_update_ifmapping( void );
bool collect( void );
bool commit_temp_data( void );
bool _verify_connection( void );
bool _verify_connection2( sqlfile* );
void collect_interface( const stringplus& num );



int main()
{
  cout << "Lemon v4.0" << endl;
  cout << " .intializing db connection." << endl;
  sqlfile *lemonDBmain = new sqlfile("localhost","3306","lemon","lemon","");  
  if( !_verify_connection2(lemonDBmain) ) {
    cerr << "Can't connect to DB." << endl;
    exit(1);
  }


  SNMPplus::init();

  //  for( int i = 0; i<10; i++ ) {
  while( true ) {
    time_t start_t = time(0);

    // Pull our config from the db...
    if( _verify_connection2(lemonDBmain) ) {
      sqlres* id_res = lemonDBmain->sql("select id from config");
      for(int i=0; i<id_res->numrows(); i++ ) {
	string_map row = id_res->row_map();

	// Fork because snmplib is leaky and broken
	pid_t pid;
	if( (pid = fork()) < 0 ) {
	  cerr << "Error forking..." << endl;
	} else {
	  if( pid == 0 ) { // Child
	    _run_id( row["id"] );
	    cout << "Child exiting." << endl;
	    exit(0);
	  } 
	  // Parent
	  long time_p = time(0);	  
	  cout << ctime(&time_p) << ": Waiting for child.." << endl;
	  waitpid( pid, NULL, 0 );
	  cout << "Child is done." << endl;
	}
      }
      delete id_res;
    }    
    
    //    delete lemonDB;
    //    exit(0);

    long diff = time(0) - start_t;
#define PERIOD 150
    if( diff < PERIOD ) {
      cout << "Sleeping for " << PERIOD - diff << "seconds..." << endl;
      sleep( PERIOD - diff );
    }
  }
  delete lemonDBmain;

  return 0;
}


void _run_id( const stringplus& t_id ) {
  S = new SNMPplus;
  Q = new SNMPquery;
  ifMap = new string_map;
  vInt = new string_map;
  V = new string_map;
  config = new string_map;
  lemonDB = new sqlfile("localhost","3306","lemon","lemon","");  
  if( !_verify_connection() ) {
    cerr << "Can't connect to DB." << endl;
    exit(1);
  }    

  id = t_id;
  sqlres* conf_res  = lemonDB->sql("select * from config where id = '%s'",id.AsPtr());
  *config = conf_res->row_map(); delete conf_res;
  if( config->size() < 1 ) {
    cerr << "   .LC::_run No config found for id:" << id.AsPtr() << endl;
    goto cleanup;
  }
  
  S->open((*config)["host"],(*config)["community"]);
  
  if( !S->good() ) {
    cerr << S->error() << endl;
    goto cleanup;
  }
  cout << "  .LC::Starting collection run..." << endl;
  if( !maybe_update_ifmapping() ) goto cleanup;
  if( !collect() )
    goto cleanup;
 cleanup:
  S->close();
  delete lemonDB;
  delete S;
  delete Q;
  delete ifMap;
  delete vInt;
  delete V;
  delete config;

}

bool maybe_update_ifmapping( void ) {
  Q->add_req(".1.3.6.1.2.1.1.3.0");
  S->get( Q );
  if( Q->results.size() != 1 )
    return false;
  iter = Q->results.begin();
  bool update = false;

  // This is how long the router tells us it's been up..
  long uptime = (*iter).second.Field(2,"()").AsLong();
  long last_uptime = 0L;
  Q->clear();
  
  // Grab the last uptime we checked.
  sqlres* res = lemonDB->sql("select val_i from system where name = '%s' and var = 'uptime'",(*config)["host"].AsPtr());
  string_map ret = res->row_map(); delete res;
  if( ret.size() < 1 ) {
    // We don't have a previous value, force the update and insert the current uptime.
    update = true;
    _reset = true;
    lemonDB->sqlV("insert into system values ('%s','uptime',%ld,'')",(*config)["host"].AsPtr(),uptime);
  } else 
    last_uptime = ret["val_i"].AsLong();
  
  // Update the uptime...
  lemonDB->sqlV("update system set val_i = %ld where name = '%s' and var = 'uptime'",uptime,(*config)["host"].AsPtr());

  // Detect probable resets.
  if( !update && uptime < last_uptime ) { update = true; _reset = true; }
  // Detect ten minute intervals.
  if( !update && uptime > (last_uptime+600)) { update = true; }

  // Detect DB problems..
  unsigned long n = 0;
  if( (n=lemonDB->sqlN("select * from ifmapping where id = '%s' and name != ''",id.AsPtr())) < 1 ) update = true;
  if( lemonDB->sqlN("select * from ifmapping where id = '%s' and temp != ''",id.AsPtr()) > 0 ) update = true;
  if( n != ifMap->size() ) update = true;

  update = true;

  if( update ) {
    V->clear();
    vInt->clear();

    // Nuke the old mappings...
    lemonDB->sqlV("delete from ifmapping where id = '%s'",id.AsPtr());
    ifMap->clear();

    // Get the x -> VLAN mappings
    for( int r=1; r<=(*config)["max_vlans"].AsLong(); r++ ) {
      Q->add_req(stringplus(".1.3.6.1.4.1.353.5.3.1.1.2.1.14.")+r); // This is the table of x to vlan names.
    }
    S->get( Q );
    for( iter=Q->results.begin();iter != Q->results.end(); iter++ ) {
      int oid_n = (*iter).first.Split(SA,128,'.'); // Decompose the OID.
      (*V)[ SA[ oid_n - 1 ] ] = (*iter).second;
    }
    Q->clear();

    // Get the x -> Interface mappings
    for( int r=1; r<=(*config)["max_vlans"].AsLong(); r++ )
      Q->add_req(stringplus(".1.3.6.1.4.1.353.5.3.1.1.2.1.1.")+r); // This is the table of x to iterface numbers.
    S->get( Q );
    for( iter=Q->results.begin();iter != Q->results.end(); iter++ ) {
      int oid_n = (*iter).first.Split(SA,128,'.'); // Decompose the OID.
      // Remap the VLAN -> Interfaces.
      (*vInt)[(*iter).second ] = (*V)[ SA[ oid_n - 1 ] ];
    }
    Q->clear();

    // Add the new mappings to the master ifMap
    for( iter=vInt->begin(); iter!=vInt->end(); iter++ )
      (*ifMap)[ (*iter).first ] = (*iter).second;

    // OK, get the real interface names..
    for( int r=1; r<=(*config)["max_intf"].AsLong(); r++ )
      Q->add_req(stringplus(".1.3.6.1.4.1.9.2.2.1.1.28.")+r);
    S->get( Q );
    for( iter=Q->results.begin();iter != Q->results.end(); iter++ ) {
      int oid_n = (*iter).first.Split(SA,128,'.'); // Decompose the OID.
      (*vInt)[ SA[ oid_n - 1 ] ] = (*iter).second;
    }
    Q->clear();

    // Add the new mappings to the master ifMap (if they have a name..)
    for( iter=vInt->begin(); iter!=vInt->end(); iter++ )
      if( (*iter).second != "" && (*iter).first != "" )
	{
	  (*iter).second.Replace("'","");
	  (*ifMap)[ (*iter).first ] = (*iter).second; 
	}
  }
  // Dump mappings to db.
  for( iter=ifMap->begin(); iter!=ifMap->end(); iter++ ) {
    lemonDB->sqlV("insert into ifmapping (id,name,val,temp) values ('%s','%s',%s,'')",
		  id.AsPtr(), (*iter).second.AsPtr(), (*iter).first.AsPtr() );
  }
  return true;
}

bool collect( void ) {  
  // Check for uncommitted temp data...
  //  if( !commit_temp_data() ) return false;
  
  // Wipe the current temp db.
  if( !_verify_connection() ) { return false; }
  lemonDB->sqlV("delete from temp_data where id = '%s'",id.AsPtr());

  for( iter=ifMap->begin(); iter!=ifMap->end(); iter++ )
    collect_interface( iter->first );    
  
  // Toggle reset if it's set.
  if( _reset )
    _reset = false;

  // Commit
  if( !commit_temp_data() ) return false;

  return true;
}

bool commit_temp_data( void ) {
  if( !_verify_connection() ) { return false; }

  // Check for any temp_data...
  if( lemonDB->sqlN("select id from temp_data where id = '%s'",id.AsPtr()) < 1 ) {
    cerr << "UhOh. There is no temp data for "<<id<<endl;
    return false;
  }
    
  // Push the data into the raw_data table..
  long n = ifMap->size();
  lemonDB->sqlV("update temp_data set level = -1 where id = '%s'",id.AsPtr());
  long tnum = lemonDB->sqlN("select level from temp_data where id = '%s' and level = -1",id.AsPtr());
  cerr << "data for " << tnum << " interfaces found." << endl;
  if( tnum != n ) {
    for( iter=ifMap->begin(); iter!=ifMap->end(); iter++ ) {
      if( lemonDB->sqlN("select * from temp_data where id = '%s' and name = '%s'",
			id.AsPtr(), (*iter).second.AsPtr() ) != 1 ) {
	cout << "!! Error !! Missing info for: " << id << ":" << (*iter).second << endl;	
      }
    }
  }
  lemonDB->sqlV("replace into raw_data (id,ts,inOct,outOct,avgInRate,avgOutRate,name,reset) select id,ts,inOct,outOct,avgInRate,avgOutRate,name,reset from temp_data where id = '%s'",id.AsPtr()); 
  //  tnum = lemonDB->sqlN("select level from raw_data where id = '%s' and level = -1",id.AsPtr());
  //  if( tnum < n ) {
  //    cerr << "!!! temp_data to raw_data mismatch in LemonCollect::collect()" << endl;
  //    cerr << " interfaces: " << n << endl;
  //    cerr << " raw data exists for " << tnum << " interfaces for last run." <<endl << endl;    
  //    for( iter=ifMap.begin(); iter!=ifMap.end(); iter++ ) {
  //      if( lemonDB->sqlN("select * from temp_data where id = '%s' and name = '%s'",
  //			id.AsPtr(), (*iter).second.AsPtr() ) != 1 ) {
  //	cout << "!! Error !! Missing info for: " << id << ":" << (*iter).second << endl;	
  //      }
  //    }
  //  } else {
  //lemonDB->sqlV("update raw_data set level = 0 where id = '%s' and level = -1",id.AsPtr());
  lemonDB->sqlV("delete from temp_data where id = '%s'",id.AsPtr());
    //  }
  return true;
}

#define LC_inOct      ".1.3.6.1.2.1.2.2.1.10."
#define LC_outOct     ".1.3.6.1.2.1.2.2.1.16."
#define LC_avgInRate  ".1.3.6.1.4.1.9.2.2.1.6."
#define LC_avgOutRate ".1.3.6.1.4.1.9.2.2.1.8."

void collect_interface( const stringplus& num ) {
  if( !_verify_connection() ) { return; }
  if( num.AsLong() <= 0 ) return;
  stringplus name = (*ifMap)[ num ],SinOct,SoutOct,SavgInRate,SavgOutRate;
  
  Q->clear();
  { Q->add_req(stringplus( LC_inOct )+num.AsPtr()); S->get(Q); SinOct = (*(Q->results.begin())).second; Q->clear();}
  { Q->add_req(stringplus( LC_outOct )+num.AsPtr()); S->get(Q); SoutOct = (*(Q->results.begin())).second; Q->clear();}
  //  { Q=new SNMPquery; Q->add_req(stringplus( LC_avgInRate )+num.AsPtr()); S->get(&Q); SavgInRate = (*(Q.results.begin())).second; }
  //  { Q=new SNMPquery; Q->add_req(stringplus( LC_avgOutRate )+num.AsPtr()); S->get(&Q); SavgOutRate = (*(Q.results.begin())).second; }

  if( SinOct == "" ) SinOct = "0"; 
  if( SoutOct == "" ) SoutOct = "0"; 
  if( SavgInRate == "" ) SavgInRate = "0"; 
  if( SavgOutRate == "" ) SavgOutRate = "0"; 

  lemonDB->sqlV("delete from temp_data where id = '%s' and name = '%s'",id.AsPtr(),name.AsPtr());
  cout << "[id:"<<id<<"] [name:"<<name<<"] "<<SinOct<<" "<<SoutOct<<endl;
  lemonDB->sqlV("insert into temp_data (id,name,ts,inOct,outOct,avgInRate,avgOutRate,reset) values ('%s','%s',now(),%s,%s,%s,%s,%ld)",
		id.AsPtr(),name.AsPtr(),
		SinOct.AsPtr(),
		SoutOct.AsPtr(),
		SavgInRate.AsPtr(),
		SavgOutRate.AsPtr(),
		_reset ? 1 : 0);
}


bool _verify_connection( void ) {
  if( !lemonDB ) lemonDB = new sqlfile("localhost","3306","lemon","lemon","");
  if( !lemonDB->isok() ) {
    delete lemonDB;
    lemonDB = new sqlfile("localhost","3306","lemon","lemon","");
  }
  return lemonDB->isok();
}

bool _verify_connection2( sqlfile* S ) {
  if( !S ) S = new sqlfile("localhost","3306","lemon","lemon","");
  if( !S->isok() ) {
    delete S;
    S = new sqlfile("localhost","3306","lemon","lemon","");
  }
  return S->isok();
}


syntax highlighted by Code2HTML, v. 0.9