mirror of
https://github.com/falcosecurity/falco.git
synced 2025-09-16 14:58:31 +00:00
new(userspace/falco): add utility for handling hot app restarts
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
This commit is contained in:
@@ -17,6 +17,7 @@ set(
|
|||||||
FALCO_SOURCES
|
FALCO_SOURCES
|
||||||
app/app.cpp
|
app/app.cpp
|
||||||
app/options.cpp
|
app/options.cpp
|
||||||
|
app/restart_handler.cpp
|
||||||
app/actions/helpers_generic.cpp
|
app/actions/helpers_generic.cpp
|
||||||
app/actions/helpers_inspector.cpp
|
app/actions/helpers_inspector.cpp
|
||||||
app/actions/configure_interesting_sets.cpp
|
app/actions/configure_interesting_sets.cpp
|
||||||
|
148
userspace/falco/app/restart_handler.cpp
Normal file
148
userspace/falco/app/restart_handler.cpp
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
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 "restart_handler.h"
|
||||||
|
#include "signals.h"
|
||||||
|
#include "../logger.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/inotify.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
|
||||||
|
void falco::app::restart_handler::trigger()
|
||||||
|
{
|
||||||
|
m_forced.store(true, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool falco::app::restart_handler::start(std::string& err)
|
||||||
|
{
|
||||||
|
m_inotify_fd = inotify_init();
|
||||||
|
if (m_inotify_fd < 0)
|
||||||
|
{
|
||||||
|
err = "could not initialize inotify handler";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& f : m_watched_files)
|
||||||
|
{
|
||||||
|
auto wd = inotify_add_watch(m_inotify_fd, f.c_str(), IN_CLOSE_WRITE);
|
||||||
|
if (wd < 0)
|
||||||
|
{
|
||||||
|
err = "could not watch file: " + f;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
falco_logger::log(LOG_DEBUG, "Watching file '" + f +"'\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &f : m_watched_dirs)
|
||||||
|
{
|
||||||
|
auto wd = inotify_add_watch(m_inotify_fd, f.c_str(), IN_CREATE | IN_DELETE);
|
||||||
|
if (wd < 0)
|
||||||
|
{
|
||||||
|
err = "could not watch directory: " + f;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
falco_logger::log(LOG_DEBUG, "Watching directory '" + f +"'\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// launch the watcher thread
|
||||||
|
m_watcher = std::thread(&falco::app::restart_handler::watcher_loop, this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void falco::app::restart_handler::stop()
|
||||||
|
{
|
||||||
|
m_stop.store(true, std::memory_order_release);
|
||||||
|
if (m_watcher.joinable())
|
||||||
|
{
|
||||||
|
m_watcher.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void falco::app::restart_handler::watcher_loop() noexcept
|
||||||
|
{
|
||||||
|
if (fcntl(m_inotify_fd, F_SETOWN, gettid()) < 0)
|
||||||
|
{
|
||||||
|
falco_logger::log(LOG_ERR, "Failed setting owner on inotify handler");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd_set set;
|
||||||
|
bool forced = false;
|
||||||
|
bool should_restart = false;
|
||||||
|
struct timeval timeout;
|
||||||
|
uint8_t buf[(10 * (sizeof(struct inotify_event) + NAME_MAX + 1))];
|
||||||
|
while (!m_stop.load(std::memory_order_acquire))
|
||||||
|
{
|
||||||
|
// wait for inotify events with a certain timeout
|
||||||
|
timeout.tv_sec = 0;
|
||||||
|
timeout.tv_usec = 100000;
|
||||||
|
FD_ZERO(&set);
|
||||||
|
FD_SET(m_inotify_fd, &set);
|
||||||
|
auto rv = select(m_inotify_fd + 1, &set, NULL, NULL, &timeout);
|
||||||
|
if (rv < 0)
|
||||||
|
{
|
||||||
|
falco_logger::log(LOG_ERR, "Failed select in inotify watcher");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if there's been a forced restart request
|
||||||
|
forced = m_forced.load(std::memory_order_acquire);
|
||||||
|
m_forced.store(false, std::memory_order_release);
|
||||||
|
|
||||||
|
if (rv > 0 || forced)
|
||||||
|
{
|
||||||
|
// if new inotify events have been received during the previous
|
||||||
|
// dry run, even if the dry run was successful, we dismiss
|
||||||
|
// the restart attempt and perform an additional dry-run for
|
||||||
|
// safety purpose (the new inotify events may be related to
|
||||||
|
// bad config/rules files changes).
|
||||||
|
should_restart = false;
|
||||||
|
|
||||||
|
// if there's date on the inotify fd, consume it
|
||||||
|
if (rv > 0)
|
||||||
|
{
|
||||||
|
auto n = read(m_inotify_fd, buf, sizeof(buf));
|
||||||
|
if (n == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (n < 0)
|
||||||
|
{
|
||||||
|
falco_logger::log(LOG_ERR, "Failed read in inotify watcher");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform a check run check and attempt restarting
|
||||||
|
if (m_on_check())
|
||||||
|
{
|
||||||
|
should_restart = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the previous dry run was successful, and no new
|
||||||
|
// inotify events have been received during the dry run,
|
||||||
|
// then we trigger the restarting signal and quit
|
||||||
|
if (should_restart)
|
||||||
|
{
|
||||||
|
// todo(jasondellaluce): make this a callback too maybe?
|
||||||
|
g_restart_signal.trigger();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
88
userspace/falco/app/restart_handler.h
Normal file
88
userspace/falco/app/restart_handler.h
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
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 <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace falco
|
||||||
|
{
|
||||||
|
namespace app
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A thread-safe helper for handling hot-reload application restarts.
|
||||||
|
*/
|
||||||
|
class restart_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief A function that performs safety checks before confirming
|
||||||
|
* a triggered application restart. Returns true if the application
|
||||||
|
* can safely be restarted.
|
||||||
|
*/
|
||||||
|
using on_check_t = std::function<bool()>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A list of files or directories paths to watch.
|
||||||
|
*/
|
||||||
|
using watch_list_t = std::vector<std::string>;
|
||||||
|
|
||||||
|
restart_handler(
|
||||||
|
on_check_t on_check,
|
||||||
|
const watch_list_t& watch_files = {},
|
||||||
|
const watch_list_t& watch_dirs = {})
|
||||||
|
: m_inotify_fd(-1),
|
||||||
|
m_stop(false),
|
||||||
|
m_forced(false),
|
||||||
|
m_on_check(on_check),
|
||||||
|
m_watched_dirs(watch_dirs),
|
||||||
|
m_watched_files(watch_files) { }
|
||||||
|
|
||||||
|
virtual ~restart_handler()
|
||||||
|
{
|
||||||
|
close(m_inotify_fd);
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
restart_handler(restart_handler&&) = default;
|
||||||
|
restart_handler& operator = (restart_handler&&) = default;
|
||||||
|
restart_handler(const restart_handler&) = delete;
|
||||||
|
restart_handler& operator = (const restart_handler&) = delete;
|
||||||
|
|
||||||
|
bool start(std::string& err);
|
||||||
|
void stop();
|
||||||
|
void trigger();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void watcher_loop() noexcept;
|
||||||
|
|
||||||
|
int m_inotify_fd;
|
||||||
|
std::thread m_watcher;
|
||||||
|
std::atomic<bool> m_stop;
|
||||||
|
std::atomic<bool> m_forced;
|
||||||
|
on_check_t m_on_check;
|
||||||
|
watch_list_t m_watched_dirs;
|
||||||
|
watch_list_t m_watched_files;
|
||||||
|
};
|
||||||
|
}; // namespace app
|
||||||
|
}; // namespace falco
|
Reference in New Issue
Block a user