* *NEW* dnsblacklist to make "hosts" style block lists * *NEW* domblacklist to make dnsmasq resolve entries to block entire domains regardless of host or subdomain. * Several fixes * Refactor CLI apps for more code re-use. * More helpful error messages. * Add whole (aka wildcard) domain block handling.master
@@ -0,0 +1,113 @@ | |||
////////////////////////////////////////////////////////////////////// | |||
// Base CLI app classes for TrafficMon tools | |||
// Written by Jonathan A. Foster <jon@jfpossibilities.com> | |||
// Started December 29th, 2021 | |||
// Copyright JF Possibilities, Inc. All rights reserved. | |||
////////////////////////////////////////////////////////////////////// | |||
#include <stdexcept> | |||
#include <iostream> | |||
#include <libgen.h> | |||
#include "appbase.h" | |||
////////////////////////////////////////////////////////////////////// | |||
// TrafficMonBaseApp | |||
////////////////////////////////////////////////////////////////////// | |||
cBaseApp &TrafficMonBaseApp::init(int argc, char **argv) { | |||
if(!config) config = new MonitorBaseConf; | |||
return cBaseApp::init(argc, argv); | |||
} | |||
unsigned TrafficMonBaseApp::do_switch(const char *arg) { | |||
if(!arg[1] && *arg=='c') return 1; | |||
return cBaseApp::do_switch(arg); | |||
} | |||
void TrafficMonBaseApp::do_switch_arg(const char *sw, const std::string &val) { | |||
if(!sw[1] && *sw=='c') config->load(val); | |||
} | |||
int TrafficMonBaseApp::main() { | |||
int x; | |||
try { | |||
if(x=cBaseApp::main()) return x; // Parse CLI args | |||
if(!config->traffic_mon.vals.size()) throw CLIerror( | |||
"You need to load a config file with a [Traffic Mon] section" | |||
); | |||
} catch(const CLIerror &e) { | |||
std::cerr << e.what() << "\n\n"; | |||
return help(); | |||
} | |||
db.open("mysql:user="+qesc(config->traffic_mon.get("db user"))+ | |||
";password="+qesc(config->traffic_mon.get("db password"))+ | |||
";host="+qesc(config->traffic_mon.get("db host"))+ | |||
";database="+qesc(config->traffic_mon.get("db name"))+ | |||
";@opt_reconnect=1"); | |||
return 0; | |||
} | |||
TrafficMonBaseApp::~TrafficMonBaseApp() { | |||
if(config) delete(config); | |||
} | |||
////////////////////////////////////////////////////////////////////// | |||
// BlackListBaseApp | |||
////////////////////////////////////////////////////////////////////// | |||
int BlackListBaseApp::help() { | |||
std::cerr << " FORMAT: " << basename(command_args[0]) << " -c {config} [-4 {address}] [-6 {address}]\n" | |||
<< '\n' | |||
<< "The config file must have a [Traffic Mon] section with the database\n" | |||
<< "credentials in it. -4 & -6 set the addresses to pin blocked names to.\n" | |||
<< "They default to the 'localhost' address in the respective family. Set\n" | |||
<< "to '' to turn off output of that family." << std::endl; | |||
return ExitCode = 1; | |||
} | |||
unsigned BlackListBaseApp::do_switch(const char *arg) { | |||
if(!arg[1] && (*arg=='4' || *arg=='6')) return 1; | |||
return TrafficMonBaseApp::do_switch(arg); | |||
} | |||
void BlackListBaseApp::do_switch_arg(const char *sw, const std::string &val) { | |||
if(!sw[1]) switch(*sw) { | |||
case '4': ipv4 = val; return; | |||
case '6': ipv6 = val; return; | |||
} | |||
TrafficMonBaseApp::do_switch_arg(sw, val); | |||
} | |||
void BlackListBaseApp::do_arg(const char *arg) { | |||
throw CLIerror("Invalid arguments"); | |||
} | |||
int BlackListBaseApp::main() { | |||
int x; | |||
if(x=TrafficMonBaseApp::main()) return x; // Parse CLI args, open conf & db | |||
if(ipv4=="" && ipv6=="") { | |||
std::cerr << "All address families turned off. Nothing to do." << std::endl; | |||
return 1; | |||
} | |||
return 0; | |||
} |
@@ -0,0 +1,85 @@ | |||
////////////////////////////////////////////////////////////////////// | |||
// Base CLI app classes for TrafficMon tools | |||
// Written by Jonathan A. Foster <jon@jfpossibilities.com> | |||
// Started December 29th, 2021 | |||
// Copyright JF Possibilities, Inc. All rights reserved. | |||
// | |||
// | |||
////////////////////////////////////////////////////////////////////// | |||
// NOTE: since GNU doesn't discard unused classes these two classes should | |||
// probably get put in separate sets of files. :-/ | |||
#ifndef __IDS_MONITOR_BASE_APP_H__ | |||
#define __IDS_MONITOR_BASE_APP_H__ | |||
#include <cppdb/frontend.h> | |||
#include "../cli.h" | |||
#include "../miniini.h" | |||
////////////////////////////////////////////////////////////////////// | |||
// The core configuration file | |||
// | |||
// This is designed so that all parts can use the same config. Tools | |||
// ignore the parts they aren't interested in. | |||
////////////////////////////////////////////////////////////////////// | |||
struct MonitorBaseConf: public MiniINI { | |||
MiniINIvars traffic_mon; // This app's config variables | |||
MonitorBaseConf() { groups["Traffic Mon"] = &traffic_mon; } | |||
}; | |||
////////////////////////////////////////////////////////////////////// | |||
// The base CLI application class used by the tools in this directory. | |||
// | |||
// Essentially this is a CLI app with a DB connection and a place | |||
// holder for a config file. | |||
////////////////////////////////////////////////////////////////////// | |||
struct TrafficMonBaseApp: public cBaseApp { | |||
cppdb::session db; | |||
MonitorBaseConf *config; | |||
// this init() will create a MonitorBaseConf if a config hasn't been assigned. | |||
virtual cBaseApp &init(int argc, char **argv); | |||
// process config file switch and load the file | |||
virtual unsigned do_switch(const char *arg); | |||
virtual void do_switch_arg(const char *sw, const std::string &val); | |||
// process CLI args, test for [traffic mon] and connect to DB. | |||
virtual int main(); | |||
// close out and free config object. | |||
virtual ~TrafficMonBaseApp(); | |||
}; | |||
////////////////////////////////////////////////////////////////////// | |||
// Blacklist report base class | |||
// | |||
// This provides generic switch handling | |||
////////////////////////////////////////////////////////////////////// | |||
struct BlackListBaseApp: public TrafficMonBaseApp { | |||
std::string ipv4, ipv6; | |||
BlackListBaseApp(): | |||
ipv4("127.0.0.1"), | |||
ipv6("::1") | |||
{} | |||
// Display generic CLI help text | |||
virtual int help(); | |||
// process -4 & -6 switches. | |||
virtual unsigned do_switch(const char *sw); | |||
virtual void do_switch_arg(const char *sw, const std::string &val); | |||
virtual void do_arg(const char *arg); | |||
virtual int main(); | |||
}; | |||
#endif |
@@ -20,11 +20,9 @@ | |||
#include <iostream> | |||
#include <stdio.h> | |||
#include <libgen.h> | |||
#include <cppdb/frontend.h> | |||
#include "../strutil.h" | |||
#include "../cli.h" | |||
#include "../miniini.h" | |||
#include "appbase.h" | |||
using namespace std; | |||
@@ -132,36 +130,19 @@ namespace cppdb { | |||
} | |||
////////////////////////////////////////////////////////////////////// | |||
// Our config file. | |||
// | |||
// This is designed so that all parts can use the same config. Tools | |||
// ignore the parts they aren't interested in. | |||
////////////////////////////////////////////////////////////////////// | |||
/* TODO: refactor this as a base class... */ | |||
struct ReportConf: public MiniINI { | |||
MiniINIvars traffic_mon; // This app's config variables | |||
ReportConf() { groups["Traffic Mon"] = &traffic_mon; } | |||
}; | |||
////////////////////////////////////////////////////////////////////// | |||
// Connection Report Generator Application Class | |||
////////////////////////////////////////////////////////////////////// | |||
struct appConnectionReport: cBaseApp { | |||
ReportConf config; | |||
ReportData rpt; | |||
cppdb::session db; | |||
string start_stamp; | |||
string end_stamp; | |||
int cli_mode; // which non-switch are we processing | |||
struct appConnectionReport: TrafficMonBaseApp { | |||
ReportData rpt; | |||
string start_stamp; | |||
string end_stamp; | |||
int cli_mode; // which non-switch are we processing | |||
appConnectionReport(): cli_mode(0) {} | |||
appConnectionReport(): cli_mode(0) { } | |||
@@ -171,21 +152,7 @@ struct appConnectionReport: cBaseApp { | |||
<< "The config file must have a [Traffic Mon] section with the database\n" | |||
<< "credentials in it. 'start' and 'stop' are the SQL timestamps for\n" | |||
<< "report time span." << endl; | |||
} | |||
virtual unsigned do_switch(const char *arg) { | |||
if(*arg=='c' && !arg[1]) return 1; | |||
throw CLIerror("Invalid switch "+string(arg)); | |||
} | |||
virtual void do_switch_arg(const char *sw, const std::string &val) { | |||
// The only way we get here is with -c | |||
config.load(val); | |||
// TODO: config validity checks | |||
return 1; | |||
} | |||
@@ -197,30 +164,22 @@ struct appConnectionReport: cBaseApp { | |||
default: throw CLIerror("Invalid arguments"); | |||
} | |||
} | |||
int main() { | |||
cppdb::result qry; | |||
int x; | |||
/// SETUP & VALIDATE CLI /// | |||
try { | |||
cBaseApp::main(); // Parse CLI args | |||
if(!config.traffic_mon.vals.size()) throw CLIerror( | |||
"You need to load a config file with a [Traffic Mon] section" | |||
); | |||
if(x = TrafficMonBaseApp::main()) return x; // Parse CLI args | |||
if(cli_mode!=2) throw CLIerror("Invlaid arguments"); | |||
} catch(const CLIerror &e) { | |||
cerr << e.what() << "\n\n"; | |||
return help(); | |||
} | |||
db.open("mysql:user="+qesc(config.traffic_mon.get("db user"))+ | |||
";password="+qesc(config.traffic_mon.get("db password"))+ | |||
";host="+qesc(config.traffic_mon.get("db host"))+ | |||
";database="+qesc(config.traffic_mon.get("db name"))+ | |||
";@opt_reconnect=1"); | |||
/// Query & load data /// | |||
qry = db << | |||
@@ -2,6 +2,7 @@ | |||
# defaults. | |||
# Configuration file for the TrafficMon server | |||
# NOTE: service won't start until this is set | |||
#CONF=/etc/poorman-ids/sample.conf | |||
# Where "run" files are placed. This is the Debian+ default: | |||
#RUN=/run | |||
@@ -0,0 +1,139 @@ | |||
////////////////////////////////////////////////////////////////////// | |||
// Dump Black Listed DNS entries | |||
// Written by Jonathan A. Foster <jon@jfpossibilities.com> | |||
// Started Ocotber 27th, 2021 | |||
// Copyright JF Possibilities, Inc. All rights reserved. | |||
// | |||
// Read the "dns" table and dump all black listed host names as | |||
// entries for a "hosts" file. This could also be easily done with a | |||
// script but I want to be able to use the same config file as every- | |||
// thing else and parsing in SH is clumsy at best. | |||
////////////////////////////////////////////////////////////////////// | |||
#include <string> | |||
#include <map> | |||
#include <iostream> | |||
#include <stdio.h> | |||
#include <libgen.h> | |||
#include "../strutil.h" | |||
#include "appbase.h" | |||
using namespace std; | |||
////////////////////////////////////////////////////////////////////// | |||
// Class to manage and test hoset names agains bad domains | |||
////////////////////////////////////////////////////////////////////// | |||
struct DomainList: public StringList { | |||
bool operator==(const std::string host) { | |||
DomainList::const_iterator i; | |||
int dl, hl = host.size(); | |||
for(i=begin(); i!=end(); i++) { | |||
if(*i==host) return true; | |||
dl = i->size()+1; | |||
if(hl>dl && host.substr(hl-dl)=="."+*i) return true; | |||
} | |||
return false; | |||
} | |||
inline bool operator!=(const std::string host) { return !(*this==host); } | |||
}; | |||
namespace cppdb { | |||
session &operator>>(cppdb::session &db, DomainList &doms) { | |||
cppdb::result qry; | |||
std::string s; | |||
doms.clear(); | |||
qry = db << "SELECT name FROM dns_wild WHERE status=2"; | |||
while(qry.next()) { | |||
qry >> s; | |||
doms.push_back(s); | |||
} | |||
return db; | |||
} | |||
} // cppdb | |||
////////////////////////////////////////////////////////////////////// | |||
// Connection Report Generator Application Class | |||
////////////////////////////////////////////////////////////////////// | |||
struct DNSblackList: BlackListBaseApp { | |||
bool all; | |||
unsigned do_switch(const char *arg) { | |||
if(*arg=='a' && !arg[1]) { all=1; return 0; } | |||
return BlackListBaseApp::do_switch(arg); | |||
} | |||
int help() { | |||
std::cerr << " FORMAT: " << basename(command_args[0]) << " -c {config} [-a] [-4 {address}] [-6 {address}]\n" | |||
<< '\n' | |||
<< "The config file must have a [Traffic Mon] section with the database\n" | |||
<< "credentials in it. -4 & -6 set the addresses to pin blocked names to.\n" | |||
<< "They default to the 'localhost' address in the respective family. Set\n" | |||
<< "to '' to turn off output of that family. -a dumps all blocked host\n" | |||
<< "names otherwise host names that are covered by a domain block will\n" | |||
<< "not be shown." << std::endl; | |||
return ExitCode = 1; | |||
} | |||
int main() { | |||
DomainList baddoms; | |||
cppdb::result qry; | |||
string s; | |||
int x; | |||
/// SETUP & VALIDATE CLI /// | |||
all = false; | |||
if(x=BlackListBaseApp::main()) return x; // Parse CLI args, open conf & db | |||
if(ipv6!="" && ipv6.size()<8) ipv6+='\t'; // an extra \t to line up columns. :-) | |||
/// Load list of bad domains /// | |||
// These should be excluded from the list below since they should be | |||
// blocked by other means and the point of domain wide blocking is to | |||
// relieve the burden on the blocking tools (dnsmasq). | |||
if(!all) db >> baddoms; | |||
/// Query & load data /// | |||
qry = db << | |||
"SELECT name " | |||
"FROM dns " | |||
"WHERE status=2 " // 2 = blocked... need this doc'd somewhere... | |||
"ORDER BY name"; | |||
while(qry.next()) { | |||
qry >> s; | |||
if(all || baddoms!=s) { // exclude blocked domains | |||
if(ipv4!="") cout << ipv4 << '\t' << s << '\n'; | |||
if(ipv6!="") cout << ipv6 << '\t' << s << '\n'; | |||
} | |||
} | |||
return 0; | |||
} | |||
}; | |||
////////////////////////////////////////////////////////////////////// | |||
// Lets run the report and dump it out | |||
////////////////////////////////////////////////////////////////////// | |||
MAIN(DNSblackList) |
@@ -0,0 +1,61 @@ | |||
////////////////////////////////////////////////////////////////////// | |||
// Dump Black Listed whole domain (*.domain.tld) entries | |||
// Written by Jonathan A. Foster <jon@jfpossibilities.com> | |||
// Started December 28th, 2021 | |||
// Copyright JF Possibilities, Inc. All rights reserved. | |||
// | |||
// Read the "dns_wild" table and dump all black listed domain names as | |||
// "address" entries for a dnsmasq.conf file. This will black list the | |||
// whole domain, subdomains, hosts and all. | |||
////////////////////////////////////////////////////////////////////// | |||
#include <string> | |||
#include <map> | |||
#include <iostream> | |||
#include <stdio.h> | |||
#include <libgen.h> | |||
#include "../strutil.h" | |||
#include "appbase.h" | |||
using namespace std; | |||
////////////////////////////////////////////////////////////////////// | |||
// Connection Report Generator Application Class | |||
////////////////////////////////////////////////////////////////////// | |||
struct DomainBlackList: BlackListBaseApp { | |||
int main() { | |||
cppdb::result qry; | |||
string s; | |||
int x; | |||
/// SETUP & VALIDATE CLI /// | |||
if(x=BlackListBaseApp::main()) return x; // Parse CLI args, open conf & db | |||
/// Query & load data /// | |||
qry = db << | |||
"SELECT name " | |||
"FROM dns_wild " | |||
"WHERE status=2 " // 2 = blocked... need this doc'd somewhere... | |||
"ORDER BY name"; | |||
while(qry.next()) { | |||
qry >> s; | |||
if(ipv4!="") cout << "address=/" << s << '/' << ipv4 << '\n'; | |||
if(ipv6!="") cout << "address=/" << s << '/' << ipv6 << '\n'; | |||
} | |||
return 0; | |||
} | |||
}; | |||
////////////////////////////////////////////////////////////////////// | |||
// Lets run the report and dump it out | |||
////////////////////////////////////////////////////////////////////// | |||
MAIN(DomainBlackList) |
@@ -21,7 +21,7 @@ | |||
NAME="trafficmon" | |||
DAEMON="/usr/sbin/$NAME" | |||
RUN=/run | |||
CONF=/etc/poorman-ids/sample.conf | |||
CONF="" | |||
SOCK="" | |||
# Pull in config | |||
@@ -52,11 +52,16 @@ CTRL() { | |||
do_start() { | |||
echo -n "Starting Traffic Monitor: " | |||
if [ -z "$CONF" ]; then | |||
echo "NOT CONFIGURED" | |||
return 0 | |||
fi | |||
if CTRL --start --oknodo -- -c "$CONF" -b -i "$PID" -p "$SOCK"; then | |||
echo "OK" | |||
return 0 #JIC | |||
else | |||
echo "FAIL" | |||
exit 1 | |||
return 1 | |||
fi | |||
} | |||
@@ -66,9 +71,10 @@ do_stop() { | |||
echo -n "Stoping Traffic Monitor: " | |||
if CTRL --stop --remove-pidfile; then | |||
echo "OK" | |||
return 0 #JIC | |||
else | |||
echo "FAIL" | |||
exit 1 | |||
return 1 | |||
fi | |||
} | |||
@@ -78,36 +84,37 @@ do_status() { | |||
echo -n "Traffic Monitor is: " | |||
if CTRL --status; then | |||
echo "Up" | |||
return 0 #JIC | |||
else | |||
echo "Down" | |||
exit 1 | |||
return 1 | |||
fi | |||
} | |||
### Main() | |||
### Main() | |||
case "$1" in | |||
start) | |||
do_start | |||
;; | |||
stop) | |||
do_stop | |||
;; | |||
restart) | |||
do_status && do_stop | |||
do_start | |||
;; | |||
status) | |||
do_status | |||
;; | |||
*) | |||
echo "$0 {start | stop | restart | status}" | |||
;; | |||
esac | |||
esac |
@@ -22,12 +22,11 @@ | |||
#include <stdexcept> | |||
#include <vector> | |||
#include <map> | |||
#include <cppdb/frontend.h> | |||
#include "../cli.h" | |||
#include "../data.h" | |||
#include "../miniini.h" | |||
#include "../config.h" | |||
#include "appbase.h" | |||
using namespace std; | |||
@@ -36,11 +35,10 @@ using namespace std; | |||
// Monitor Config | |||
////////////////////////////////////////////////////////////////////// | |||
struct MonitorConf: public MiniINI { | |||
MiniINIvars traffic_mon; // This app's config variables | |||
struct MonitorConf: public MonitorBaseConf { | |||
INIusList us; | |||
MonitorConf() { groups["Traffic Mon"] = &traffic_mon; groups["us"] = &us; } | |||
MonitorConf() { groups["us"] = &us; } | |||
}; | |||
@@ -51,11 +49,9 @@ struct MonitorConf: public MiniINI { | |||
////////////////////////////////////////////////////////////////////// | |||
//#define DEBUG | |||
struct TrafficMon: public cBaseApp { | |||
MonitorConf config; | |||
struct TrafficMon: public TrafficMonBaseApp { | |||
LogAnalyzer analyze; | |||
istream *log; | |||
cppdb::session db; | |||
LiveBug bug; | |||
bool setup_db; | |||
bool piping; // are we sucking on a pipe? | |||
@@ -76,13 +72,14 @@ struct TrafficMon: public cBaseApp { | |||
running( false), | |||
line_no( 0) | |||
{ | |||
analyze.us = &(config.us.vals); | |||
config = new MonitorConf; | |||
analyze.us = &(((MonitorConf *)config)->us.vals); | |||
} | |||
void help() { | |||
cerr << | |||
int help() { | |||
cerr << | |||
"\n" | |||
"trafficmon -c {config file} [-i] [-d] [-b] [-p {pipe name} | [{log name} ...]]\n" | |||
"\n" | |||
@@ -96,7 +93,7 @@ struct TrafficMon: public cBaseApp { | |||
"Other arguments are log files to be processed. This can be used to bulk\n" | |||
"load data prior to going live with an always on daemon or for catching\n" | |||
"up if the daemon stopped for some reason."; | |||
ExitCode = 1; | |||
return ExitCode = 1; | |||
} | |||
@@ -105,13 +102,12 @@ struct TrafficMon: public cBaseApp { | |||
if(sw[1]==0) { | |||
switch(*sw) { | |||
case 'b': background_me = true; return 0; | |||
case 'c': return 1; | |||
case 'd': setup_db = true; return 0; | |||
case 'i': return 1; | |||
case 'p': piping = true; return !running; // second pass treats it as a file | |||
} | |||
} | |||
throw CLIerror("Unrecognized Switch"); | |||
return TrafficMonBaseApp::do_switch(sw); | |||
} | |||
@@ -120,40 +116,16 @@ struct TrafficMon: public cBaseApp { | |||
// If "running" is set then we've already done this. So skip it! | |||
if(running) return; | |||
switch(*sw) { | |||
if(sw[1]==0) switch(*sw) { | |||
/// Load config file /// | |||
case 'c': | |||
// This is only called with "c". See above | |||
config.load(val); | |||
if(!config.us.vals.size()) throw CLIerror( | |||
"The configuration files MUST contain an [us] section with " | |||
"appropriate values" | |||
); | |||
if(!config.traffic_mon.vals.size()) throw CLIerror( | |||
"The configuration files MUST contain an [Traffic Mon] section with " | |||
"appropriate values" | |||
); | |||
/// configure resources from config file /// | |||
// TODO: pre-validate some of these? | |||
db.open("mysql:user="+qesc(config.traffic_mon.get("db user"))+ | |||
";password="+qesc(config.traffic_mon.get("db password"))+ | |||
";host="+qesc(config.traffic_mon.get("db host"))+ | |||
";database="+qesc(config.traffic_mon.get("db name"))+ | |||
";@opt_reconnect=1"); | |||
return; | |||
/// PID file to write ID in if we're daemonizing /// | |||
case 'i': | |||
pid_file = val; | |||
return; | |||
/// Make pipe if requested /// | |||
case 'p': | |||
// This can't wait for "running" since we want it to fail in foreground | |||
// Make read/write by process owner (root) only | |||
@@ -163,6 +135,8 @@ struct TrafficMon: public cBaseApp { | |||
} | |||
inp_ct++; | |||
} | |||
TrafficMonBaseApp::do_switch_arg(sw, val); | |||
} | |||
@@ -200,7 +174,8 @@ restart: | |||
// probably should daemonize around here. | |||
int run() { | |||
string l; | |||
cppdb::statement db_add = db << // prepare insert statement | |||
// prepare insert statement | |||
cppdb::statement db_add = db << | |||
"INSERT INTO connections " | |||
"(us, us_port, them, them_port, them_name, protocol, inbound) " | |||
"VALUES (?,?,?,?,?,?,?)"; | |||
@@ -262,20 +237,42 @@ restart: | |||
db << "CREATE TABLE dns (" | |||
"name CHAR(128) NOT NULL PRIMARY KEY," | |||
"decided TIMESTAMP NOT NULL," | |||
"status TINYINT(1) NOT NULL DEFAULT 0" | |||
"status TINYINT(1) NOT NULL DEFAULT 0," | |||
"note CHAR(128) NOT NULL DEFAULT ''" | |||
") Engine=MyISAM" | |||
<< cppdb::exec; | |||
/// wild card DNS list /// | |||
// NOTE: All of these names are treated as if prefixed with "*.". At this | |||
// time there are only plans to implement this as a black list. | |||
// Many domains are sevices with threat potential and discovering | |||
// all of the host and subdomains may not be viable. Think ad and | |||
// tracking services. | |||
db << "CREATE TABLE dns_wild (" | |||
"name CHAR(128) NOT NULL PRIMARY KEY," | |||
"decided TIMESTAMP NOT NULL," | |||
"status TINYINT(1) NOT NULL DEFAULT 2," | |||
"note CHAR(128) NOT NULL DEFAULT ''" | |||
") Engine=MyISAM" | |||
<< cppdb::exec; | |||
} | |||
int main() { | |||
int x; | |||
try { | |||
cBaseApp::main(); | |||
if(!config.us.vals.size()) | |||
// Since -c requires something in [us] we'll only get here if a config | |||
// wasn't loaded. | |||
throw CLIerror("You must specify a config file to load"); | |||
if(x=TrafficMonBaseApp::main()) return x; | |||
if(!((MonitorConf*)config)->us.vals.size()) throw CLIerror( | |||
"The configuration files MUST contain an [us] section with " | |||
"appropriate values" | |||
); | |||
if(!config->traffic_mon.vals.size()) throw CLIerror( | |||
"The configuration files MUST contain an [Traffic Mon] section with " | |||
"appropriate values" | |||
); | |||
if(piping && inp_ct!=1) throw CLIerror( | |||
"Pipe requires one and only one file name to read from." | |||
); | |||
@@ -304,6 +301,8 @@ restart: | |||
try { | |||
// CLI processed now lets analyze data. | |||
running = true; | |||
// TODO: this is going to break things? Probably should prevent reloading conf. DB is opened in TrafficMonBaseApp::main() | |||
// Actually I think it may actualy work... | |||
cBaseApp::main(); // re-run CLI args for inputs | |||
if(!log) { | |||
// no inputs spec'd on CLI assume stdin (we're not a daemon) | |||
@@ -322,8 +321,6 @@ restart: | |||
return ExitCode; | |||
} | |||
}; | |||