summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDexesTTP <dexes.ttp@gmail.com>2021-02-08 20:40:58 +0100
committerAndreas Kling <kling@serenityos.org>2021-02-11 13:13:32 +0100
commit2acbb811b1751233833f9ea89f7e76774262a706 (patch)
tree6bd8f5fdcd73bb01258cd2e4feceb2300faea2fb
parent0304ab3e678151c2f73fd91050a99e8875410ac0 (diff)
downloadserenity-2acbb811b1751233833f9ea89f7e76774262a706.zip
LibCore: Added FileWatcher, a binding for the watch_file syscall
This wrapper abstracts the watch_file setup and file handling, and allows using the watch_file events as part of the event loop via the Core::Notifier class. Also renames the existing DirectoryWatcher class to BlockingFileWatcher, and adds support for the Modified mode in this class.
-rw-r--r--AK/Debug.h.in4
-rw-r--r--Meta/CMake/all_the_debug_macros.cmake1
-rw-r--r--Userland/Libraries/LibCore/CMakeLists.txt2
-rw-r--r--Userland/Libraries/LibCore/DirectoryWatcher.cpp98
-rw-r--r--Userland/Libraries/LibCore/FileWatcher.cpp171
-rw-r--r--Userland/Libraries/LibCore/FileWatcher.h (renamed from Userland/Libraries/LibCore/DirectoryWatcher.h)53
-rw-r--r--Userland/Services/CrashDaemon/main.cpp6
7 files changed, 216 insertions, 119 deletions
diff --git a/AK/Debug.h.in b/AK/Debug.h.in
index 6a23a9935c..4b6ca779de 100644
--- a/AK/Debug.h.in
+++ b/AK/Debug.h.in
@@ -138,6 +138,10 @@
#cmakedefine01 FILE_CONTENT_DEBUG
#endif
+#ifndef FILE_WATCHER_DEBUG
+#cmakedefine01 FILE_WATCHER_DEBUG
+#endif
+
#ifndef FILL_PATH_DEBUG
#cmakedefine01 FILL_PATH_DEBUG
#endif
diff --git a/Meta/CMake/all_the_debug_macros.cmake b/Meta/CMake/all_the_debug_macros.cmake
index 097ae7a555..d2fa65a8e2 100644
--- a/Meta/CMake/all_the_debug_macros.cmake
+++ b/Meta/CMake/all_the_debug_macros.cmake
@@ -165,6 +165,7 @@ set(CPP_DEBUG ON)
set(DEBUG_SPAM ON)
set(DEBUG_CPP_LANGUAGE_SERVER ON)
set(DEBUG_AUTOCOMPLETE ON)
+set(FILE_WATCHER_DEBUG ON)
# False positive: DEBUG is a flag but it works differently.
# set(DEBUG ON)
diff --git a/Userland/Libraries/LibCore/CMakeLists.txt b/Userland/Libraries/LibCore/CMakeLists.txt
index ee6c046bb9..2705753b34 100644
--- a/Userland/Libraries/LibCore/CMakeLists.txt
+++ b/Userland/Libraries/LibCore/CMakeLists.txt
@@ -5,11 +5,11 @@ set(SOURCES
ConfigFile.cpp
Command.cpp
DateTime.cpp
- DirectoryWatcher.cpp
DirIterator.cpp
ElapsedTimer.cpp
Event.cpp
EventLoop.cpp
+ FileWatcher.cpp
File.cpp
GetPassword.cpp
Gzip.cpp
diff --git a/Userland/Libraries/LibCore/DirectoryWatcher.cpp b/Userland/Libraries/LibCore/DirectoryWatcher.cpp
deleted file mode 100644
index a363c2461d..0000000000
--- a/Userland/Libraries/LibCore/DirectoryWatcher.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "DirectoryWatcher.h"
-#include <AK/LexicalPath.h>
-#include <AK/Optional.h>
-#include <LibCore/DirIterator.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-
-namespace Core {
-
-// Only supported in serenity mode because we use `watch_file`
-#ifdef __serenity__
-
-DirectoryWatcher::DirectoryWatcher(const String& path)
- : m_path(path)
-{
- m_watcher_fd = watch_file(path.characters(), path.length());
- ASSERT(m_watcher_fd != -1);
-}
-
-DirectoryWatcher::~DirectoryWatcher()
-{
- close(m_watcher_fd);
-}
-
-Optional<DirectoryWatcher::Event> DirectoryWatcher::wait_for_event()
-{
- InodeWatcherEvent event {};
- int rc = read(m_watcher_fd, &event, sizeof(event));
- if (rc <= 0)
- return {};
-
- Event result;
- if (event.type == InodeWatcherEvent::Type::ChildAdded)
- result.type = Event::Type::ChildAdded;
- else if (event.type == InodeWatcherEvent::Type::ChildRemoved)
- result.type = Event::Type::ChildRemoved;
- else
- return {};
-
- auto child_path = get_child_with_inode_index(event.inode_index);
- if (!LexicalPath(child_path).is_valid())
- return {};
-
- result.child_path = child_path;
- return result;
-}
-
-String DirectoryWatcher::get_child_with_inode_index(unsigned child_inode_index) const
-{
- DirIterator iterator(m_path, Core::DirIterator::SkipDots);
- if (iterator.has_error()) {
- return {};
- }
-
- while (iterator.has_next()) {
- auto child_full_path = String::formatted("{}/{}", m_path, iterator.next_path());
- struct stat st;
-
- if (lstat(child_full_path.characters(), &st)) {
- return {};
- }
-
- if (st.st_ino == child_inode_index) {
- return child_full_path;
- }
- }
- return {};
-}
-
-#endif
-
-}
diff --git a/Userland/Libraries/LibCore/FileWatcher.cpp b/Userland/Libraries/LibCore/FileWatcher.cpp
new file mode 100644
index 0000000000..19bb73eb8a
--- /dev/null
+++ b/Userland/Libraries/LibCore/FileWatcher.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
+ * Copyright (c) 2021, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "FileWatcher.h"
+#include <AK/Debug.h>
+#include <AK/Function.h>
+#include <AK/LexicalPath.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 <LibCore/DirIterator.h>
+#include <LibCore/Notifier.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+
+namespace Core {
+
+// Only supported in serenity mode because we use `watch_file`
+#ifdef __serenity__
+
+static String get_child_path_from_inode_index(const String& path, unsigned child_inode_index)
+{
+ DirIterator iterator(path, Core::DirIterator::SkipDots);
+ if (iterator.has_error()) {
+ return {};
+ }
+
+ while (iterator.has_next()) {
+ auto child_full_path = String::formatted("{}/{}", path, iterator.next_path());
+ struct stat st;
+
+ if (lstat(child_full_path.characters(), &st)) {
+ return {};
+ }
+
+ if (st.st_ino == child_inode_index) {
+ return child_full_path;
+ }
+ }
+ return {};
+}
+
+BlockingFileWatcher::BlockingFileWatcher(const String& path)
+ : m_path(path)
+{
+ m_watcher_fd = watch_file(path.characters(), path.length());
+ ASSERT(m_watcher_fd != -1);
+}
+
+BlockingFileWatcher::~BlockingFileWatcher()
+{
+ close(m_watcher_fd);
+}
+
+Optional<FileWatcherEvent> BlockingFileWatcher::wait_for_event()
+{
+ InodeWatcherEvent event {};
+ int rc = read(m_watcher_fd, &event, sizeof(event));
+ if (rc <= 0)
+ return {};
+
+ FileWatcherEvent result;
+ if (event.type == InodeWatcherEvent::Type::ChildAdded)
+ result.type = FileWatcherEvent::Type::ChildAdded;
+ else if (event.type == InodeWatcherEvent::Type::ChildRemoved)
+ result.type = FileWatcherEvent::Type::ChildRemoved;
+ else if (event.type == InodeWatcherEvent::Type::Modified)
+ result.type = FileWatcherEvent::Type::Modified;
+ else
+ return {};
+
+ if (result.type == FileWatcherEvent::Type::ChildAdded || result.type == FileWatcherEvent::Type::ChildRemoved) {
+ auto child_path = get_child_path_from_inode_index(m_path, event.inode_index);
+ if (!LexicalPath(child_path).is_valid())
+ return {};
+
+ result.child_path = child_path;
+ }
+
+ return result;
+}
+
+Result<NonnullRefPtr<FileWatcher>, String> FileWatcher::watch(const String& path)
+{
+ auto watch_fd = watch_file(path.characters(), path.length());
+ if (watch_fd < 0) {
+ return String::formatted("Could not watch file '{}' : {}", path.characters(), strerror(errno));
+ }
+
+ fcntl(watch_fd, F_SETFD, FD_CLOEXEC);
+ if (watch_fd < 0) {
+ return String::formatted("Could not watch file '{}' : {}", path.characters(), strerror(errno));
+ }
+
+ dbgln_if(FILE_WATCHER_DEBUG, "Started watcher for file '{}'", path.characters());
+ auto notifier = Notifier::construct(watch_fd, Notifier::Event::Read);
+ return adopt(*new FileWatcher(move(notifier), move(path)));
+}
+
+FileWatcher::FileWatcher(NonnullRefPtr<Notifier> notifier, const String& path)
+ : m_notifier(move(notifier))
+ , m_path(path)
+{
+ m_notifier->on_ready_to_read = [this] {
+ InodeWatcherEvent event {};
+ int rc = read(m_notifier->fd(), &event, sizeof(event));
+ if (rc <= 0)
+ return;
+
+ FileWatcherEvent result;
+ if (event.type == InodeWatcherEvent::Type::ChildAdded) {
+ result.type = FileWatcherEvent::Type::ChildAdded;
+ } else if (event.type == InodeWatcherEvent::Type::ChildRemoved) {
+ result.type = FileWatcherEvent::Type::ChildRemoved;
+ } else if (event.type == InodeWatcherEvent::Type::Modified) {
+ result.type = FileWatcherEvent::Type::Modified;
+ } else {
+ warnln("Unknown event type {} returned by the watch_file descriptor for {}", (unsigned)event.type, m_path.characters());
+ return;
+ }
+
+ if (result.type == FileWatcherEvent::Type::ChildAdded || result.type == FileWatcherEvent::Type::ChildRemoved) {
+ auto child_path = get_child_path_from_inode_index(m_path, event.inode_index);
+ if (!LexicalPath(child_path).is_valid())
+ return;
+
+ result.child_path = child_path;
+ }
+
+ on_change(result);
+ };
+}
+
+FileWatcher::~FileWatcher()
+{
+ m_notifier->on_ready_to_read = nullptr;
+ close(m_notifier->fd());
+ dbgln_if(FILE_WATCHER_DEBUG, "Ended watcher for file '{}'", m_path.characters());
+}
+
+#endif
+
+}
diff --git a/Userland/Libraries/LibCore/DirectoryWatcher.h b/Userland/Libraries/LibCore/FileWatcher.h
index 63be2c1aa7..8a7df3acc9 100644
--- a/Userland/Libraries/LibCore/DirectoryWatcher.h
+++ b/Userland/Libraries/LibCore/FileWatcher.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
+ * Copyright (c) 2021, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,34 +29,52 @@
#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 <LibCore/Notifier.h>
namespace Core {
-class DirectoryWatcher {
- AK_MAKE_NONCOPYABLE(DirectoryWatcher);
+struct FileWatcherEvent {
+ enum class Type {
+ Modified,
+ ChildAdded,
+ ChildRemoved,
+ };
+ Type type;
+ String child_path;
+};
+
+class BlockingFileWatcher {
+ AK_MAKE_NONCOPYABLE(BlockingFileWatcher);
public:
- explicit DirectoryWatcher(const String& path);
- ~DirectoryWatcher();
-
- struct Event {
- enum class Type {
- ChildAdded,
- ChildRemoved,
- };
- Type type;
- String child_path;
- };
+ explicit BlockingFileWatcher(const String& path);
+ ~BlockingFileWatcher();
- Optional<Event> wait_for_event();
+ Optional<FileWatcherEvent> wait_for_event();
private:
- String get_child_with_inode_index(unsigned) const;
-
String m_path;
int m_watcher_fd { -1 };
};
+class FileWatcher : public RefCounted<FileWatcher> {
+ AK_MAKE_NONCOPYABLE(FileWatcher);
+
+public:
+ static Result<NonnullRefPtr<FileWatcher>, String> watch(const String& path);
+ ~FileWatcher();
+
+ Function<void(FileWatcherEvent)> on_change;
+
+private:
+ FileWatcher(NonnullRefPtr<Notifier>, const String& path);
+
+ NonnullRefPtr<Notifier> m_notifier;
+ String m_path;
+};
+
}
diff --git a/Userland/Services/CrashDaemon/main.cpp b/Userland/Services/CrashDaemon/main.cpp
index 8dcd318566..b651675864 100644
--- a/Userland/Services/CrashDaemon/main.cpp
+++ b/Userland/Services/CrashDaemon/main.cpp
@@ -25,7 +25,7 @@
*/
#include <AK/LexicalPath.h>
-#include <LibCore/DirectoryWatcher.h>
+#include <LibCore/FileWatcher.h>
#include <LibCoreDump/Backtrace.h>
#include <LibCoreDump/Reader.h>
#include <serenity.h>
@@ -89,11 +89,11 @@ int main()
return 1;
}
- Core::DirectoryWatcher watcher { "/tmp/coredump" };
+ Core::BlockingFileWatcher watcher { "/tmp/coredump" };
while (true) {
auto event = watcher.wait_for_event();
ASSERT(event.has_value());
- if (event.value().type != Core::DirectoryWatcher::Event::Type::ChildAdded)
+ if (event.value().type != Core::FileWatcherEvent::Type::ChildAdded)
continue;
auto coredump_path = event.value().child_path;
dbgln("New coredump file: {}", coredump_path);