Merge pull request #50 from draios/cmdline-opts-daemonize

Clean up handling cmdline options wrt config file.
This commit is contained in:
Mark Stemm 2016-05-11 08:46:59 -07:00
commit 92c4c8f622
3 changed files with 116 additions and 34 deletions

View File

@ -7,18 +7,22 @@ using namespace std;
// If we don't have a configuration file, we just use stdout output and all other defaults
void falco_configuration::init()
void falco_configuration::init(std::list<std::string> &cmdline_options)
{
init_cmdline_options(cmdline_options);
output_config stdout_output;
stdout_output.name = "stdout";
m_outputs.push_back(stdout_output);
}
void falco_configuration::init(string conf_filename)
void falco_configuration::init(string conf_filename, std::list<std::string> &cmdline_options)
{
string m_config_file = conf_filename;
m_config = new yaml_configuration(m_config_file);
init_cmdline_options(cmdline_options);
m_rules_filename = m_config->get_scalar<string>("rules_file", "/etc/falco_rules.yaml");
m_json_output = m_config->get_scalar<bool>("json_output", false);
@ -58,3 +62,40 @@ void falco_configuration::init(string conf_filename)
falco_logger::log_stderr = m_config->get_scalar<bool>("log_stderr", false);
falco_logger::log_syslog = m_config->get_scalar<bool>("log_syslog", true);
}
static bool split(const string &str, char delim, pair<string,string> &parts)
{
size_t pos;
if ((pos = str.find_first_of(delim)) == string::npos) {
return false;
}
parts.first = str.substr(0, pos);
parts.second = str.substr(pos + 1);
return true;
}
void falco_configuration::init_cmdline_options(std::list<std::string> &cmdline_options)
{
for(const string &option : cmdline_options)
{
set_cmdline_option(option);
}
}
void falco_configuration::set_cmdline_option(const std::string &opt)
{
pair<string,string> keyval;
pair<string,string> subkey;
if (! split(opt, '=', keyval)) {
throw sinsp_exception("Error parsing config option \"" + opt + "\". Must be of the form key=val or key.subkey=val");
}
if (split(keyval.first, '.', subkey)) {
m_config->set_scalar(subkey.first, subkey.second, keyval.second);
} else {
m_config->set_scalar(keyval.first, keyval.second);
}
}

View File

@ -56,6 +56,19 @@ public:
return default_value;
}
/**
* Set the top-level node identified by key to value
*/
template<typename T>
void set_scalar(const std::string &key, const T& value)
{
auto node = m_root;
if (node.IsDefined())
{
node[key] = value;
}
}
/**
* Get a scalar value defined inside a 2 level nested structure like:
* file_output:
@ -84,6 +97,19 @@ public:
return default_value;
}
/**
* Set the second-level node identified by key[key][subkey] to value.
*/
template<typename T>
void set_scalar(const std::string& key, const std::string& subkey, const T& value)
{
auto node = m_root;
if (node.IsDefined())
{
node[key][subkey] = value;
}
}
private:
YAML::Node m_root;
};
@ -92,12 +118,23 @@ private:
class falco_configuration
{
public:
void init(std::string conf_filename);
void init();
void init(std::string conf_filename, std::list<std::string> &cmdline_options);
void init(std::list<std::string> &cmdline_options);
std::string m_rules_filename;
bool m_json_output;
std::vector<output_config> m_outputs;
private:
void init_cmdline_options(std::list<std::string> &cmdline_options);
/**
* Given a <key>=<value> specifier, set the appropriate option
* in the underlying yaml config. <key> can contain '.'
* characters for nesting. Currently only 1- or 2- level keys
* are supported and only scalar values are supported.
*/
void set_cmdline_option(const std::string &spec);
yaml_configuration* m_config;
};

View File

@ -29,19 +29,18 @@ extern "C" {
#include <yaml-cpp/yaml.h>
std::vector<string> valid_output_names {"stdout", "syslog"};
//
// Program help
//
static void usage()
{
printf(
"Usage: falco [options] rules_filename\n\n"
"Usage: falco [options]\n\n"
"Options:\n"
" -h, --help Print this page\n"
" -c Configuration file (default " FALCO_SOURCE_CONF_FILE ", " FALCO_INSTALL_CONF_FILE ")\n"
" -o Output type (options are 'stdout', 'syslog', default is 'stdout')\n"
" -o, --option <key>=<val> Set the value of option <key> to <val>. Overrides values in configuration file.\n"
" <key> can be a two-part <key>.<subkey>\n"
" -d, --daemon Run as a daemon\n"
" -p, --pidfile <pid_file> When run as a daemon, write pid to specified file\n"
" -e <events_file> Read the events from <events_file> (in .scap format) instead of tapping into live.\n"
@ -50,9 +49,26 @@ static void usage()
);
}
static void display_fatal_err(const string &msg, bool daemon)
{
falco_logger::log(LOG_ERR, msg);
/**
* If stderr logging is not enabled, also log to stderr. When
* daemonized this will simply write to /dev/null.
*/
if (! falco_logger::log_stderr)
{
std::cerr << msg;
}
}
string lua_on_event = "on_event";
string lua_add_output = "add_output";
// Splitting into key=value or key.subkey=value will be handled by configuration class.
std::list<string> cmdline_options;
//
// Event processing loop
//
@ -194,7 +210,6 @@ int falco_init(int argc, char **argv)
sinsp_evt::param_fmt event_buffer_format;
int long_index = 0;
string lua_main_filename;
string output_name = "syslog";
string scap_filename;
string conf_filename;
string rules_filename;
@ -207,14 +222,15 @@ int falco_init(int argc, char **argv)
{
{"help", no_argument, 0, 'h' },
{"daemon", no_argument, 0, 'd' },
{"pidfile", required_argument, 0, 'd' },
{"option", required_argument, 0, 'o'},
{"pidfile", required_argument, 0, 'p' },
{0, 0, 0, 0}
};
try
{
inspector = new sinsp();
bool valid;
//
// Parse the args
@ -232,12 +248,7 @@ int falco_init(int argc, char **argv)
conf_filename = optarg;
break;
case 'o':
valid = std::find(valid_output_names.begin(), valid_output_names.end(), optarg) != valid_output_names.end();
if (!valid)
{
throw sinsp_exception(string("Invalid output name ") + optarg);
}
output_name = optarg;
cmdline_options.push_back(optarg);
break;
case 'e':
scap_filename = optarg;
@ -262,15 +273,7 @@ int falco_init(int argc, char **argv)
// Some combinations of arguments are not allowed.
if (daemon && pidfilename == "") {
falco_logger::log(LOG_ERR, "If -d is provided, a pid file must also be provided. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
}
if (daemon && output_name == "stdout") {
falco_logger::log(LOG_ERR, "If -d is provided, can not output to stdout. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
throw sinsp_exception("If -d is provided, a pid file must also be provided");
}
ifstream* conf_stream;
@ -279,9 +282,7 @@ int falco_init(int argc, char **argv)
conf_stream = new ifstream(conf_filename);
if (!conf_stream->good())
{
falco_logger::log(LOG_ERR, "Could not find configuration file at " + conf_filename + ". Exiting.\n");
result = EXIT_FAILURE;
goto exit;
throw sinsp_exception("Could not find configuration file at " + conf_filename);
}
}
else
@ -308,13 +309,13 @@ int falco_init(int argc, char **argv)
falco_configuration config;
if (conf_filename.size())
{
config.init(conf_filename);
config.init(conf_filename, cmdline_options);
// log after config init because config determines where logs go
falco_logger::log(LOG_INFO, "Falco initialized with configuration file " + conf_filename + "\n");
}
else
{
config.init();
config.init(cmdline_options);
falco_logger::log(LOG_INFO, "Falco initialized. No configuration file found, proceeding with defaults\n");
}
@ -441,10 +442,13 @@ int falco_init(int argc, char **argv)
goto exit;
}
// Close stdin, stdout, stderr.
// Close stdin, stdout, stderr and reopen to /dev/null
close(0);
close(1);
close(2);
open("/dev/null", O_RDONLY);
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR);
}
do_inspect(inspector,
@ -455,13 +459,13 @@ int falco_init(int argc, char **argv)
}
catch(sinsp_exception& e)
{
falco_logger::log(LOG_ERR, "Runtime error: " + string(e.what()) + ". Exiting.\n");
display_fatal_err("Runtime error: " + string(e.what()) + ". Exiting.\n", daemon);
result = EXIT_FAILURE;
}
catch(...)
{
falco_logger::log(LOG_ERR, "Unexpected error, Exiting\n");
display_fatal_err("Unexpected error, Exiting\n", daemon);
result = EXIT_FAILURE;
}