event/InotifyEvent: new class wrapping inotify
Replaces class InotifySource.
This commit is contained in:
@@ -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',
|
||||
]
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user