b2a7ac2996
the same as the ram and cap quota
527 lines
15 KiB
C++
527 lines
15 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;
|
|
typedef String<32> Depot_rom_server;
|
|
typedef String<100> Launcher_name;
|
|
|
|
private:
|
|
|
|
Allocator &_alloc;
|
|
|
|
Reconstructible<Buffered_xml> _start_xml; /* from config */
|
|
Constructible<Buffered_xml> _launcher_xml { };
|
|
Constructible<Buffered_xml> _pkg_xml { }; /* from blueprint */
|
|
|
|
/*
|
|
* State of the condition check for generating the start node of
|
|
* the child. I.e., if the child is complete and configured but
|
|
* a used server component is missing, we need to suppress the start
|
|
* node until the condition is satisfied.
|
|
*/
|
|
enum Condition { UNCHECKED, SATISFIED, UNSATISFIED };
|
|
|
|
Condition _condition { UNCHECKED };
|
|
|
|
Name const _name;
|
|
|
|
bool _defined_by_launcher() const
|
|
{
|
|
/*
|
|
* If the <start> node lacks a 'pkg' attribute, we expect the
|
|
* policy to be defined by a launcher XML snippet.
|
|
*/
|
|
return _start_xml.constructed() && !_start_xml->xml().has_attribute("pkg");
|
|
}
|
|
|
|
Archive::Path _config_pkg_path() const
|
|
{
|
|
if (_defined_by_launcher() && _launcher_xml.constructed())
|
|
return _launcher_xml->xml().attribute_value("pkg", Archive::Path());
|
|
|
|
return _start_xml->xml().attribute_value("pkg", Archive::Path());
|
|
}
|
|
|
|
Launcher_name _launcher_name() const
|
|
{
|
|
if (!_defined_by_launcher())
|
|
return Launcher_name();
|
|
|
|
if (_start_xml->xml().has_attribute("launcher"))
|
|
return _start_xml->xml().attribute_value("launcher", Launcher_name());
|
|
|
|
return _start_xml->xml().attribute_value("name", Launcher_name());
|
|
}
|
|
|
|
/*
|
|
* 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();
|
|
|
|
/*
|
|
* Quota definitions obtained from the blueprint
|
|
*/
|
|
Number_of_bytes _pkg_ram_quota { 0 };
|
|
unsigned long _pkg_cap_quota { 0 };
|
|
unsigned long _pkg_cpu_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,
|
|
Depot_rom_server const &,
|
|
Depot_rom_server const &) 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());
|
|
sub_node.with_raw_node([&] (char const *start, size_t length) {
|
|
xml.append(start, length); });
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (!start_node.differs_from(_start_xml->xml()))
|
|
return;
|
|
|
|
Archive::Path const old_pkg_path = _config_pkg_path();
|
|
|
|
/* import new start node */
|
|
_start_xml.construct(_alloc, start_node);
|
|
|
|
Archive::Path const new_pkg_path = _config_pkg_path();
|
|
|
|
/* invalidate blueprint if 'pkg' path changed */
|
|
if (old_pkg_path != new_pkg_path) {
|
|
_blueprint_pkg_path = new_pkg_path;
|
|
_pkg_xml.destruct();
|
|
|
|
/* 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;
|
|
|
|
/* package was missing but is installed now */
|
|
_pkg_incomplete = false;
|
|
|
|
Xml_node const runtime = pkg.sub_node("runtime");
|
|
|
|
_pkg_ram_quota = runtime.attribute_value("ram", Number_of_bytes());
|
|
_pkg_cap_quota = runtime.attribute_value("caps", 0UL);
|
|
_pkg_cpu_quota = runtime.attribute_value("cpu", 0UL);
|
|
|
|
_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 apply_launcher(Launcher_name const &name, Xml_node launcher)
|
|
{
|
|
if (!_defined_by_launcher())
|
|
return;
|
|
|
|
if (_launcher_name() != name)
|
|
return;
|
|
|
|
if (_launcher_xml.constructed() && !launcher.differs_from(_launcher_xml->xml()))
|
|
return;
|
|
|
|
_launcher_xml.construct(_alloc, launcher);
|
|
|
|
_blueprint_pkg_path = _config_pkg_path();
|
|
}
|
|
|
|
/*
|
|
* \return true if condition changed
|
|
*/
|
|
template <typename COND_FN>
|
|
bool apply_condition(COND_FN const &fn)
|
|
{
|
|
Condition const orig_condition = _condition;
|
|
|
|
Xml_node launcher_xml = _launcher_xml.constructed()
|
|
? _launcher_xml->xml()
|
|
: Xml_node("<empty/>");
|
|
|
|
if (_start_xml.constructed())
|
|
_condition = fn(_start_xml->xml(), launcher_xml)
|
|
? SATISFIED : UNSATISFIED;
|
|
|
|
return _condition != orig_condition;
|
|
}
|
|
|
|
template <typename FN>
|
|
void apply_if_unsatisfied(FN const &fn) const
|
|
{
|
|
Xml_node launcher_xml = _launcher_xml.constructed()
|
|
? _launcher_xml->xml()
|
|
: Xml_node("<empty/>");
|
|
|
|
if (_condition == UNSATISFIED && _start_xml.constructed())
|
|
fn(_start_xml->xml(), launcher_xml, _name);
|
|
}
|
|
|
|
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;
|
|
|
|
log(path, " incomplete or missing");
|
|
|
|
_pkg_incomplete = true;
|
|
}
|
|
|
|
/**
|
|
* Reconsider deployment of child after installing missing archives
|
|
*/
|
|
void reset_incomplete()
|
|
{
|
|
if (_pkg_incomplete) {
|
|
_pkg_incomplete = false;
|
|
_pkg_xml.destruct();
|
|
}
|
|
}
|
|
|
|
bool blueprint_needed() const
|
|
{
|
|
if (_configured() || _pkg_incomplete)
|
|
return false;
|
|
|
|
if (_defined_by_launcher() && !_launcher_xml.constructed())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void gen_query(Xml_generator &xml) const
|
|
{
|
|
if (blueprint_needed())
|
|
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
|
|
* \param cached_depot_rom name of the server that provides the depot
|
|
* content as ROM modules. If the string is
|
|
* invalid, ROM requests are routed to the
|
|
* parent.
|
|
* \param uncached_depot_rom name of the depot-ROM server used to obtain
|
|
* the content of the depot user "local", which
|
|
* is assumed to be mutable
|
|
*/
|
|
inline void gen_start_node(Xml_generator &, Xml_node common,
|
|
Depot_rom_server const &cached_depot_rom,
|
|
Depot_rom_server const &uncached_depot_rom) const;
|
|
|
|
/**
|
|
* Generate installation entry needed for the completion of the child
|
|
*/
|
|
void gen_installation_entry(Xml_generator &xml) const
|
|
{
|
|
if (!_pkg_incomplete) return;
|
|
|
|
xml.node("archive", [&] () {
|
|
xml.attribute("path", _config_pkg_path());
|
|
xml.attribute("source", "no");
|
|
});
|
|
}
|
|
|
|
bool incomplete() const { return _pkg_incomplete; }
|
|
};
|
|
|
|
|
|
void Depot_deploy::Child::gen_start_node(Xml_generator &xml, Xml_node common,
|
|
Depot_rom_server const &cached_depot_rom,
|
|
Depot_rom_server const &uncached_depot_rom) const
|
|
{
|
|
if (!_configured() || _condition == UNSATISFIED)
|
|
return;
|
|
|
|
if (_defined_by_launcher() && !_launcher_xml.constructed())
|
|
return;
|
|
|
|
if (!_pkg_xml->xml().has_sub_node("runtime")) {
|
|
warning("blueprint for '", _name, "' lacks runtime information");
|
|
return;
|
|
}
|
|
|
|
xml.node("start", [&] () {
|
|
|
|
xml.attribute("name", _name);
|
|
|
|
unsigned long caps = _pkg_cap_quota;
|
|
if (_defined_by_launcher())
|
|
caps = _launcher_xml->xml().attribute_value("caps", caps);
|
|
caps = _start_xml->xml().attribute_value("caps", caps);
|
|
|
|
xml.attribute("caps", caps);
|
|
|
|
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); });
|
|
|
|
Number_of_bytes ram = _pkg_ram_quota;
|
|
if (_defined_by_launcher())
|
|
ram = _launcher_xml->xml().attribute_value("ram", ram);
|
|
ram = _start_xml->xml().attribute_value("ram", ram);
|
|
|
|
xml.node("resource", [&] () {
|
|
xml.attribute("name", "RAM");
|
|
xml.attribute("quantum", String<32>(ram));
|
|
});
|
|
|
|
unsigned long cpu_quota = _pkg_cpu_quota;
|
|
if (_defined_by_launcher())
|
|
cpu_quota = _launcher_xml->xml().attribute_value("cpu", cpu_quota);
|
|
cpu_quota = _start_xml->xml().attribute_value("cpu", cpu_quota);
|
|
|
|
xml.node("resource", [&] () {
|
|
xml.attribute("name", "CPU");
|
|
xml.attribute("quantum", cpu_quota);
|
|
});
|
|
|
|
Xml_node const runtime = _pkg_xml->xml().sub_node("runtime");
|
|
|
|
/*
|
|
* Insert inline '<config>' node if provided by the start node,
|
|
* the launcher definition (if a launcher is user), 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 (_defined_by_launcher() && _launcher_xml->xml().has_sub_node("config")) {
|
|
_gen_copy_of_sub_node(xml, _launcher_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, "audio_in", "Audio_in");
|
|
_gen_provides_sub_node(xml, service, "audio_out", "Audio_out");
|
|
_gen_provides_sub_node(xml, service, "block", "Block");
|
|
_gen_provides_sub_node(xml, service, "file_system", "File_system");
|
|
_gen_provides_sub_node(xml, service, "framebuffer", "Framebuffer");
|
|
_gen_provides_sub_node(xml, service, "input", "Input");
|
|
_gen_provides_sub_node(xml, service, "log", "LOG");
|
|
_gen_provides_sub_node(xml, service, "nic", "Nic");
|
|
_gen_provides_sub_node(xml, service, "nitpicker", "Nitpicker");
|
|
_gen_provides_sub_node(xml, service, "report", "Report");
|
|
_gen_provides_sub_node(xml, service, "rom", "ROM");
|
|
_gen_provides_sub_node(xml, service, "terminal", "Terminal");
|
|
_gen_provides_sub_node(xml, service, "timer", "Timer");
|
|
});
|
|
});
|
|
}
|
|
|
|
xml.node("route", [&] () {
|
|
_gen_routes(xml, common, cached_depot_rom, uncached_depot_rom); });
|
|
});
|
|
}
|
|
|
|
|
|
void Depot_deploy::Child::_gen_routes(Xml_generator &xml, Xml_node common,
|
|
Depot_rom_server const &cached_depot_rom,
|
|
Depot_rom_server const &uncached_depot_rom) 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");
|
|
route.with_raw_content([&] (char const *start, size_t length) {
|
|
xml.append(start, length); });
|
|
}
|
|
|
|
/*
|
|
* Add routes given in the launcher definition.
|
|
*/
|
|
if (_launcher_xml.constructed() && _launcher_xml->xml().has_sub_node("route")) {
|
|
Xml_node const route = _launcher_xml->xml().sub_node("route");
|
|
route.with_raw_content([&] (char const *start, size_t length) {
|
|
xml.append(start, length); });
|
|
}
|
|
|
|
/**
|
|
* Return name of depot-ROM server used for obtaining the 'path'
|
|
*
|
|
* If the depot path refers to the depot-user "local", route the
|
|
* session request to the non-cached ROM service.
|
|
*/
|
|
auto rom_server = [&] (Path const &path) {
|
|
|
|
return (String<7>(path) == "local/") ? uncached_depot_rom
|
|
: cached_depot_rom;
|
|
};
|
|
|
|
/*
|
|
* 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");
|
|
typedef String<160> Path;
|
|
Path const path = rom.attribute_value("path", Path());
|
|
|
|
if (cached_depot_rom.valid())
|
|
xml.node("child", [&] () {
|
|
xml.attribute("name", rom_server(path));
|
|
xml.attribute("label", path); });
|
|
else
|
|
xml.node("parent", [&] () {
|
|
xml.attribute("label", path); });
|
|
});
|
|
});
|
|
}
|
|
|
|
/*
|
|
* Add common routes as defined in our config.
|
|
*/
|
|
common.with_raw_content([&] (char const *start, size_t length) {
|
|
xml.append(start, length); });
|
|
|
|
/*
|
|
* 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);
|
|
|
|
if (cached_depot_rom.valid()) {
|
|
xml.node("child", [&] () {
|
|
xml.attribute("name", rom_server(path));
|
|
xml.attribute("label", path);
|
|
});
|
|
} else {
|
|
xml.node("parent", [&] () {
|
|
xml.attribute("label", path); });
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
#endif /* _CHILD_H_ */
|