Deamon management
This commit is contained in:
109
daemon/config_watch.cpp
Normal file
109
daemon/config_watch.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
// gKeyMapper daemon - watches the mappings file for changes (inotify).
|
||||
|
||||
#include "config_watch.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace gkeymapper::daemon {
|
||||
|
||||
namespace {
|
||||
|
||||
// IN_CLOSE_WRITE is the useful one (the writer closed the file); IN_MODIFY
|
||||
// covers writers that hold the file open, IN_MOVED_TO/CREATE the atomic save.
|
||||
constexpr uint32_t kMask = IN_CLOSE_WRITE | IN_MODIFY | IN_MOVED_TO | IN_CREATE;
|
||||
|
||||
std::string directory_of(const std::string& path) {
|
||||
size_t slash = path.find_last_of('/');
|
||||
if (slash == std::string::npos) {
|
||||
return ".";
|
||||
}
|
||||
return slash == 0 ? std::string("/") : path.substr(0, slash);
|
||||
}
|
||||
|
||||
std::string basename_of(const std::string& path) {
|
||||
size_t slash = path.find_last_of('/');
|
||||
return slash == std::string::npos ? path : path.substr(slash + 1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ConfigWatcher::~ConfigWatcher() {
|
||||
if (fd_ >= 0) {
|
||||
::close(fd_);
|
||||
}
|
||||
}
|
||||
|
||||
bool ConfigWatcher::init(const std::string& path) {
|
||||
name_ = basename_of(path);
|
||||
directory_ = directory_of(path);
|
||||
|
||||
fd_ = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||
if (fd_ < 0) {
|
||||
std::fprintf(stderr,
|
||||
"gkeymapper-daemon: inotify unavailable (%s); "
|
||||
"use SIGHUP to reload\n",
|
||||
std::strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
int watch = inotify_add_watch(fd_, directory_.c_str(), kMask);
|
||||
if (watch < 0 && errno == ENOENT) {
|
||||
// First start before anything was ever saved: create the directory the
|
||||
// GUI would create and try once more.
|
||||
::mkdir(directory_.c_str(), 0755);
|
||||
watch = inotify_add_watch(fd_, directory_.c_str(), kMask);
|
||||
}
|
||||
if (watch < 0) {
|
||||
std::fprintf(stderr,
|
||||
"gkeymapper-daemon: cannot watch %s (%s); "
|
||||
"use SIGHUP to reload\n",
|
||||
directory_.c_str(), std::strerror(errno));
|
||||
::close(fd_);
|
||||
fd_ = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::fprintf(stderr, "gkeymapper-daemon: watching %s for changes\n",
|
||||
directory_.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConfigWatcher::poll_events() {
|
||||
if (fd_ < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
alignas(inotify_event) char buffer[4096];
|
||||
for (;;) {
|
||||
ssize_t length = ::read(fd_, buffer, sizeof(buffer));
|
||||
if (length <= 0) {
|
||||
return; // nothing queued (EAGAIN) or a read error
|
||||
}
|
||||
for (ssize_t offset = 0; offset < length;) {
|
||||
const auto* event = reinterpret_cast<const inotify_event*>(buffer + offset);
|
||||
offset += static_cast<ssize_t>(sizeof(inotify_event)) + event->len;
|
||||
|
||||
// Only the configured file matters; the directory may hold others.
|
||||
if (event->len == 0 || name_ == event->name) {
|
||||
pending_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ConfigWatcher::take_change() {
|
||||
if (!pending_) {
|
||||
return false;
|
||||
}
|
||||
pending_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gkeymapper::daemon
|
||||
Reference in New Issue
Block a user