mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-18 21:27:56 +00:00
parent
1aba1fe8b1
commit
b4f596b197
10
repos/gems/recipes/src/fs_query/content.mk
Normal file
10
repos/gems/recipes/src/fs_query/content.mk
Normal file
@ -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)
|
1
repos/gems/recipes/src/fs_query/hash
Normal file
1
repos/gems/recipes/src/fs_query/hash
Normal file
@ -0,0 +1 @@
|
||||
2018-08-21 474d21ebb2298e4b411dcef5c32a452c053b09c7
|
4
repos/gems/recipes/src/fs_query/used_apis
Normal file
4
repos/gems/recipes/src/fs_query/used_apis
Normal file
@ -0,0 +1,4 @@
|
||||
base
|
||||
os
|
||||
vfs
|
||||
report_session
|
199
repos/gems/run/fs_query.run
Normal file
199
repos/gems/run/fs_query.run
Normal file
@ -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 {
|
||||
<config>
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="LOG"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="PD"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="IO_PORT"/>
|
||||
</parent-provides>
|
||||
<default-route>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
<default caps="100"/>
|
||||
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides> <service name="Timer"/> </provides>
|
||||
</start>
|
||||
|
||||
<start name="report_rom">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides> <service name="Report"/> <service name="ROM"/> </provides>
|
||||
<config verbose="yes"/>
|
||||
</start>
|
||||
|
||||
<start name="vfs">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<provides><service name="File_system"/></provides>
|
||||
<config>
|
||||
<vfs>
|
||||
<ram/>
|
||||
<import>
|
||||
<dir name="items">
|
||||
<inline name="1">first</inline>
|
||||
<inline name="2">second</inline>
|
||||
<inline name="3"><third/></inline>
|
||||
</dir>
|
||||
<inline name="4">fourth</inline>
|
||||
</import>
|
||||
</vfs>
|
||||
<default-policy root="/" writeable="yes"/>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="fs_query">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<config>
|
||||
<vfs> <dir name="fs"> <fs writeable="yes"/> </dir> </vfs>
|
||||
<query path="/fs/items" content="yes"/>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="test" caps="700">
|
||||
<binary name="sequence"/>
|
||||
<resource name="RAM" quantum="64M"/>
|
||||
<config>
|
||||
<start name="sleep" caps="500">
|
||||
<binary name="noux"/>
|
||||
<config stdin="/dev/null" stdout="/dev/log" stderr="/dev/log">
|
||||
<fstab>
|
||||
<tar name="coreutils.tar" />
|
||||
<dir name="dev"> <log/> <null/> </dir>
|
||||
</fstab>
|
||||
<start name="/bin/sleep"> <arg value="0.5"/> </start>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="remove" caps="500">
|
||||
<binary name="noux"/>
|
||||
<config stdin="/dev/null" stdout="/dev/log" stderr="/dev/log">
|
||||
<fstab>
|
||||
<tar name="coreutils.tar" />
|
||||
<dir name="fs"> <fs writeable="yes"/> </dir>
|
||||
<dir name="dev"> <log/> <null/> </dir>
|
||||
</fstab>
|
||||
<start name="/bin/rm"> <arg value="/fs/items/2"/> </start>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="sleep" caps="500">
|
||||
<binary name="noux"/>
|
||||
<config stdin="/dev/null" stdout="/dev/log" stderr="/dev/log">
|
||||
<fstab>
|
||||
<tar name="coreutils.tar" />
|
||||
<dir name="dev"> <log/> <null/> </dir>
|
||||
</fstab>
|
||||
<start name="/bin/sleep"> <arg value="0.5"/> </start>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="add" caps="500">
|
||||
<binary name="noux"/>
|
||||
<config stdin="/dev/null" stdout="/dev/log" stderr="/dev/log">
|
||||
<fstab>
|
||||
<tar name="coreutils.tar" />
|
||||
<dir name="fs"> <fs writeable="yes"/> </dir>
|
||||
<dir name="dev"> <log/> <null/> </dir>
|
||||
</fstab>
|
||||
<start name="/bin/cp">
|
||||
<arg value="/fs/4"/>
|
||||
<arg value="/fs/items/"/>
|
||||
</start>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="sleep" caps="500">
|
||||
<binary name="noux"/>
|
||||
<config stdin="/dev/null" stdout="/dev/log" stderr="/dev/log">
|
||||
<fstab>
|
||||
<tar name="coreutils.tar" />
|
||||
<dir name="dev"> <log/> <null/> </dir>
|
||||
</fstab>
|
||||
<start name="/bin/sleep"> <arg value="0.5"/> </start>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="modify" caps="500">
|
||||
<binary name="noux"/>
|
||||
<config stdin="/dev/null" stdout="/dev/log" stderr="/dev/log">
|
||||
<fstab>
|
||||
<tar name="bash.tar" />
|
||||
<dir name="fs"> <fs writeable="yes"/> </dir>
|
||||
<dir name="dev"> <log/> <null/> </dir>
|
||||
</fstab>
|
||||
<start name="/bin/bash">
|
||||
<arg value="-c"/>
|
||||
<arg value="echo updated > /fs/items/3"/>
|
||||
</start>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="sleep" caps="500">
|
||||
<binary name="noux"/>
|
||||
<config stdin="/dev/null" stdout="/dev/log" stderr="/dev/log">
|
||||
<fstab>
|
||||
<tar name="coreutils.tar" />
|
||||
<dir name="dev"> <log/> <null/> </dir>
|
||||
</fstab>
|
||||
<start name="/bin/sleep"> <arg value="1"/> </start>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
</config>
|
||||
</start>
|
||||
</config>
|
||||
}
|
||||
|
||||
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] <listing>
|
||||
[init -> report_rom] <dir path="/fs/items">
|
||||
[init -> report_rom] <file name="4">fourth</file>
|
||||
[init -> report_rom] <file name="1">first</file>
|
||||
[init -> report_rom] <file name="3">updated
|
||||
[init -> report_rom] </file>
|
||||
[init -> report_rom] </dir>
|
||||
[init -> report_rom] </listing>
|
||||
}
|
14
repos/gems/src/app/fs_query/README
Normal file
14
repos/gems/src/app/fs_query/README
Normal file
@ -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 '<query>' nodes within its '<config>' node. Each
|
||||
'<query>' 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 '<dir>' node with the
|
||||
list of files as '<file>' nodes featuring the corresponding 'name' as
|
||||
attribute value.
|
||||
|
||||
A '<query>' can be equipped with a 'content="yes"' attribute. If set, the
|
||||
content of the queried files is supplemented as body of the '<file>' nodes.
|
||||
The reported content is limited to 4 KiB per file. If the content is valid
|
||||
XML, the '<file>' node contains an attribute 'xml="yes"' indicating that
|
||||
the XML information is inserted as is. Otherwise, the content is sanitized.
|
222
repos/gems/src/app/fs_query/main.cc
Normal file
222
repos/gems/src/app/fs_query/main.cc
Normal file
@ -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 <base/registry.h>
|
||||
#include <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <os/reporter.h>
|
||||
#include <gems/vfs.h>
|
||||
|
||||
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<Registered<Watched_file> > _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<Watched_file>(_files, _dir, entry.name());
|
||||
} catch (...) { }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
virtual ~Watched_directory()
|
||||
{
|
||||
_files.for_each([&] (Registered<Watched_file> &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<Main> _config_handler {
|
||||
_env.ep(), *this, &Main::_handle_config };
|
||||
|
||||
Expanding_reporter _reporter { _env, "listing", "listing" };
|
||||
|
||||
Registry<Registered<Watched_directory> > _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<Watched_directory> &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<Watched_directory>(_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);
|
||||
}
|
||||
|
3
repos/gems/src/app/fs_query/target.mk
Normal file
3
repos/gems/src/app/fs_query/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = fs_query
|
||||
SRC_CC = main.cc
|
||||
LIBS += base vfs
|
@ -18,6 +18,7 @@ fetchurl_lwip
|
||||
fpu
|
||||
fs_log
|
||||
fs_packet
|
||||
fs_query
|
||||
fs_report
|
||||
gdb_monitor
|
||||
init
|
||||
|
Loading…
Reference in New Issue
Block a user