refactor(userspace/engine): turn app methods into simple functions

Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
This commit is contained in:
Jason Dellaluce
2023-02-08 17:40:49 +00:00
committed by poiana
parent 374136be18
commit fe859bda2d
37 changed files with 817 additions and 1454 deletions

View File

@@ -15,36 +15,38 @@ configure_file(config_falco.h.in config_falco.h)
set(
FALCO_SOURCES
application.cpp
app_cmdline_options.cpp
app_actions/create_signal_handlers.cpp
app_actions/daemonize.cpp
app_actions/init_falco_engine.cpp
app_actions/init_inspectors.cpp
app_actions/init_clients.cpp
app_actions/init_outputs.cpp
app_actions/list_fields.cpp
app_actions/list_plugins.cpp
app_actions/load_config.cpp
app_actions/load_plugins.cpp
app_actions/load_rules_files.cpp
app_actions/open_inspector.cpp
app_actions/process_events.cpp
app_actions/print_generated_gvisor_config.cpp
app_actions/print_help.cpp
app_actions/print_ignored_events.cpp
app_actions/print_plugin_info.cpp
app_actions/print_support.cpp
app_actions/print_syscall_events.cpp
app_actions/print_version.cpp
app_actions/print_page_size.cpp
app_actions/compute_syscall_buffer_size.cpp
app_actions/select_event_sources.cpp
app_actions/start_grpc_server.cpp
app_actions/start_webserver.cpp
app_actions/validate_rules_files.cpp
app_actions/create_requested_paths.cpp
app_actions/configure_interesting_sets.cpp
app/app.cpp
app/state.cpp
app/signals.cpp
app/options.cpp
app/actions/create_signal_handlers.cpp
app/actions/daemonize.cpp
app/actions/init_falco_engine.cpp
app/actions/init_inspectors.cpp
app/actions/init_clients.cpp
app/actions/init_outputs.cpp
app/actions/list_fields.cpp
app/actions/list_plugins.cpp
app/actions/load_config.cpp
app/actions/load_plugins.cpp
app/actions/load_rules_files.cpp
app/actions/open_inspector.cpp
app/actions/process_events.cpp
app/actions/print_generated_gvisor_config.cpp
app/actions/print_help.cpp
app/actions/print_ignored_events.cpp
app/actions/print_plugin_info.cpp
app/actions/print_support.cpp
app/actions/print_syscall_events.cpp
app/actions/print_version.cpp
app/actions/print_page_size.cpp
app/actions/compute_syscall_buffer_size.cpp
app/actions/select_event_sources.cpp
app/actions/start_grpc_server.cpp
app/actions/start_webserver.cpp
app/actions/validate_rules_files.cpp
app/actions/create_requested_paths.cpp
app/actions/configure_interesting_sets.cpp
configuration.cpp
logger.cpp
falco_outputs.cpp

View File

@@ -0,0 +1,111 @@
/*
Copyright (C) 2023 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include "../state.h"
#include "../run_result.h"
namespace falco {
namespace app {
namespace actions {
falco::app::run_result create_signal_handlers(falco::app::state& s);
falco::app::run_result attach_inotify_signals(falco::app::state& s);
falco::app::run_result daemonize(falco::app::state& s);
falco::app::run_result init_falco_engine(falco::app::state& s);
falco::app::run_result init_inspectors(falco::app::state& s);
falco::app::run_result init_clients(falco::app::state& s);
falco::app::run_result init_outputs(falco::app::state& s);
falco::app::run_result list_fields(falco::app::state& s);
falco::app::run_result list_plugins(falco::app::state& s);
falco::app::run_result load_config(falco::app::state& s);
falco::app::run_result require_config_file(falco::app::state& s);
falco::app::run_result load_plugins(falco::app::state& s);
falco::app::run_result load_rules_files(falco::app::state& s);
falco::app::run_result create_requested_paths(falco::app::state& s);
falco::app::run_result print_generated_gvisor_config(falco::app::state& s);
falco::app::run_result print_help(falco::app::state& s);
falco::app::run_result print_ignored_events(falco::app::state& s);
falco::app::run_result print_plugin_info(falco::app::state& s);
falco::app::run_result print_support(falco::app::state& s);
falco::app::run_result print_syscall_events(falco::app::state& s);
falco::app::run_result print_version(falco::app::state& s);
falco::app::run_result print_page_size(falco::app::state& s);
falco::app::run_result process_events(falco::app::state& s);
falco::app::run_result select_event_sources(falco::app::state& s);
falco::app::run_result configure_syscall_buffer_size(falco::app::state& s);
falco::app::run_result start_grpc_server(falco::app::state& s);
falco::app::run_result start_webserver(falco::app::state& s);
falco::app::run_result validate_rules_files(falco::app::state& s);
// teardown
bool unregister_signal_handlers(falco::app::state& s, std::string &errstr);
bool stop_grpc_server(falco::app::state& s, std::string &errstr);
bool stop_webserver(falco::app::state& s, std::string &errstr);
// helpers
bool check_rules_plugin_requirements(falco::app::state& s, std::string& err);
falco::app::run_result open_offline_inspector(falco::app::state& s);
void print_enabled_event_sources(falco::app::state& s);
void configure_interesting_sets(falco::app::state& s);
void format_plugin_info(std::shared_ptr<sinsp_plugin> p, std::ostream& os);
falco::app::run_result open_live_inspector(
falco::app::state& s,
std::shared_ptr<sinsp> inspector,
const std::string& source);
template<class InputIterator>
void read_files(InputIterator begin, InputIterator end,
std::vector<std::string>& rules_contents,
falco::load_result::rules_contents_t& rc)
{
// Read the contents in a first pass
for(auto it = begin; it != end; it++)
{
std::string &filename = *it;
std::ifstream is;
is.open(filename);
if (!is.is_open())
{
throw falco_exception("Could not open file " + filename + " for reading");
}
std::string rules_content((std::istreambuf_iterator<char>(is)),
std::istreambuf_iterator<char>());
rules_contents.emplace_back(std::move(rules_content));
}
// Populate the map in a second pass to avoid
// references becoming invalid.
auto it = begin;
auto rit = rules_contents.begin();
for(; it != end && rit != rules_contents.end(); it++, rit++)
{
rc.emplace(*it, *rit);
}
// Both it and rit must be at the end, otherwise
// there's a bug in the above
if(it != end || rit != rules_contents.end())
{
throw falco_exception("Unexpected mismatch in rules content name/rules content sets?");
}
}
}; // namespace actions
}; // namespace app
}; // namespace falco

View File

@@ -14,26 +14,27 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
using namespace falco::app;
using namespace falco::app::actions;
/* These indexes could change over the Falco releases. */
#define MIN_INDEX 1
#define MAX_INDEX 10
#define DEFAULT_BYTE_SIZE 1 << 23
application::run_result application::configure_syscall_buffer_size()
falco::app::run_result falco::app::actions::configure_syscall_buffer_size(falco::app::state& s)
{
/* We don't need to compute the syscall buffer dimension if we are in capture mode or if the
* the syscall source is not enabled.
*/
if(is_capture_mode() || m_state->enabled_sources.find(falco_common::syscall_source) == m_state->enabled_sources.end() || is_gvisor_enabled())
if(s.is_capture_mode() || s.enabled_sources.find(falco_common::syscall_source) == s.enabled_sources.end() || s.is_gvisor_enabled())
{
return run_result::ok();
}
uint16_t index = m_state->config->m_syscall_buf_size_preset;
uint16_t index = s.config->m_syscall_buf_size_preset;
if(index < MIN_INDEX || index > MAX_INDEX)
{
return run_result::fatal("The 'syscall_buf_size_preset' value must be between '" + std::to_string(MIN_INDEX) + "' and '" + std::to_string(MAX_INDEX) + "'\n");
@@ -48,7 +49,7 @@ application::run_result application::configure_syscall_buffer_size()
long page_size = getpagesize();
if(page_size <= 0)
{
m_state->syscall_buffer_bytes_size = DEFAULT_BYTE_SIZE;
s.syscall_buffer_bytes_size = DEFAULT_BYTE_SIZE;
falco_logger::log(LOG_WARNING, "Unable to get the system page size through 'getpagesize()'. Try to use the default syscall buffer dimension: " + std::to_string(DEFAULT_BYTE_SIZE) + " bytes\n");
return run_result::ok();
}
@@ -65,7 +66,7 @@ application::run_result application::configure_syscall_buffer_size()
return run_result::fatal("The chosen syscall buffer size '" + std::to_string(chosen_size) + "' is not greater than '2 * " + std::to_string(page_size) + "' where '" + std::to_string(page_size) + "' is your system page size. Please configure a greater 'syscall_buf_size_preset' value in the Falco configuration file\n");
}
m_state->syscall_buffer_bytes_size = chosen_size;
s.syscall_buffer_bytes_size = chosen_size;
falco_logger::log(LOG_INFO, "The chosen syscall buffer dimension is: " + std::to_string(chosen_size) + " bytes (" + std::to_string(chosen_size / (uint64_t)(1024 * 1024)) + " MBs)\n");
return run_result::ok();
}

View File

@@ -14,11 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
using namespace falco::app;
using namespace falco::app::actions;
void application::configure_interesting_sets()
void falco::app::actions::configure_interesting_sets(falco::app::state& s)
{
/// TODO: in the next future we need to change the interface of `enforce_simple_ppm_sc_set`
/// and `enforce_sinsp_state_tp` APIs, they shouldn't require an inspector to be called!
@@ -32,8 +33,8 @@ void application::configure_interesting_sets()
* so we use the `simple_set`, this `simple_set` contains all the syscalls of the `libsinsp` state
* plus syscalls for Falco default rules.
*/
m_state->ppm_sc_of_interest = inspector->enforce_simple_ppm_sc_set();
m_state->ppm_event_info_of_interest = inspector->get_event_set_from_ppm_sc_set(m_state->ppm_sc_of_interest);
s.ppm_sc_of_interest = inspector->enforce_simple_ppm_sc_set();
s.ppm_event_info_of_interest = inspector->get_event_set_from_ppm_sc_set(s.ppm_sc_of_interest);
/* Fill-up the set of event infos of interest */
for (uint32_t ev = 2; ev < PPM_EVENT_MAX; ev++)
@@ -47,7 +48,7 @@ void application::configure_interesting_sets()
metaevents and in the procexit tracepoint event. */
if (sinsp::is_metaevent(ev) || ev == PPME_PROCEXIT_1_E)
{
m_state->ppm_event_info_of_interest.insert(ev);
s.ppm_event_info_of_interest.insert(ev);
}
}
}
@@ -56,6 +57,6 @@ void application::configure_interesting_sets()
* the `sched_switch` tracepoint since it is highly noisy and not so useful
* for our state/events enrichment.
*/
m_state->tp_of_interest = inspector->enforce_sinsp_state_tp();
m_state->tp_of_interest.erase(SCHED_SWITCH);
s.tp_of_interest = inspector->enforce_sinsp_state_tp();
s.tp_of_interest.erase(SCHED_SWITCH);
}

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
#include "falco_utils.h"
#include <sys/stat.h>
@@ -27,67 +27,9 @@ limitations under the License.
#endif
using namespace falco::app;
using namespace falco::app::actions;
application::run_result application::create_requested_paths()
{
if(!m_options.gvisor_config.empty())
{
// This is bad: parsing gvisor config to get endpoint
// to be able to auto-create the path to the file for the user.
std::ifstream reader(m_options.gvisor_config);
if (reader.fail())
{
return run_result::fatal(m_options.gvisor_config + ": cannot open file");
}
nlohmann::json parsed_json;
std::string gvisor_socket;
try
{
parsed_json = nlohmann::json::parse(reader);
}
catch (const std::exception &e)
{
return run_result::fatal(m_options.gvisor_config + ": cannot parse JSON: " + e.what());
}
try
{
gvisor_socket = parsed_json["trace_session"]["sinks"][0]["config"]["endpoint"];
}
catch (const std::exception &e)
{
return run_result::fatal(m_options.gvisor_config + ": failed to fetch config.endpoint: " + e.what());
}
int ret = create_dir(gvisor_socket);
if (ret != 0)
{
return run_result::fatal(gvisor_socket + ": " + strerror(errno));
}
}
if (m_state->config->m_grpc_enabled && !m_state->config->m_grpc_bind_address.empty())
{
if(falco::utils::network::is_unix_scheme(m_state->config->m_grpc_bind_address))
{
auto server_path = m_state->config->m_grpc_bind_address.substr(
falco::utils::network::UNIX_SCHEME.length()
);
int ret = create_dir(server_path);
if(ret != 0)
{
return run_result::fatal(server_path + ": " + strerror(errno));
}
}
}
// TODO: eventually other files written by Falco whose destination is
// customizable by users, must be handled here.
return run_result::ok();
}
int application::create_dir(const std::string &path)
static int create_dir(const std::string &path)
{
// Properly reset errno
errno = 0;
@@ -109,3 +51,63 @@ int application::create_dir(const std::string &path)
}
return 0;
}
falco::app::run_result falco::app::actions::create_requested_paths(falco::app::state& s)
{
if(!s.options.gvisor_config.empty())
{
// This is bad: parsing gvisor config to get endpoint
// to be able to auto-create the path to the file for the user.
std::ifstream reader(s.options.gvisor_config);
if (reader.fail())
{
return run_result::fatal(s.options.gvisor_config + ": cannot open file");
}
nlohmann::json parsed_json;
std::string gvisor_socket;
try
{
parsed_json = nlohmann::json::parse(reader);
}
catch (const std::exception &e)
{
return run_result::fatal(s.options.gvisor_config + ": cannot parse JSON: " + e.what());
}
try
{
gvisor_socket = parsed_json["trace_session"]["sinks"][0]["config"]["endpoint"];
}
catch (const std::exception &e)
{
return run_result::fatal(s.options.gvisor_config + ": failed to fetch config.endpoint: " + e.what());
}
int ret = create_dir(gvisor_socket);
if (ret != 0)
{
return run_result::fatal(gvisor_socket + ": " + strerror(errno));
}
}
if (s.config->m_grpc_enabled && !s.config->m_grpc_bind_address.empty())
{
if(falco::utils::network::is_unix_scheme(s.config->m_grpc_bind_address))
{
auto server_path = s.config->m_grpc_bind_address.substr(
falco::utils::network::UNIX_SCHEME.length()
);
int ret = create_dir(server_path);
if(ret != 0)
{
return run_result::fatal(server_path + ": " + strerror(errno));
}
}
}
// TODO: eventually other files written by Falco whose destination is
// customizable by users, must be handled here.
return run_result::ok();
}

View File

@@ -21,9 +21,11 @@ limitations under the License.
#include <sys/inotify.h>
#include <fcntl.h>
#include "application.h"
#include "actions.h"
#include "../signals.h"
using namespace falco::app;
using namespace falco::app::actions;
// This is initially set to a dummy application. When
// create_signal_handlers is called, it will be rebound to the
@@ -50,7 +52,7 @@ static void restart_signal_handler(int signal)
falco::app::g_restart.store(APP_SIGNAL_SET, std::memory_order_seq_cst);
}
bool application::create_handler(int sig, void (*func)(int), run_result &ret)
bool create_handler(int sig, void (*func)(int), run_result &ret)
{
ret = run_result::ok();
if(signal(sig, func) == SIG_ERR)
@@ -70,7 +72,7 @@ bool application::create_handler(int sig, void (*func)(int), run_result &ret)
return ret.success;
}
application::run_result application::create_signal_handlers()
falco::app::run_result falco::app::actions::create_signal_handlers(falco::app::state& s)
{
falco::app::g_terminate.store(APP_SIGNAL_NOT_SET, std::memory_order_seq_cst);
falco::app::g_restart.store(APP_SIGNAL_NOT_SET, std::memory_order_seq_cst);
@@ -95,9 +97,9 @@ application::run_result application::create_signal_handlers()
return ret;
}
application::run_result application::attach_inotify_signals()
falco::app::run_result falco::app::actions::attach_inotify_signals(falco::app::state& s)
{
if (m_state->config->m_watch_config_files)
if (s.config->m_watch_config_files)
{
inot_fd = inotify_init();
if (inot_fd == -1)
@@ -131,15 +133,15 @@ application::run_result application::attach_inotify_signals()
}
// Watch conf file
int wd = inotify_add_watch(inot_fd, m_options.conf_filename.c_str(), IN_CLOSE_WRITE);
int wd = inotify_add_watch(inot_fd, s.options.conf_filename.c_str(), IN_CLOSE_WRITE);
if (wd == -1)
{
return run_result::fatal("Failed to watch conf file");
}
falco_logger::log(LOG_DEBUG, "Watching " + m_options.conf_filename +"\n");
falco_logger::log(LOG_DEBUG, "Watching " + s.options.conf_filename +"\n");
// Watch rules files
for (const auto &rule : m_state->config->m_loaded_rules_filenames)
for (const auto &rule : s.config->m_loaded_rules_filenames)
{
wd = inotify_add_watch(inot_fd, rule.c_str(), IN_CLOSE_WRITE | IN_ONESHOT);
if (wd == -1)
@@ -152,7 +154,7 @@ application::run_result application::attach_inotify_signals()
// Watch specified rules folders, if any:
// any newly created/removed file within the folder
// will trigger a Falco restart.
for (const auto &fld : m_state->config->m_loaded_rules_folders)
for (const auto &fld : s.config->m_loaded_rules_folders)
{
// For folders, we watch if any file is created or destroyed within
wd = inotify_add_watch(inot_fd, fld.c_str(), IN_CREATE | IN_DELETE | IN_ONESHOT);
@@ -166,7 +168,7 @@ application::run_result application::attach_inotify_signals()
return run_result::ok();
}
bool application::unregister_signal_handlers(std::string &errstr)
bool falco::app::actions::unregister_signal_handlers(falco::app::state& s, std::string &errstr)
{
run_result ret;
close(inot_fd);

View File

@@ -18,17 +18,18 @@ limitations under the License.
#include <sys/stat.h>
#include <fcntl.h>
#include "application.h"
#include "actions.h"
using namespace falco::app;
using namespace falco::app::actions;
static bool s_daemonized = false;
application::run_result application::daemonize()
falco::app::run_result falco::app::actions::daemonize(falco::app::state& s)
{
// If daemonizing, do it here so any init errors will
// be returned in the foreground process.
if (m_options.daemon && !s_daemonized) {
if (s.options.daemon && !s_daemonized) {
pid_t pid, sid;
pid = fork();
@@ -38,11 +39,11 @@ application::run_result application::daemonize()
} else if (pid > 0) {
// parent. Write child pid to pidfile and exit
std::ofstream pidfile;
pidfile.open(m_options.pidfilename);
pidfile.open(s.options.pidfilename);
if (!pidfile.good())
{
falco_logger::log(LOG_ERR, "Could not write pid to pid file " + m_options.pidfilename + ". Exiting.\n");
falco_logger::log(LOG_ERR, "Could not write pid to pid file " + s.options.pidfilename + ". Exiting.\n");
exit(-1);
}
pidfile << pid;

View File

@@ -14,39 +14,40 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
using namespace falco::app;
using namespace falco::app::actions;
application::run_result application::init_clients()
falco::app::run_result falco::app::actions::init_clients(falco::app::state& s)
{
#ifndef MINIMAL_BUILD
// k8s is useful only if the syscall source is enabled
if (m_state->enabled_sources.find(falco_common::syscall_source) == m_state->enabled_sources.end())
if (s.enabled_sources.find(falco_common::syscall_source) == s.enabled_sources.end())
{
return run_result::ok();
}
auto inspector = m_state->source_infos.at(falco_common::syscall_source)->inspector;
auto inspector = s.source_infos.at(falco_common::syscall_source)->inspector;
falco_logger::log(LOG_DEBUG, "Setting metadata download max size to " + std::to_string(m_state->config->m_metadata_download_max_mb) + " MB\n");
falco_logger::log(LOG_DEBUG, "Setting metadata download chunk wait time to " + std::to_string(m_state->config->m_metadata_download_chunk_wait_us) + " μs\n");
falco_logger::log(LOG_DEBUG, "Setting metadata download watch frequency to " + std::to_string(m_state->config->m_metadata_download_watch_freq_sec) + " seconds\n");
inspector->set_metadata_download_params(m_state->config->m_metadata_download_max_mb * 1024 * 1024, m_state->config->m_metadata_download_chunk_wait_us, m_state->config->m_metadata_download_watch_freq_sec);
falco_logger::log(LOG_DEBUG, "Setting metadata download max size to " + std::to_string(s.config->m_metadata_download_max_mb) + " MB\n");
falco_logger::log(LOG_DEBUG, "Setting metadata download chunk wait time to " + std::to_string(s.config->m_metadata_download_chunk_wait_us) + " μs\n");
falco_logger::log(LOG_DEBUG, "Setting metadata download watch frequency to " + std::to_string(s.config->m_metadata_download_watch_freq_sec) + " seconds\n");
inspector->set_metadata_download_params(s.config->m_metadata_download_max_mb * 1024 * 1024, s.config->m_metadata_download_chunk_wait_us, s.config->m_metadata_download_watch_freq_sec);
//
// Run k8s, if required
//
char *k8s_api_env = NULL;
if(!m_options.k8s_api.empty() ||
if(!s.options.k8s_api.empty() ||
(k8s_api_env = getenv("FALCO_K8S_API")))
{
// Create string pointers for some config vars
// and pass to inspector. The inspector then
// owns the pointers.
std::string *k8s_api_ptr = new std::string((!m_options.k8s_api.empty() ? m_options.k8s_api : k8s_api_env));
std::string *k8s_api_cert_ptr = new std::string(m_options.k8s_api_cert);
std::string *k8s_node_name_ptr = new std::string(m_options.k8s_node_name);
std::string *k8s_api_ptr = new std::string((!s.options.k8s_api.empty() ? s.options.k8s_api : k8s_api_env));
std::string *k8s_api_cert_ptr = new std::string(s.options.k8s_api_cert);
std::string *k8s_node_name_ptr = new std::string(s.options.k8s_node_name);
if(k8s_api_cert_ptr->empty())
{
@@ -55,7 +56,7 @@ application::run_result application::init_clients()
*k8s_api_cert_ptr = k8s_cert_env;
}
}
inspector->init_k8s_client(k8s_api_ptr, k8s_api_cert_ptr, k8s_node_name_ptr, m_options.verbose);
inspector->init_k8s_client(k8s_api_ptr, k8s_api_cert_ptr, k8s_node_name_ptr, s.options.verbose);
}
//
@@ -63,20 +64,20 @@ application::run_result application::init_clients()
// Run mesos, if required
// todo(leogr): remove in Falco 0,.35
//
if(!m_options.mesos_api.empty())
if(!s.options.mesos_api.empty())
{
// Differs from init_k8s_client in that it
// passes a pointer but the inspector does
// *not* own it and does not use it after
// init_mesos_client() returns.
falco_logger::log(LOG_WARNING, "Mesos support has been DEPRECATED and will be removed in the next version!\n");
inspector->init_mesos_client(&(m_options.mesos_api), m_options.verbose);
inspector->init_mesos_client(&(s.options.mesos_api), s.options.verbose);
}
else if(char* mesos_api_env = getenv("FALCO_MESOS_API"))
{
falco_logger::log(LOG_WARNING, "Mesos support has been DEPRECATED and will be removed in the next version!\n");
std::string mesos_api_copy = mesos_api_env;
inspector->init_mesos_client(&mesos_api_copy, m_options.verbose);
inspector->init_mesos_client(&mesos_api_copy, s.options.verbose);
}
#endif

View File

@@ -14,56 +14,57 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
#include <plugin_manager.h>
using namespace falco::app;
using namespace falco::app::actions;
void application::configure_output_format()
void configure_output_format(falco::app::state& s)
{
std::string output_format;
bool replace_container_info = false;
if(m_options.print_additional == "c" || m_options.print_additional == "container")
if(s.options.print_additional == "c" || s.options.print_additional == "container")
{
output_format = "container=%container.name (id=%container.id)";
replace_container_info = true;
}
else if(m_options.print_additional == "cg" || m_options.print_additional == "container-gvisor")
else if(s.options.print_additional == "cg" || s.options.print_additional == "container-gvisor")
{
output_format = "container=%container.name (id=%container.id) vpid=%proc.vpid vtid=%thread.vtid";
replace_container_info = true;
}
else if(m_options.print_additional == "k" || m_options.print_additional == "kubernetes")
else if(s.options.print_additional == "k" || s.options.print_additional == "kubernetes")
{
output_format = "k8s.ns=%k8s.ns.name k8s.pod=%k8s.pod.name container=%container.id";
replace_container_info = true;
}
else if(m_options.print_additional == "kg" || m_options.print_additional == "kubernetes-gvisor")
else if(s.options.print_additional == "kg" || s.options.print_additional == "kubernetes-gvisor")
{
output_format = "k8s.ns=%k8s.ns.name k8s.pod=%k8s.pod.name container=%container.id vpid=%proc.vpid vtid=%thread.vtid";
replace_container_info = true;
}
else if(m_options.print_additional == "m" || m_options.print_additional == "mesos")
else if(s.options.print_additional == "m" || s.options.print_additional == "mesos")
{
output_format = "task=%mesos.task.name container=%container.id";
replace_container_info = true;
}
else if(!m_options.print_additional.empty())
else if(!s.options.print_additional.empty())
{
output_format = m_options.print_additional;
output_format = s.options.print_additional;
replace_container_info = false;
}
if(!output_format.empty())
{
m_state->engine->set_extra(output_format, replace_container_info);
s.engine->set_extra(output_format, replace_container_info);
}
}
void application::add_source_to_engine(const std::string& src)
void add_source_to_engine(falco::app::state& s, const std::string& src)
{
auto src_info = m_state->source_infos.at(src);
auto src_info = s.source_infos.at(src);
std::shared_ptr<gen_event_filter_factory> filter_factory = nullptr;
std::shared_ptr<gen_event_formatter_factory> formatter_factory = nullptr;
@@ -76,37 +77,37 @@ void application::add_source_to_engine(const std::string& src)
}
else
{
auto &filterchecks = m_state->source_infos.at(src)->filterchecks;
auto &filterchecks = s.source_infos.at(src)->filterchecks;
filter_factory = std::shared_ptr<gen_event_filter_factory>(
new sinsp_filter_factory(src_info->inspector.get(), filterchecks));
formatter_factory = std::shared_ptr<gen_event_formatter_factory>(
new sinsp_evt_formatter_factory(src_info->inspector.get(), filterchecks));
}
if(m_state->config->m_json_output)
if(s.config->m_json_output)
{
formatter_factory->set_output_format(gen_event_formatter::OF_JSON);
}
src_info->engine_idx = m_state->engine->add_source(
src_info->engine_idx = s.engine->add_source(
src, filter_factory, formatter_factory);
}
application::run_result application::init_falco_engine()
falco::app::run_result falco::app::actions::init_falco_engine(falco::app::state& s)
{
// add all non-syscall event sources in engine
for (const auto& src : m_state->loaded_sources)
for (const auto& src : s.loaded_sources)
{
if (src != falco_common::syscall_source)
{
// we skip the syscall as we want it to be the one added for last
// in the engine. This makes the source index assignment easier.
add_source_to_engine(src);
add_source_to_engine(s, src);
}
}
// add syscall as last source
add_source_to_engine(falco_common::syscall_source);
add_source_to_engine(s, falco_common::syscall_source);
// note: in capture mode, we can assume that the plugin source index will
// be the same in both the falco engine and the sinsp plugin manager.
@@ -116,16 +117,16 @@ application::run_result application::init_falco_engine()
// is because in that case event sources are scattered across different
// inspectors. Since this is an implementation-based assumption, we
// check this and return an error to spot regressions in the future.
if (is_capture_mode())
if (s.is_capture_mode())
{
auto manager = m_state->offline_inspector->get_plugin_manager();
auto manager = s.offline_inspector->get_plugin_manager();
for (const auto &p : manager->plugins())
{
if (p->caps() & CAP_SOURCING)
{
bool added = false;
auto source_idx = manager->source_idx_by_plugin_id(p->id(), added);
auto engine_idx = m_state->source_infos.at(p->event_source())->engine_idx;
auto engine_idx = s.source_infos.at(p->event_source())->engine_idx;
if (!added || source_idx != engine_idx)
{
return run_result::fatal("Could not add event source in the engine: " + p->event_source());
@@ -134,8 +135,8 @@ application::run_result application::init_falco_engine()
}
}
configure_output_format();
m_state->engine->set_min_priority(m_state->config->m_min_priority);
configure_output_format(s);
s.engine->set_min_priority(s.config->m_min_priority);
return run_result::ok();
}

View File

@@ -14,20 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
#include <unordered_set>
#include <plugin_manager.h>
using namespace falco::app;
using namespace falco::app::actions;
void application::init_syscall_inspector(
std::shared_ptr<sinsp> inspector,
const falco::app::cmdline_options& opts)
static void init_syscall_inspector(falco::app::state& s, std::shared_ptr<sinsp> inspector)
{
inspector->set_buffer_format(opts.event_buffer_format);
inspector->set_buffer_format(s.options.event_buffer_format);
// If required, set the CRI paths
for (auto &p : opts.cri_socket_paths)
for (auto &p : s.options.cri_socket_paths)
{
if (!p.empty())
{
@@ -36,19 +35,19 @@ void application::init_syscall_inspector(
}
// Decide whether to do sync or async for CRI metadata fetch
inspector->set_cri_async(!opts.disable_cri_async);
inspector->set_cri_async(!s.options.disable_cri_async);
//
// If required, set the snaplen
//
if(opts.snaplen != 0)
if(s.options.snaplen != 0)
{
inspector->set_snaplen(opts.snaplen);
inspector->set_snaplen(s.options.snaplen);
}
if(!opts.all_events)
if(!s.options.all_events)
{
configure_interesting_sets();
configure_interesting_sets(s);
}
inspector->set_hostname_and_port_resolution_mode(false);
@@ -99,27 +98,27 @@ static bool populate_filterchecks(
return true;
}
application::run_result application::init_inspectors()
falco::app::run_result falco::app::actions::init_inspectors(falco::app::state& s)
{
std::string err;
std::unordered_set<std::string> used_plugins;
const auto& all_plugins = m_state->offline_inspector->get_plugin_manager()->plugins();
const auto& all_plugins = s.offline_inspector->get_plugin_manager()->plugins();
for (const auto &src : m_state->loaded_sources)
for (const auto &src : s.loaded_sources)
{
auto src_info = m_state->source_infos.at(src);
auto src_info = s.source_infos.at(src);
// in capture mode, every event source uses the offline inspector.
// in live mode, we create a new inspector for each event source
src_info->inspector = is_capture_mode()
? m_state->offline_inspector
src_info->inspector = s.is_capture_mode()
? s.offline_inspector
: std::make_shared<sinsp>();
// handle syscall and plugin sources differently
// todo(jasondellaluce): change this once we support extracting plugin fields from syscalls too
if (src == falco_common::syscall_source)
{
init_syscall_inspector(src_info->inspector, m_options);
init_syscall_inspector(s, src_info->inspector);
continue;
}
@@ -128,10 +127,10 @@ application::run_result application::init_inspectors()
for (const auto& p : all_plugins)
{
std::shared_ptr<sinsp_plugin> plugin = nullptr;
auto config = m_state->plugin_configs.at(p->name());
auto config = s.plugin_configs.at(p->name());
auto is_input = p->caps() & CAP_SOURCING && p->event_source() == src;
if (is_capture_mode())
if (s.is_capture_mode())
{
// in capture mode, every plugin is already registered
// in the offline inspector by the load_plugins action

View File

@@ -17,13 +17,14 @@ limitations under the License.
#include <stdlib.h>
#include <unistd.h>
#include "application.h"
#include "actions.h"
using namespace falco::app;
using namespace falco::app::actions;
application::run_result application::init_outputs()
falco::app::run_result falco::app::actions::init_outputs(falco::app::state& s)
{
if (m_state->config->m_outputs.empty())
if (s.config->m_outputs.empty())
{
return run_result::fatal("No output configured, please make sure at least one output is configured and enabled.");
}
@@ -48,15 +49,15 @@ application::run_result application::init_outputs()
hostname = c_hostname;
}
m_state->outputs.reset(new falco_outputs(
m_state->engine,
m_state->config->m_outputs,
m_state->config->m_json_output,
m_state->config->m_json_include_output_property,
m_state->config->m_json_include_tags_property,
m_state->config->m_output_timeout,
m_state->config->m_buffered_outputs,
m_state->config->m_time_format_iso_8601,
s.outputs.reset(new falco_outputs(
s.engine,
s.config->m_outputs,
s.config->m_json_output,
s.config->m_json_include_output_property,
s.config->m_json_include_tags_property,
s.config->m_output_timeout,
s.config->m_buffered_outputs,
s.config->m_time_format_iso_8601,
hostname));
return run_result::ok();

View File

@@ -16,20 +16,21 @@ limitations under the License.
#include "fields_info.h"
#include "application.h"
#include "actions.h"
using namespace falco::app;
using namespace falco::app::actions;
application::run_result application::list_fields()
falco::app::run_result falco::app::actions::list_fields(falco::app::state& s)
{
if(m_options.list_fields)
if(s.options.list_fields)
{
if(m_options.list_source_fields != "" &&
!m_state->engine->is_source_valid(m_options.list_source_fields))
if(s.options.list_source_fields != "" &&
!s.engine->is_source_valid(s.options.list_source_fields))
{
return run_result::fatal("Value for --list must be a valid source type");
}
m_state->engine->list_fields(m_options.list_source_fields, m_options.verbose, m_options.names_only, m_options.markdown);
s.engine->list_fields(s.options.list_source_fields, s.options.verbose, s.options.names_only, s.options.markdown);
return run_result::exit();
}

View File

@@ -14,18 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
#include <plugin_manager.h>
using namespace falco::app;
using namespace falco::app::actions;
application::run_result application::list_plugins()
falco::app::run_result falco::app::actions::list_plugins(falco::app::state& s)
{
if(m_options.list_plugins)
if(s.options.list_plugins)
{
std::ostringstream os;
std::unique_ptr<sinsp> inspector(new sinsp());
const auto& configs = m_state->config->m_plugins;
const auto& configs = s.config->m_plugins;
for (auto &c : configs)
{
// load the plugin (no need to initialize it)

View File

@@ -14,21 +14,22 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
using namespace falco::app;
using namespace falco::app::actions;
application::run_result application::load_config()
falco::app::run_result falco::app::actions::load_config(falco::app::state& s)
{
try
{
if (!m_options.conf_filename.empty())
if (!s.options.conf_filename.empty())
{
m_state->config->init(m_options.conf_filename, m_options.cmdline_config_options);
s.config->init(s.options.conf_filename, s.options.cmdline_config_options);
}
else
{
m_state->config->init(m_options.cmdline_config_options);
s.config->init(s.options.cmdline_config_options);
}
}
catch (std::exception& e)
@@ -37,25 +38,25 @@ application::run_result application::load_config()
}
// log after config init because config determines where logs go
falco_logger::set_time_format_iso_8601(m_state->config->m_time_format_iso_8601);
falco_logger::set_time_format_iso_8601(s.config->m_time_format_iso_8601);
falco_logger::log(LOG_INFO, "Falco version: " + std::string(FALCO_VERSION) + " (" + std::string(FALCO_TARGET_ARCH) + ")\n");
if (!m_state->cmdline.empty())
if (!s.cmdline.empty())
{
falco_logger::log(LOG_DEBUG, "CLI args: " + m_state->cmdline);
falco_logger::log(LOG_DEBUG, "CLI args: " + s.cmdline);
}
if (!m_options.conf_filename.empty())
if (!s.options.conf_filename.empty())
{
falco_logger::log(LOG_INFO, "Falco initialized with configuration file: " + m_options.conf_filename + "\n");
falco_logger::log(LOG_INFO, "Falco initialized with configuration file: " + s.options.conf_filename + "\n");
}
m_state->config->m_buffered_outputs = !m_options.unbuffered_outputs;
s.config->m_buffered_outputs = !s.options.unbuffered_outputs;
return run_result::ok();
}
application::run_result application::require_config_file()
falco::app::run_result falco::app::actions::require_config_file(falco::app::state& s)
{
if (m_options.conf_filename.empty())
if (s.options.conf_filename.empty())
{
#ifndef BUILD_TYPE_RELEASE
return run_result::fatal(std::string("You must create a config file at ") + FALCO_SOURCE_CONF_FILE + ", " + FALCO_INSTALL_CONF_FILE + " or by passing -c");

View File

@@ -14,15 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
#include <plugin_manager.h>
using namespace falco::app;
using namespace falco::app::actions;
application::run_result application::load_plugins()
falco::app::run_result falco::app::actions::load_plugins(falco::app::state& s)
{
#ifdef MUSL_OPTIMIZED
if (!m_state->config->m_plugins.empty())
if (!s.config->m_plugins.empty())
{
return run_result::fatal("Can not load/use plugins with musl optimized build");
}
@@ -31,30 +32,30 @@ application::run_result application::load_plugins()
// Initialize the set of loaded event sources.
// By default, the set includes the 'syscall' event source
m_state->source_infos.clear();
m_state->source_infos.insert(empty_src_info, falco_common::syscall_source);
m_state->loaded_sources = { falco_common::syscall_source };
s.source_infos.clear();
s.source_infos.insert(empty_src_info, falco_common::syscall_source);
s.loaded_sources = { falco_common::syscall_source };
// Initialize map of plugin configs
m_state->plugin_configs.clear();
s.plugin_configs.clear();
// Initialize the offline inspector. This is used to load all the configured
// plugins in order to have them available every time we need to access
// their static info. If Falco is in capture mode, this inspector is also
// used to open and read the trace file
m_state->offline_inspector.reset(new sinsp());
s.offline_inspector.reset(new sinsp());
// Load all the configured plugins
for(auto &p : m_state->config->m_plugins)
for(auto &p : s.config->m_plugins)
{
falco_logger::log(LOG_INFO, "Loading plugin '" + p.m_name + "' from file " + p.m_library_path + "\n");
auto plugin = m_state->offline_inspector->register_plugin(p.m_library_path);
m_state->plugin_configs.insert(p, plugin->name());
auto plugin = s.offline_inspector->register_plugin(p.m_library_path);
s.plugin_configs.insert(p, plugin->name());
if(plugin->caps() & CAP_SOURCING)
{
auto sname = plugin->event_source();
m_state->source_infos.insert(empty_src_info, sname);
m_state->loaded_sources.insert(sname);
s.source_infos.insert(empty_src_info, sname);
s.loaded_sources.insert(sname);
}
}

View File

@@ -14,41 +14,42 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
#include <plugin_manager.h>
#include <unordered_set>
using namespace falco::app;
using namespace falco::app::actions;
bool application::check_rules_plugin_requirements(std::string& err)
bool falco::app::actions::check_rules_plugin_requirements(falco::app::state& s, std::string& err)
{
// Ensure that all plugins are compatible with the loaded set of rules
// note: offline inspector contains all the loaded plugins
std::vector<falco_engine::plugin_version_requirement> plugin_reqs;
for (const auto &plugin : m_state->offline_inspector->get_plugin_manager()->plugins())
for (const auto &plugin : s.offline_inspector->get_plugin_manager()->plugins())
{
falco_engine::plugin_version_requirement req;
req.name = plugin->name();
req.version = plugin->plugin_version().as_string();
plugin_reqs.push_back(req);
}
return m_state->engine->check_plugin_requirements(plugin_reqs, err);
return s.engine->check_plugin_requirements(plugin_reqs, err);
}
void application::check_for_ignored_events()
static void check_for_ignored_events(falco::app::state& s)
{
/* Get the events from the rules. */
std::set<uint16_t> rule_events;
std::string source = falco_common::syscall_source;
m_state->engine->evttypes_for_ruleset(source, rule_events);
s.engine->evttypes_for_ruleset(source, rule_events);
/* Get the events we consider interesting from the application state `ppm_sc` codes. */
std::unique_ptr<sinsp> inspector(new sinsp());
std::unordered_set<uint32_t> events(rule_events.begin(), rule_events.end());
auto event_names = inspector->get_events_names(events);
for (const auto& n : inspector->get_events_names(m_state->ppm_event_info_of_interest))
for (const auto& n : inspector->get_events_names(s.ppm_event_info_of_interest))
{
event_names.erase(n);
}
@@ -70,37 +71,37 @@ void application::check_for_ignored_events()
std::cerr << std::endl << "These events might be associated with syscalls undefined on your architecture (please take a look here: https://marcin.juszkiewicz.com.pl/download/tables/syscalls.html). If syscalls are instead defined, you have to run Falco with `-A` to catch these events" << std::endl;
}
application::run_result application::load_rules_files()
falco::app::run_result falco::app::actions::load_rules_files(falco::app::state& s)
{
std::string all_rules;
if (!m_options.rules_filenames.empty())
if (!s.options.rules_filenames.empty())
{
m_state->config->m_rules_filenames = m_options.rules_filenames;
s.config->m_rules_filenames = s.options.rules_filenames;
}
if(m_state->config->m_rules_filenames.empty())
if(s.config->m_rules_filenames.empty())
{
return run_result::fatal("You must specify at least one rules file/directory via -r or a rules_file entry in falco.yaml");
}
falco_logger::log(LOG_DEBUG, "Configured rules filenames:\n");
for (const auto& path : m_state->config->m_rules_filenames)
for (const auto& path : s.config->m_rules_filenames)
{
falco_logger::log(LOG_DEBUG, std::string(" ") + path + "\n");
}
for (const auto &path : m_state->config->m_rules_filenames)
for (const auto &path : s.config->m_rules_filenames)
{
falco_configuration::read_rules_file_directory(path, m_state->config->m_loaded_rules_filenames, m_state->config->m_loaded_rules_folders);
falco_configuration::read_rules_file_directory(path, s.config->m_loaded_rules_filenames, s.config->m_loaded_rules_folders);
}
std::vector<std::string> rules_contents;
falco::load_result::rules_contents_t rc;
try {
read_files(m_state->config->m_loaded_rules_filenames.begin(),
m_state->config->m_loaded_rules_filenames.end(),
read_files(s.config->m_loaded_rules_filenames.begin(),
s.config->m_loaded_rules_filenames.end(),
rules_contents,
rc);
}
@@ -109,12 +110,12 @@ application::run_result application::load_rules_files()
return run_result::fatal(e.what());
}
for(auto &filename : m_state->config->m_loaded_rules_filenames)
for(auto &filename : s.config->m_loaded_rules_filenames)
{
falco_logger::log(LOG_INFO, "Loading rules from file " + filename + "\n");
std::unique_ptr<falco::load_result> res;
res = m_state->engine->load_rules(rc.at(filename), filename);
res = s.engine->load_rules(rc.at(filename), filename);
if(!res->successful())
{
@@ -123,56 +124,56 @@ application::run_result application::load_rules_files()
}
// If verbose is true, also print any warnings
if(m_options.verbose && res->has_warnings())
if(s.options.verbose && res->has_warnings())
{
fprintf(stderr, "%s\n", res->as_string(true, rc).c_str());
}
}
std::string err = "";
if (!check_rules_plugin_requirements(err))
if (!check_rules_plugin_requirements(s, err))
{
return run_result::fatal(err);
}
for (const auto& substring : m_options.disabled_rule_substrings)
for (const auto& substring : s.options.disabled_rule_substrings)
{
falco_logger::log(LOG_INFO, "Disabling rules matching substring: " + substring + "\n");
m_state->engine->enable_rule(substring, false);
s.engine->enable_rule(substring, false);
}
if(!m_options.disabled_rule_tags.empty())
if(!s.options.disabled_rule_tags.empty())
{
for(auto &tag : m_options.disabled_rule_tags)
for(auto &tag : s.options.disabled_rule_tags)
{
falco_logger::log(LOG_INFO, "Disabling rules with tag: " + tag + "\n");
}
m_state->engine->enable_rule_by_tag(m_options.disabled_rule_tags, false);
s.engine->enable_rule_by_tag(s.options.disabled_rule_tags, false);
}
if(!m_options.enabled_rule_tags.empty())
if(!s.options.enabled_rule_tags.empty())
{
// Since we only want to enable specific
// rules, first disable all rules.
m_state->engine->enable_rule(all_rules, false);
for(auto &tag : m_options.enabled_rule_tags)
s.engine->enable_rule(all_rules, false);
for(auto &tag : s.options.enabled_rule_tags)
{
falco_logger::log(LOG_INFO, "Enabling rules with tag: " + tag + "\n");
}
m_state->engine->enable_rule_by_tag(m_options.enabled_rule_tags, true);
s.engine->enable_rule_by_tag(s.options.enabled_rule_tags, true);
}
/* Reading a scap file we have no concepts of ignored events we read all we need. */
if(!m_options.all_events && !is_capture_mode())
if(!s.options.all_events && !s.is_capture_mode())
{
/* Here we have already initialized the application state with the interesting syscalls,
* so we have to check if any event types used by the loaded rules are not considered by
* Falco interesting set.
*/
check_for_ignored_events();
check_for_ignored_events(s);
}
if(m_options.all_events && m_options.modern_bpf)
if(s.options.all_events && s.options.modern_bpf)
{
/* Right now the modern BPF probe doesn't support the -A flag, we implemented just
* the "simple set" syscalls.
@@ -180,15 +181,15 @@ application::run_result application::load_rules_files()
falco_logger::log(LOG_INFO, "The '-A' flag has no effect with the modern BPF probe, no further syscalls will be added\n");
}
if (m_options.describe_all_rules)
if (s.options.describe_all_rules)
{
m_state->engine->describe_rule(NULL);
s.engine->describe_rule(NULL);
return run_result::exit();
}
if (!m_options.describe_rule.empty())
if (!s.options.describe_rule.empty())
{
m_state->engine->describe_rule(&(m_options.describe_rule));
s.engine->describe_rule(&(s.options.describe_rule));
return run_result::exit();
}

View File

@@ -20,28 +20,30 @@ limitations under the License.
#include <plugin_manager.h>
#include "application.h"
#include "actions.h"
/* DEPRECATED: we will remove it in Falco 0.34. */
#define FALCO_BPF_ENV_VARIABLE "FALCO_BPF_PROBE"
using namespace falco::app;
using namespace falco::app::actions;
application::run_result application::open_offline_inspector()
falco::app::run_result falco::app::actions::open_offline_inspector(falco::app::state& s)
{
try
{
m_state->offline_inspector->open_savefile(m_options.trace_filename);
falco_logger::log(LOG_INFO, "Reading system call events from file: " + m_options.trace_filename + "\n");
s.offline_inspector->open_savefile(s.options.trace_filename);
falco_logger::log(LOG_INFO, "Reading system call events from file: " + s.options.trace_filename + "\n");
return run_result::ok();
}
catch (sinsp_exception &e)
{
return run_result::fatal("Could not open trace filename " + m_options.trace_filename + " for reading: " + e.what());
return run_result::fatal("Could not open trace filename " + s.options.trace_filename + " for reading: " + e.what());
}
}
application::run_result application::open_live_inspector(
falco::app::run_result falco::app::actions::open_live_inspector(
falco::app::state& s,
std::shared_ptr<sinsp> inspector,
const std::string& source)
{
@@ -53,7 +55,7 @@ application::run_result application::open_live_inspector(
{
if (p->caps() & CAP_SOURCING && p->event_source() == source)
{
auto cfg = m_state->plugin_configs.at(p->name());
auto cfg = s.plugin_configs.at(p->name());
falco_logger::log(LOG_INFO, "Opening capture with plugin '" + cfg->m_name + "'\n");
inspector->open_plugin(cfg->m_name, cfg->m_open_params);
return run_result::ok();
@@ -61,7 +63,7 @@ application::run_result application::open_live_inspector(
}
return run_result::fatal("Can't open inspector for plugin event source: " + source);
}
else if (m_options.userspace) /* udig engine. */
else if (s.options.userspace) /* udig engine. */
{
// open_udig() is the underlying method used in the capture code to parse userspace events from the kernel.
//
@@ -70,16 +72,16 @@ application::run_result application::open_live_inspector(
falco_logger::log(LOG_INFO, "Opening capture with udig\n");
inspector->open_udig();
}
else if(!m_options.gvisor_config.empty()) /* gvisor engine. */
else if(!s.options.gvisor_config.empty()) /* gvisor engine. */
{
falco_logger::log(LOG_INFO, "Opening capture with gVisor. Configuration path: " + m_options.gvisor_config);
inspector->open_gvisor(m_options.gvisor_config, m_options.gvisor_root);
falco_logger::log(LOG_INFO, "Opening capture with gVisor. Configuration path: " + s.options.gvisor_config);
inspector->open_gvisor(s.options.gvisor_config, s.options.gvisor_root);
}
else if(m_options.modern_bpf) /* modern BPF engine. */
else if(s.options.modern_bpf) /* modern BPF engine. */
{
falco_logger::log(LOG_INFO, "Opening capture with modern BPF probe.");
falco_logger::log(LOG_INFO, "One ring buffer every '" + std::to_string(m_state->config->m_cpus_for_each_syscall_buffer) + "' CPUs.");
inspector->open_modern_bpf(m_state->syscall_buffer_bytes_size, m_state->config->m_cpus_for_each_syscall_buffer, true, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
falco_logger::log(LOG_INFO, "One ring buffer every '" + std::to_string(s.config->m_cpus_for_each_syscall_buffer) + "' CPUs.");
inspector->open_modern_bpf(s.syscall_buffer_bytes_size, s.config->m_cpus_for_each_syscall_buffer, true, s.ppm_sc_of_interest, s.tp_of_interest);
}
else if(getenv(FALCO_BPF_ENV_VARIABLE) != NULL) /* BPF engine. */
{
@@ -97,14 +99,14 @@ application::run_result application::open_live_inspector(
bpf_probe_path = full_path;
}
falco_logger::log(LOG_INFO, "Opening capture with BPF probe. BPF probe path: " + std::string(bpf_probe_path));
inspector->open_bpf(bpf_probe_path, m_state->syscall_buffer_bytes_size, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
inspector->open_bpf(bpf_probe_path, s.syscall_buffer_bytes_size, s.ppm_sc_of_interest, s.tp_of_interest);
}
else /* Kernel module (default). */
{
try
{
falco_logger::log(LOG_INFO, "Opening capture with Kernel module");
inspector->open_kmod(m_state->syscall_buffer_bytes_size, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
inspector->open_kmod(s.syscall_buffer_bytes_size, s.ppm_sc_of_interest, s.tp_of_interest);
}
catch(sinsp_exception &e)
{
@@ -114,7 +116,7 @@ application::run_result application::open_live_inspector(
{
falco_logger::log(LOG_ERR, "Unable to load the driver\n");
}
inspector->open_kmod(m_state->syscall_buffer_bytes_size, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
inspector->open_kmod(s.syscall_buffer_bytes_size, s.ppm_sc_of_interest, s.tp_of_interest);
}
}
}

View File

@@ -15,16 +15,17 @@ limitations under the License.
*/
#include "config_falco.h"
#include "application.h"
#include "actions.h"
using namespace falco::app;
using namespace falco::app::actions;
application::run_result application::print_generated_gvisor_config()
falco::app::run_result falco::app::actions::print_generated_gvisor_config(falco::app::state& s)
{
if(!m_options.gvisor_generate_config_with_socket.empty())
if(!s.options.gvisor_generate_config_with_socket.empty())
{
std::unique_ptr<sinsp> s(new sinsp());
std::string gvisor_config = s->generate_gvisor_config(m_options.gvisor_generate_config_with_socket);
std::unique_ptr<sinsp> i(new sinsp());
std::string gvisor_config = i->generate_gvisor_config(s.options.gvisor_generate_config_with_socket);
printf("%s\n", gvisor_config.c_str());
return run_result::exit();
}

View File

@@ -1,5 +1,5 @@
/*
Copyright (C) 2022 The Falco Authors.
Copyright (C) 2023 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,15 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
using namespace falco::app;
using namespace falco::app::actions;
application::run_result application::print_help()
falco::app::run_result falco::app::actions::print_help(falco::app::state& s)
{
if(m_options.help)
if(s.options.help)
{
printf("%s", m_options.usage().c_str());
printf("%s", s.options.usage().c_str());
return run_result::exit();
}
return run_result::ok();

View File

@@ -14,19 +14,20 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
using namespace falco::app;
using namespace falco::app::actions;
/// TODO: probably in the next future would be more meaningful to print the ignored syscalls rather than
/// the ignored events, or maybe change the name of the events since right now they are almost the same of
/// the syscalls.
application::run_result application::print_ignored_events()
falco::app::run_result falco::app::actions::print_ignored_events(falco::app::state& s)
{
/* If the option is true we print the events ignored with Falco `-A`, otherwise
* we return immediately.
*/
if(!m_options.print_ignored_events)
if(!s.options.print_ignored_events)
{
return run_result::ok();
}
@@ -36,7 +37,7 @@ application::run_result application::print_ignored_events()
* we don't care if we populate these sets even if the `-A` flag
* is not set.
*/
configure_interesting_sets();
configure_interesting_sets(s);
/* Search for all the ignored syscalls. */
std::unordered_set<uint32_t> all_events;
@@ -52,7 +53,7 @@ application::run_result application::print_ignored_events()
std::unique_ptr<sinsp> inspector(new sinsp());
auto ignored_event_names = inspector->get_events_names(all_events);
for (const auto &n : inspector->get_events_names(m_state->ppm_event_info_of_interest))
for (const auto &n : inspector->get_events_names(s.ppm_event_info_of_interest))
{
ignored_event_names.erase(n);
}

View File

@@ -14,13 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
using namespace falco::app;
using namespace falco::app::actions;
application::run_result application::print_page_size()
falco::app::run_result falco::app::actions::print_page_size(falco::app::state& s)
{
if(m_options.print_page_size)
if(s.options.print_page_size)
{
long page_size = getpagesize();
if(page_size <= 0)

View File

@@ -14,12 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
#include <plugin_manager.h>
using namespace falco::app;
using namespace falco::app::actions;
void application::format_plugin_info(std::shared_ptr<sinsp_plugin> p, std::ostream& os) const
void falco::app::actions::format_plugin_info(std::shared_ptr<sinsp_plugin> p, std::ostream& os)
{
os << "Name: " << p->name() << std::endl;
os << "Description: " << p->description() << std::endl;
@@ -37,21 +38,21 @@ void application::format_plugin_info(std::shared_ptr<sinsp_plugin> p, std::ostre
}
}
application::run_result application::print_plugin_info()
falco::app::run_result falco::app::actions::print_plugin_info(falco::app::state& s)
{
#ifdef MUSL_OPTIMIZED
if(!m_options.print_plugin_info.empty())
if(!s.options.print_plugin_info.empty())
{
return run_result::fatal("Can not load or use plugins with musl optimized build");
}
#else // MUSL_OPTIMIZED
if(!m_options.print_plugin_info.empty())
if(!s.options.print_plugin_info.empty())
{
std::unique_ptr<sinsp> inspector(new sinsp());
for(auto &pc : m_state->config->m_plugins)
for(auto &pc : s.config->m_plugins)
{
if (pc.m_name == m_options.print_plugin_info
|| pc.m_library_path == m_options.print_plugin_info)
if (pc.m_name == s.options.print_plugin_info
|| pc.m_library_path == s.options.print_plugin_info)
{
// load the plugin
auto p = inspector->register_plugin(pc.m_library_path);
@@ -123,7 +124,7 @@ application::run_result application::print_plugin_info()
return run_result::exit();
}
}
return run_result::fatal("can't find plugin and print its info: " + m_options.print_plugin_info);
return run_result::fatal("can't find plugin and print its info: " + s.options.print_plugin_info);
}
#endif // MUSL_OPTIMIZED

View File

@@ -17,9 +17,11 @@ limitations under the License.
#include <sys/utsname.h>
#include "versions_info.h"
#include "application.h"
#include "actions.h"
#include "../../versions_info.h"
using namespace falco::app;
using namespace falco::app::actions;
static std::string read_file(const std::string &filename)
{
@@ -30,9 +32,9 @@ static std::string read_file(const std::string &filename)
return str;
}
application::run_result application::print_support()
falco::app::run_result falco::app::actions::print_support(falco::app::state& s)
{
if(m_options.print_support)
if(s.options.print_support)
{
nlohmann::json support;
struct utsname sysinfo;
@@ -43,7 +45,7 @@ application::run_result application::print_support()
return run_result::fatal(std::string("Could not uname() to find system info: ") + strerror(errno));
}
const versions_info infos(m_state->offline_inspector);
const falco::versions_info infos(s.offline_inspector);
support["version"] = infos.falco_version;
support["engine_info"] = infos.as_json();
@@ -52,10 +54,10 @@ application::run_result application::print_support()
support["system_info"]["release"] = sysinfo.release;
support["system_info"]["version"] = sysinfo.version;
support["system_info"]["machine"] = sysinfo.machine;
support["cmdline"] = m_state->cmdline;
support["config"] = read_file(m_options.conf_filename);
support["cmdline"] = s.cmdline;
support["config"] = read_file(s.options.conf_filename);
support["rules_files"] = nlohmann::json::array();
for(auto filename : m_state->config->m_loaded_rules_filenames)
for(auto filename : s.config->m_loaded_rules_filenames)
{
nlohmann::json finfo;
finfo["name"] = filename;

View File

@@ -14,9 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
using namespace falco::app;
using namespace falco::app::actions;
struct event_entry
{
@@ -68,14 +69,14 @@ static std::vector<event_entry> get_event_entries(bool include_generics, const s
return events;
}
application::run_result application::print_syscall_events()
falco::app::run_result falco::app::actions::print_syscall_events(falco::app::state& s)
{
if(m_options.list_syscall_events)
if(s.options.list_syscall_events)
{
configure_interesting_sets();
const auto events = get_event_entries(true, m_state->ppm_event_info_of_interest);
configure_interesting_sets(s);
const auto events = get_event_entries(true, s.ppm_event_info_of_interest);
if(m_options.markdown)
if(s.options.markdown)
{
printf("Falco | Dir | Event\n");
printf(":-----|:----|:-----\n");
@@ -84,7 +85,7 @@ application::run_result application::print_syscall_events()
for (const auto& e : events)
{
char dir = e.is_enter ? '>' : '<';
if (m_options.markdown)
if (s.options.markdown)
{
printf(e.available ? "Yes" : "No");
printf(" | %c | **%s**(", dir, e.name.c_str());

View File

@@ -16,17 +16,18 @@ limitations under the License.
#include <nlohmann/json.hpp>
#include "application.h"
#include "versions_info.h"
#include "actions.h"
#include "../../versions_info.h"
using namespace falco::app;
using namespace falco::app::actions;
application::run_result application::print_version()
falco::app::run_result falco::app::actions::print_version(falco::app::state& s)
{
if(m_options.print_version_info)
if(s.options.print_version_info)
{
const versions_info info(m_state->offline_inspector);
if(m_state->config->m_json_output)
const falco::versions_info info(s.offline_inspector);
if(s.config->m_json_output)
{
printf("%s\n", info.as_json().dump().c_str());
}

View File

@@ -25,23 +25,105 @@ limitations under the License.
#include "falco_utils.h"
#include "event_drops.h"
#ifndef MINIMAL_BUILD
#include "webserver.h"
#endif
#include "stats_writer.h"
#include "application.h"
#include "actions.h"
#include "falco_outputs.h"
#include "token_bucket.h"
#include "app_cmdline_options.h"
#include "../options.h"
#include "../signals.h"
#include "../../semaphore.h"
#include "../../stats_writer.h"
#ifndef MINIMAL_BUILD
#include "../../webserver.h"
#endif
#include <plugin_manager.h>
using namespace falco::app;
using namespace falco::app::actions;
class source_sync_context
{
public:
source_sync_context(falco::semaphore& s)
: m_finished(false), m_joined(false), m_semaphore(s) { }
source_sync_context(source_sync_context&&) = default;
source_sync_context& operator = (source_sync_context&&) = default;
source_sync_context(const source_sync_context&) = delete;
source_sync_context& operator = (const source_sync_context&) = delete;
inline void finish()
{
bool v = false;
while (!m_finished.compare_exchange_weak(
v, true,
std::memory_order_seq_cst,
std::memory_order_seq_cst))
{
if (v)
{
throw falco_exception("source_sync_context has been finished twice");
}
}
m_semaphore.release();
}
inline void join()
{
bool v = false;
while (!m_joined.compare_exchange_weak(
v, true,
std::memory_order_seq_cst,
std::memory_order_seq_cst))
{
if (v)
{
throw falco_exception("source_sync_context has been joined twice");
}
}
}
inline bool joined()
{
return m_joined.load(std::memory_order_seq_cst);
}
inline bool finished()
{
return m_finished.load(std::memory_order_seq_cst);
}
private:
// set to true when the event processing loop finishes
std::atomic<bool> m_finished;
// set to true when the result has been collected after finishing
std::atomic<bool> m_joined;
// used to notify the waiting thread when finished gets set to true
falco::semaphore& m_semaphore;
};
struct live_context
{
live_context() = default;
live_context(live_context&&) = default;
live_context& operator = (live_context&&) = default;
live_context(const live_context&) = default;
live_context& operator = (const live_context&) = default;
// the name of the source of which events are processed
std::string source;
// the result of the event processing loop
run_result res;
// if non-null, the thread on which events are processed
std::unique_ptr<std::thread> thread;
// used for thread synchronization purposes
std::unique_ptr<source_sync_context> sync;
};
//
// Event processing loop
//
application::run_result application::do_inspect(
static falco::app::run_result do_inspect(
falco::app::state& s,
std::shared_ptr<sinsp> inspector,
const std::string& source, // an empty source represents capture mode
std::shared_ptr<stats_writer> statsw,
@@ -56,24 +138,24 @@ application::run_result application::do_inspect(
uint64_t duration_start = 0;
uint32_t timeouts_since_last_success_or_msg = 0;
token_bucket rate_limiter;
bool rate_limiter_enabled = m_state->config->m_notifications_rate > 0;
bool rate_limiter_enabled = s.config->m_notifications_rate > 0;
bool source_engine_idx_found = false;
bool is_capture_mode = source.empty();
bool syscall_source_engine_idx = m_state->source_infos.at(falco_common::syscall_source)->engine_idx;
bool syscall_source_engine_idx = s.source_infos.at(falco_common::syscall_source)->engine_idx;
std::size_t source_engine_idx = 0;
std::vector<std::string> source_names = inspector->get_plugin_manager()->sources();
source_names.push_back(falco_common::syscall_source);
if (!is_capture_mode)
{
source_engine_idx = m_state->source_infos.at(source)->engine_idx;
source_engine_idx = s.source_infos.at(source)->engine_idx;
}
// if enabled, init rate limiter
if (rate_limiter_enabled)
{
rate_limiter.init(
m_state->config->m_notifications_rate,
m_state->config->m_notifications_max_burst);
s.config->m_notifications_rate,
s.config->m_notifications_max_burst);
}
// reset event counter
@@ -83,12 +165,12 @@ application::run_result application::do_inspect(
if (check_drops_and_timeouts)
{
sdropmgr.init(inspector,
m_state->outputs, // drop manager has its own rate limiting logic
m_state->config->m_syscall_evt_drop_actions,
m_state->config->m_syscall_evt_drop_threshold,
m_state->config->m_syscall_evt_drop_rate,
m_state->config->m_syscall_evt_drop_max_burst,
m_state->config->m_syscall_evt_simulate_drops);
s.outputs, // drop manager has its own rate limiting logic
s.config->m_syscall_evt_drop_actions,
s.config->m_syscall_evt_drop_threshold,
s.config->m_syscall_evt_drop_rate,
s.config->m_syscall_evt_drop_max_burst,
s.config->m_syscall_evt_simulate_drops);
}
//
@@ -105,17 +187,23 @@ application::run_result application::do_inspect(
if (should_reopen_outputs())
{
reopen_outputs();
falco::app::reopen_outputs([&s]()
{
if(s.outputs != nullptr)
{
s.outputs->reopen_outputs();
}
});
}
if(should_terminate())
{
terminate();
falco::app::terminate();
break;
}
else if(should_restart())
{
restart();
falco::app::restart();
break;
}
else if(rc == SCAP_TIMEOUT)
@@ -123,11 +211,11 @@ application::run_result application::do_inspect(
if(unlikely(ev == nullptr))
{
timeouts_since_last_success_or_msg++;
if(timeouts_since_last_success_or_msg > m_state->config->m_syscall_evt_timeout_max_consecutives
if(timeouts_since_last_success_or_msg > s.config->m_syscall_evt_timeout_max_consecutives
&& check_drops_and_timeouts)
{
std::string rule = "Falco internal: timeouts notification";
std::string msg = rule + ". " + std::to_string(m_state->config->m_syscall_evt_timeout_max_consecutives) + " consecutive timeouts without event.";
std::string msg = rule + ". " + std::to_string(s.config->m_syscall_evt_timeout_max_consecutives) + " consecutive timeouts without event.";
std::string last_event_time_str = "none";
if(duration_start > 0)
{
@@ -137,7 +225,7 @@ application::run_result application::do_inspect(
{"last_event_time", last_event_time_str},
};
auto now = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
m_state->outputs->handle_msg(now, falco_common::PRIORITY_DEBUG, msg, rule, o);
s.outputs->handle_msg(now, falco_common::PRIORITY_DEBUG, msg, rule, o);
// Reset the timeouts counter, Falco alerted
timeouts_since_last_success_or_msg = 0;
}
@@ -210,12 +298,12 @@ application::run_result application::do_inspect(
// engine, which will match the event against the set
// of rules. If a match is found, pass the event to
// the outputs.
std::unique_ptr<falco_engine::rule_result> res = m_state->engine->process_event(source_engine_idx, ev);
std::unique_ptr<falco_engine::rule_result> res = s.engine->process_event(source_engine_idx, ev);
if(res)
{
if (!rate_limiter_enabled || rate_limiter.claim())
{
m_state->outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, res->format, res->tags);
s.outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, res->format, res->tags);
}
else
{
@@ -229,12 +317,13 @@ application::run_result application::do_inspect(
return run_result::ok();
}
void application::process_inspector_events(
static void process_inspector_events(
falco::app::state& s,
std::shared_ptr<sinsp> inspector,
std::shared_ptr<stats_writer> statsw,
std::string source, // an empty source represents capture mode
application::source_sync_context* sync,
application::run_result* res) noexcept
source_sync_context* sync,
run_result* res) noexcept
{
try
{
@@ -244,19 +333,19 @@ void application::process_inspector_events(
syscall_evt_drop_mgr sdropmgr;
bool is_capture_mode = source.empty();
bool check_drops_timeouts = is_capture_mode
|| (source == falco_common::syscall_source && !is_gvisor_enabled());
|| (source == falco_common::syscall_source && !s.is_gvisor_enabled());
duration = ((double)clock()) / CLOCKS_PER_SEC;
*res = do_inspect(inspector, source, statsw, sdropmgr, check_drops_timeouts,
uint64_t(m_options.duration_to_tot*ONE_SECOND_IN_NS),
*res = do_inspect(s, inspector, source, statsw, sdropmgr, check_drops_timeouts,
uint64_t(s.options.duration_to_tot*ONE_SECOND_IN_NS),
num_evts);
duration = ((double)clock()) / CLOCKS_PER_SEC - duration;
inspector->get_capture_stats(&cstats);
if(m_options.verbose)
if(s.options.verbose)
{
if (source == falco_common::syscall_source)
{
@@ -288,7 +377,7 @@ void application::process_inspector_events(
}
}
static std::shared_ptr<stats_writer> init_stats_writer(const cmdline_options& opts)
static std::shared_ptr<stats_writer> init_stats_writer(const options& opts)
{
auto statsw = std::make_shared<stats_writer>();
if (!opts.stats_filename.empty())
@@ -303,76 +392,58 @@ static std::shared_ptr<stats_writer> init_stats_writer(const cmdline_options& op
return statsw;
}
application::run_result application::process_events()
falco::app::run_result falco::app::actions::process_events(falco::app::state& s)
{
application::run_result res = run_result::ok();
run_result res = run_result::ok();
bool termination_forced = false;
// Notify engine that we finished loading and enabling all rules
m_state->engine->complete_rule_loading();
s.engine->complete_rule_loading();
// Initialize stats writer
auto statsw = init_stats_writer(m_options);
auto statsw = init_stats_writer(s.options);
// Start processing events
if(is_capture_mode())
if(s.is_capture_mode())
{
res = open_offline_inspector();
res = open_offline_inspector(s);
if (!res.success)
{
return res;
}
process_inspector_events(m_state->offline_inspector, statsw, "", nullptr, &res);
m_state->offline_inspector->close();
process_inspector_events(s, s.offline_inspector, statsw, "", nullptr, &res);
s.offline_inspector->close();
// Honor -M also when using a trace file.
// Since inspection stops as soon as all events have been consumed
// just await the given duration is reached, if needed.
if(m_options.duration_to_tot > 0)
if(s.options.duration_to_tot > 0)
{
std::this_thread::sleep_for(std::chrono::seconds(m_options.duration_to_tot));
std::this_thread::sleep_for(std::chrono::seconds(s.options.duration_to_tot));
}
}
else
{
struct live_context
{
live_context() = default;
live_context(live_context&&) = default;
live_context& operator = (live_context&&) = default;
live_context(const live_context&) = default;
live_context& operator = (const live_context&) = default;
// the name of the source of which events are processed
std::string source;
// the result of the event processing loop
application::run_result res;
// if non-null, the thread on which events are processed
std::unique_ptr<std::thread> thread;
// used for thread synchronization purposes
std::unique_ptr<application::source_sync_context> sync;
};
print_enabled_event_sources();
print_enabled_event_sources(s);
// start event processing for all enabled sources
falco::semaphore termination_sem(m_state->enabled_sources.size());
falco::semaphore termination_sem(s.enabled_sources.size());
std::vector<live_context> ctxs;
ctxs.reserve(m_state->enabled_sources.size());
for (const auto& source : m_state->enabled_sources)
ctxs.reserve(s.enabled_sources.size());
for (const auto& source : s.enabled_sources)
{
ctxs.emplace_back();
auto& ctx = ctxs[ctxs.size() - 1];
ctx.source = source;
ctx.sync.reset(new application::source_sync_context(termination_sem));
auto src_info = m_state->source_infos.at(source);
ctx.sync.reset(new source_sync_context(termination_sem));
auto src_info = s.source_infos.at(source);
try
{
falco_logger::log(LOG_DEBUG, "Opening event source '" + source + "'\n");
termination_sem.acquire();
res = open_live_inspector(src_info->inspector, source);
res = open_live_inspector(s, src_info->inspector, source);
if (!res.success)
{
// note: we don't return here because we need to reach
@@ -382,16 +453,16 @@ application::run_result application::process_events()
break;
}
if (m_state->enabled_sources.size() == 1)
if (s.enabled_sources.size() == 1)
{
// optimization: with only one source we don't spawn additional threads
process_inspector_events(src_info->inspector, statsw, source, ctx.sync.get(), &ctx.res);
process_inspector_events(s, src_info->inspector, statsw, source, ctx.sync.get(), &ctx.res);
}
else
{
ctx.thread.reset(new std::thread(
&application::process_inspector_events,
this, src_info->inspector, statsw, source, ctx.sync.get(), &ctx.res));
ctx.thread.reset(new std::thread([&s, &src_info, &statsw, &source, &ctx](){
process_inspector_events(s, src_info->inspector, statsw, source, ctx.sync.get(), &ctx.res);
}));
}
}
catch (std::exception &e)
@@ -445,7 +516,7 @@ application::run_result application::process_events()
}
falco_logger::log(LOG_DEBUG, "Closing event source '" + ctx.source + "'\n");
m_state->source_infos.at(ctx.source)->inspector->close();
s.source_infos.at(ctx.source)->inspector->close();
res = run_result::merge(res, ctx.res);
ctx.sync->join();
@@ -455,7 +526,7 @@ application::run_result application::process_events()
}
}
m_state->engine->print_stats();
s.engine->print_stats();
return res;
}

View File

@@ -11,15 +11,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
using namespace falco::app;
using namespace falco::app::actions;
void application::print_enabled_event_sources()
void falco::app::actions::print_enabled_event_sources(falco::app::state& s)
{
/* Print all enabled sources. */
std::string str;
for (const auto &s : m_state->enabled_sources)
for (const auto &s : s.enabled_sources)
{
str += str.empty() ? "" : ", ";
str += s;
@@ -27,46 +28,46 @@ void application::print_enabled_event_sources()
falco_logger::log(LOG_INFO, "Enabled event sources: " + str + "\n");
}
application::run_result application::select_event_sources()
falco::app::run_result falco::app::actions::select_event_sources(falco::app::state& s)
{
m_state->enabled_sources = m_state->loaded_sources;
s.enabled_sources = s.loaded_sources;
// event sources selection is meaningless when reading trace files
if (is_capture_mode())
if (s.is_capture_mode())
{
return run_result::ok();
}
if (!m_options.enable_sources.empty() && !m_options.disable_sources.empty())
if (!s.options.enable_sources.empty() && !s.options.disable_sources.empty())
{
return run_result::fatal("You can not mix --enable-source and --disable-source");
}
if (!m_options.enable_sources.empty())
if (!s.options.enable_sources.empty())
{
m_state->enabled_sources.clear();
for(const auto &src : m_options.enable_sources)
s.enabled_sources.clear();
for(const auto &src : s.options.enable_sources)
{
if (m_state->loaded_sources.find(src) == m_state->loaded_sources.end())
if (s.loaded_sources.find(src) == s.loaded_sources.end())
{
return run_result::fatal("Attempted enabling an unknown event source: " + src);
}
m_state->enabled_sources.insert(src);
s.enabled_sources.insert(src);
}
}
else if (!m_options.disable_sources.empty())
else if (!s.options.disable_sources.empty())
{
for(const auto &src : m_options.disable_sources)
for(const auto &src : s.options.disable_sources)
{
if (m_state->loaded_sources.find(src) == m_state->loaded_sources.end())
if (s.loaded_sources.find(src) == s.loaded_sources.end())
{
return run_result::fatal("Attempted disabling an unknown event source: " + src);
}
m_state->enabled_sources.erase(src);
s.enabled_sources.erase(src);
}
}
if(m_state->enabled_sources.empty())
if(s.enabled_sources.empty())
{
return run_result::fatal("Must enable at least one event source");
}

View File

@@ -14,43 +14,44 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
#ifndef MINIMAL_BUILD
#include "grpc_server.h"
using namespace falco::app;
using namespace falco::app::actions;
application::run_result application::start_grpc_server()
falco::app::run_result falco::app::actions::start_grpc_server(falco::app::state& s)
{
// gRPC server
if(m_state->config->m_grpc_enabled)
if(s.config->m_grpc_enabled)
{
falco_logger::log(LOG_INFO, "gRPC server threadiness equals to " + std::to_string(m_state->config->m_grpc_threadiness) + "\n");
falco_logger::log(LOG_INFO, "gRPC server threadiness equals to " + std::to_string(s.config->m_grpc_threadiness) + "\n");
// TODO(fntlnz,leodido): when we want to spawn multiple threads we need to have a queue per thread, or implement
// different queuing mechanisms, round robin, fanout? What we want to achieve?
m_state->grpc_server.init(
m_state->config->m_grpc_bind_address,
m_state->config->m_grpc_threadiness,
m_state->config->m_grpc_private_key,
m_state->config->m_grpc_cert_chain,
m_state->config->m_grpc_root_certs,
m_state->config->m_log_level
s.grpc_server.init(
s.config->m_grpc_bind_address,
s.config->m_grpc_threadiness,
s.config->m_grpc_private_key,
s.config->m_grpc_cert_chain,
s.config->m_grpc_root_certs,
s.config->m_log_level
);
m_state->grpc_server_thread = std::thread([this] {
m_state->grpc_server.run();
s.grpc_server_thread = std::thread([&s] {
s.grpc_server.run();
});
}
return run_result::ok();
}
bool application::stop_grpc_server(std::string &errstr)
bool falco::app::actions::stop_grpc_server(falco::app::state& s, std::string &errstr)
{
if(m_state->grpc_server_thread.joinable())
if(s.grpc_server_thread.joinable())
{
m_state->grpc_server.shutdown();
m_state->grpc_server_thread.join();
s.grpc_server.shutdown();
s.grpc_server_thread.join();
}
return true;

View File

@@ -14,41 +14,42 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
#ifndef MINIMAL_BUILD
#include "webserver.h"
using namespace falco::app;
using namespace falco::app::actions;
application::run_result application::start_webserver()
falco::app::run_result falco::app::actions::start_webserver(falco::app::state& s)
{
if(!is_capture_mode() && m_state->config->m_webserver_enabled)
if(!s.is_capture_mode() && s.config->m_webserver_enabled)
{
std::string ssl_option = (m_state->config->m_webserver_ssl_enabled ? " (SSL)" : "");
std::string ssl_option = (s.config->m_webserver_ssl_enabled ? " (SSL)" : "");
falco_logger::log(LOG_INFO, "Starting health webserver with threadiness "
+ std::to_string(m_state->config->m_webserver_threadiness)
+ std::to_string(s.config->m_webserver_threadiness)
+ ", listening on port "
+ std::to_string(m_state->config->m_webserver_listen_port)
+ std::to_string(s.config->m_webserver_listen_port)
+ ssl_option + "\n");
m_state->webserver.start(
m_state->offline_inspector,
m_state->config->m_webserver_threadiness,
m_state->config->m_webserver_listen_port,
m_state->config->m_webserver_k8s_healthz_endpoint,
m_state->config->m_webserver_ssl_certificate,
m_state->config->m_webserver_ssl_enabled);
s.webserver.start(
s.offline_inspector,
s.config->m_webserver_threadiness,
s.config->m_webserver_listen_port,
s.config->m_webserver_k8s_healthz_endpoint,
s.config->m_webserver_ssl_certificate,
s.config->m_webserver_ssl_enabled);
}
return run_result::ok();
}
bool application::stop_webserver(std::string &errstr)
bool falco::app::actions::stop_webserver(falco::app::state& s, std::string &errstr)
{
if(!is_capture_mode())
if(!s.is_capture_mode())
{
m_state->webserver.stop();
s.webserver.stop();
}
return true;
}

View File

@@ -14,22 +14,23 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "actions.h"
#include <string>
using namespace falco::app;
using namespace falco::app::actions;
application::run_result application::validate_rules_files()
falco::app::run_result falco::app::actions::validate_rules_files(falco::app::state& s)
{
if(m_options.validate_rules_filenames.size() > 0)
if(s.options.validate_rules_filenames.size() > 0)
{
std::vector<std::string> rules_contents;
falco::load_result::rules_contents_t rc;
try {
read_files(m_options.validate_rules_filenames.begin(),
m_options.validate_rules_filenames.end(),
read_files(s.options.validate_rules_filenames.begin(),
s.options.validate_rules_filenames.end(),
rules_contents,
rc);
}
@@ -63,7 +64,7 @@ application::run_result application::validate_rules_files()
std::string summary;
falco_logger::log(LOG_INFO, "Validating rules file(s):\n");
for(auto file : m_options.validate_rules_filenames)
for(auto file : s.options.validate_rules_filenames)
{
falco_logger::log(LOG_INFO, " " + file + "\n");
}
@@ -73,19 +74,19 @@ application::run_result application::validate_rules_files()
std::string err = "";
nlohmann::json results = nlohmann::json::array();
for(auto &filename : m_options.validate_rules_filenames)
for(auto &filename : s.options.validate_rules_filenames)
{
std::unique_ptr<falco::load_result> res;
res = m_state->engine->load_rules(rc.at(filename), filename);
if (!check_rules_plugin_requirements(err))
res = s.engine->load_rules(rc.at(filename), filename);
if (!check_rules_plugin_requirements(s, err))
{
return run_result::fatal(err);
}
successful &= res->successful();
if(m_state->config->m_json_output)
if(s.config->m_json_output)
{
results.push_back(res->as_json(rc));
}
@@ -110,14 +111,14 @@ application::run_result application::validate_rules_files()
summary += filename + ": Ok, with warnings";
// If verbose is true, print the warnings now.
if(m_options.verbose)
if(s.options.verbose)
{
fprintf(stderr, "%s\n", res->as_string(true, rc).c_str());
}
}
}
if(m_state->config->m_json_output)
if(s.config->m_json_output)
{
nlohmann::json res;
res["falco_load_results"] = results;

110
userspace/falco/app/app.cpp Normal file
View File

@@ -0,0 +1,110 @@
/*
Copyright (C) 2023 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "app.h"
#include "state.h"
#include "signals.h"
#include "actions/actions.h"
bool falco::app::run(int argc, char** argv, bool& restart, std::string& errstr)
{
falco::app::state s;
falco::app::run_result res;
if(!s.options.parse(argc, argv, errstr))
{
return false;
}
for(char **arg = argv; *arg; arg++)
{
if(s.cmdline.size() > 0)
{
s.cmdline += " ";
}
s.cmdline += *arg;
}
// The order here is the order in which the methods will be
// called. Before changing the order, ensure that all
// dependencies are honored (e.g. don't process events before
// loading plugins, opening inspector, etc.).
std::list<std::function<run_result(falco::app::state&)>> run_steps = {
falco::app::actions::load_config,
falco::app::actions::print_help,
falco::app::actions::print_version,
falco::app::actions::print_page_size,
falco::app::actions::print_generated_gvisor_config,
falco::app::actions::print_ignored_events,
falco::app::actions::print_syscall_events,
falco::app::actions::require_config_file,
falco::app::actions::print_plugin_info,
falco::app::actions::list_plugins,
falco::app::actions::load_plugins,
falco::app::actions::init_inspectors,
falco::app::actions::init_falco_engine,
falco::app::actions::list_fields,
falco::app::actions::select_event_sources,
falco::app::actions::validate_rules_files,
falco::app::actions::load_rules_files,
falco::app::actions::print_support,
falco::app::actions::create_signal_handlers,
falco::app::actions::attach_inotify_signals,
falco::app::actions::create_requested_paths,
falco::app::actions::daemonize,
falco::app::actions::init_outputs,
falco::app::actions::init_clients,
falco::app::actions::configure_syscall_buffer_size,
falco::app::actions::start_grpc_server,
falco::app::actions::start_webserver,
falco::app::actions::process_events,
};
std::list<std::function<bool(falco::app::state&, std::string&)>> teardown_steps = {
falco::app::actions::unregister_signal_handlers,
falco::app::actions::stop_grpc_server,
falco::app::actions::stop_webserver,
};
for (auto &func : run_steps)
{
res = func(s);
if(!res.proceed)
{
break;
}
}
for (auto &func : teardown_steps)
{
std::string errstr;
if(!func(s, errstr))
{
// Note only printing warning here--we want all functions
// to occur even if some return errors.
fprintf(stderr, "Could not tear down in run(): %s\n", errstr.c_str());
}
}
if(!res.success)
{
errstr = res.errstr;
}
restart = falco::app::should_restart();
return res.success;
}

27
userspace/falco/app/app.h Normal file
View File

@@ -0,0 +1,27 @@
/*
Copyright (C) 2023 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <string>
namespace falco {
namespace app {
bool run(int argc, char** argv, bool& restart, std::string& errstr);
}; // namespace app
}; // namespace falco

View File

@@ -1,221 +0,0 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "app_cmdline_options.h"
#include "configuration.h"
#include "config_falco.h"
#include <fstream>
namespace falco {
namespace app {
// Most bool member variables do not need to be set explicitly, as
// they are bound to command line options that have default
// values. However, a few options can be ifdef'd out so explicitly
// initialize their linked variables.
cmdline_options::cmdline_options()
: event_buffer_format(sinsp_evt::PF_NORMAL),
gvisor_config(""),
list_fields(false),
list_plugins(false),
list_syscall_events(false),
markdown(false),
modern_bpf(false),
m_cmdline_opts("falco", "Falco - Cloud Native Runtime Security")
{
define();
}
cmdline_options::~cmdline_options()
{
}
bool cmdline_options::parse(int argc, char **argv, std::string &errstr)
{
try {
m_cmdline_parsed = m_cmdline_opts.parse(argc, argv);
}
catch (std::exception &e)
{
errstr = e.what();
return false;
}
// Some options require additional processing/validation
std::ifstream conf_stream;
if (!conf_filename.empty())
{
conf_stream.open(conf_filename);
if (!conf_stream.is_open())
{
errstr = std::string("Could not find configuration file at ") + conf_filename;
return false;
}
}
else
{
#ifndef BUILD_TYPE_RELEASE
conf_stream.open(FALCO_SOURCE_CONF_FILE);
if (conf_stream.is_open())
{
conf_filename = FALCO_SOURCE_CONF_FILE;
}
else
#endif
{
conf_stream.open(FALCO_INSTALL_CONF_FILE);
if (conf_stream.is_open())
{
conf_filename = FALCO_INSTALL_CONF_FILE;
}
else
{
// Note we do not return false here. Although there is
// no valid config file, some ways of running falco
// (e.g. --help, --list) do not need a config file.
//
// Later, when it comes time to read a config file, if
// the filename is empty we exit with an error.
conf_filename = "";
}
}
}
if(m_cmdline_parsed.count("b") > 0)
{
event_buffer_format = sinsp_evt::PF_BASE64;
}
if(m_cmdline_parsed.count("r") > 0)
{
for(auto &path : m_cmdline_parsed["r"].as<std::vector<std::string>>())
{
rules_filenames.push_back(path);
}
}
// Convert the vectors of enabled/disabled tags into sets to match falco engine API
if(m_cmdline_parsed.count("T") > 0)
{
for(auto &tag : m_cmdline_parsed["T"].as<std::vector<std::string>>())
{
disabled_rule_tags.insert(tag);
}
}
if(m_cmdline_parsed.count("t") > 0)
{
for(auto &tag : m_cmdline_parsed["t"].as<std::vector<std::string>>())
{
enabled_rule_tags.insert(tag);
}
}
// Some combinations of arguments are not allowed.
// You can't both disable and enable rules
if((disabled_rule_substrings.size() + disabled_rule_tags.size() > 0) &&
enabled_rule_tags.size() > 0)
{
errstr = std::string("You can not specify both disabled (-D/-T) and enabled (-t) rules");
return false;
}
if (daemon && pidfilename == "") {
errstr = std::string("If -d is provided, a pid file must also be provided");
return false;
}
list_fields = m_cmdline_parsed.count("list") > 0 ? true : false;
return true;
}
std::string cmdline_options::usage()
{
return m_cmdline_opts.help();
}
void cmdline_options::define()
{
m_cmdline_opts.add_options()
("h,help", "Print this page", cxxopts::value(help)->default_value("false"))
#ifdef BUILD_TYPE_RELEASE
("c", "Configuration file. If not specified uses " FALCO_INSTALL_CONF_FILE ".", cxxopts::value(conf_filename), "<path>")
#else
("c", "Configuration file. If not specified tries " FALCO_SOURCE_CONF_FILE ", " FALCO_INSTALL_CONF_FILE ".", cxxopts::value(conf_filename), "<path>")
#endif
("A", "Monitor all events, including those not interesting to Falco. Please use the -i option to list all ignored events. This option has effect only on live captures.", cxxopts::value(all_events)->default_value("false"))
("b,print-base64", "Print data buffers in base64. This is useful for encoding binary data that needs to be used over media designed to consume this format.")
("cri", "Path to CRI socket for container metadata. Use the specified socket to fetch data from a CRI-compatible runtime. If not specified, uses the libs default. This option can be passed multiple times to specify socket to be tried until a successful one is found.", cxxopts::value(cri_socket_paths), "<path>")
("d,daemon", "Run as a daemon.", cxxopts::value(daemon)->default_value("false"))
("disable-cri-async", "Disable asynchronous CRI metadata fetching. This is useful to let the input event wait for the container metadata fetch to finish before moving forward. Async fetching, in some environments leads to empty fields for container metadata when the fetch is not fast enough to be completed asynchronously. This can have a performance penalty on your environment depending on the number of containers and the frequency at which they are created/started/stopped.", cxxopts::value(disable_cri_async)->default_value("false"))
("disable-source", "Disable a specific event source. By default, all loaded sources get enabled. Available sources are 'syscall' and all sources defined by loaded plugins supporting the event sourcing capability. This option can be passed multiple times. This has no offect when reading events from a trace file. Can not disable all event sources. Can not be mixed with --enable-source.", cxxopts::value(disable_sources), "<event_source>")
("D", "Disable any rules with names having the substring <substring>. This option can be passed multiple times. Can not be mixed with -t.", cxxopts::value(disabled_rule_substrings), "<substring>")
("e", "Read the events from a trace file <events_file> in .scap format instead of tapping into live.", cxxopts::value(trace_filename), "<events_file>")
("enable-source", "Enable a specific event source. If used, all loaded sources get disabled by default and only the ones passed with this option get enabled. Available sources are 'syscall' and all sources defined by loaded plugins supporting the event sourcing capability. This option can be passed multiple times. This has no offect when reading events from a trace file. Can not be mixed with --disable-source.", cxxopts::value(enable_sources), "<event_source>")
#ifdef HAS_GVISOR
("g,gvisor-config", "Parse events from gVisor using the specified configuration file. A falco-compatible configuration file can be generated with --gvisor-generate-config and can be used for both runsc and Falco.", cxxopts::value(gvisor_config), "<gvisor_config>")
("gvisor-generate-config", "Generate a configuration file that can be used for gVisor.", cxxopts::value<std::string>(gvisor_generate_config_with_socket)->implicit_value("/run/falco/gvisor.sock"), "<socket_path>")
("gvisor-root", "gVisor root directory for storage of container state. Equivalent to runsc --root flag.", cxxopts::value(gvisor_root), "<gvisor_root>")
#endif
#ifdef HAS_MODERN_BPF
("modern-bpf", "[EXPERIMENTAL] Use BPF modern probe to capture system events.", cxxopts::value(modern_bpf)->default_value("false"))
#endif
("i", "Print all events that are ignored by default (i.e. without the -A flag) and exit.", cxxopts::value(print_ignored_events)->default_value("false"))
#ifndef MINIMAL_BUILD
("k,k8s-api", "Enable Kubernetes support by connecting to the API server specified as argument. E.g. \"http://admin:password@127.0.0.1:8080\". The API server can also be specified via the environment variable FALCO_K8S_API.", cxxopts::value(k8s_api), "<url>")
("K,k8s-api-cert", "Use the provided files names to authenticate user and (optionally) verify the K8S API server identity. Each entry must specify full (absolute, or relative to the current directory) path to the respective file. Private key password is optional (needed only if key is password protected). CA certificate is optional. For all files, only PEM file format is supported. Specifying CA certificate only is obsoleted - when single entry is provided for this option, it will be interpreted as the name of a file containing bearer token. Note that the format of this command-line option prohibits use of files whose names contain ':' or '#' characters in the file name.", cxxopts::value(k8s_api_cert), "(<bt_file> | <cert_file>:<key_file[#password]>[:<ca_cert_file>])")
("k8s-node", "The node name will be used as a filter when requesting metadata of pods to the API server. Usually, this should be set to the current node on which Falco is running. If empty, no filter is set, which may have a performance penalty on large clusters.", cxxopts::value(k8s_node_name), "<node_name>")
#endif
("L", "Show the name and description of all rules and exit.", cxxopts::value(describe_all_rules)->default_value("false"))
("l", "Show the name and description of the rule with name <rule> and exit.", cxxopts::value(describe_rule), "<rule>")
("list", "List all defined fields. If <source> is provided, only list those fields for the source <source>. Current values for <source> are \"syscall\" or any source from a configured plugin with event sourcing capability.", cxxopts::value(list_source_fields)->implicit_value(""), "<source>")
("list-syscall-events", "List all defined system call events.", cxxopts::value<bool>(list_syscall_events))
#ifndef MUSL_OPTIMIZED
("list-plugins", "Print info on all loaded plugins and exit.", cxxopts::value(list_plugins)->default_value("false"))
#endif
#ifndef MINIMAL_BUILD
("m,mesos-api", "This feature has been DEPRECATED and will be removed in the next version.", cxxopts::value(mesos_api), "<url[,marathon_url]>")
#endif
("M", "Stop collecting after <num_seconds> reached.", cxxopts::value(duration_to_tot)->default_value("0"), "<num_seconds>")
("markdown", "When used with --list/--list-syscall-events, print the content in Markdown format", cxxopts::value<bool>(markdown))
("N", "When used with --list, only print field names.", cxxopts::value(names_only)->default_value("false"))
("o,option", "Set the value of option <opt> to <val>. Overrides values in configuration file. <opt> can be identified using its location in configuration file using dot notation. Elements which are entries of lists can be accessed via square brackets [].\n E.g. base.id = val\n base.subvalue.subvalue2 = val\n base.list[1]=val", cxxopts::value(cmdline_config_options), "<opt>=<val>")
("plugin-info", "Print info for a single plugin and exit.\nThis includes all descriptivo info like name and author, along with the\nschema format for the init configuration and a list of suggested open parameters.\n<plugin_name> can be the name of the plugin or its configured library_path.", cxxopts::value(print_plugin_info), "<plugin_name>")
("p,print", "Add additional information to each falco notification's output.\nWith -pc or -pcontainer will use a container-friendly format.\nWith -pk or -pkubernetes will use a kubernetes-friendly format.\nAdditionally, specifying -pc/-pk will change the interpretation of %container.info in rule output fields.", cxxopts::value(print_additional), "<output_format>")
("P,pidfile", "When run as a daemon, write pid to specified file", cxxopts::value(pidfilename)->default_value("/var/run/falco.pid"), "<pid_file>")
("r", "Rules file/directory (defaults to value set in configuration file, or /etc/falco_rules.yaml). This option can be passed multiple times to read from multiple files/directories.", cxxopts::value<std::vector<std::string>>(), "<rules_file>")
("s", "If specified, append statistics related to Falco's reading/processing of events to this file (only useful in live mode).", cxxopts::value(stats_filename), "<stats_file>")
("stats-interval", "When using -s <stats_file>, write statistics every <msec> ms. This uses signals, so don't recommend intervals below 200 ms. Defaults to 5000 (5 seconds).", cxxopts::value(stats_interval)->default_value("5000"), "<msec>")
("S,snaplen", "Capture the first <len> bytes of each I/O buffer. By default, the first 80 bytes are captured. Use this option with caution, it can generate huge trace files.", cxxopts::value(snaplen)->default_value("0"), "<len>")
("support", "Print support information including version, rules files used, etc. and exit.", cxxopts::value(print_support)->default_value("false"))
("T", "Disable any rules with a tag=<tag>. This option can be passed multiple times. Can not be mized with -t", cxxopts::value<std::vector<std::string>>(), "<tag>")
("t", "Only run those rules with a tag=<tag>. This option can be passed multiple times. Can not be mixed with -T/-D.", cxxopts::value<std::vector<std::string>>(), "<tag>")
("U,unbuffered", "Turn off output buffering to configured outputs. This causes every single line emitted by falco to be flushed which generates higher CPU usage but is useful when piping those outputs into another process or into a script.", cxxopts::value(unbuffered_outputs)->default_value("false"))
("u,userspace", "Parse events from userspace. To be used in conjunction with the ptrace(2) based driver (pdig)", cxxopts::value(userspace)->default_value("false"))
("V,validate", "Read the contents of the specified rules(s) file and exit. This option can be passed multiple times to validate multiple files.", cxxopts::value(validate_rules_filenames), "<rules_file>")
("v", "Verbose output.", cxxopts::value(verbose)->default_value("false"))
("version", "Print version number.", cxxopts::value(print_version_info)->default_value("false"))
("page-size", "Print the system page size (may help you to choose the right syscall ring-buffer size).", cxxopts::value(print_page_size)->default_value("false"));
m_cmdline_opts.set_width(140);
}
}; // namespace app
}; // namespace falco

View File

@@ -1,95 +0,0 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <event.h>
#include <cxxopts.hpp>
#include <string>
#include <vector>
#include <set>
namespace falco {
namespace app {
class cmdline_options {
public:
cmdline_options();
~cmdline_options();
// Each of these maps directly to a command line option.
bool help;
std::string conf_filename;
bool all_events;
sinsp_evt::param_fmt event_buffer_format;
std::vector<std::string> cri_socket_paths;
bool daemon;
bool disable_cri_async;
std::vector<std::string> disable_sources;
std::vector<std::string> disabled_rule_substrings;
std::vector<std::string> enable_sources;
std::string trace_filename;
std::string gvisor_config;
std::string gvisor_generate_config_with_socket;
std::string gvisor_root;
std::string k8s_api;
std::string k8s_api_cert;
std::string k8s_node_name;
bool describe_all_rules;
std::string describe_rule;
bool print_ignored_events;
bool list_fields;
std::string list_source_fields;
bool list_plugins;
std::string print_plugin_info;
bool list_syscall_events;
bool markdown;
std::string mesos_api;
int duration_to_tot;
bool names_only;
std::vector<std::string> cmdline_config_options;
std::string print_additional;
std::string pidfilename;
// Rules list as passed by the user, via cmdline option '-r'
std::list<std::string> rules_filenames;
std::string stats_filename;
uint64_t stats_interval;
uint64_t snaplen;
bool print_support;
std::set<std::string> disabled_rule_tags;
std::set<std::string> enabled_rule_tags;
bool unbuffered_outputs;
bool userspace;
std::vector<std::string> validate_rules_filenames;
bool verbose;
bool print_version_info;
bool print_page_size;
bool modern_bpf;
bool parse(int argc, char **argv, std::string &errstr);
std::string usage();
private:
void define();
cxxopts::Options m_cmdline_opts;
cxxopts::ParseResult m_cmdline_parsed;
};
}; // namespace application
}; // namespace falco

View File

@@ -1,246 +0,0 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// The falco "app" holds application-level configuration and contains
// the implementation of any subcommand-like behaviors like --list, -i
// (print_ignored_events), etc.
// It also contains the code to initialize components like the
// inspector, falco engine, etc.
#include "application.h"
#include "falco_common.h"
static inline bool should_take_action_to_signal(std::atomic<int>& v)
{
// we expected the signal to be received, and we try to set action-taken flag
int value = APP_SIGNAL_SET;
while (!v.compare_exchange_weak(
value,
APP_SIGNAL_ACTION_TAKEN,
std::memory_order_seq_cst,
std::memory_order_seq_cst))
{
// application already took action, there's no need to do it twice
if (value == APP_SIGNAL_ACTION_TAKEN)
{
return false;
}
// signal did was not really received, so we "fake" receiving it
if (value == APP_SIGNAL_NOT_SET)
{
v.store(APP_SIGNAL_SET, std::memory_order_seq_cst);
}
// reset "expected" CAS variable and keep looping until we succeed
value = APP_SIGNAL_SET;
}
return true;
}
namespace falco {
namespace app {
std::atomic<int> g_terminate(APP_SIGNAL_NOT_SET);
std::atomic<int> g_restart(APP_SIGNAL_NOT_SET);
std::atomic<int> g_reopen_outputs(APP_SIGNAL_NOT_SET);
application::run_result::run_result()
: success(true), errstr(""), proceed(true)
{
}
application::run_result::~run_result()
{
}
application::state::state()
: loaded_sources(),
enabled_sources(),
source_infos(),
plugin_configs(),
ppm_sc_of_interest(),
tp_of_interest(),
syscall_buffer_bytes_size(DEFAULT_DRIVER_BUFFER_BYTES_DIM)
{
config = std::make_shared<falco_configuration>();
engine = std::make_shared<falco_engine>();
offline_inspector = std::make_shared<sinsp>();
outputs = nullptr;
}
application::state::~state()
{
}
application::application()
: m_initialized(false)
{
}
application::~application()
{
}
void application::terminate(bool verbose)
{
if (should_take_action_to_signal(falco::app::g_terminate))
{
if (verbose)
{
falco_logger::log(LOG_INFO, "SIGINT received, exiting...\n");
}
}
}
void application::reopen_outputs(bool verbose)
{
if (should_take_action_to_signal(falco::app::g_reopen_outputs))
{
if (verbose)
{
falco_logger::log(LOG_INFO, "SIGUSR1 received, reopening outputs...\n");
}
if(m_state != nullptr && m_state->outputs != nullptr)
{
m_state->outputs->reopen_outputs();
}
falco::app::g_reopen_outputs.store(APP_SIGNAL_NOT_SET);
}
}
void application::restart(bool verbose)
{
if (should_take_action_to_signal(falco::app::g_restart))
{
if (verbose)
{
falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n");
}
}
}
bool application::init(int argc, char **argv, std::string &errstr)
{
if(m_initialized)
{
throw falco_exception("Application already initialized");
}
m_state.reset(new state());
if(!m_options.parse(argc, argv, errstr))
{
return false;
}
for(char **arg = argv; *arg; arg++)
{
if(m_state->cmdline.size() > 0)
{
m_state->cmdline += " ";
}
m_state->cmdline += *arg;
}
m_initialized = true;
return true;
}
bool application::run(std::string &errstr, bool &restart)
{
run_result res;
// The order here is the order in which the methods will be
// called. Before changing the order, ensure that all
// dependencies are honored (e.g. don't process events before
// loading plugins, opening inspector, etc.).
std::list<std::function<run_result()>> run_steps = {
std::bind(&application::load_config, this),
std::bind(&application::print_help, this),
std::bind(&application::print_version, this),
std::bind(&application::print_page_size, this),
std::bind(&application::print_generated_gvisor_config, this),
std::bind(&application::print_ignored_events, this),
std::bind(&application::print_syscall_events, this),
std::bind(&application::require_config_file, this),
std::bind(&application::print_plugin_info, this),
std::bind(&application::list_plugins, this),
std::bind(&application::load_plugins, this),
std::bind(&application::init_inspectors, this),
std::bind(&application::init_falco_engine, this),
std::bind(&application::list_fields, this),
std::bind(&application::select_event_sources, this),
std::bind(&application::validate_rules_files, this),
std::bind(&application::load_rules_files, this),
std::bind(&application::print_support, this),
std::bind(&application::create_signal_handlers, this),
std::bind(&application::attach_inotify_signals, this),
std::bind(&application::create_requested_paths, this),
std::bind(&application::daemonize, this),
std::bind(&application::init_outputs, this),
std::bind(&application::init_clients, this),
std::bind(&application::configure_syscall_buffer_size, this),
#ifndef MINIMAL_BUILD
std::bind(&application::start_grpc_server, this),
std::bind(&application::start_webserver, this),
#endif
std::bind(&application::process_events, this)
};
std::list<std::function<bool(std::string &)>> teardown_steps = {
std::bind(&application::unregister_signal_handlers, this, std::placeholders::_1),
#ifndef MINIMAL_BUILD
std::bind(&application::stop_grpc_server, this, std::placeholders::_1),
std::bind(&application::stop_webserver, this, std::placeholders::_1)
#endif
};
for (auto &func : run_steps)
{
res = func();
if(!res.proceed)
{
break;
}
}
for (auto &func : teardown_steps)
{
std::string errstr;
if(!func(errstr))
{
// Note only printing warning here--we want all functions
// to occur even if some return errors.
fprintf(stderr, "Could not tear down in run(): %s\n", errstr.c_str());
}
}
if(!res.success)
{
errstr = res.errstr;
}
restart = should_restart();
return res.success;
}
}; // namespace app
}; // namespace falco

View File

@@ -1,410 +0,0 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include "semaphore.h"
#include "configuration.h"
#include "stats_writer.h"
#ifndef MINIMAL_BUILD
#include "grpc_server.h"
#include "webserver.h"
#include "indexed_vector.h"
#endif
#include "app_cmdline_options.h"
#include <string>
#include <atomic>
#include <unordered_set>
#define APP_SIGNAL_NOT_SET 0 // The signal flag is not set
#define APP_SIGNAL_SET 1 // The signal flag has been set
#define APP_SIGNAL_ACTION_TAKEN 2 // The signal flag has been set and the application took action
namespace falco {
namespace app {
// these are used to control the lifecycle of the application
// through signal handlers or internal calls
extern std::atomic<int> g_terminate;
extern std::atomic<int> g_restart;
extern std::atomic<int> g_reopen_outputs;
class application {
public:
application();
virtual ~application();
application(application&&) = default;
application& operator = (application&&) = default;
application(const application&) = delete;
application& operator = (const application&) = delete;
bool init(int argc, char **argv, std::string &errstr);
// Returns whether the application completed with errors or
// not. errstr will contain details when run() returns false.
//
// If restart (generally set by signal handlers) is
// true, the application should be restarted instead of
// exiting.
bool run(std::string &errstr, bool &restart);
private:
// Holds the state used and shared by the below methods that
// actually implement the application. Declared as a
// standalone class to allow for a bit of separation between
// application state and instance variables, and to also defer
// initializing this state until application::init.
struct state
{
// Holds the info mapped for each loaded event source
struct source_info
{
// The index of the given event source in the state's falco_engine,
// as returned by falco_engine::add_source
std::size_t engine_idx;
// The filtercheck list containing all fields compatible
// with the given event source
filter_check_list filterchecks;
// The inspector assigned to this event source. If in capture mode,
// all event source will share the same inspector. If the event
// source is a plugin one, the assigned inspector must have that
// plugin registered in its plugin manager
std::shared_ptr<sinsp> inspector;
};
state();
virtual ~state();
std::shared_ptr<falco_configuration> config;
std::shared_ptr<falco_outputs> outputs;
std::shared_ptr<falco_engine> engine;
// The set of loaded event sources (by default, the syscall event
// source plus all event sources coming from the loaded plugins)
std::unordered_set<std::string> loaded_sources;
// The set of enabled event sources (can be altered by using
// the --enable-source and --disable-source options)
std::unordered_set<std::string> enabled_sources;
// Used to load all plugins to get their info. In capture mode,
// this is also used to open the capture file and read its events
std::shared_ptr<sinsp> offline_inspector;
// List of all the information mapped to each event source
// indexed by event source name
indexed_vector<source_info> source_infos;
// List of all plugin configurations indexed by plugin name as returned
// by their sinsp_plugin::name method
indexed_vector<falco_configuration::plugin_config> plugin_configs;
std::string cmdline;
// Set of events we want the driver to capture
std::unordered_set<uint32_t> ppm_event_info_of_interest;
// Set of syscalls we want the driver to capture
std::unordered_set<uint32_t> ppm_sc_of_interest;
// Set of tracepoints we want the driver to capture
std::unordered_set<uint32_t> tp_of_interest;
// Dimension of the syscall buffer in bytes.
uint64_t syscall_buffer_bytes_size;
#ifndef MINIMAL_BUILD
falco::grpc::server grpc_server;
std::thread grpc_server_thread;
falco_webserver webserver;
#endif
};
// Used in the below methods to indicate how to proceed.
struct run_result {
// Successful result
inline static run_result ok()
{
run_result r;
r.success = true;
r.errstr = "";
r.proceed = true;
return r;
}
// Successful result that causes the program to stop
inline static run_result exit()
{
run_result r = ok();
r.proceed = false;
return r;
}
// Failure result that causes the program to stop with an error
inline static run_result fatal(const std::string& err)
{
run_result r;
r.success = false;
r.errstr = err;
r.proceed = false;
return r;
}
// Merges two run results into one
inline static run_result merge(const run_result& a, const run_result& b)
{
auto res = ok();
res.proceed = a.proceed && b.proceed;
res.success = a.success && b.success;
res.errstr = a.errstr;
if (!b.errstr.empty())
{
res.errstr += res.errstr.empty() ? "" : "\n";
res.errstr += b.errstr;
}
return res;
}
run_result();
virtual ~run_result();
run_result(run_result&&) = default;
run_result& operator = (run_result&&) = default;
run_result(const run_result&) = default;
run_result& operator = (const run_result&) = default;
// If true, the method completed successfully.
bool success;
// If success==false, details on the error.
std::string errstr;
// If true, subsequent methods should be performed. If
// false, subsequent methods should *not* be performed
// and falco should tear down/exit/restart.
bool proceed;
};
// used to synchronize different event source running in parallel
class source_sync_context
{
public:
source_sync_context(falco::semaphore& s)
: m_finished(false), m_joined(false), m_semaphore(s) { }
source_sync_context(source_sync_context&&) = default;
source_sync_context& operator = (source_sync_context&&) = default;
source_sync_context(const source_sync_context&) = delete;
source_sync_context& operator = (const source_sync_context&) = delete;
inline void finish()
{
bool v = false;
while (!m_finished.compare_exchange_weak(
v, true,
std::memory_order_seq_cst,
std::memory_order_seq_cst))
{
if (v)
{
throw falco_exception("source_sync_context has been finished twice");
}
}
m_semaphore.release();
}
inline void join()
{
bool v = false;
while (!m_joined.compare_exchange_weak(
v, true,
std::memory_order_seq_cst,
std::memory_order_seq_cst))
{
if (v)
{
throw falco_exception("source_sync_context has been joined twice");
}
}
}
inline bool joined()
{
return m_joined.load(std::memory_order_seq_cst);
}
inline bool finished()
{
return m_finished.load(std::memory_order_seq_cst);
}
private:
// set to true when the event processing loop finishes
std::atomic<bool> m_finished;
// set to true when the result has been collected after finishing
std::atomic<bool> m_joined;
// used to notify the waiting thread when finished gets set to true
falco::semaphore& m_semaphore;
};
// Convenience method. Read a sequence of filenames and fill
// in a vector of rules contents.
// Also fill in the provided rules_contents_t with a mapping from
// filename (reference) to content (reference).
// falco_exception if any file could not be read.
template<class InputIterator>
void read_files(InputIterator begin, InputIterator end,
std::vector<std::string>& rules_contents,
falco::load_result::rules_contents_t& rc)
{
// Read the contents in a first pass
for(auto it = begin; it != end; it++)
{
std::string &filename = *it;
std::ifstream is;
is.open(filename);
if (!is.is_open())
{
throw falco_exception("Could not open file " + filename + " for reading");
}
std::string rules_content((std::istreambuf_iterator<char>(is)),
std::istreambuf_iterator<char>());
rules_contents.emplace_back(std::move(rules_content));
}
// Populate the map in a second pass to avoid
// references becoming invalid.
auto it = begin;
auto rit = rules_contents.begin();
for(; it != end && rit != rules_contents.end(); it++, rit++)
{
rc.emplace(*it, *rit);
}
// Both it and rit must be at the end, otherwise
// there's a bug in the above
if(it != end || rit != rules_contents.end())
{
throw falco_exception("Unexpected mismatch in rules content name/rules content sets?");
}
}
// These methods comprise the code the application "runs". The
// order in which the methods run is in application.cpp.
run_result create_signal_handlers();
run_result attach_inotify_signals();
run_result daemonize();
run_result init_falco_engine();
run_result init_inspectors();
run_result init_clients();
run_result init_outputs();
run_result list_fields();
run_result list_plugins();
run_result load_config();
run_result require_config_file();
run_result load_plugins();
run_result load_rules_files();
run_result create_requested_paths();
run_result print_generated_gvisor_config();
run_result print_help();
run_result print_ignored_events();
run_result print_plugin_info();
run_result print_support();
run_result print_syscall_events();
run_result print_version();
run_result print_page_size();
run_result process_events();
run_result select_event_sources();
void configure_interesting_sets();
application::run_result configure_syscall_buffer_size();
#ifndef MINIMAL_BUILD
run_result start_grpc_server();
run_result start_webserver();
#endif
run_result validate_rules_files();
// These methods comprise application teardown. The order in
// which the methods run is in application.cpp.
bool close_inspector(std::string &errstr);
bool unregister_signal_handlers(std::string &errstr);
#ifndef MINIMAL_BUILD
bool stop_grpc_server(std::string &errstr);
bool stop_webserver(std::string &errstr);
#endif
// Methods called by the above methods
int create_dir(const std::string &path);
bool create_handler(int sig, void (*func)(int), run_result &ret);
void configure_output_format();
void check_for_ignored_events();
bool check_rules_plugin_requirements(std::string& err);
void format_plugin_info(std::shared_ptr<sinsp_plugin> p, std::ostream& os) const;
run_result open_offline_inspector();
run_result open_live_inspector(std::shared_ptr<sinsp> inspector, const std::string& source);
void add_source_to_engine(const std::string& src);
void print_enabled_event_sources();
void init_syscall_inspector(std::shared_ptr<sinsp> inspector, const falco::app::cmdline_options& opts);
run_result do_inspect(
std::shared_ptr<sinsp> inspector,
const std::string& source, // an empty source represents capture mode
std::shared_ptr<stats_writer> statsw,
syscall_evt_drop_mgr &sdropmgr,
bool check_drops_and_timeouts,
uint64_t duration_to_tot_ns,
uint64_t &num_evts);
void process_inspector_events(
std::shared_ptr<sinsp> inspector,
std::shared_ptr<stats_writer> statsw,
std::string source, // an empty source represents capture mode
application::source_sync_context* sync,
run_result* res) noexcept;
/* Returns true if we are in capture mode. */
inline bool is_capture_mode() const
{
return !m_options.trace_filename.empty();
}
inline bool is_gvisor_enabled() const
{
return !m_options.gvisor_config.empty();
}
// used in signal handlers to control the flow of the application
void terminate(bool verbose=true);
void restart(bool verbose=true);
void reopen_outputs(bool verbose=true);
inline bool should_terminate()
{
return g_terminate.load(std::memory_order_seq_cst) != APP_SIGNAL_NOT_SET;
}
inline bool should_restart()
{
return g_restart.load(std::memory_order_seq_cst) != APP_SIGNAL_NOT_SET;
}
inline bool should_reopen_outputs()
{
return g_reopen_outputs.load(std::memory_order_seq_cst) != APP_SIGNAL_NOT_SET;
}
std::unique_ptr<state> m_state;
cmdline_options m_options;
bool m_initialized;
};
}; // namespace app
}; // namespace falco

View File

@@ -19,7 +19,7 @@ limitations under the License.
#include <iostream>
#include "application.h"
#include "app/app.h"
#include "logger.h"
#include "banned.h" // This raises a compilation error when certain functions are used
@@ -42,26 +42,11 @@ static void display_fatal_err(const std::string &&msg)
//
int falco_init(int argc, char **argv, bool &restart)
{
falco::app::application app;
restart = false;
std::string errstr;
bool successful = app.init(argc, argv, errstr);
if(!successful)
{
fprintf(stderr, "Runtime error: %s. Exiting.\n", errstr.c_str());
return EXIT_FAILURE;
}
try
{
bool success;
std::string errstr;
success = app.run(errstr, restart);
if(!success)
if (!falco::app::run(argc, argv, restart, errstr))
{
fprintf(stderr, "Error: %s\n", errstr.c_str());
return EXIT_FAILURE;