os: make internal report_rom classes reusable

This patch moves the formerly internal classes of the report-ROM service
to the public location os/include/report_rom/ so that they can be reused
by other components such as the upcoming clipboard.
This commit is contained in:
Norman Feske
2015-09-23 11:06:25 +02:00
committed by Christian Helmuth
parent 702646a4a3
commit 02d07655ce
10 changed files with 639 additions and 447 deletions

View File

@ -16,10 +16,11 @@
#include <base/env.h>
#include <os/server.h>
#include <os/config.h>
#include <report_rom/rom_service.h>
#include <report_rom/report_service.h>
/* local includes */
#include <rom_service.h>
#include <report_service.h>
#include "rom_registry.h"
namespace Server {
@ -36,9 +37,7 @@ struct Server::Main
Genode::Sliced_heap sliced_heap = { env()->ram_session(),
env()->rm_session() };
Rom::Registry rom_registry = { sliced_heap };
Xml_node _rom_config_node()
Xml_node _rom_config_node() const
{
try {
return Genode::config()->xml_node().sub_node("rom"); }
@ -48,7 +47,7 @@ struct Server::Main
}
}
Xml_node rom_config = _rom_config_node();
Rom::Registry rom_registry = { sliced_heap, _rom_config_node() };
bool _verbose_config()
{
@ -60,7 +59,7 @@ struct Server::Main
bool verbose = _verbose_config();
Report::Root report_root = { ep, sliced_heap, rom_registry, verbose };
Rom ::Root rom_root = { ep, sliced_heap, rom_registry, rom_config};
Rom ::Root rom_root = { ep, sliced_heap, rom_registry };
Main(Entrypoint &ep) : ep(ep)
{

View File

@ -1,140 +0,0 @@
/*
* \brief Server that aggregates reports and exposes them as ROM modules
* \author Norman Feske
* \date 2014-01-11
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _REPORT_SERVICE_H_
#define _REPORT_SERVICE_H_
/* Genode includes */
#include <util/arg_string.h>
#include <report_session/report_session.h>
#include <root/component.h>
#include <os/print_lines.h>
/* local includes */
#include <rom_registry.h>
namespace Report {
struct Session_component;
struct Root;
}
struct Report::Session_component : Genode::Rpc_object<Session>, Rom::Writer
{
private:
Rom::Registry_for_writer &_registry;
Genode::Attached_ram_dataspace _ds;
Rom::Module &_module;
bool &_verbose;
Rom::Module &_create_module(Rom::Module::Name const &name)
{
try {
return _registry.lookup(*this, name);
} catch (...) {
throw Genode::Root::Invalid_args();
}
}
static void _log_lines(char const *string, size_t len)
{
Genode::print_lines<200>(string, len,
[&] (char const *line) { PLOG(" %s", line); });
}
public:
Session_component(Rom::Module::Name const &name, size_t buffer_size,
Rom::Registry_for_writer &registry, bool &verbose)
:
_registry(registry),
_ds(Genode::env()->ram_session(), buffer_size),
_module(_create_module(name)),
_verbose(verbose)
{ }
/**
* Destructor
*
* Clear report when the report session gets closes.
*/
~Session_component()
{
_module.write_content(0, 0);
_registry.release(*this, _module);
}
Dataspace_capability dataspace() override { return _ds.cap(); }
void submit(size_t length) override
{
length = Genode::min(length, _ds.size());
if (_verbose) {
PLOG("report '%s'", _module.name().string());
_log_lines(_ds.local_addr<char>(), length);
}
_module.write_content(_ds.local_addr<char>(), length);
}
void response_sigh(Genode::Signal_context_capability) override { }
size_t obtain_response() override { return 0; }
};
struct Report::Root : Genode::Root_component<Session_component>
{
private:
Rom::Registry_for_writer &_rom_registry;
bool &_verbose;
protected:
Session_component *_create_session(const char *args) override
{
using namespace Genode;
/* read label from session arguments */
char label[200];
Arg_string::find_arg(args, "label").string(label, sizeof(label), "");
/* read report buffer size from session arguments */
size_t const buffer_size =
Arg_string::find_arg(args, "buffer_size").ulong_value(0);
return new (md_alloc())
Session_component(Rom::Module::Name(label), buffer_size,
_rom_registry, _verbose);
}
public:
Root(Server::Entrypoint &ep,
Genode::Allocator &md_alloc,
Rom::Registry_for_writer &rom_registry,
bool &verbose)
:
Genode::Root_component<Session_component>(&ep.rpc_ep(), &md_alloc),
_rom_registry(rom_registry), _verbose(verbose)
{ }
};
#endif /* _REPORT_SERVICE_H_ */

View File

@ -1,208 +0,0 @@
/*
* \brief ROM module written by report service, read by ROM service
* \author Norman Feske
* \date 2014-01-11
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _ROM_MODULE_
#define _ROM_MODULE_
/* Genode includes */
#include <util/volatile_object.h>
#include <os/attached_ram_dataspace.h>
namespace Rom {
using Genode::size_t;
using Genode::Lazy_volatile_object;
using Genode::Attached_ram_dataspace;
class Module;
class Registry;
class Writer;
class Reader;
class Buffer;
typedef Genode::List<Module> Module_list;
typedef Genode::List<Reader> Reader_list;
}
struct Rom::Writer { };
struct Rom::Reader : Reader_list::Element
{
virtual void notify_module_changed() const = 0;
};
/**
* A Rom::Module gets created as soon as either a ROM client or a Report client
* refers to it.
*
* XXX We never know which of both types of client is actually connected. How
* should pay for it? There are two choices: The backing store could be payed
* by the server, thereby exposing the server to possibe resource exhaustion
* triggered by a malicious client. Alternatively, we could make all clients of
* either kind of service pay that refer to the Rom::Module. In the worst case,
* however, if there are many client for a single report, the paid-for RAM
* quota will never be used. For now, we simply allocate the backing store from
* the server's quota.
*
* The Rom::Module gets destroyed no client refers to it anymore.
*/
struct Rom::Module : Module_list::Element
{
public:
typedef Genode::String<200> Name;
private:
Name _name;
/**
* The ROM module may be read by any number of ROM clients
*/
Reader_list mutable _readers;
/**
* There must be only one or no writer
*/
Writer const mutable * _writer = 0;
/**
* Dataspace used as backing store
*
* The buffer for the content is not allocated from the heap to
* allow for the immediate release of the underlying backing store when
* the module gets destructed.
*/
Lazy_volatile_object<Attached_ram_dataspace> _ds;
/**
* Content size, which may less than the capacilty of '_ds'.
*/
size_t _size = 0;
void _notify_readers() const
{
for (Reader const *r = _readers.first(); r; r = r->next())
r->notify_module_changed();
}
/********************************
** Interface used by registry **
********************************/
friend class Registry;
/**
* Constructor
*/
Module(Name const &name) : _name(name) { }
bool _reader_is_registered(Reader const &reader) const
{
for (Reader const *r = _readers.first(); r; r = r->next())
if (r == &reader)
return true;
return false;
}
void _register(Reader const &reader) const { _readers.insert(&reader); }
void _unregister(Reader const &reader) const { _readers.remove(&reader); }
void _register(Writer const &writer) const
{
class Unexpected_multiple_writers { };
if (_writer)
throw Unexpected_multiple_writers();
_writer = &writer;
}
void _unregister(Writer const &writer) const
{
class Unexpected_unknown_writer { };
if (_writer != &writer)
throw Unexpected_unknown_writer();
_writer = 0;
}
bool _has_name(Name const &name) const { return name == _name; }
bool _is_in_use() const { return _readers.first() || _writer; }
public:
/**
* Assign new content to the ROM module
*
* Called by report service when a new report comes in.
*/
void write_content(char const * const src, size_t const src_len)
{
_size = 0;
/*
* Realloc backing store if needed
*
* Take a terminating zero into account, which we append to each
* report. This way, we do not need to trust report clients to
* append a zero termination to textual reports.
*/
if (!_ds.is_constructed() || _ds->size() < (src_len + 1))
_ds.construct(Genode::env()->ram_session(), (src_len + 1));
/* copy content into backing store */
_size = src_len;
Genode::memcpy(_ds->local_addr<char>(), src, _size);
/* append zero termination */
_ds->local_addr<char>()[src_len] = 0;
/* notify ROM clients that access the module */
for (Reader const *r = _readers.first(); r; r = r->next())
r->notify_module_changed();
}
/**
* Exception type
*/
class Buffer_too_small { };
/**
* Read content of ROM module
*
* Called by ROM service when a dataspace is obtained by the client.
*/
size_t read_content(char *dst, size_t dst_len) const
{
if (!_ds.is_constructed())
return 0;
if (dst_len < _size)
throw Buffer_too_small();
Genode::memcpy(dst, _ds->local_addr<char>(), _size);
return _size;
}
size_t size() const { return _size; }
Name name() const { return _name; }
};
#endif /* _ROM_MODULE_ */

View File

@ -14,29 +14,10 @@
#ifndef _ROM_REGISTRY_H_
#define _ROM_REGISTRY_H_
/* local includes */
#include <rom_module.h>
/* Genode includes */
#include <report_rom/rom_registry.h>
namespace Rom {
struct Registry_for_reader;
struct Registry_for_writer;
}
struct Rom::Registry_for_reader
{
virtual Module const &lookup(Reader const &reader, Module::Name const &name) = 0;
virtual void release(Reader const &reader, Module const &module) = 0;
};
struct Rom::Registry_for_writer
{
virtual Module &lookup(Writer const &writer, Module::Name const &name) = 0;
virtual void release(Writer const &writer, Module const &module) = 0;
};
namespace Rom { struct Registry; }
struct Rom::Registry : Registry_for_reader, Registry_for_writer, Genode::Noncopyable
@ -45,8 +26,38 @@ struct Rom::Registry : Registry_for_reader, Registry_for_writer, Genode::Noncopy
Genode::Allocator &_md_alloc;
Xml_node _config;
Module_list _modules;
struct Read_write_policy : Module::Read_policy, Module::Write_policy
{
bool read_permitted(Module const &,
Writer const &,
Reader const &) const override
{
/*
* The access-control policy is applied at the ROM-session
* construction time by applying the '_report_name' method
* on the session label. Once connected to a ROM module,
* the ROM client is always allowed to read the ROM content.
*/
return true;
}
bool write_permitted(Module const &, Writer const &) const override
{
/*
* Because the report-session label is used as the module name
* for the writer, each report session refers to a distinct
* module. Report client can write to their respective modules
* at any time.
*/
return true;
}
} _read_write_policy;
Module &_lookup(Module::Name const name)
{
for (Module *m = _modules.first(); m; m = m->next())
@ -57,10 +68,12 @@ struct Rom::Registry : Registry_for_reader, Registry_for_writer, Genode::Noncopy
/* XXX proper accounting for the used memory is missing */
/* XXX if we run out of memory, the server will abort */
Module * const module = new (&_md_alloc) Module(name);
Module * const module = new (&_md_alloc)
Module(name, _read_write_policy, _read_write_policy);
_modules.insert(module);
return *module;
}
void _try_to_destroy(Module const &module)
@ -73,7 +86,7 @@ struct Rom::Registry : Registry_for_reader, Registry_for_writer, Genode::Noncopy
}
template <typename USER>
Module &_lookup(USER const &user, Module::Name const &name)
Module &_lookup(USER &user, Module::Name const &name)
{
Module &module = _lookup(name);
module._register(user);
@ -81,34 +94,82 @@ struct Rom::Registry : Registry_for_reader, Registry_for_writer, Genode::Noncopy
}
template <typename USER>
void _release(USER const &user, Module const &module)
void _release(USER &user, Module const &module)
{
module._unregister(user);
/*
* The '_release' function is called by both the report service
* and the ROM service. The latter has merely a 'const' version
* of the module because it is not supposed to modify it. However,
* when closing a ROM session, we have to disassociate the ROM
* session from the module. To do that, we need a non-const
* reference to the module.
*/
const_cast<Module &>(module)._unregister(user);
_try_to_destroy(module);
}
/**
* Return report name that corresponds to the given ROM session label
*
* \throw Registry_for_reader::Lookup_failed
*/
Module::Name _report_name(Module::Name const &rom_label) const
{
try {
for (Xml_node node = _config.sub_node("policy");
true; node = node.next("policy")) {
if (!node.has_attribute("label")
|| !node.has_attribute("report")
|| !node.attribute("label").has_value(rom_label.string()))
continue;
char report[Rom::Module::Name::capacity()];
node.attribute("report").value(report, sizeof(report));
return Rom::Module::Name(report);
}
} catch (Xml_node::Nonexistent_sub_node) { }
PWRN("no valid policy for label \"%s\"", rom_label.string());
throw Root::Invalid_args();
}
public:
Registry(Genode::Allocator &md_alloc) : _md_alloc(md_alloc) { }
Registry(Genode::Allocator &md_alloc, Xml_node config)
:
_md_alloc(md_alloc), _config(config)
{ }
Module &lookup(Writer const &writer, Module::Name const &name) override
Module &lookup(Writer &writer, Module::Name const &name) override
{
return _lookup(writer, name);
Module &module = _lookup(writer, name);
/*
* Enforce invariant that each module can have only one writer at a
* time.
*/
if (module._num_writers() > 1) {
release(writer, module);
throw Root::Invalid_args();
}
return module;
}
void release(Writer const &writer, Module const &module) override
void release(Writer &writer, Module &module) override
{
return _release(writer, module);
}
Module const &lookup(Reader const &reader, Module::Name const &name) override
Readable_module &lookup(Reader &reader, Module::Name const &rom_label) override
{
return _lookup(reader, name);
return _lookup(reader, _report_name(rom_label));
}
void release(Reader const &reader, Module const &module) override
void release(Reader &reader, Readable_module &module) override
{
return _release(reader, module);
return _release(reader, static_cast<Module &>(module));
}
};

View File

@ -1,172 +0,0 @@
/*
* \brief ROM service
* \author Norman Feske
* \date 2014-01-11
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _ROM_SERVICE_H_
#define _ROM_SERVICE_H_
/* Genode includes */
#include <util/arg_string.h>
#include <util/xml_node.h>
#include <rom_session/rom_session.h>
#include <root/component.h>
/* local includes */
#include <rom_registry.h>
namespace Rom {
class Session_component;
class Root;
using Genode::Xml_node;
}
class Rom::Session_component : public Genode::Rpc_object<Genode::Rom_session>,
public Reader
{
private:
Registry_for_reader &_registry;
Module const &_module;
Lazy_volatile_object<Genode::Attached_ram_dataspace> _ds;
size_t _content_size = 0;
Genode::Signal_context_capability _sigh;
public:
Session_component(Registry_for_reader &registry,
Rom::Module::Name const &name)
:
_registry(registry), _module(_registry.lookup(*this, name))
{ }
~Session_component()
{
_registry.release(*this, _module);
}
Genode::Rom_dataspace_capability dataspace() override
{
using namespace Genode;
/* replace dataspace by new one */
/* XXX we could keep the old dataspace if the size fits */
_ds.construct(env()->ram_session(), _module.size());
/* fill dataspace content with report contained in module */
_content_size = _module.read_content(_ds->local_addr<char>(), _ds->size());
/* cast RAM into ROM dataspace capability */
Dataspace_capability ds_cap = static_cap_cast<Dataspace>(_ds->cap());
return static_cap_cast<Rom_dataspace>(ds_cap);
}
bool update() override
{
if (!_ds.is_constructed() || _module.size() > _ds->size())
return false;
size_t const new_content_size =
_module.read_content(_ds->local_addr<char>(), _ds->size());
/* clear difference between old and new content */
if (new_content_size < _content_size)
Genode::memset(_ds->local_addr<char>() + new_content_size, 0,
_content_size - new_content_size);
_content_size = new_content_size;
return true;
}
void sigh(Genode::Signal_context_capability sigh) override
{
_sigh = sigh;
}
/**
* Implementation of 'Reader' interface
*/
void notify_module_changed() const override
{
if (_sigh.valid())
Genode::Signal_transmitter(_sigh).submit();
}
};
class Rom::Root : public Genode::Root_component<Session_component>
{
private:
Registry_for_reader &_registry;
Xml_node const &_config;
typedef Rom::Module::Name Label;
/**
* Determine module name for label according to the configured policy
*/
Rom::Module::Name _module_name(Label const &label) const
{
try {
for (Xml_node node = _config.sub_node("policy");
true; node = node.next("policy")) {
if (!node.has_attribute("label")
|| !node.has_attribute("report")
|| !node.attribute("label").has_value(label.string()))
continue;
char report[Rom::Module::Name::capacity()];
node.attribute("report").value(report, sizeof(report));
return Rom::Module::Name(report);
}
} catch (Xml_node::Nonexistent_sub_node) { }
PWRN("no valid policy for label \"%s\"", label.string());
throw Root::Invalid_args();
}
protected:
Session_component *_create_session(const char *args) override
{
using namespace Genode;
/* read label of ROM module from args */
char label[Label::capacity()];
Arg_string::find_arg(args, "label").string(label, sizeof(label), "");
return new (md_alloc())
Session_component(_registry, _module_name(Label(label)));
}
public:
Root(Server::Entrypoint &ep,
Genode::Allocator &md_alloc,
Registry_for_reader &registry,
Xml_node const &config)
:
Genode::Root_component<Session_component>(&ep.rpc_ep(), &md_alloc),
_registry(registry),
_config(config)
{ }
};
#endif /* _ROM_SERVICE_H_ */