genode/repos/os/include/report_rom/rom_module.h
Norman Feske eba9c15746 Follow practices suggested by "Effective C++"
The patch adjust the code of the base, base-<kernel>, and os repository.
To adapt existing components to fix violations of the best practices
suggested by "Effective C++" as reported by the -Weffc++ compiler
argument. The changes follow the patterns outlined below:

* A class with virtual functions can no longer publicly inherit base
  classed without a vtable. The inherited object may either be moved
  to a member variable, or inherited privately. The latter would be
  used for classes that inherit 'List::Element' or 'Avl_node'. In order
  to enable the 'List' and 'Avl_tree' to access the meta data, the
  'List' must become a friend.

* Instead of adding a virtual destructor to abstract base classes,
  we inherit the new 'Interface' class, which contains a virtual
  destructor. This way, single-line abstract base classes can stay
  as compact as they are now. The 'Interface' utility resides in
  base/include/util/interface.h.

* With the new warnings enabled, all member variables must be explicitly
  initialized. Basic types may be initialized with '='. All other types
  are initialized with braces '{ ... }' or as class initializers. If
  basic types and non-basic types appear in a row, it is nice to only
  use the brace syntax (also for basic types) and align the braces.

* If a class contains pointers as members, it must now also provide a
  copy constructor and assignment operator. In the most cases, one
  would make them private, effectively disallowing the objects to be
  copied. Unfortunately, this warning cannot be fixed be inheriting
  our existing 'Noncopyable' class (the compiler fails to detect that
  the inheriting class cannot be copied and still gives the error).
  For now, we have to manually add declarations for both the copy
  constructor and assignment operator as private class members. Those
  declarations should be prepended with a comment like this:

        /*
         * Noncopyable
         */
        Thread(Thread const &);
        Thread &operator = (Thread const &);

  In the future, we should revisit these places and try to replace
  the pointers with references. In the presence of at least one
  reference member, the compiler would no longer implicitly generate
  a copy constructor. So we could remove the manual declaration.

Issue #465
2018-01-17 12:14:35 +01:00

315 lines
7.7 KiB
C++

/*
* \brief ROM module written by report service, read by ROM service
* \author Norman Feske
* \date 2014-01-11
*/
/*
* Copyright (C) 2014-2017 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.
*/
#ifndef _INCLUDE__REPORT_ROM__ROM_MODULE_H_
#define _INCLUDE__REPORT_ROM__ROM_MODULE_H_
/* Genode includes */
#include <util/reconstructible.h>
#include <os/session_policy.h>
#include <base/attached_ram_dataspace.h>
namespace Rom {
using Genode::size_t;
using Genode::Constructible;
using Genode::Attached_ram_dataspace;
using Genode::Interface;
class Module;
class Readable_module;
class Registry;
class Writer;
class Reader;
class Buffer;
typedef Genode::List<Module> Module_list;
typedef Genode::List<Reader> Reader_list;
typedef Genode::List<Writer> Writer_list;
}
struct Rom::Writer : private Writer_list::Element, Interface
{
friend class Genode::List<Writer>;
using Writer_list::Element::next;
virtual Genode::Session_label label() const = 0;
};
struct Rom::Reader : private Reader_list::Element, Interface
{
friend class Genode::List<Reader>;
using Reader_list::Element::next;
virtual void notify_module_changed() = 0;
virtual void notify_module_invalidated() = 0;
};
struct Rom::Readable_module : Interface
{
/**
* Exception type
*/
class Buffer_too_small { };
/**
* Read content of ROM module
*
* Called by ROM service when a dataspace is obtained by the client.
*
* \throw Buffer_too_small
*/
virtual size_t read_content(Reader const &reader, char *dst,
size_t dst_len) const = 0;
virtual size_t size() 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 paid
* 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 when no client refers to it anymore.
*/
struct Rom::Module : private Module_list::Element, Readable_module
{
private:
friend class Genode::List<Module>;
/*
* Noncopyable
*/
Module(Module const &);
Module &operator = (Module const &);
public:
using Module_list::Element::next;
typedef Genode::String<200> Name;
struct Read_policy : Interface
{
/**
* Return true if the reader is allowed to read the module content
*/
virtual bool read_permitted(Module const &,
Writer const &, Reader const &) const = 0;
};
struct Write_policy : Interface
{
/**
* Return true of the writer is permitted to write content
*
* This policy hook can be used to implement dynamic policies
* as employed by the clipboard, which blocks reports from
* unfocused clients.
*/
virtual bool write_permitted(Module const &, Writer const &) const = 0;
};
private:
Name _name;
Genode::Ram_session &_ram;
Genode::Region_map &_rm;
Read_policy const &_read_policy;
Write_policy const &_write_policy;
Reader_list mutable _readers { };
Writer_list mutable _writers { };
/**
* Origin of the content currently stored in the module
*/
Writer const *_last_writer = nullptr;
/**
* 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.
*/
Constructible<Attached_ram_dataspace> _ds { };
/**
* Content size, which may less than the capacilty of '_ds'.
*/
size_t _size = 0;
/********************************
** Interface used by registry **
********************************/
friend class Registry;
/**
* Constructor
*
* \param ram RAM session from which to allocate the module's
* backing store
* \param rm region map of the local address space, needed
* to access the allocated backing store
* \param name module name
* \param read_policy policy hook function that is evaluated each
* time when the module content is obtained
* \param write_policy policy hook function that is evaluated each
* time when the module content is changed
*/
Module(Genode::Ram_session &ram,
Genode::Region_map &rm,
Name const &name,
Read_policy const &read_policy,
Write_policy const &write_policy)
:
_name(name), _ram(ram), _rm(rm),
_read_policy(read_policy), _write_policy(write_policy)
{ }
/*************************************************
** Interface to be used by the 'Registry' only **
*************************************************/
bool _reader_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 &reader) { _readers.insert(&reader); }
void _unregister(Reader &reader) { _readers.remove(&reader); }
void _register(Writer &writer)
{
_writers.insert(&writer);
}
void _unregister(Writer const &writer)
{
_writers.remove(&writer);
/* clear content if its origin disappears */
if (_last_writer == &writer) {
Genode::memset(_ds->local_addr<char>(), 0, _size);
_size = 0;
_last_writer = nullptr;
}
}
bool _has_name(Name const &name) const { return name == _name; }
bool _in_use() const
{
return _readers.first() || _writers.first();
}
unsigned _num_writers() const
{
unsigned cnt = 0;
for (Writer const *w = _writers.first(); w; w = w->next())
cnt++;
return cnt;
}
public:
/**
* Assign new content to the ROM module
*
* Called by report service when a new report comes in.
*/
void write_content(Writer const &writer, char const * const src, size_t const src_len)
{
if (!_write_policy.write_permitted(*this, writer))
return;
_size = 0;
_last_writer = &writer;
/*
* 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.constructed() || _ds->size() < (src_len + 1))
_ds.construct(_ram, _rm, (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 *r = _readers.first(); r; r = r->next()) {
if (_read_policy.read_permitted(*this, *_last_writer, *r))
r->notify_module_changed();
else
r->notify_module_invalidated();
}
}
/**
* Readable_module interface
*/
size_t read_content(Reader const &reader, char *dst, size_t dst_len) const override
{
if (!_ds.constructed() || !_last_writer)
return 0;
if (!_read_policy.read_permitted(*this, *_last_writer, reader))
return 0;
if (dst_len < _size)
throw Buffer_too_small();
Genode::memcpy(dst, _ds->local_addr<char>(), _size);
return _size;
}
virtual size_t size() const override { return _size; }
Name name() const { return _name; }
};
#endif /* _INCLUDE__REPORT_ROM__ROM_MODULE_H_ */