From b4f596b1974493b31e3cafd56a02cf134d715ceb Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Mon, 20 Aug 2018 16:47:43 +0200 Subject: [PATCH] Tool for querying information from file system Issue #2936 --- repos/gems/recipes/src/fs_query/content.mk | 10 + repos/gems/recipes/src/fs_query/hash | 1 + repos/gems/recipes/src/fs_query/used_apis | 4 + repos/gems/run/fs_query.run | 199 ++++++++++++++++++ repos/gems/src/app/fs_query/README | 14 ++ repos/gems/src/app/fs_query/main.cc | 222 +++++++++++++++++++++ repos/gems/src/app/fs_query/target.mk | 3 + tool/autopilot.list | 1 + 8 files changed, 454 insertions(+) create mode 100644 repos/gems/recipes/src/fs_query/content.mk create mode 100644 repos/gems/recipes/src/fs_query/hash create mode 100644 repos/gems/recipes/src/fs_query/used_apis create mode 100644 repos/gems/run/fs_query.run create mode 100644 repos/gems/src/app/fs_query/README create mode 100644 repos/gems/src/app/fs_query/main.cc create mode 100644 repos/gems/src/app/fs_query/target.mk diff --git a/repos/gems/recipes/src/fs_query/content.mk b/repos/gems/recipes/src/fs_query/content.mk new file mode 100644 index 0000000000..f00e374753 --- /dev/null +++ b/repos/gems/recipes/src/fs_query/content.mk @@ -0,0 +1,10 @@ +SRC_DIR := src/app/fs_query + +include $(GENODE_DIR)/repos/base/recipes/src/content.inc + +MIRROR_FROM_REP_DIR := include/gems/vfs.h + +content: $(MIRROR_FROM_REP_DIR) + +$(MIRROR_FROM_REP_DIR): + $(mirror_from_rep_dir) diff --git a/repos/gems/recipes/src/fs_query/hash b/repos/gems/recipes/src/fs_query/hash new file mode 100644 index 0000000000..7c2d52582e --- /dev/null +++ b/repos/gems/recipes/src/fs_query/hash @@ -0,0 +1 @@ +2018-08-21 474d21ebb2298e4b411dcef5c32a452c053b09c7 diff --git a/repos/gems/recipes/src/fs_query/used_apis b/repos/gems/recipes/src/fs_query/used_apis new file mode 100644 index 0000000000..ade9ce3115 --- /dev/null +++ b/repos/gems/recipes/src/fs_query/used_apis @@ -0,0 +1,4 @@ +base +os +vfs +report_session diff --git a/repos/gems/run/fs_query.run b/repos/gems/run/fs_query.run new file mode 100644 index 0000000000..2e7c965db8 --- /dev/null +++ b/repos/gems/run/fs_query.run @@ -0,0 +1,199 @@ +create_boot_directory + +import_from_depot \ + genodelabs/src/[base_src] \ + genodelabs/src/coreutils \ + genodelabs/src/bash \ + genodelabs/src/init \ + genodelabs/src/libc \ + genodelabs/src/noux \ + genodelabs/src/posix \ + genodelabs/src/report_rom \ + genodelabs/src/vfs \ + genodelabs/src/vfs_import + +install_config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + first + second + + + fourth + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +build { app/sequence app/fs_query } + +build_boot_image { sequence fs_query } + +append qemu_args " -nographic -serial mon:stdio " + +run_genode_until {.*child "test" exited with exit value 0.*\n} 30 + +grep_output {\[init -> report_rom\].*} + +set num_listings [regexp -all {report 'fs_query -> listing'} $output dummy] + +# we expect at least four intermediate reports +if {$num_listings < 4} { + puts "Error: too few reports generated" + exit 1 +} + +# +# We cannot reliably compare the full output because some file operations +# may trigger one or two reports depending on the timing of signal delivery. +# However, we can at least check the last report for validity. +# +regsub {.*report 'fs_query -> listing'} $output {} output + +compare_output_to { +[init -> report_rom] +[init -> report_rom] +[init -> report_rom] fourth +[init -> report_rom] first +[init -> report_rom] updated +[init -> report_rom] +[init -> report_rom] +[init -> report_rom] +} diff --git a/repos/gems/src/app/fs_query/README b/repos/gems/src/app/fs_query/README new file mode 100644 index 0000000000..1af1b13eca --- /dev/null +++ b/repos/gems/src/app/fs_query/README @@ -0,0 +1,14 @@ +The fs_query component queries and monitors information stored on a file +system. The file system is configured as a component-local VFS. The component +accepts any number of '' nodes within its '' node. Each +'' node must contain a 'path' attribute pointing to a directory to +watch. The component generates a report labeled "listing". For each +existing queried directory, the report contains a '' node with the +list of files as '' nodes featuring the corresponding 'name' as +attribute value. + +A '' can be equipped with a 'content="yes"' attribute. If set, the +content of the queried files is supplemented as body of the '' nodes. +The reported content is limited to 4 KiB per file. If the content is valid +XML, the '' node contains an attribute 'xml="yes"' indicating that +the XML information is inserted as is. Otherwise, the content is sanitized. diff --git a/repos/gems/src/app/fs_query/main.cc b/repos/gems/src/app/fs_query/main.cc new file mode 100644 index 0000000000..3107ec0b9c --- /dev/null +++ b/repos/gems/src/app/fs_query/main.cc @@ -0,0 +1,222 @@ +/* + * \brief Tool for querying information from a file system + * \author Norman Feske + * \date 2018-08-17 + */ + +/* + * Copyright (C) 2018 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + +namespace Fs_query { + using namespace Genode; + struct Watched_file; + struct Watched_directory; + struct Main; +} + + +struct Fs_query::Watched_file +{ + File_content::Path const _name; + + Watcher _watcher; + + Watched_file(Directory const &dir, File_content::Path name) + : _name(name), _watcher(dir, name) { } + + virtual ~Watched_file() { } + + void _gen_content(Xml_generator &xml, Allocator &alloc, Directory const &dir) const + { + File_content content(alloc, dir, _name, File_content::Limit{4*1024}); + + bool content_is_xml = false; + + content.xml([&] (Xml_node node) { + if (!node.has_type("empty")) { + xml.attribute("xml", "yes"); + xml.append("\n"); + xml.append(node.addr(), node.size()); + content_is_xml = true; + } + }); + + if (!content_is_xml) { + content.bytes([&] (char const *base, size_t len) { + xml.append_sanitized(base, len); }); + } + } + + void gen_query_response(Xml_generator &xml, Xml_node query, + Allocator &alloc, Directory const &dir) const + { + try { + xml.node("file", [&] () { + xml.attribute("name", _name); + + if (query.attribute_value("content", false)) + _gen_content(xml, alloc, dir); + }); + } + /* + * File may have disappeared since last traversal. This condition + * is detected on the attempt to obtain the file content. + */ + catch (Directory::Nonexistent_file) { + warning("could not obtain content of nonexistent file ", _name); } + catch (File::Open_failed) { + warning("cannot open file ", _name, " for reading"); } + catch (Xml_generator::Buffer_exceeded) { throw; } + } +}; + + +struct Fs_query::Watched_directory +{ + Allocator &_alloc; + + Directory::Path const _rel_path; + + Directory const _dir; + + Registry > _files { }; + + Watcher _watcher; + + Watched_directory(Allocator &alloc, Directory &other, Directory::Path const &rel_path) + : + _alloc(alloc), _rel_path(rel_path), + _dir(other, rel_path), _watcher(other, rel_path) + { + _dir.for_each_entry([&] (Directory::Entry const &entry) { + if (entry.type() == Vfs::Directory_service::DIRENT_TYPE_FILE) { + try { + new (_alloc) Registered(_files, _dir, entry.name()); + } catch (...) { } + } + }); + } + + virtual ~Watched_directory() + { + _files.for_each([&] (Registered &file) { + destroy(_alloc, &file); }); + } + + bool has_name(Directory::Path const &name) const { return _rel_path == name; } + + void gen_query_response(Xml_generator &xml, Xml_node query) const + { + xml.node("dir", [&] () { + xml.attribute("path", _rel_path); + _files.for_each([&] (Watched_file const &file) { + file.gen_query_response(xml, query, _alloc, _dir); }); + }); + } +}; + + +struct Fs_query::Main : Vfs::Watch_response_handler +{ + Env &_env; + + Heap _heap { _env.ram(), _env.rm() }; + + Attached_rom_dataspace _config { _env, "config" }; + + Vfs::Global_file_system_factory _fs_factory { _heap }; + + /** + * Vfs::Watch_response_handler interface + */ + void handle_watch_response(Vfs::Vfs_watch_handle::Context*) override + { + Signal_transmitter(_config_handler).submit(); + } + + struct Vfs_env : Vfs::Env + { + Main &_main; + + struct Io_response_dummy : Vfs::Io_response_handler { + void handle_io_response(Vfs::Vfs_handle::Context*) override { } + } _io_dummy { }; + + Vfs_env(Main &main) : _main(main) { } + + Genode::Env &env() override { return _main._env; } + Allocator &alloc() override { return _main._heap; } + Vfs::File_system &root_dir() override { return _main._root_dir_fs; } + Vfs::Io_response_handler &io_handler() override { return _io_dummy; } + Vfs::Watch_response_handler &watch_handler() override { return _main; } + + } _vfs_env { *this }; + + Vfs::Dir_file_system _root_dir_fs { + _vfs_env, _config.xml().sub_node("vfs"), _fs_factory }; + + Directory _root_dir { _vfs_env }; + + Signal_handler
_config_handler { + _env.ep(), *this, &Main::_handle_config }; + + Expanding_reporter _reporter { _env, "listing", "listing" }; + + Registry > _dirs { }; + + void _gen_listing(Xml_generator &xml, Xml_node config) const + { + config.for_each_sub_node("query", [&] (Xml_node query) { + Directory::Path const path = query.attribute_value("path", Directory::Path()); + _dirs.for_each([&] (Watched_directory const &dir) { + if (dir.has_name(path)) + dir.gen_query_response(xml, query); + }); + }); + } + + void _handle_config() + { + _config.update(); + + Xml_node const config = _config.xml(); + + _root_dir_fs.apply_config(config.sub_node("vfs")); + + _dirs.for_each([&] (Registered &dir) { + destroy(_heap, &dir); }); + + config.for_each_sub_node("query", [&] (Xml_node query) { + Directory::Path const path = query.attribute_value("path", Directory::Path()); + new (_heap) Registered(_dirs, _heap, _root_dir, path); + }); + + _reporter.generate([&] (Xml_generator &xml) { + _gen_listing(xml, config); }); + } + + Main(Env &env) : _env(env) + { + _config.sigh(_config_handler); + _handle_config(); + } +}; + + +void Component::construct(Genode::Env &env) +{ + static Fs_query::Main main(env); +} + diff --git a/repos/gems/src/app/fs_query/target.mk b/repos/gems/src/app/fs_query/target.mk new file mode 100644 index 0000000000..73cde1a240 --- /dev/null +++ b/repos/gems/src/app/fs_query/target.mk @@ -0,0 +1,3 @@ +TARGET = fs_query +SRC_CC = main.cc +LIBS += base vfs diff --git a/tool/autopilot.list b/tool/autopilot.list index 3f6ed181ed..c84f1b52f9 100644 --- a/tool/autopilot.list +++ b/tool/autopilot.list @@ -18,6 +18,7 @@ fetchurl_lwip fpu fs_log fs_packet +fs_query fs_report gdb_monitor init