event/InotifyEvent: new class wrapping inotify

Replaces class InotifySource.
This commit is contained in:
Max Kellermann
2022-06-30 11:27:13 +02:00
parent ff4cf6c6d1
commit 0f4bf5569a
12 changed files with 301 additions and 230 deletions

View File

@@ -43,7 +43,6 @@ db_glue_sources = [
if enable_inotify
db_glue_sources += [
'update/InotifyDomain.cxx',
'update/InotifySource.cxx',
'update/InotifyQueue.cxx',
'update/InotifyUpdate.cxx',
]

View File

@@ -1,115 +0,0 @@
/*
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "InotifySource.hxx"
#include "InotifyDomain.hxx"
#include "io/FileDescriptor.hxx"
#include "system/Error.hxx"
#include "Log.hxx"
#include <cerrno>
#include <climits>
#include <cstdint>
#include <sys/inotify.h>
void
InotifySource::OnSocketReady([[maybe_unused]] unsigned flags) noexcept
{
uint8_t buffer[4096];
static_assert(sizeof(buffer) >= sizeof(struct inotify_event) + NAME_MAX + 1,
"inotify buffer too small");
auto ifd = socket_event.GetFileDescriptor();
ssize_t nbytes = ifd.Read(buffer, sizeof(buffer));
if (nbytes <= 0) {
if (nbytes < 0)
FmtError(inotify_domain,
"Failed to read from inotify: {}",
strerror(errno));
else
LogError(inotify_domain,
"end of file from inotify");
socket_event.Cancel();
return;
}
const uint8_t *p = buffer, *const end = p + nbytes;
while (true) {
const size_t remaining = end - p;
const auto *event =
(const struct inotify_event *)p;
if (remaining < sizeof(*event) ||
remaining < sizeof(*event) + event->len)
break;
const char *name;
if (event->len > 0 && event->name[event->len - 1] == 0)
name = event->name;
else
name = nullptr;
callback(event->wd, event->mask, name, callback_ctx);
p += sizeof(*event) + event->len;
}
}
static FileDescriptor
InotifyInit()
{
FileDescriptor fd;
if (!fd.CreateInotify())
throw MakeErrno("inotify_init() has failed");
return fd;
}
InotifySource::InotifySource(EventLoop &_loop,
mpd_inotify_callback_t _callback, void *_ctx)
:socket_event(_loop, BIND_THIS_METHOD(OnSocketReady),
InotifyInit()),
callback(_callback), callback_ctx(_ctx)
{
socket_event.ScheduleRead();
}
int
InotifySource::Add(const char *path_fs, unsigned mask)
{
auto ifd = socket_event.GetFileDescriptor();
int wd = inotify_add_watch(ifd.Get(), path_fs, mask);
if (wd < 0)
throw MakeErrno("inotify_add_watch() has failed");
return wd;
}
void
InotifySource::Remove(unsigned wd) noexcept
{
auto ifd = socket_event.GetFileDescriptor();
int ret = inotify_rm_watch(ifd.Get(), wd);
if (ret < 0 && errno != EINVAL)
FmtError(inotify_domain, "inotify_rm_watch() has failed: {}",
strerror(errno));
/* EINVAL may happen here when the file has been deleted; the
kernel seems to auto-unregister deleted files */
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_INOTIFY_SOURCE_HXX
#define MPD_INOTIFY_SOURCE_HXX
#include "event/PipeEvent.hxx"
typedef void (*mpd_inotify_callback_t)(int wd, unsigned mask,
const char *name, void *ctx);
class InotifySource final {
PipeEvent socket_event;
mpd_inotify_callback_t callback;
void *callback_ctx;
public:
/**
* Creates a new inotify source and registers it in the
* #EventLoop.
*
* Throws #std::system_error on error.
*
* @param callback a callback invoked for events received from
* the kernel
*/
InotifySource(EventLoop &_loop,
mpd_inotify_callback_t callback, void *ctx);
~InotifySource() noexcept {
socket_event.Close();
}
/**
* Adds a path to the notify list.
*
* Throws #std::system_error on error.
*
* @return a watch descriptor
*/
int Add(const char *path_fs, unsigned mask);
/**
* Removes a path from the notify list.
*
* @param wd the watch descriptor returned by mpd_inotify_source_add()
*/
void Remove(unsigned wd) noexcept;
private:
void OnSocketReady(unsigned flags) noexcept;
};
#endif

View File

@@ -118,7 +118,7 @@ InotifyUpdate::Disable(WatchDirectory &directory) noexcept
for (WatchDirectory &child : directory.children)
Disable(child);
source.Remove(directory.descriptor);
inotify_event.RemoveWatch(directory.descriptor);
}
void
@@ -199,7 +199,8 @@ try {
continue;
try {
ret = source.Add(child_path_fs.c_str(), IN_MASK);
ret = inotify_event.AddWatch(child_path_fs.c_str(),
IN_MASK);
} catch (...) {
FmtError(inotify_domain,
"Failed to register {}: {}",
@@ -240,7 +241,7 @@ WatchDirectory::GetDepth() const noexcept
inline
InotifyUpdate::InotifyUpdate(EventLoop &loop, UpdateService &update,
unsigned _max_depth)
:source(loop, InotifyCallback, this),
:inotify_event(loop, *this),
queue(loop, update),
max_depth(_max_depth)
{
@@ -251,7 +252,7 @@ InotifyUpdate::~InotifyUpdate() noexcept = default;
inline void
InotifyUpdate::Start(Path path)
{
int descriptor = source.Add(path.c_str(), IN_MASK);
int descriptor = inotify_event.AddWatch(path.c_str(), IN_MASK);
root = std::make_unique<WatchDirectory>(path, descriptor);
root->LoadExcludeList(path);
@@ -262,8 +263,7 @@ InotifyUpdate::Start(Path path)
}
void
InotifyUpdate::InotifyCallback(int wd, unsigned mask,
[[maybe_unused]] const char *name) noexcept
InotifyUpdate::OnInotify(int wd, unsigned mask, const char *)
{
auto i = directories.find(wd);
if (i == directories.end())
@@ -310,6 +310,12 @@ InotifyUpdate::InotifyCallback(int wd, unsigned mask,
}
}
void
InotifyUpdate::OnInotifyError(std::exception_ptr error) noexcept
{
LogError(error, "inotify error");
}
std::unique_ptr<InotifyUpdate>
mpd_inotify_init(EventLoop &loop, Storage &storage, UpdateService &update,
unsigned max_depth)

View File

@@ -20,8 +20,8 @@
#ifndef MPD_INOTIFY_UPDATE_HXX
#define MPD_INOTIFY_UPDATE_HXX
#include "InotifySource.hxx"
#include "InotifyQueue.hxx"
#include "event/InotifyEvent.hxx"
#include <map>
#include <memory>
@@ -33,8 +33,8 @@ struct WatchDirectory;
/**
* Glue code between InotifySource and InotifyQueue.
*/
class InotifyUpdate {
InotifySource source;
class InotifyUpdate final : InotifyHandler {
InotifyEvent inotify_event;
InotifyQueue queue;
const unsigned max_depth;
@@ -50,14 +50,6 @@ public:
void Start(Path path);
private:
void InotifyCallback(int wd, unsigned mask, const char *name) noexcept;
static void InotifyCallback(int wd, unsigned mask,
const char *name, void *ctx) noexcept {
auto &iu = *(InotifyUpdate *)ctx;
iu.InotifyCallback(wd, mask, name);
}
void AddToMap(WatchDirectory &directory) noexcept;
void RemoveFromMap(WatchDirectory &directory) noexcept;
void Disable(WatchDirectory &directory) noexcept;
@@ -66,6 +58,11 @@ private:
void RecursiveWatchSubdirectories(WatchDirectory &parent,
Path path_fs,
unsigned depth) noexcept;
private:
/* virtual methods from class InotifyHandler */
void OnInotify(int wd, unsigned mask, const char *name) override;
void OnInotifyError(std::exception_ptr error) noexcept override;
};
/**