genode/repos/gems/src/app/depot_deploy/child.h
Norman Feske 5286456e48 depot_deploy: graceful handling of missing content
This patch improves the error handling for the case where the depot
lacks the content of the to-be-deployed pkg. Instead of infinitely
reattempting to obtain blueprints for such content, the deploy tool
prints a single message.
2018-02-16 08:42:29 +01:00

326 lines
8.8 KiB
C++

/*
* \brief Child representation
* \author Norman Feske
* \date 2018-01-23
*/
/*
* 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.
*/
#ifndef _CHILD_H_
#define _CHILD_H_
/* Genode includes */
#include <util/list_model.h>
#include <base/service.h>
#include <os/reporter.h>
#include <os/buffered_xml.h>
#include <depot/archive.h>
namespace Depot_deploy {
using namespace Depot;
struct Child;
}
class Depot_deploy::Child : public List_model<Child>::Element
{
public:
typedef String<100> Name;
typedef String<80> Binary_name;
typedef String<80> Config_name;
private:
Allocator &_alloc;
Reconstructible<Buffered_xml> _start_xml; /* from config */
Constructible<Buffered_xml> _pkg_xml { }; /* from blueprint */
Name const _name;
Archive::Path _config_pkg_path() const
{
return _start_xml->xml().attribute_value("pkg", Archive::Path());
}
/*
* The pkg-archive path of the current blueprint query, which may
* deviate from pkg path given in the config, once the config is
* updated.
*/
Archive::Path _blueprint_pkg_path = _config_pkg_path();
Ram_quota _ram_quota { 0 };
Cap_quota _cap_quota { 0 };
Binary_name _binary_name { };
Config_name _config_name { };
/*
* Set if the depot query for the child's blueprint failed.
*/
bool _pkg_incomplete = false;
bool _configured() const
{
return _pkg_xml.constructed()
&& (_config_pkg_path() == _blueprint_pkg_path);
}
inline void _gen_routes(Xml_generator &, Xml_node common) const;
static void _gen_provides_sub_node(Xml_generator &xml, Xml_node service,
Xml_node::Type const &node_type,
Service::Name const &service_name)
{
if (service.type() == node_type)
xml.node("service", [&] () {
xml.attribute("name", service_name); });
}
static void _gen_copy_of_sub_node(Xml_generator &xml, Xml_node from_node,
Xml_node::Type const &sub_node_type)
{
if (!from_node.has_sub_node(sub_node_type.string()))
return;
Xml_node const sub_node = from_node.sub_node(sub_node_type.string());
xml.append(sub_node.addr(), sub_node.size());
}
public:
Child(Allocator &alloc, Xml_node start_node)
:
_alloc(alloc),
_start_xml(_alloc, start_node),
_name(_start_xml->xml().attribute_value("name", Name()))
{ }
Name name() const { return _name; }
void apply_config(Xml_node start_node)
{
/*
* String-compare new with current start node to quicky skip
* the start nodes that have not changed.
*/
bool const start_node_changed =
(start_node.size() != _start_xml->xml().size()) ||
(strcmp(start_node.addr(), _start_xml->xml().addr(),
start_node.size()) != 0);
if (!start_node_changed)
return;
Archive::Path const pkg =
start_node.attribute_value("pkg", Archive::Path());
/* invalidate blueprint if 'pkg' attribute of start node changed */
if (pkg != _config_pkg_path())
_pkg_xml.destruct();
/* import new start node */
_start_xml.construct(_alloc, start_node);
/* reset error state, attempt to obtain the blueprint again */
_pkg_incomplete = false;
}
void apply_blueprint(Xml_node pkg)
{
if (pkg.attribute_value("path", Archive::Path()) != _blueprint_pkg_path)
return;
Xml_node const runtime = pkg.sub_node("runtime");
Number_of_bytes const ram { runtime.attribute_value("ram", Number_of_bytes()) };
_ram_quota = Ram_quota { _start_xml->xml().attribute_value("ram", ram) };
unsigned long const caps = runtime.attribute_value("caps", 0UL);
_cap_quota = Cap_quota { _start_xml->xml().attribute_value("caps", caps) };
_binary_name = runtime.attribute_value("binary", Binary_name());
_config_name = runtime.attribute_value("config", Config_name());
/* keep copy of the blueprint info */
_pkg_xml.construct(_alloc, pkg);
}
void mark_as_incomplete(Xml_node missing)
{
/* print error message only once */
if(_pkg_incomplete)
return;
Archive::Path const path = missing.attribute_value("path", Archive::Path());
if (path != _blueprint_pkg_path)
return;
error(path, " incomplete or missing");
_pkg_incomplete = true;
}
void gen_query(Xml_generator &xml) const
{
if (_configured() || _pkg_incomplete)
return;
xml.node("blueprint", [&] () {
xml.attribute("pkg", _blueprint_pkg_path); });
}
/**
* Generate start node of init configuration
*
* \param common session routes to be added in addition to the ones
* found in the pkg blueprint
*/
inline void gen_start_node(Xml_generator &, Xml_node common) const;
};
void Depot_deploy::Child::gen_start_node(Xml_generator &xml, Xml_node common) const
{
if (!_configured())
return;
if (!_pkg_xml->xml().has_sub_node("runtime")) {
warning("blueprint for '", _name, "' lacks runtime information");
return;
}
xml.node("start", [&] () {
xml.attribute("name", _name);
xml.attribute("caps", _cap_quota.value);
typedef String<64> Version;
Version const version = _start_xml->xml().attribute_value("version", Version());
if (version.valid())
xml.attribute("version", version);
xml.node("binary", [&] () { xml.attribute("name", _binary_name); });
xml.node("resource", [&] () {
xml.attribute("name", "RAM");
xml.attribute("quantum", String<32>(Number_of_bytes(_ram_quota.value)));
});
Xml_node const runtime = _pkg_xml->xml().sub_node("runtime");
/*
* Insert inline '<config>' node if provided by the start node or the
* blueprint. The former is preferred over the latter.
*/
if (_start_xml->xml().has_sub_node("config")) {
_gen_copy_of_sub_node(xml, _start_xml->xml(), "config");
} else {
if (runtime.has_sub_node("config"))
_gen_copy_of_sub_node(xml, runtime, "config");
}
/*
* Declare services provided by the subsystem.
*/
if (runtime.has_sub_node("provides")) {
xml.node("provides", [&] () {
runtime.sub_node("provides").for_each_sub_node([&] (Xml_node service) {
_gen_provides_sub_node(xml, service, "rom", "ROM");
_gen_provides_sub_node(xml, service, "log", "LOG");
_gen_provides_sub_node(xml, service, "timer", "Timer");
_gen_provides_sub_node(xml, service, "block", "Block");
_gen_provides_sub_node(xml, service, "report", "Report");
_gen_provides_sub_node(xml, service, "nic", "Nic");
_gen_provides_sub_node(xml, service, "nitpicker", "Nitpicker");
_gen_provides_sub_node(xml, service, "framebuffer", "Framebuffer");
_gen_provides_sub_node(xml, service, "input", "Input");
_gen_provides_sub_node(xml, service, "audio_out", "Audio_out");
_gen_provides_sub_node(xml, service, "audio_in", "Audio_in");
_gen_provides_sub_node(xml, service, "file_system", "File_system");
});
});
}
xml.node("route", [&] () { _gen_routes(xml, common); });
});
}
void Depot_deploy::Child::_gen_routes(Xml_generator &xml, Xml_node common) const
{
if (!_pkg_xml.constructed())
return;
typedef String<160> Path;
/*
* Add routes given in the start node.
*/
if (_start_xml->xml().has_sub_node("route")) {
Xml_node const route = _start_xml->xml().sub_node("route");
xml.append(route.content_base(), route.content_size());
}
/*
* Redirect config ROM request to label as given in the 'config' attribute,
* if present. We need to search the blueprint's <rom> nodes for the
* matching ROM module to rewrite the label with the configuration's path
* within the depot.
*/
if (_config_name.valid()) {
_pkg_xml->xml().for_each_sub_node("rom", [&] (Xml_node rom) {
if (!rom.has_attribute("path"))
return;
if (rom.attribute_value("label", Name()) != _config_name)
return;
/* we found the <rom> node for the config ROM */
xml.node("service", [&] () {
xml.attribute("name", "ROM");
xml.attribute("label", "config");
xml.node("parent", [&] () {
typedef String<160> Path;
xml.attribute("label", rom.attribute_value("path", Path()));
});
});
});
}
/*
* Add common routes as defined in our config.
*/
xml.append(common.content_base(), common.content_size());
/*
* Add ROM routing rule with the label rewritten to the path within the
* depot.
*/
_pkg_xml->xml().for_each_sub_node("rom", [&] (Xml_node rom) {
if (!rom.has_attribute("path"))
return;
typedef Name Label;
Path const path = rom.attribute_value("path", Path());
Label const label = rom.attribute_value("label", Label());
xml.node("service", [&] () {
xml.attribute("name", "ROM");
xml.attribute("label_last", label);
xml.node("parent", [&] () {
xml.attribute("label", path); });
});
});
}
#endif /* _CHILD_H_ */