src/controller/parameters.cpp (293 lines of code) (raw):
//------------------------------------------------------------------------------
// Author: Pavel Karneliuk
// Description: Class provides validation and access to application parameters
// Copyright (c) 2013 EPAM Systems
//------------------------------------------------------------------------------
/*
This file is part of Nfstrace.
Nfstrace is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 2 of the License.
Nfstrace is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Nfstrace. If not, see <http://www.gnu.org/licenses/>.
*/
//------------------------------------------------------------------------------
#include <iostream>
#include <memory>
#include <dirent.h>
#include <stdexcept>
#include <unistd.h>
#include "analysis/plugin.h"
#include "controller/build_info.h"
#include "controller/cmdline_args.h"
#include "controller/cmdline_parser.h"
#include "controller/parameters.h"
#include "filtration/pcap/network_interfaces.h"
//------------------------------------------------------------------------------
namespace NST
{
namespace controller
{
namespace // implementation
{
static std::unique_ptr<const class ParametersImpl> impl;
using CLI = NST::controller::cmdline::Args;
class ParametersImpl final : public cmdline::CmdlineParser<CLI>
{
friend class NST::controller::Parameters;
public:
ParametersImpl(int argc, char** argv)
: rpc_message_limit{0}
{
parse(argc, argv);
if(get(CLI::ArgHelp).to_bool())
{
std::cout << PROGRAM_BUILD_INFO << std::endl;
print_usage(std::cout, argv[0]);
for(const auto& a : analysis_modules)
{
const std::string& path{a.path};
try
{
std::cout << "Usage of " << path << ":\n";
std::cout << NST::analysis::Plugin::usage_of(path) << std::endl;
}
catch(std::runtime_error& e)
{
std::cout << e.what() << std::endl;
}
}
}
validate();
if(get(CLI::ArgEnum).is("-") || get(CLI::ArgEnum).is("interfaces"))
{
std::cout << "\nAvailable interfaces:" << std::endl;
NST::filtration::pcap::NetworkInterfaces interfaces;
if(interfaces.begin() != interfaces.end())
{
for(auto i : interfaces)
{
std::cout << i << '\n';
for(auto a : i) std::cout << '\t' << a << '\n';
}
std::cout << "[default]: " << interfaces.default_device() << '\n';
}
else
{
std::cerr << "Note: Reading list of network interfaces may "
"require that you have special privileges."
<< std::endl;
}
}
if(get(CLI::ArgEnum).is("-") || get(CLI::ArgEnum).is("plugins"))
{
std::cout << "\nAvailable plugins:" << std::endl;
DIR* dir;
if((dir = opendir(MODULES_DIRECTORY_PATH)) != nullptr)
{
struct dirent* ent;
while((ent = readdir(dir)) != nullptr)
{
std::string full_path = std::string{MODULES_DIRECTORY_PATH} + ent->d_name;
std::string plugin_usage;
try
{
plugin_usage = NST::analysis::Plugin::usage_of(full_path);
std::cout << ent->d_name << ":" << std::endl;
std::cout << plugin_usage << std::endl;
std::cout << std::endl;
}
catch(std::runtime_error& e)
{
}
}
closedir(dir);
}
else
{
std::cerr << "Error: Can't access " << MODULES_DIRECTORY_PATH << std::endl;
}
}
if(!get(CLI::ArgEnum).is("none")) return;
// cashed values
const std::string program_path(argv[0]);
size_t found{program_path.find_last_of("/\\")};
program = program_path.substr(found + 1);
const int limit{get(CLI::ArgMSize).to_int()};
if(limit < 1 || limit > 4000)
{
throw cmdline::CLIError{std::string{"Invalid limit of RPC messages: "} + get(CLI::ArgMSize).to_cstr()};
}
rpc_message_limit = limit;
}
~ParametersImpl() override {}
protected:
void set_multiple_value(int index, char* const v) override
{
if(index == CLI::ArgAnalyzers) // may have multiple values
{
const std::string arg{v};
size_t ind{arg.find('#')};
if(ind == std::string::npos)
{
analysis_modules.emplace_back(path_to_pam(arg));
}
else
{
const std::string path{arg, 0, ind};
const std::string args{arg, ind + 1};
analysis_modules.emplace_back(path_to_pam(path), args);
}
}
}
private:
std::string default_iofile() const
{
// create string: PROGRAMNAME-BPF-FILTER.pcap
std::string str{impl->program};
str.push_back('-');
str.append(get(CLI::ArgFilter).to_cstr());
str.append(".pcap");
std::replace(str.begin(), str.end(), ' ', '-');
return str;
}
std::string path_to_pam(const std::string& path) const
{
std::string result_path;
if(access(path.c_str(), F_OK) != -1)
{
result_path = path;
}
else
{
result_path = std::string{MODULES_DIRECTORY_PATH} + path;
}
return result_path;
}
// cashed values
unsigned short rpc_message_limit;
std::string program; // name of program in command line
std::vector<AParams> analysis_modules;
};
} // unnamed namespace
Parameters::Parameters(int argc, char** argv)
{
// init global instance only once
if(impl) throw std::runtime_error{"initialized twice"};
impl = std::make_unique<ParametersImpl>(argc, argv);
}
Parameters::~Parameters()
{
impl.reset();
}
bool Parameters::show_help() const
{
return impl->get(CLI::ArgHelp).to_bool();
}
bool Parameters::show_enum() const
{
return !impl->get(CLI::ArgEnum).is("none");
}
const std::string& Parameters::program_name() const
{
return impl->program;
}
RunningMode Parameters::running_mode() const
{
const auto& mode = impl->get(CLI::ArgMode);
if(mode.is(CLI::profiling_mode))
{
return RunningMode::Profiling;
}
else if(mode.is(CLI::dumping_mode))
{
return RunningMode::Dumping;
}
else if(mode.is(CLI::analysis_mode))
{
return RunningMode::Analysis;
}
else if(mode.is(CLI::draining_mode))
{
return RunningMode::Draining;
}
throw cmdline::CLIError{std::string{"Unknown mode: "} + mode.to_cstr()};
}
std::string Parameters::input_file() const
{
// TODO: add file validation
return impl->is_default(CLI::ArgIFile) ? impl->default_iofile() : impl->get(CLI::ArgIFile);
}
const std::string Parameters::dropuser() const
{
return impl->get(CLI::ArgDropRoot);
}
const std::string Parameters::log_path() const
{
return impl->get(CLI::ArgLogPath);
}
unsigned short Parameters::queue_capacity() const
{
const int capacity = impl->get(CLI::ArgQSize).to_int();
if(capacity < 1 || capacity > 65535)
{
throw cmdline::CLIError(std::string{"Invalid value of queue capacity: "} + impl->get(CLI::ArgQSize).to_cstr());
}
return capacity;
}
bool Parameters::trace() const
{
// enable tracing if no analysis module was passed
return impl->get(CLI::ArgTrace).to_bool() || impl->analysis_modules.empty();
}
int Parameters::verbose_level() const
{
return impl->get(CLI::ArgVerbose).to_int();
}
const Parameters::CaptureParams Parameters::capture_params() const
{
Parameters::CaptureParams params;
params.interface = impl->get(CLI::ArgInterface);
params.filter = impl->get(CLI::ArgFilter);
params.snaplen = impl->get(CLI::ArgSnaplen).to_int();
params.timeout_ms = impl->get(CLI::ArgTimeout).to_int();
params.buffer_size = impl->get(CLI::ArgBSize).to_int() * 1024 * 1024; // MBytes
params.promisc = impl->get(CLI::ArgPromisc).to_bool();
// check interface
if(impl->is_default(CLI::ArgInterface))
{
params.interface = NST::filtration::pcap::NetworkInterfaces::default_device();
}
// check capture buffer size
if(params.buffer_size < 1024 * 1024) // less than 1 MBytes
{
throw cmdline::CLIError{std::string{"Invalid value of kernel buffer size: "} + impl->get(CLI::ArgBSize).to_cstr()};
}
// check max length of raw captured UDP packet
if(params.snaplen < 1 || params.snaplen > 65535)
{
throw cmdline::CLIError{std::string{"Invalid value of max length of raw captured UDP packet: "} + impl->get(CLI::ArgSnaplen).to_cstr()};
}
// check the read timeout that will be used on a capture
if(params.timeout_ms < 1)
{
throw cmdline::CLIError{std::string{"Invalid value of read timeout that will be used on a capture: "} + impl->get(CLI::ArgTimeout).to_cstr()};
}
// check and set capture direction
const auto& direction = impl->get(CLI::ArgDirection);
if(direction.is("in"))
{
params.direction = decltype(params.direction)::IN;
}
else if(direction.is("out"))
{
params.direction = decltype(params.direction)::OUT;
}
else if(direction.is("inout"))
{
params.direction = decltype(params.direction)::INOUT;
}
else
{
throw cmdline::CLIError{std::string{"Unknown capturing direction: "} + direction.to_cstr()};
}
return params;
}
const Parameters::DumpingParams Parameters::dumping_params() const
{
std::string ofile = impl->is_default(CLI::ArgOFile) ? impl->default_iofile() : impl->get(CLI::ArgOFile);
// TODO: add file validation
const int dsize = impl->get(CLI::ArgDSize).to_int();
if(dsize != 0 && ofile == "-") // '-' is alias for stdout in libpcap dumps
{
throw cmdline::CLIError{std::string{"Output file \"-\" means stdout, the dump-size must be 0"}};
}
Parameters::DumpingParams params;
params.output_file = ofile;
params.command = impl->get(CLI::ArgCommand);
params.size_limit = dsize * 1024 * 1024; // MBytes
return params;
}
const std::vector<AParams>& Parameters::analysis_modules() const
{
return impl->analysis_modules;
}
unsigned short Parameters::rpcmsg_limit()
{
return impl->rpc_message_limit;
}
} // namespace controller
} // namespace NST
//------------------------------------------------------------------------------