Files
falco/userspace/falco/configuration.h
Mark Stemm 79f9843256 Clean up handling cmdline options wrt config file.
Remove the old use of the '-o' command line option, it wasn't being
used.

Allow any config file option to be overridden on the command line, via
--option/-o. These options are applied to the configuration object after
reading the file, ensuring the command line options override anything in
the config file.

To support this, add some methods to yaml_configuration that allows you
to set the value for a top level key or key + subkey, and methods to
falco_configuration that allow providing a set of command line arguments
alongside the config file.

Ensure that any fatal error is always printed to stderr even if stderr
logging is not enabled. This makes sure that falco won't silently exit
on an error. This is especially important when daemonizing and when an
initial fatal error occurs first.

As a part of this, change all fatal errors to throw exceptions instead,
so all fatal errors get routed through the exception handler.

Improve daemonization by reopening stdin/stdout/stderr to /dev/null so
you don't have to worry about writing to a closed stderr on exit.
2016-05-10 15:52:59 -07:00

141 lines
2.9 KiB
C++

#pragma once
#include <yaml-cpp/yaml.h>
#include <iostream>
struct output_config
{
std::string name;
std::map<std::string, std::string> options;
};
class yaml_configuration
{
public:
std::string m_path;
yaml_configuration(const std::string& path)
{
m_path = path;
YAML::Node config;
std::vector<output_config> outputs;
try
{
m_root = YAML::LoadFile(path);
}
catch (const YAML::BadFile& ex)
{
std::cerr << "Error reading config file (" + path + "): " + ex.what() + "\n";
throw;
}
catch (const YAML::ParserException& ex)
{
std::cerr << "Cannot read config file (" + path + "): " + ex.what() + "\n";
throw;
}
}
/**
* Get a scalar value defined at the top level of the config
*/
template<typename T>
const T get_scalar(const std::string& key, const T& default_value)
{
try
{
auto node = m_root[key];
if (node.IsDefined())
{
return node.as<T>();
}
} catch (const YAML::BadConversion& ex)
{
std::cerr << "Cannot read config file (" + m_path + "): wrong type at key " + key + "\n";
throw;
}
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:
* enabled: true
* filename: output_file.txt
*
* get_scalar<bool>("file_output", "enabled", false)
*/
template<typename T>
const T get_scalar(const std::string& key, const std::string& subkey, const T& default_value)
{
try
{
auto node = m_root[key][subkey];
if (node.IsDefined())
{
return node.as<T>();
}
}
catch (const YAML::BadConversion& ex)
{
std::cerr << "Cannot read config file (" + m_path + "): wrong type at key " + key + "\n";
throw;
}
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;
};
class falco_configuration
{
public:
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;
};