mirror of
https://github.com/falcosecurity/falco.git
synced 2025-07-10 13:13:40 +00:00
refactor(userspace/falco): add an ad-hoc concurrent object for signal handlers
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
This commit is contained in:
parent
5470a88b61
commit
eb3bf7260d
@ -16,7 +16,6 @@ configure_file(config_falco.h.in config_falco.h)
|
|||||||
set(
|
set(
|
||||||
FALCO_SOURCES
|
FALCO_SOURCES
|
||||||
app/app.cpp
|
app/app.cpp
|
||||||
app/signals.cpp
|
|
||||||
app/options.cpp
|
app/options.cpp
|
||||||
app/actions/helpers_generic.cpp
|
app/actions/helpers_generic.cpp
|
||||||
app/actions/helpers_inspector.cpp
|
app/actions/helpers_inspector.cpp
|
||||||
|
@ -19,6 +19,10 @@ limitations under the License.
|
|||||||
#include "signals.h"
|
#include "signals.h"
|
||||||
#include "actions/actions.h"
|
#include "actions/actions.h"
|
||||||
|
|
||||||
|
falco::atomic_signal_handler falco::app::g_terminate_signal;
|
||||||
|
falco::atomic_signal_handler falco::app::g_restart_signal;
|
||||||
|
falco::atomic_signal_handler falco::app::g_reopen_outputs_signal;
|
||||||
|
|
||||||
using app_action = std::function<falco::app::run_result(falco::app::state&)>;
|
using app_action = std::function<falco::app::run_result(falco::app::state&)>;
|
||||||
|
|
||||||
bool falco::app::run(int argc, char** argv, bool& restart, std::string& errstr)
|
bool falco::app::run(int argc, char** argv, bool& restart, std::string& errstr)
|
||||||
@ -99,7 +103,7 @@ bool falco::app::run(int argc, char** argv, bool& restart, std::string& errstr)
|
|||||||
errstr = res.errstr;
|
errstr = res.errstr;
|
||||||
}
|
}
|
||||||
|
|
||||||
restart = falco::app::should_restart();
|
restart = falco::app::g_restart_signal.triggered();
|
||||||
|
|
||||||
return res.success;
|
return res.success;
|
||||||
}
|
}
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
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 "signals.h"
|
|
||||||
#include "../logger.h"
|
|
||||||
#include "../falco_outputs.h"
|
|
||||||
|
|
||||||
std::atomic<int> falco::app::g_terminate(APP_SIGNAL_NOT_SET);
|
|
||||||
std::atomic<int> falco::app::g_restart(APP_SIGNAL_NOT_SET);
|
|
||||||
std::atomic<int> falco::app::g_reopen_outputs(APP_SIGNAL_NOT_SET);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void falco::app::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 falco::app::reopen_outputs(std::function<void()> on_reopen, 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");
|
|
||||||
}
|
|
||||||
on_reopen();
|
|
||||||
falco::app::g_reopen_outputs.store(APP_SIGNAL_NOT_SET);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void falco::app::restart(bool verbose)
|
|
||||||
{
|
|
||||||
if (should_take_action_to_signal(falco::app::g_restart))
|
|
||||||
{
|
|
||||||
if (verbose)
|
|
||||||
{
|
|
||||||
falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,41 +16,14 @@ limitations under the License.
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <atomic>
|
#include "../atomic_signal_handler.h"
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#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 falco {
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
// todo(jasondellaluce): hide this into a class
|
extern atomic_signal_handler g_terminate_signal;
|
||||||
extern std::atomic<int> g_terminate;
|
extern atomic_signal_handler g_restart_signal;
|
||||||
extern std::atomic<int> g_restart;
|
extern atomic_signal_handler g_reopen_outputs_signal;
|
||||||
extern std::atomic<int> g_reopen_outputs;
|
|
||||||
|
|
||||||
void terminate(bool verbose=true);
|
|
||||||
|
|
||||||
void restart(bool verbose=true);
|
|
||||||
|
|
||||||
void reopen_outputs(std::function<void()> on_reopen, 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}; // namespace app
|
}; // namespace app
|
||||||
}; // namespace falco
|
}; // namespace falco
|
||||||
|
137
userspace/falco/atomic_signal_handler.h
Normal file
137
userspace/falco/atomic_signal_handler.h
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
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 <mutex>
|
||||||
|
#include <atomic>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace falco
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief A concurrent object that helps properly handling
|
||||||
|
* system signals from multiple threads.
|
||||||
|
*/
|
||||||
|
class atomic_signal_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
atomic_signal_handler(): m_triggered(false), m_handled(false) { }
|
||||||
|
atomic_signal_handler(atomic_signal_handler&&) = default;
|
||||||
|
atomic_signal_handler& operator = (atomic_signal_handler&&) = default;
|
||||||
|
atomic_signal_handler(const atomic_signal_handler&) = delete;
|
||||||
|
atomic_signal_handler& operator = (const atomic_signal_handler&) = delete;
|
||||||
|
~atomic_signal_handler() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if the underlying atomic implementation
|
||||||
|
* is lock-free as per C++ standard semantics.
|
||||||
|
*/
|
||||||
|
inline bool is_lock_free() const
|
||||||
|
{
|
||||||
|
return m_handled.is_lock_free() && m_triggered.is_lock_free();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resets the handler to its initial state, which is
|
||||||
|
* non-triggered and non-handled.
|
||||||
|
*/
|
||||||
|
inline void reset()
|
||||||
|
{
|
||||||
|
m_handled.store(false, std::memory_order_seq_cst);
|
||||||
|
m_triggered.store(false, std::memory_order_seq_cst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if the signal has been triggered.
|
||||||
|
*/
|
||||||
|
inline bool triggered() const
|
||||||
|
{
|
||||||
|
return m_triggered.load(std::memory_order_seq_cst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if the signal has been handled.
|
||||||
|
*/
|
||||||
|
inline bool handled() const
|
||||||
|
{
|
||||||
|
return m_handled.load(std::memory_order_seq_cst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Triggers the signal. Must generally be invoked from
|
||||||
|
* within an actual signal handler (created with the `signal`
|
||||||
|
* system call). Can eventually be invoked for "faking"
|
||||||
|
* the triggering of a signal programmatically.
|
||||||
|
*/
|
||||||
|
inline void trigger()
|
||||||
|
{
|
||||||
|
m_triggered.store(true, std::memory_order_seq_cst);
|
||||||
|
m_handled.store(false, std::memory_order_seq_cst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief If a signal is triggered, performs an handler action.
|
||||||
|
* The action function will be invoked exactly once among all the
|
||||||
|
* simultaneus calls. The action will not be performed if the
|
||||||
|
* signal is not triggered, or if the triggered has already been
|
||||||
|
* handled. When an action is being performed, all the simultaneus
|
||||||
|
* callers will wait and be blocked up until its execution is finished.
|
||||||
|
* If the handler action throws an exception, it will be considered
|
||||||
|
* performed. After the first handler has been performed, every
|
||||||
|
* other invocation of handle() will be skipped and return false
|
||||||
|
* up until the next invocation of reset().
|
||||||
|
*
|
||||||
|
* @param f The action to perform.
|
||||||
|
* @return true If the action has been performed.
|
||||||
|
* @return false If the action has not been performed.
|
||||||
|
*/
|
||||||
|
inline bool handle(std::function<void()> f)
|
||||||
|
{
|
||||||
|
if (triggered() && !handled())
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m_mtx);
|
||||||
|
if (!handled())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
f();
|
||||||
|
// note: the action may have forcely resetted
|
||||||
|
// the signal handler, so we don't want to create
|
||||||
|
// an inconsistent state
|
||||||
|
if (triggered())
|
||||||
|
{
|
||||||
|
m_handled.store(true, std::memory_order_seq_cst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
if (triggered())
|
||||||
|
{
|
||||||
|
m_handled.store(true, std::memory_order_seq_cst);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex m_mtx;
|
||||||
|
std::atomic<bool> m_triggered;
|
||||||
|
std::atomic<bool> m_handled;
|
||||||
|
};
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user