cleanup(app_actions): include evttypes from rules in configure_interesting_sets

Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
This commit is contained in:
Melissa Kilby
2023-01-24 23:16:45 +00:00
committed by poiana
parent 34ed5a5fc9
commit 91c185a178
11 changed files with 866 additions and 70 deletions

View File

@@ -24,6 +24,7 @@ namespace app {
namespace actions {
falco::app::run_result attach_inotify_signals(falco::app::state& s);
falco::app::run_result configure_interesting_sets(falco::app::state& s);
falco::app::run_result configure_syscall_buffer_size(falco::app::state& s);
falco::app::run_result create_requested_paths(falco::app::state& s);
falco::app::run_result create_signal_handlers(falco::app::state& s);

View File

@@ -0,0 +1,23 @@
/*
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 <unordered_set>
#include <string>
std::string concat_syscalls_names(std::unordered_set<std::string> const syscalls_names);
// TODO interim helper methods below shall be integrated into sinsp APIs
std::unordered_set<uint32_t> get_syscalls_ppm_codes(const std::unordered_set<std::string> syscalls_names);
std::unordered_set<std::string> get_difference_syscalls_names(std::unordered_set<std::string> syscalls_names_reference, std::unordered_set<std::string> syscalls_names_comparison);

View File

@@ -25,7 +25,11 @@ namespace actions {
bool check_rules_plugin_requirements(falco::app::state& s, std::string& err);
void print_enabled_event_sources(falco::app::state& s);
void configure_interesting_sets(falco::app::state& s);
void extract_rules_event_names(falco::app::state& s, std::unique_ptr<sinsp>& inspector, std::unordered_set<std::string>& rules_evttypes_names);
void activate_interesting_events(falco::app::state& s, std::unique_ptr<sinsp>& inspector);
void activate_interesting_syscalls(falco::app::state& s, std::unique_ptr<sinsp>& inspector, const std::unordered_set<std::string>& rules_evttypes_names);
void activate_interesting_kernel_tracepoints(falco::app::state& s, std::unique_ptr<sinsp>& inspector);
void check_for_ignored_events(falco::app::state& s);
void format_plugin_info(std::shared_ptr<sinsp_plugin> p, std::ostream& os);
falco::app::run_result open_offline_inspector(falco::app::state& s);
falco::app::run_result open_live_inspector(

View File

@@ -15,19 +15,74 @@ limitations under the License.
*/
#include "helpers.h"
#include "actions.h"
#include "configure_interesting_sets.h"
#include <unordered_set>
#include <sinsp.h>
#include <sstream>
using namespace falco::app;
using namespace falco::app::actions;
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!
std::unique_ptr<sinsp> inspector(new sinsp());
extern sinsp_evttables g_infotables;
/* Please note: here we fill these 2 sets because we are interested in only some features, if we leave
* them empty `libsinsp` will fill them with all the available syscalls and all the available tracepoints!
*/
std::string concat_syscalls_names(std::unordered_set<std::string> const syscalls_names)
{
std::set<std::string> syscalls_names_ordered = {};
for (const auto &n : syscalls_names)
{
syscalls_names_ordered.insert(n);
}
std::stringstream ss;
std::copy(syscalls_names_ordered.begin(), syscalls_names_ordered.end(),
std::ostream_iterator<std::string>(ss, ", "));
std::string syscalls_names_str = ss.str();
return syscalls_names_str.substr(0, syscalls_names_str.size() - 2);
}
std::unordered_set<uint32_t> get_syscalls_ppm_codes(const std::unordered_set<std::string> syscalls_names)
{
std::unordered_set<uint32_t> ppm_sc_set = {};
for (int ppm_sc_code = 0; ppm_sc_code < PPM_SC_MAX; ++ppm_sc_code)
{
std::string ppm_sc_name = g_infotables.m_syscall_info_table[ppm_sc_code].name;
if (syscalls_names.find(ppm_sc_name) != syscalls_names.end())
{
ppm_sc_set.insert(ppm_sc_code);
}
}
return ppm_sc_set;
}
std::unordered_set<std::string> get_difference_syscalls_names(std::unordered_set<std::string> syscalls_names_reference, std::unordered_set<std::string> syscalls_names_comparison)
{
std::unordered_set<std::string> out = syscalls_names_comparison;
for (const auto &ppm_sc_name : syscalls_names_reference)
{
if (syscalls_names_comparison.find(ppm_sc_name) != syscalls_names_comparison.end())
{
out.erase(ppm_sc_name);
}
}
return out;
}
void falco::app::actions::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;
s.engine->evttypes_for_ruleset(source, rule_events);
/* Get PPME events we consider interesting from the application state as idx codes. */
std::unique_ptr<sinsp> inspector(new sinsp());
std::unordered_set<uint32_t> ppme_events_codes(rule_events.begin(), rule_events.end());
auto event_names = inspector->get_events_names(ppme_events_codes);
for (const auto& n : inspector->get_events_names(s.ppm_event_info_of_interest))
{
event_names.erase(n);
}
/* Here the `libsinsp` state set is not enough, we need other syscalls used in the rules,
* so we use the `simple_set`, this `simple_set` contains all the syscalls of the `libsinsp` state
@@ -36,7 +91,39 @@ void falco::app::actions::configure_interesting_sets(falco::app::state& s)
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 */
if(event_names.empty())
{
return;
}
/* Get the names of the ignored events (syscall and non syscall events) and print them. */
std::cerr << "Loaded rules match ignored event types: warning (ignored-evttype): " + concat_syscalls_names(event_names) << std::endl;
std::cerr << "If syscalls in rules include high volume I/O syscalls (-> activate via `-A` flag), else (2) syscalls might be associated with syscalls undefined on your architecture (https://marcin.juszkiewicz.com.pl/download/tables/syscalls.html)" << std::endl;
}
void falco::app::actions::extract_rules_event_names(falco::app::state& s, std::unique_ptr<sinsp>& inspector, std::unordered_set<std::string>& rules_evttypes_names)
{
/* Get all (positive) PPME events from all rules as idx codes.
* Events names from negative filter expression statements are NOT included.
* PPME events in libsinsp are needed to map each event type into it's enter and exit event if applicable (e.g. for syscall events).
*/
std::set<uint16_t> rule_events;
std::string source = falco_common::syscall_source;
s.engine->evttypes_for_ruleset(source, rule_events);
std::unordered_set<uint32_t> ppme_events_codes(rule_events.begin(), rule_events.end());
/* Translate PPME event idx codes to consolidated event names.
* Those are the exact event type (evt.type) names from the rules and hence also contain non syscall names, e.g. "container".
*/
rules_evttypes_names = inspector->get_events_names(ppme_events_codes);
}
void falco::app::actions::activate_interesting_events(falco::app::state& s, std::unique_ptr<sinsp>& inspector)
{
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. This is needed to ensure the critical non syscall PPME events are activated as well, e.g. container or proc exit events. */
for (uint32_t ev = 2; ev < PPM_EVENT_MAX; ev++)
{
if (!sinsp::is_old_version_event(ev)
@@ -53,10 +140,84 @@ void falco::app::actions::configure_interesting_sets(falco::app::state& s)
}
}
/* In this case we get the tracepoints for the `libsinsp` state and we remove
* the `sched_switch` tracepoint since it is highly noisy and not so useful
/* Reading a scap file we have no concepts of ignored events we read all we need. */
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.
*/
falco::app::actions::check_for_ignored_events(s);
}
}
void falco::app::actions::activate_interesting_syscalls(falco::app::state& s, std::unique_ptr<sinsp>& inspector, const std::unordered_set<std::string>& rules_evttypes_names)
{
/* Translate PPME event names to PPM syscall idx codes.
* PPM syscall idx codes can be viewed as condensed libsinsp lookup table to map a system call name to it's actual system syscall id (as defined by the Linux kernel).
* Hence here we don't need syscall enter and exit distinction.
*/
std::unordered_set<uint32_t> rules_ppm_sc_set = get_syscalls_ppm_codes(rules_evttypes_names);
std::unordered_set<std::string> rules_syscalls_names = inspector->get_syscalls_names(rules_ppm_sc_set);
if (rules_syscalls_names.size() > 0)
{
falco_logger::log(LOG_INFO, "(" + std::to_string(rules_syscalls_names.size()) + ") syscalls activated in rules: " + concat_syscalls_names(rules_syscalls_names) + "\n");
}
/*
*
* DEFAULT OPTION:
*
* Current enforce_simple_ppm_sc_set approach includes multiple steps:
* (1) Enforce all positive syscalls from each Falco rule
* (2) Enforce a static set of syscalls in addition to the syscalls defined in Falco's rules
* (3) Enforce `libsinsp` state set (non-adaptive, not conditioned by rules, but based on PPME event table flags indicating generic sinsp state modifications)
* -> Final set is union of (1), (2) and (3)
*
*/
/* Derive union of rules_ppm_sc_set (all syscalls defined in Falco rules) and enforced syscalls for libsinsp state and declare ppm_sc_of_interest. */
s.ppm_sc_of_interest = inspector->enforce_simple_ppm_sc_set(rules_ppm_sc_set);
/* Derive the diff between the additional syscalls added via libsinsp state enforcement and the syscalls from each Falco rule. */
std::unordered_set<std::string> non_rules_syscalls_names = get_difference_syscalls_names(rules_syscalls_names, inspector->get_syscalls_names(s.ppm_sc_of_interest));
if (non_rules_syscalls_names.size() > 0)
{
falco_logger::log(LOG_INFO, "+(" + std::to_string(non_rules_syscalls_names.size()) + ") syscalls activated (Falco's set of additional syscalls including syscalls needed for state engine): " + concat_syscalls_names(non_rules_syscalls_names) + "\n");
}
std::unordered_set<std::string> final_syscalls_names = inspector->get_syscalls_names(s.ppm_sc_of_interest);
if (final_syscalls_names.size() > 0)
{
falco_logger::log(LOG_INFO, "(" + std::to_string(final_syscalls_names.size()) + ") syscalls in total activated (final set): " + concat_syscalls_names(final_syscalls_names) + "\n");
}
}
void falco::app::actions::activate_interesting_kernel_tracepoints(falco::app::state& s, std::unique_ptr<sinsp>& inspector)
{
/* Kernel tracepoints activation
*
* Activate all tracepoints except `sched_switch` tracepoint since it is highly noisy and not so useful
* for our state/events enrichment.
*/
s.tp_of_interest = inspector->enforce_sinsp_state_tp();
s.tp_of_interest.erase(SCHED_SWITCH);
}
falco::app::run_result falco::app::actions::configure_interesting_sets(falco::app::state& s)
{
std::unique_ptr<sinsp> inspector(new sinsp());
std::unordered_set<std::string> rules_evttypes_names;
falco::app::actions::extract_rules_event_names(s, inspector, rules_evttypes_names); // when reaching this code all evttypes are valid
falco::app::actions::activate_interesting_syscalls(s, inspector, rules_evttypes_names);
falco::app::actions::activate_interesting_events(s, inspector);
falco::app::actions::activate_interesting_kernel_tracepoints(s, inspector);
return run_result::ok();
}

View File

@@ -48,11 +48,6 @@ static void init_syscall_inspector(falco::app::state& s, std::shared_ptr<sinsp>
inspector->set_snaplen(s.options.snaplen);
}
if(!s.options.all_events)
{
configure_interesting_sets(s);
}
inspector->set_hostname_and_port_resolution_mode(false);
}

View File

@@ -24,40 +24,6 @@ limitations under the License.
using namespace falco::app;
using namespace falco::app::actions;
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;
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(s.ppm_event_info_of_interest))
{
event_names.erase(n);
}
if(event_names.empty())
{
return;
}
/* Get the names of the ignored events and print them. */
std::cerr << "Rules match ignored syscall: warning (ignored-evttype):" << std::endl;
std::cerr << "Loaded rules match the following events: ";
bool first = true;
for(const auto& it : event_names)
{
std::cerr << (first ? "" : ", ") << it.c_str();
first = false;
}
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;
}
falco::app::run_result falco::app::actions::load_rules_files(falco::app::state& s)
{
std::string all_rules;
@@ -150,16 +116,6 @@ falco::app::run_result falco::app::actions::load_rules_files(falco::app::state&
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(!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(s);
}
if(s.options.all_events && s.options.modern_bpf)
{
/* Right now the modern BPF probe doesn't support the -A flag, we implemented just

View File

@@ -33,14 +33,7 @@ falco::app::run_result falco::app::actions::print_ignored_events(falco::app::sta
return run_result::ok();
}
/* Fill the application syscall and tracepoint sets.
* The execution will be interrupted after this call so
* we don't care if we populate these sets even if the `-A` flag
* is not set.
*/
configure_interesting_sets(s);
/* Search for all the ignored syscalls. */
/* Search for all the ignored syscalls after having set the syscalls and events of interest in configure_interesting_sets() app action. */
std::unordered_set<uint32_t> all_events;
for (uint32_t j = 0; j < PPM_EVENT_MAX; j++)
{

View File

@@ -74,7 +74,6 @@ falco::app::run_result falco::app::actions::print_syscall_events(falco::app::sta
{
if(s.options.list_syscall_events)
{
configure_interesting_sets(s);
const auto events = get_event_entries(true, s.ppm_event_info_of_interest);
if(s.options.markdown)

View File

@@ -70,6 +70,7 @@ bool falco::app::run(int argc, char** argv, bool& restart, std::string& errstr)
falco::app::actions::daemonize,
falco::app::actions::init_outputs,
falco::app::actions::init_clients,
falco::app::actions::configure_interesting_sets,
falco::app::actions::configure_syscall_buffer_size,
falco::app::actions::start_grpc_server,
falco::app::actions::start_webserver,

View File

@@ -0,0 +1,249 @@
/*
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"
using namespace std::placeholders;
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::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_interesting_sets, this),
std::bind(&application::print_ignored_events, this),
std::bind(&application::print_syscall_events, 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, _1),
#ifndef MINIMAL_BUILD
std::bind(&application::stop_grpc_server, this, _1),
std::bind(&application::stop_webserver, this, _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

@@ -0,0 +1,414 @@
/*
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((istreambuf_iterator<char>(is)),
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();
run_result 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 extract_rules_event_names(std::unique_ptr<sinsp>& inspector, std::unordered_set<std::string>& rules_evttypes_names); // should be called before syscalls and events activations
void activate_interesting_syscalls(std::unique_ptr<sinsp>& inspector, const std::unordered_set<std::string>& rules_evttypes_names);
void activate_interesting_events(std::unique_ptr<sinsp>& inspector); // should be called after calling activate_interesting_syscalls
void activate_interesting_kernel_tracepoints(std::unique_ptr<sinsp>& inspector); // independent of syscalls and events activations in terms of order
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