/*
* \brief Init component
* \author Norman Feske
* \date 2010-04-27
*/
/*
* Copyright (C) 2010-2016 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.
*/
/* Genode includes */
#include
#include
#include
#include
/* init includes */
#include
namespace Init {
using namespace Genode;
/**
* Read priority-levels declaration from config
*/
inline long read_prio_levels(Xml_node config)
{
long const prio_levels = config.attribute_value("prio_levels", 0UL);
if (prio_levels && (prio_levels != (1 << log2(prio_levels)))) {
warning("prio levels is not power of two, priorities are disabled");
return 0;
}
return prio_levels;
}
/**
* Read affinity-space parameters from config
*
* If no affinity space is declared, construct a space with a single element,
* width and height being 1. If only one of both dimensions is specified, the
* other dimension is set to 1.
*/
inline Genode::Affinity::Space read_affinity_space(Xml_node config)
{
try {
Xml_node node = config.sub_node("affinity-space");
return Affinity::Space(node.attribute_value("width", 1),
node.attribute_value("height", 1));
} catch (...) {
return Affinity::Space(1, 1); }
}
/**
* Read parent-provided services from config
*/
inline void determine_parent_services(Registry &services,
Xml_node config, Allocator &alloc,
bool verbose)
{
if (verbose)
log("parent provides");
config.sub_node("parent-provides")
.for_each_sub_node("service", [&] (Xml_node node) {
Service::Name name = node.attribute_value("name", Service::Name());
new (alloc) Init::Parent_service(services, name);
if (verbose)
log(" service \"", name, "\"");
});
}
}
/********************
** Child registry **
********************/
namespace Init { struct Alias; }
/**
* Representation of an alias for a child
*/
struct Init::Alias : Genode::List::Element
{
typedef Genode::String<128> Name;
typedef Genode::String<128> Child;
Name name;
Child child;
/**
* Exception types
*/
class Name_is_missing { };
class Child_is_missing { };
/**
* Utility to read a string attribute from an XML node
*
* \param STR string type
* \param EXC exception type raised if attribute is not present
*
* \param node XML node
* \param attr_name name of attribute to read
*/
template
static STR _read_string_attr(Genode::Xml_node node, char const *attr_name)
{
char buf[STR::size()];
if (!node.has_attribute(attr_name))
throw EXC();
node.attribute(attr_name).value(buf, sizeof(buf));
return STR(buf);
}
/**
* Constructor
*
* \throw Name_is_missing
* \throw Child_is_missing
*/
Alias(Genode::Xml_node alias)
:
name (_read_string_attr (alias, "name")),
child(_read_string_attr(alias, "child"))
{ }
};
namespace Init {
typedef Genode::List > Child_list;
struct Child_registry;
}
class Init::Child_registry : public Name_registry, Child_list
{
private:
List _aliases;
public:
/**
* Exception type
*/
class Alias_name_is_not_unique { };
/**
* Register child
*/
void insert(Child *child)
{
Child_list::insert(&child->_list_element);
}
/**
* Unregister child
*/
void remove(Child *child)
{
Child_list::remove(&child->_list_element);
}
/**
* Register alias
*/
void insert_alias(Alias *alias)
{
if (!unique(alias->name.string())) {
error("alias name ", alias->name, " is not unique");
throw Alias_name_is_not_unique();
}
_aliases.insert(alias);
}
/**
* Unregister alias
*/
void remove_alias(Alias *alias)
{
_aliases.remove(alias);
}
/**
* Return any of the registered children, or 0 if no child exists
*/
Child *any()
{
return first() ? first()->object() : 0;
}
/**
* Return any of the registered aliases, or 0 if no alias exists
*/
Alias *any_alias()
{
return _aliases.first() ? _aliases.first() : 0;
}
void report_state(Xml_generator &xml, Report_detail const &detail) const
{
Genode::List_element const *curr = first();
for (; curr; curr = curr->next())
curr->object()->report_state(xml, detail);
/* check for name clash with an existing alias */
for (Alias const *a = _aliases.first(); a; a = a->next()) {
xml.node("alias", [&] () {
xml.attribute("name", a->name);
xml.attribute("child", a->child);
});
}
}
/*****************************
** Name-registry interface **
*****************************/
bool unique(const char *name) const
{
/* check for name clash with an existing child */
Genode::List_element const *curr = first();
for (; curr; curr = curr->next())
if (curr->object()->has_name(name))
return false;
/* check for name clash with an existing alias */
for (Alias const *a = _aliases.first(); a; a = a->next()) {
if (Alias::Name(name) == a->name)
return false;
}
return true;
}
Name deref_alias(Name const &name) override
{
for (Alias const *a = _aliases.first(); a; a = a->next())
if (name == a->name)
return a->child;
return name;
}
};
namespace Init {
struct State_reporter;
struct Main;
}
class Init::State_reporter : public Report_update_trigger
{
public:
struct Producer
{
virtual void produce_state_report(Xml_generator &xml,
Report_detail const &) const = 0;
};
private:
Env &_env;
Producer &_producer;
Constructible _reporter;
size_t _buffer_size = 0;
Reconstructible _report_detail;
unsigned _report_delay_ms = 0;
/* version string from config, to be reflected in the report */
typedef String<64> Version;
Version _version;
Constructible _timer;
Signal_handler _timer_handler {
_env.ep(), *this, &State_reporter::_handle_timer };
bool _scheduled = false;
void _handle_timer()
{
_scheduled = false;
try {
Reporter::Xml_generator xml(*_reporter, [&] () {
if (_version.valid())
xml.attribute("version", _version);
_producer.produce_state_report(xml, *_report_detail);
});
}
catch(Xml_generator::Buffer_exceeded) {
error("state report exceeds maximum size");
/* try to reflect the error condition as state report */
try {
Reporter::Xml_generator xml(*_reporter, [&] () {
xml.attribute("error", "report buffer exceeded"); });
}
catch (...) { }
}
}
public:
State_reporter(Env &env, Producer &producer)
:
_env(env), _producer(producer)
{ }
void apply_config(Xml_node config)
{
try {
Xml_node report = config.sub_node("report");
/* (re-)construct reporter whenever the buffer size is changed */
Number_of_bytes const buffer_size =
report.attribute_value("buffer", Number_of_bytes(4096));
if (buffer_size != _buffer_size || !_reporter.constructed()) {
_buffer_size = buffer_size;
_reporter.construct(_env, "state", "state", _buffer_size);
}
_report_detail.construct(report);
_report_delay_ms = report.attribute_value("delay_ms", 100UL);
_reporter->enabled(true);
}
catch (Xml_node::Nonexistent_sub_node) {
_report_detail.construct();
_report_delay_ms = 0;
if (_reporter.constructed())
_reporter->enabled(false);
}
_version = config.attribute_value("version", Version());
if (_report_delay_ms) {
if (!_timer.constructed()) {
_timer.construct(_env);
_timer->sigh(_timer_handler);
}
trigger_report_update();
}
}
void trigger_report_update() override
{
if (!_scheduled && _timer.constructed() && _report_delay_ms) {
_timer->trigger_once(_report_delay_ms*1000);
_scheduled = true;
}
}
};
struct Init::Main : State_reporter::Producer
{
Env &_env;
Registry _parent_services;
Registry _child_services;
Child_registry _children;
Heap _heap { _env.ram(), _env.rm() };
Attached_rom_dataspace _config { _env, "config" };
Reconstructible _verbose { _config.xml() };
unsigned _child_cnt = 0;
void _handle_resource_avail() { }
void produce_state_report(Xml_generator &xml, Report_detail const &detail) const
{
if (detail.init_ram())
xml.node("ram", [&] () { generate_ram_info(xml, _env.ram()); });
if (detail.children())
_children.report_state(xml, detail);
}
State_reporter _state_reporter { _env, *this };
Signal_handler _resource_avail_handler {
_env.ep(), *this, &Main::_handle_resource_avail };
void _handle_config();
Signal_handler _config_handler {
_env.ep(), *this, &Main::_handle_config };
Main(Env &env) : _env(env)
{
_config.sigh(_config_handler);
/* prevent init to block for resource upgrades (never satisfied by core) */
_env.parent().resource_avail_sigh(_resource_avail_handler);
_handle_config();
}
};
void Init::Main::_handle_config()
{
/* kill all currently running children */
while (_children.any()) {
Init::Child *child = _children.any();
_children.remove(child);
destroy(_heap, child);
}
/* remove all known aliases */
while (_children.any_alias()) {
Init::Alias *alias = _children.any_alias();
_children.remove_alias(alias);
destroy(_heap, alias);
}
/* reset knowledge about parent services */
_parent_services.for_each([&] (Init::Parent_service &service) {
destroy(_heap, &service); });
_config.update();
_verbose.construct(_config.xml());
_state_reporter.apply_config(_config.xml());
try { determine_parent_services(_parent_services, _config.xml(),
_heap, _verbose->enabled()); }
catch (...) { }
/* determine default route for resolving service requests */
Xml_node default_route_node("");
try {
default_route_node =
_config.xml().sub_node("default-route"); }
catch (...) { }
/* create aliases */
_config.xml().for_each_sub_node("alias", [&] (Xml_node alias_node) {
try {
_children.insert_alias(new (_heap) Alias(alias_node)); }
catch (Alias::Name_is_missing) {
warning("missing 'name' attribute in '' entry"); }
catch (Alias::Child_is_missing) {
warning("missing 'child' attribute in '' entry"); }
});
/* create children */
try {
_config.xml().for_each_sub_node("start", [&] (Xml_node start_node) {
try {
_children.insert(new (_heap)
Init::Child(_env, _heap, *_verbose,
Init::Child::Id { ++_child_cnt },
_state_reporter,
start_node, default_route_node,
_children, read_prio_levels(_config.xml()),
read_affinity_space(_config.xml()),
_parent_services, _child_services));
}
catch (Rom_connection::Rom_connection_failed) {
/*
* The binary does not exist. An error message is printed
* by the Rom_connection constructor.
*/
}
catch (Ram_session::Alloc_failed) {
warning("failed to allocate memory during child construction"); }
catch (Region_map::Attach_failed) {
warning("failed to attach dataspace to local address space "
"during child construction"); }
catch (Parent::Service_denied) {
warning("failed to create session during child construction"); }
});
}
catch (Xml_node::Nonexistent_sub_node) { error("no children to start"); }
catch (Xml_node::Invalid_syntax) { error("config has invalid syntax"); }
catch (Init::Child::Child_name_is_not_unique) { }
catch (Init::Child_registry::Alias_name_is_not_unique) { }
}
void Component::construct(Genode::Env &env) { static Init::Main main(env); }