watch config file

This commit is contained in:
PancakeTAS 2025-07-16 21:16:03 +02:00 committed by Pancake
parent a5ea1893ab
commit 7a460c7faf
2 changed files with 81 additions and 13 deletions

View file

@ -1,21 +1,30 @@
#include "config/config.hpp" #include "config/config.hpp"
#include "common/exception.hpp" #include "common/exception.hpp"
#include <filesystem> #include <linux/limits.h>
#include <optional> #include <sys/inotify.h>
#include <toml11/find.hpp> #include <toml11/find.hpp>
#include <toml11/parser.hpp> #include <toml11/parser.hpp>
#include <toml.hpp> #include <toml.hpp>
#include <unistd.h>
#include <unordered_map> #include <unordered_map>
#include <string_view> #include <string_view>
#include <filesystem>
#include <exception> #include <exception>
#include <stdexcept> #include <stdexcept>
#include <iostream>
#include <optional>
#include <cstddef> #include <cstddef>
#include <utility> #include <utility>
#include <cstring>
#include <chrono>
#include <thread>
#include <cerrno>
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <string> #include <string>
#include <array>
using namespace Config; using namespace Config;
@ -25,15 +34,17 @@ namespace {
} }
bool Config::loadAndWatchConfig(const std::string& file) { bool Config::loadAndWatchConfig(const std::string& file) {
if (!std::filesystem::exists(file))
return false;
// parse config file // parse config file
toml::value toml{}; std::optional<toml::value> parsed;
if (std::filesystem::exists(file)) { try {
try { parsed.emplace(toml::parse(file));
toml = toml::parse(file); } catch (const std::exception& e) {
} catch (const std::exception& e) { throw LSFG::rethrowable_error("Unable to parse configuration file", e);
throw LSFG::rethrowable_error("Unable to parse configuration file", e);
}
} }
auto& toml = *parsed;
// parse global configuration // parse global configuration
auto& global = globalConf; auto& global = globalConf;
@ -81,9 +92,66 @@ bool Config::loadAndWatchConfig(const std::string& file) {
} }
// prepare config watcher // prepare config watcher
// (TODO) std::thread([file = file, valid = global.valid]() {
try {
const int fd = inotify_init();
if (fd < 0)
throw std::runtime_error("Failed to initialize inotify\n"
"- " + std::string(strerror(errno)));
return false; const int wd = inotify_add_watch(fd, file.c_str(), IN_MODIFY | IN_CLOSE_WRITE);
if (wd < 0) {
close(fd);
throw std::runtime_error("Failed to add inotify watch for " + file + "\n"
"- " + std::string(strerror(errno)));
}
// watch for changes
std::optional<std::chrono::steady_clock::time_point> discard_until;
std::array<char, (sizeof(inotify_event) + NAME_MAX + 1) * 20> buffer{};
while (true) {
const ssize_t len = read(fd, buffer.data(), buffer.size());
if (len <= 0 && errno != EINTR) {
inotify_rm_watch(fd, wd);
close(fd);
throw std::runtime_error("Error reading inotify event\n"
"- " + std::string(strerror(errno)));
}
size_t i{};
while (std::cmp_less(i, len)) {
auto* event = reinterpret_cast<inotify_event*>(&buffer.at(i));
i += sizeof(inotify_event) + event->len;
if (event->len <= 0)
continue;
// stall a bit, then mark as invalid
discard_until.emplace(std::chrono::steady_clock::now()
+ std::chrono::milliseconds(500));
}
auto now = std::chrono::steady_clock::now();
if (discard_until.has_value() && now > *discard_until) {
discard_until.reset();
// mark config as invalid
valid->store(false, std::memory_order_release);
// and wait until it has been marked as valid again
while (!valid->load(std::memory_order_acquire))
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
}
} catch (const std::exception& e) {
std::cerr << "lsfg-vk: Error in config watcher thread:\n";
std::cerr << "- " << e.what() << '\n';
}
}).detach();
return true;
} }
Configuration Config::getConfig(std::string_view name) { Configuration Config::getConfig(std::string_view name) {

View file

@ -67,7 +67,7 @@ namespace {
try { try {
Config::loadAndWatchConfig(file); Config::loadAndWatchConfig(file);
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "lsfg-vk: An error occured while trying to parse the configuration, exiting:" << '\n'; std::cerr << "lsfg-vk: An error occured while trying to parse the configuration, exiting:\n";
std::cerr << "- " << e.what() << '\n'; std::cerr << "- " << e.what() << '\n';
exit(0); exit(0);
} }
@ -77,7 +77,7 @@ namespace {
try { try {
conf = Config::getConfig(name); conf = Config::getConfig(name);
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "lsfg-vk: The configuration for " << name << " is invalid, exiting." << '\n'; std::cerr << "lsfg-vk: The configuration for " << name << " is invalid, exiting.\n";
std::cerr << e.what() << '\n'; std::cerr << e.what() << '\n';
exit(0); exit(0);
} }