mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-01 13:44:21 +00:00
Utilities: Introduce the watchfs utility
This utility is meant mainly for debugging purposes, to watch regular files and directories being modified.
This commit is contained in:
parent
925bea444b
commit
b38dbe9c3e
55
Base/usr/share/man/man1/watchfs.md
Normal file
55
Base/usr/share/man/man1/watchfs.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
## Name
|
||||
|
||||
watchfs - watch a file or directory being changed
|
||||
|
||||
## Synopsis
|
||||
|
||||
```**sh
|
||||
$ watchfs [options...] [path...]
|
||||
```
|
||||
|
||||
## Description
|
||||
|
||||
`watchfs` watches files and directories being changed.
|
||||
|
||||
## Options
|
||||
|
||||
* `--help`: Display this message
|
||||
* `-E`, `--exit-after-change`: Wait for first change and exit
|
||||
* `-a`, `--watch-all-events`: Watch all types of events
|
||||
* `-d`, `--watch-delete-events`: Watch file deletion events
|
||||
* `-m`, `--watch-file-modify-events`: Watch file content being modified
|
||||
* `-M`, `--watch-file-metadata-events`: Watch file metadata being modified
|
||||
* `-c`, `--watch-directory-child-creation-events`: Watch directory child creation events
|
||||
* `-D`, `--watch-directory-child-deletion-events`: Watch directory child deletion events
|
||||
|
||||
## Arguments
|
||||
|
||||
* `path`: Files and/or directories to watch
|
||||
|
||||
## Examples
|
||||
|
||||
```sh
|
||||
# watch /tmp with all events being handled (child creation and deletion)
|
||||
$ watchfs -a /tmp/
|
||||
# watch /tmp with child creation events being handled
|
||||
$ watchfs -c /tmp/
|
||||
# watch /tmp with child creation events being handled
|
||||
$ watchfs -D /tmp/
|
||||
|
||||
# watch /tmp with all events being handled (child creation and deletion) and exit after first change
|
||||
$ watchfs -E /tmp/
|
||||
|
||||
# watch /tmp/test_file with all events being handled (file being deleted, metadata being modified or content modified)
|
||||
$ watchfs -a /tmp/test_file
|
||||
# watch /tmp/test_file being deleted
|
||||
$ watchfs -d /tmp/test_file
|
||||
# watch /tmp/test_file being metadata-modified
|
||||
$ watchfs -M /tmp/test_file
|
||||
# watch /tmp/test_file being content-modified
|
||||
$ watchfs -m /tmp/test_file
|
||||
```
|
||||
|
||||
## See also
|
||||
* [`listdir`(1)](help://man/1/listdir) to list directory entries
|
||||
* [`ls`(1)](help://man/1/ls) to list directory contents
|
|
@ -4,7 +4,7 @@ list(APPEND REQUIRED_TARGETS
|
|||
arp base64 basename cat chmod chown clear comm cp cut date dd df diff dirname dmesg du echo env expr false
|
||||
file find grep groups head host hostname id ifconfig kill killall ln logout ls mkdir mount mv network-settings nproc
|
||||
patch pgrep pidof ping pkill pmap ps readlink realpath reboot rm rmdir sed route seq shutdown sleep sort stat stty su tail test
|
||||
touch tr true umount uname uniq uptime w wc which whoami xargs yes
|
||||
touch tr true umount uname uniq uptime w watchfs wc which whoami xargs yes
|
||||
)
|
||||
list(APPEND RECOMMENDED_TARGETS
|
||||
aconv adjtime aplay abench asctl bt checksum chres cksum copy fortune gzip init install keymap lsirq lsof lspci lzcat man mkfs.fat mknod mktemp
|
||||
|
|
170
Userland/Utilities/watchfs.cpp
Normal file
170
Userland/Utilities/watchfs.cpp
Normal file
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <LibCore/DirIterator.h>
|
||||
#include <LibCore/DirectoryEntry.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/FileWatcher.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibMain/Main.h>
|
||||
|
||||
static ErrorOr<NonnullRefPtr<Core::FileWatcher>> watch_directory_child_creation(StringView path, bool exit_after_first_change)
|
||||
{
|
||||
auto watcher = TRY(Core::FileWatcher::create());
|
||||
|
||||
watcher->on_change = [path, exit_after_first_change](auto&) {
|
||||
outln("{} has new file", path);
|
||||
if (exit_after_first_change)
|
||||
exit(1);
|
||||
};
|
||||
|
||||
TRY(watcher->add_watch(path, Core::FileWatcherEvent::Type::ChildCreated));
|
||||
return watcher;
|
||||
}
|
||||
|
||||
static ErrorOr<NonnullRefPtr<Core::FileWatcher>> watch_directory_child_deletion(StringView path, bool exit_after_first_change)
|
||||
{
|
||||
auto watcher = TRY(Core::FileWatcher::create());
|
||||
|
||||
watcher->on_change = [path, exit_after_first_change](auto&) {
|
||||
outln("{} has file being deleted", path);
|
||||
if (exit_after_first_change)
|
||||
exit(1);
|
||||
};
|
||||
|
||||
TRY(watcher->add_watch(path, Core::FileWatcherEvent::Type::ChildDeleted));
|
||||
return watcher;
|
||||
}
|
||||
|
||||
static ErrorOr<NonnullRefPtr<Core::FileWatcher>> watch_file_content_modified(StringView path, bool exit_after_first_change)
|
||||
{
|
||||
auto watcher = TRY(Core::FileWatcher::create());
|
||||
|
||||
watcher->on_change = [path, exit_after_first_change](auto&) {
|
||||
outln("{} content is modified", path);
|
||||
if (exit_after_first_change)
|
||||
exit(1);
|
||||
};
|
||||
|
||||
TRY(watcher->add_watch(path, Core::FileWatcherEvent::Type::ContentModified));
|
||||
return watcher;
|
||||
}
|
||||
|
||||
static ErrorOr<NonnullRefPtr<Core::FileWatcher>> watch_file_metadata_modified(StringView path, bool exit_after_first_change)
|
||||
{
|
||||
auto watcher = TRY(Core::FileWatcher::create());
|
||||
|
||||
watcher->on_change = [path, exit_after_first_change](auto&) {
|
||||
outln("{} metadata is modified", path);
|
||||
if (exit_after_first_change)
|
||||
exit(1);
|
||||
};
|
||||
|
||||
TRY(watcher->add_watch(path, Core::FileWatcherEvent::Type::MetadataModified));
|
||||
return watcher;
|
||||
}
|
||||
|
||||
static ErrorOr<NonnullRefPtr<Core::FileWatcher>> watch_file_being_deleted(StringView path, bool exit_after_first_change)
|
||||
{
|
||||
auto watcher = TRY(Core::FileWatcher::create());
|
||||
|
||||
watcher->on_change = [path, exit_after_first_change](auto&) {
|
||||
outln("{} is deleted", path);
|
||||
if (exit_after_first_change)
|
||||
exit(1);
|
||||
};
|
||||
|
||||
TRY(watcher->add_watch(path, Core::FileWatcherEvent::Type::Deleted));
|
||||
return watcher;
|
||||
}
|
||||
|
||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
{
|
||||
TRY(Core::System::pledge("stdio rpath"));
|
||||
|
||||
Vector<StringView> paths;
|
||||
bool flag_exit_after_first_change = false;
|
||||
bool flag_watch_all_events = false;
|
||||
bool flag_watch_file_being_deleted = false;
|
||||
bool flag_watch_file_being_content_modified = false;
|
||||
bool flag_watch_file_being_metadata_modified = false;
|
||||
bool flag_watch_directory_child_creation = false;
|
||||
bool flag_watch_directory_child_deletion = false;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.set_general_help("Watch for filesystem activity in a directory.");
|
||||
args_parser.add_option(flag_exit_after_first_change, "Wait for first change and exit", "exit-after-change", 'E');
|
||||
args_parser.add_option(flag_watch_all_events, "Watch all types of events", "watch-all-events", 'a');
|
||||
args_parser.add_option(flag_watch_file_being_deleted, "Watch file deletion events", "watch-delete-events", 'd');
|
||||
args_parser.add_option(flag_watch_file_being_content_modified, "Watch file content being modified", "watch-file-modify-events", 'm');
|
||||
args_parser.add_option(flag_watch_file_being_metadata_modified, "Watch file metadata being modified", "watch-file-metadata-events", 'M');
|
||||
args_parser.add_option(flag_watch_directory_child_creation, "Watch directory child creation events", "watch-directory-child-creation-events", 'c');
|
||||
args_parser.add_option(flag_watch_directory_child_deletion, "Watch directory child deletion events", "watch-directory-child-deletion-events", 'D');
|
||||
args_parser.add_positional_argument(paths, "Path to watch", "path", Core::ArgsParser::Required::No);
|
||||
args_parser.parse(arguments);
|
||||
|
||||
if (flag_watch_all_events) {
|
||||
flag_watch_file_being_deleted = true;
|
||||
flag_watch_file_being_content_modified = true;
|
||||
flag_watch_file_being_metadata_modified = true;
|
||||
flag_watch_directory_child_creation = true;
|
||||
flag_watch_directory_child_deletion = true;
|
||||
}
|
||||
|
||||
if (paths.is_empty())
|
||||
paths.append("."sv);
|
||||
|
||||
Vector<NonnullRefPtr<Core::FileWatcher>> watchers;
|
||||
|
||||
Core::EventLoop event_loop;
|
||||
|
||||
for (auto& path : paths) {
|
||||
auto st = TRY(Core::System::stat(path));
|
||||
auto directory_entry_type = Core::DirectoryEntry::directory_entry_type_from_stat(st.st_mode);
|
||||
switch (directory_entry_type) {
|
||||
case Core::DirectoryEntry::Type::Directory: {
|
||||
if (flag_watch_directory_child_creation) {
|
||||
auto watcher = TRY(watch_directory_child_creation(path, flag_exit_after_first_change));
|
||||
TRY(watchers.try_append(watcher));
|
||||
}
|
||||
|
||||
if (flag_watch_directory_child_deletion) {
|
||||
auto watcher = TRY(watch_directory_child_deletion(path, flag_exit_after_first_change));
|
||||
TRY(watchers.try_append(watcher));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Core::DirectoryEntry::Type::File: {
|
||||
if (flag_watch_file_being_content_modified) {
|
||||
auto watcher = TRY(watch_file_content_modified(path, flag_exit_after_first_change));
|
||||
TRY(watchers.try_append(watcher));
|
||||
}
|
||||
if (flag_watch_file_being_metadata_modified) {
|
||||
auto watcher = TRY(watch_file_metadata_modified(path, flag_exit_after_first_change));
|
||||
TRY(watchers.try_append(watcher));
|
||||
}
|
||||
if (flag_watch_file_being_deleted) {
|
||||
auto watcher = TRY(watch_file_being_deleted(path, flag_exit_after_first_change));
|
||||
TRY(watchers.try_append(watcher));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
warnln("Trying to watch unsupported file type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (watchers.is_empty())
|
||||
return Error::from_string_literal("Watchers list is empty");
|
||||
|
||||
event_loop.exec();
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue