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 // 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; output_config stdout_output;
stdout_output.name = "stdout"; stdout_output.name = "stdout";
m_outputs.push_back(stdout_output); 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; string m_config_file = conf_filename;
m_config = new yaml_configuration(m_config_file); 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_rules_filename = m_config->get_scalar<string>("rules_file", "/etc/falco_rules.yaml");
m_json_output = m_config->get_scalar<bool>("json_output", false); 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_stderr = m_config->get_scalar<bool>("log_stderr", false);
falco_logger::log_syslog = m_config->get_scalar<bool>("log_syslog", true); 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; 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: * Get a scalar value defined inside a 2 level nested structure like:
* file_output: * file_output:
@ -84,6 +97,19 @@ public:
return default_value; 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: private:
YAML::Node m_root; YAML::Node m_root;
}; };
@ -92,12 +118,23 @@ private:
class falco_configuration class falco_configuration
{ {
public: public:
void init(std::string conf_filename); void init(std::string conf_filename, std::list<std::string> &cmdline_options);
void init(); void init(std::list<std::string> &cmdline_options);
std::string m_rules_filename; std::string m_rules_filename;
bool m_json_output; bool m_json_output;
std::vector<output_config> m_outputs; std::vector<output_config> m_outputs;
private: 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; yaml_configuration* m_config;
}; };

View File

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