diff --git a/userspace/falco/configuration.cpp b/userspace/falco/configuration.cpp index 74e5775e..4727d0dd 100644 --- a/userspace/falco/configuration.cpp +++ b/userspace/falco/configuration.cpp @@ -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 &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 &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("rules_file", "/etc/falco_rules.yaml"); m_json_output = m_config->get_scalar("json_output", false); @@ -58,3 +62,40 @@ void falco_configuration::init(string conf_filename) falco_logger::log_stderr = m_config->get_scalar("log_stderr", false); falco_logger::log_syslog = m_config->get_scalar("log_syslog", true); } + +static bool split(const string &str, char delim, pair &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 &cmdline_options) +{ + for(const string &option : cmdline_options) + { + set_cmdline_option(option); + } +} + +void falco_configuration::set_cmdline_option(const std::string &opt) +{ + pair keyval; + pair 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); + } +} diff --git a/userspace/falco/configuration.h b/userspace/falco/configuration.h index 48d93c7e..d389aa37 100644 --- a/userspace/falco/configuration.h +++ b/userspace/falco/configuration.h @@ -56,6 +56,19 @@ public: return default_value; } + /** + * Set the top-level node identified by key to value + */ + template + 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 + 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 &cmdline_options); + void init(std::list &cmdline_options); + std::string m_rules_filename; bool m_json_output; std::vector m_outputs; private: + void init_cmdline_options(std::list &cmdline_options); + + /** + * Given a = specifier, set the appropriate option + * in the underlying yaml config. 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; }; diff --git a/userspace/falco/falco.cpp b/userspace/falco/falco.cpp index d02ca5fe..53d009fc 100644 --- a/userspace/falco/falco.cpp +++ b/userspace/falco/falco.cpp @@ -29,19 +29,18 @@ extern "C" { #include -std::vector 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 = Set the value of option to . Overrides values in configuration file.\n" + " can be a two-part .\n" " -d, --daemon Run as a daemon\n" " -p, --pidfile When run as a daemon, write pid to specified file\n" " -e Read the events from (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 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; }