diff --git a/userspace/falco/application.cpp b/userspace/falco/application.cpp index d9750a20..8296e432 100644 --- a/userspace/falco/application.cpp +++ b/userspace/falco/application.cpp @@ -24,9 +24,40 @@ limitations under the License. #include "application.h" #include "falco_common.h" +using namespace std::placeholders; + namespace falco { namespace app { +std::string application::s_syscall_source = falco_common::syscall_source; +std::string application::s_k8s_audit_source = "k8s_audit"; + +application::run_result::run_result() + : success(true), errstr(""), proceed(true) +{ +} + +application::run_result::~run_result() +{ +} + +application::state::state() + : restart(false), + terminate(false), + reopen_outputs(false), + enabled_sources({application::s_syscall_source, application::s_k8s_audit_source}), + trace_is_scap(true) +{ + config = std::make_shared(); + outputs = std::make_shared(); + engine = std::make_shared(); + inspector = std::make_shared(); +} + +application::state::~state() +{ +} + application::application() : m_initialized(false) { @@ -36,26 +67,129 @@ application::~application() { } -cmdline_options &application::options() +void application::terminate() { - if(!m_initialized) + if(m_state != nullptr) { - throw falco_exception("App init() not called yet"); + m_state->terminate = true; } +} - return m_cmdline_options; +void application::reopen_outputs() +{ + if(m_state != nullptr) + { + m_state->reopen_outputs = true; + } +} + +void application::restart() +{ + if(m_state != nullptr) + { + m_state->restart = true; + } } bool application::init(int argc, char **argv, std::string &errstr) { - if(!m_cmdline_options.parse(argc, argv, 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> run_steps = { + std::bind(&application::print_help, this), + std::bind(&application::print_version, this), + std::bind(&application::create_signal_handlers, this), + std::bind(&application::load_config, this), + std::bind(&application::init_inspector, this), + std::bind(&application::init_falco_engine, this), + std::bind(&application::load_plugins, this), + std::bind(&application::list_fields, this), + std::bind(&application::list_plugins, this), + std::bind(&application::load_rules_files, this), + std::bind(&application::print_ignored_events, this), + std::bind(&application::print_support, this), + std::bind(&application::validate_rules_files, this), + std::bind(&application::daemonize, this), + std::bind(&application::init_outputs, this), + std::bind(&application::open_inspector, 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> teardown_steps = { + std::bind(&application::close_inspector, this, _1), + 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 = m_state->restart; + + return res.success; +} + }; // namespace app }; // namespace falco diff --git a/userspace/falco/application.h b/userspace/falco/application.h index 44486668..4e022c08 100644 --- a/userspace/falco/application.h +++ b/userspace/falco/application.h @@ -14,19 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -// The falco "app" will eventually replace the monolithic code in -// falco.cpp. We expect it will be responsible for the following: -// - Parsing/validating command line options -// - Parsing/validating falco config -// - Initialize prerequisites (inspector, falco engine, webserver, etc) -// - Loading plugins -// - Loading/validating rules -// - Command/subcommand execution (e.g. --list/--list-fields, or -// nothing specified to run "main" loop) - -// For now, it is only responsible for command line options. #pragma once +#include "configuration.h" +#ifndef MINIMAL_BUILD +#include "grpc_server.h" +#include "webserver.h" +#endif + #include "app_cmdline_options.h" #include @@ -36,16 +31,150 @@ namespace app { class application { public: - application(); virtual ~application(); - cmdline_options &options(); + // These are only used in signal handlers. Other than there, + // the control flow of the application should not be changed + // from the outside. + void terminate(); + void reopen_outputs(); + void restart(); + bool init(int argc, char **argv, std::string &errstr); -private: + // 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); - cmdline_options m_cmdline_options; +private: + static std::string s_syscall_source; + static std::string s_k8s_audit_source; + + // 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. + class state { + public: + state(); + virtual ~state(); + + bool restart; + bool terminate; + bool reopen_outputs; + + std::shared_ptr config; + std::shared_ptr outputs; + std::shared_ptr engine; + std::shared_ptr inspector; + std::set enabled_sources; + + // The event sources that correspond to "syscalls" and + // "k8s_audit events". + std::size_t syscall_source_idx; + std::size_t k8s_audit_source_idx; + + // The event source actually used to process events in + // process_events(). Will generally be + // syscall_source_idx, or a plugin index if plugins + // are loaded. + std::size_t event_source_idx; + + std::list plugin_infos; + + // All filterchecks created by plugins go in this + // list. If we ever support multiple event sources at + // the same time, this, and the factories created in + // init_inspector/load_plugins, will have to be a map + // from event source to filtercheck list. + filter_check_list plugin_filter_checks; + + std::map required_engine_versions; + + std::string cmdline; + + bool trace_is_scap; +#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 { + + run_result(); + virtual ~run_result(); + + // 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; + }; + + // 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 daemonize(); + run_result init_falco_engine(); + run_result init_inspector(); + run_result init_outputs(); + run_result list_fields(); + run_result list_plugins(); + run_result load_config(); + run_result load_plugins(); + run_result load_rules_files(); + run_result open_inspector(); + run_result print_help(); + run_result print_ignored_events(); + run_result print_support(); + run_result print_version(); + run_result process_events(); +#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 + bool create_handler(int sig, void (*func)(int), run_result &ret); + void configure_output_format(); + void check_for_ignored_events(); + void print_all_ignored_events(); + void read_k8s_audit_trace_file(string &trace_filename); + uint64_t do_inspect(syscall_evt_drop_mgr &sdropmgr, + uint64_t duration_to_tot_ns, + run_result &result); + + // This could probably become a direct object once lua is + // removed from falco. Currently, creating any global + // application object results in a crash in + // falco_common::init(), as it loads all lua modules. + std::unique_ptr m_state; + cmdline_options m_options; bool m_initialized; };