refactor(userspace/falco): update actions to use new hot restarter utility with dry-run safetyc checks

Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
This commit is contained in:
Jason Dellaluce 2023-02-22 15:47:54 +00:00 committed by poiana
parent 561022ebb6
commit cd155ed6f5
6 changed files with 93 additions and 95 deletions

View File

@ -23,7 +23,6 @@ namespace falco {
namespace app { namespace app {
namespace actions { 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_interesting_sets(falco::app::state& s);
falco::app::run_result configure_syscall_buffer_size(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_requested_paths(falco::app::state& s);

View File

@ -16,23 +16,15 @@ limitations under the License.
#include <functional> #include <functional>
#include <string.h>
#include <signal.h>
#include <sys/inotify.h>
#include <fcntl.h>
#include "actions.h" #include "actions.h"
#include "../app.h"
#include "../signals.h" #include "../signals.h"
using namespace falco::app; using namespace falco::app;
using namespace falco::app::actions; using namespace falco::app::actions;
// This is initially set to a dummy application. When static std::shared_ptr<falco::app::restart_handler> s_restarter;
// create_signal_handlers is called, it will be rebound to the
// provided application, and in unregister_signal_handlers it will be
// rebound back to the dummy application.
static int inot_fd;
static void terminate_signal_handler(int signal) static void terminate_signal_handler(int signal)
{ {
@ -46,7 +38,10 @@ static void reopen_outputs_signal_handler(int signal)
static void restart_signal_handler(int signal) static void restart_signal_handler(int signal)
{ {
falco::app::g_restart_signal.trigger(); if (s_restarter != nullptr)
{
s_restarter->trigger();
}
} }
bool create_handler(int sig, void (*func)(int), run_result &ret) bool create_handler(int sig, void (*func)(int), run_result &ret)
@ -94,89 +89,65 @@ falco::app::run_result falco::app::actions::create_signal_handlers(falco::app::s
! create_handler(SIGUSR1, ::reopen_outputs_signal_handler, ret) || ! create_handler(SIGUSR1, ::reopen_outputs_signal_handler, ret) ||
! create_handler(SIGHUP, ::restart_signal_handler, ret)) ! create_handler(SIGHUP, ::restart_signal_handler, ret))
{ {
// we use the if just to make sure we return at the first failed statement return ret;
} }
falco::app::restart_handler::watch_list_t files_to_watch;
falco::app::restart_handler::watch_list_t dirs_to_watch;
if (s.config->m_watch_config_files)
{
files_to_watch.push_back(s.options.conf_filename);
files_to_watch.insert(
files_to_watch.end(),
s.config->m_loaded_rules_filenames.begin(),
s.config->m_loaded_rules_filenames.end());
dirs_to_watch.insert(
dirs_to_watch.end(),
s.config->m_loaded_rules_folders.begin(),
s.config->m_loaded_rules_folders.end());
}
s.restarter = std::make_shared<falco::app::restart_handler>([&s]{
bool tmp = false;
bool success = false;
std::string err;
falco::app::state tmp_state(s.cmdline, s.options);
tmp_state.options.dry_run = true;
try
{
success = falco::app::run(tmp_state, tmp, err);
}
catch (std::exception& e)
{
err = e.what();
}
catch (...)
{
err = "unknown error";
}
if (!success && s.outputs != nullptr)
{
std::string rule = "Falco internal: hot restart failure";
std::string msg = rule + ": " + err;
std::map<std::string, std::string> o = {};
auto now = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
s.outputs->handle_msg(now, falco_common::PRIORITY_CRITICAL, msg, rule, o);
}
return success;
}, files_to_watch, dirs_to_watch);
ret = run_result::ok();
ret.success = s.restarter->start(ret.errstr);
ret.proceed = ret.success;
if (ret.success)
{
s_restarter = s.restarter;
}
return ret; return ret;
} }
falco::app::run_result falco::app::actions::attach_inotify_signals(falco::app::state& s)
{
if (s.options.dry_run)
{
falco_logger::log(LOG_DEBUG, "Skipping attaching inotify signals in dry-run\n");
return run_result::ok();
}
if (s.config->m_watch_config_files)
{
inot_fd = inotify_init();
if (inot_fd == -1)
{
return run_result::fatal("Could not create inotify handler");
}
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = restart_signal_handler;
if (sigaction(SIGIO, &sa, NULL) == -1)
{
return run_result::fatal("Failed to link SIGIO to inotify handler");
}
/* Set owner process that is to receive "I/O possible" signal */
if (fcntl(inot_fd, F_SETOWN, getpid()) == -1)
{
return run_result::fatal("Failed to setting owner on inotify handler");
}
/*
* Enable "I/O possible" signaling and make I/O nonblocking
* for file descriptor
*/
int flags = fcntl(inot_fd, F_GETFL);
if (fcntl(inot_fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK) == -1)
{
return run_result::fatal("Failed to setting flags on inotify handler");
}
// Watch conf file
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 " + s.options.conf_filename +"\n");
// Watch rules files
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)
{
return run_result::fatal("Failed to watch rule file: " + rule);
}
falco_logger::log(LOG_DEBUG, "Watching " + rule +"\n");
}
// Watch specified rules folders, if any:
// any newly created/removed file within the folder
// will trigger a Falco restart.
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);
if (wd == -1)
{
return run_result::fatal("Failed to watch rule folder: " + fld);
}
falco_logger::log(LOG_DEBUG, "Watching " + fld +" folder\n");
}
}
return run_result::ok();
}
falco::app::run_result falco::app::actions::unregister_signal_handlers(falco::app::state& s) falco::app::run_result falco::app::actions::unregister_signal_handlers(falco::app::state& s)
{ {
if (s.options.dry_run) if (s.options.dry_run)
@ -185,8 +156,13 @@ falco::app::run_result falco::app::actions::unregister_signal_handlers(falco::ap
return run_result::ok(); return run_result::ok();
} }
s_restarter = nullptr;
if (s.restarter != nullptr)
{
s.restarter->stop();
}
run_result ret; run_result ret;
close(inot_fd);
if(! create_handler(SIGINT, SIG_DFL, ret) || if(! create_handler(SIGINT, SIG_DFL, ret) ||
! create_handler(SIGTERM, SIG_DFL, ret) || ! create_handler(SIGTERM, SIG_DFL, ret) ||
! create_handler(SIGUSR1, SIG_DFL, ret) || ! create_handler(SIGUSR1, SIG_DFL, ret) ||

View File

@ -208,8 +208,9 @@ static falco::app::run_result do_inspect(
} }
else if(falco::app::g_restart_signal.triggered()) else if(falco::app::g_restart_signal.triggered())
{ {
falco::app::g_restart_signal.handle([&](){ falco::app::g_restart_signal.handle([&s](){
falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n"); falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n");
s.restart.store(true);
}); });
break; break;
} }

View File

@ -40,7 +40,11 @@ bool falco::app::run(int argc, char** argv, bool& restart, std::string& errstr)
} }
s.cmdline += *arg; s.cmdline += *arg;
} }
return falco::app::run(s, restart, errstr);
}
bool falco::app::run(falco::app::state& s, bool& restart, std::string& errstr)
{
// The order here is the order in which the methods will be // The order here is the order in which the methods will be
// called. Before changing the order, ensure that all // called. Before changing the order, ensure that all
// dependencies are honored (e.g. don't process events before // dependencies are honored (e.g. don't process events before
@ -64,11 +68,10 @@ bool falco::app::run(int argc, char** argv, bool& restart, std::string& errstr)
falco::app::actions::validate_rules_files, falco::app::actions::validate_rules_files,
falco::app::actions::load_rules_files, falco::app::actions::load_rules_files,
falco::app::actions::print_support, falco::app::actions::print_support,
falco::app::actions::init_outputs,
falco::app::actions::create_signal_handlers, falco::app::actions::create_signal_handlers,
falco::app::actions::attach_inotify_signals,
falco::app::actions::create_requested_paths, falco::app::actions::create_requested_paths,
falco::app::actions::daemonize, falco::app::actions::daemonize,
falco::app::actions::init_outputs,
falco::app::actions::init_clients, falco::app::actions::init_clients,
falco::app::actions::configure_interesting_sets, falco::app::actions::configure_interesting_sets,
falco::app::actions::configure_syscall_buffer_size, falco::app::actions::configure_syscall_buffer_size,
@ -104,7 +107,7 @@ bool falco::app::run(int argc, char** argv, bool& restart, std::string& errstr)
errstr = res.errstr; errstr = res.errstr;
} }
restart = falco::app::g_restart_signal.triggered(); restart = s.restart;
return res.success; return res.success;
} }

View File

@ -16,12 +16,15 @@ limitations under the License.
#pragma once #pragma once
#include "state.h"
#include <string> #include <string>
namespace falco { namespace falco {
namespace app { namespace app {
bool run(int argc, char** argv, bool& restart, std::string& errstr); bool run(int argc, char** argv, bool& restart, std::string& errstr);
bool run(falco::app::state& s, bool& restart, std::string& errstr);
}; // namespace app }; // namespace app
}; // namespace falco }; // namespace falco

View File

@ -19,6 +19,7 @@ limitations under the License.
#include "indexed_vector.h" #include "indexed_vector.h"
#include "options.h" #include "options.h"
#include "restart_handler.h"
#include "../configuration.h" #include "../configuration.h"
#include "../stats_writer.h" #include "../stats_writer.h"
#ifndef MINIMAL_BUILD #ifndef MINIMAL_BUILD
@ -30,6 +31,7 @@ limitations under the License.
#include <string> #include <string>
#include <memory> #include <memory>
#include <atomic>
#include <unordered_set> #include <unordered_set>
namespace falco { namespace falco {
@ -59,6 +61,7 @@ struct state
}; };
state(): state():
restart(false),
loaded_sources(), loaded_sources(),
enabled_sources(), enabled_sources(),
source_infos(), source_infos(),
@ -71,7 +74,15 @@ struct state
engine = std::make_shared<falco_engine>(); engine = std::make_shared<falco_engine>();
offline_inspector = std::make_shared<sinsp>(); offline_inspector = std::make_shared<sinsp>();
outputs = nullptr; outputs = nullptr;
restarter = nullptr;
} }
state(const std::string& cmd, const falco::app::options& opts): state()
{
cmdline = cmd;
options = opts;
}
~state() = default; ~state() = default;
state(state&&) = default; state(state&&) = default;
state& operator = (state&&) = default; state& operator = (state&&) = default;
@ -80,6 +91,8 @@ struct state
std::string cmdline; std::string cmdline;
falco::app::options options; falco::app::options options;
std::atomic<bool> restart;
std::shared_ptr<falco_configuration> config; std::shared_ptr<falco_configuration> config;
std::shared_ptr<falco_outputs> outputs; std::shared_ptr<falco_outputs> outputs;
@ -114,6 +127,9 @@ struct state
// Dimension of the syscall buffer in bytes. // Dimension of the syscall buffer in bytes.
uint64_t syscall_buffer_bytes_size; uint64_t syscall_buffer_bytes_size;
// Helper responsible for watching of handling hot application restarts
std::shared_ptr<restart_handler> restarter;
#ifndef MINIMAL_BUILD #ifndef MINIMAL_BUILD
falco::grpc::server grpc_server; falco::grpc::server grpc_server;
std::thread grpc_server_thread; std::thread grpc_server_thread;