
With the new InodeWatcher API, the old style of creating a watcher per inode will no longer work. Therefore the FileWatcher API has been updated to support multiple watches, and its users have also been refactored to the new style. At the moment, all operations done on a (Blocking)FileWatcher return Result objects, however, this may be changed in the future if it becomes too obnoxious. :^) Co-authored-by: Gunnar Beutner <gunnar@beutner.name>
124 lines
3.2 KiB
C++
124 lines
3.2 KiB
C++
/*
|
|
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
|
|
* Copyright (c) 2021, the SerenityOS developers.
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/EnumBits.h>
|
|
#include <AK/Function.h>
|
|
#include <AK/Noncopyable.h>
|
|
#include <AK/NonnullRefPtr.h>
|
|
#include <AK/RefCounted.h>
|
|
#include <AK/Result.h>
|
|
#include <AK/String.h>
|
|
#include <Kernel/API/InodeWatcherEvent.h>
|
|
#include <Kernel/API/InodeWatcherFlags.h>
|
|
#include <LibCore/Notifier.h>
|
|
|
|
namespace Core {
|
|
|
|
struct FileWatcherEvent {
|
|
enum class Type {
|
|
Invalid = 0,
|
|
MetadataModified = 1 << 0,
|
|
ContentModified = 1 << 1,
|
|
Deleted = 1 << 2,
|
|
ChildCreated = 1 << 3,
|
|
ChildDeleted = 1 << 4,
|
|
};
|
|
Type type;
|
|
String event_path;
|
|
};
|
|
|
|
AK_ENUM_BITWISE_OPERATORS(FileWatcherEvent::Type);
|
|
|
|
class FileWatcherBase {
|
|
public:
|
|
virtual ~FileWatcherBase() { }
|
|
|
|
Result<bool, String> add_watch(String path, FileWatcherEvent::Type event_mask);
|
|
Result<bool, String> remove_watch(String path);
|
|
bool is_watching(String const& path) const { return m_path_to_wd.find(path) != m_path_to_wd.end(); }
|
|
|
|
protected:
|
|
FileWatcherBase(int watcher_fd)
|
|
: m_watcher_fd(watcher_fd)
|
|
{
|
|
}
|
|
|
|
int m_watcher_fd { -1 };
|
|
HashMap<String, unsigned> m_path_to_wd;
|
|
HashMap<unsigned, String> m_wd_to_path;
|
|
};
|
|
|
|
class BlockingFileWatcher final : public FileWatcherBase {
|
|
AK_MAKE_NONCOPYABLE(BlockingFileWatcher);
|
|
|
|
public:
|
|
explicit BlockingFileWatcher(InodeWatcherFlags = InodeWatcherFlags::None);
|
|
~BlockingFileWatcher();
|
|
|
|
Optional<FileWatcherEvent> wait_for_event();
|
|
};
|
|
|
|
class FileWatcher final : public FileWatcherBase
|
|
, public RefCounted<FileWatcher> {
|
|
AK_MAKE_NONCOPYABLE(FileWatcher);
|
|
|
|
public:
|
|
static Result<NonnullRefPtr<FileWatcher>, String> create(InodeWatcherFlags = InodeWatcherFlags::None);
|
|
~FileWatcher();
|
|
|
|
Function<void(FileWatcherEvent const&)> on_change;
|
|
|
|
private:
|
|
FileWatcher(int watcher_fd, NonnullRefPtr<Notifier>);
|
|
|
|
NonnullRefPtr<Notifier> m_notifier;
|
|
};
|
|
|
|
}
|
|
|
|
namespace AK {
|
|
|
|
template<>
|
|
struct Formatter<Core::FileWatcherEvent> : Formatter<FormatString> {
|
|
void format(FormatBuilder& builder, const Core::FileWatcherEvent& value)
|
|
{
|
|
Formatter<FormatString>::format(builder, "FileWatcherEvent(\"{}\", {})", value.event_path, value.type);
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct Formatter<Core::FileWatcherEvent::Type> : Formatter<FormatString> {
|
|
void format(FormatBuilder& builder, const Core::FileWatcherEvent::Type& value)
|
|
{
|
|
char const* type;
|
|
switch (value) {
|
|
case Core::FileWatcherEvent::Type::ChildCreated:
|
|
type = "ChildCreated";
|
|
break;
|
|
case Core::FileWatcherEvent::Type::ChildDeleted:
|
|
type = "ChildDeleted";
|
|
break;
|
|
case Core::FileWatcherEvent::Type::Deleted:
|
|
type = "Deleted";
|
|
break;
|
|
case Core::FileWatcherEvent::Type::ContentModified:
|
|
type = "ContentModified";
|
|
break;
|
|
case Core::FileWatcherEvent::Type::MetadataModified:
|
|
type = "MetadataModified";
|
|
break;
|
|
default:
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
|
|
builder.put_string(type);
|
|
}
|
|
};
|
|
|
|
}
|