/* * \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 #include #include #include #include namespace Depot_deploy { using namespace Depot; struct Child; } class Depot_deploy::Child : public List_model::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 _start_xml; /* from config */ Constructible _launcher_xml { }; Constructible _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 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 bool apply_condition(COND_FN const &fn) { Condition const orig_condition = _condition; Xml_node launcher_xml = _launcher_xml.constructed() ? _launcher_xml->xml() : Xml_node(""); if (_start_xml.constructed()) _condition = fn(_start_xml->xml(), launcher_xml) ? SATISFIED : UNSATISFIED; return _condition != orig_condition; } template void apply_if_unsatisfied(FN const &fn) const { Xml_node launcher_xml = _launcher_xml.constructed() ? _launcher_xml->xml() : Xml_node(""); 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); }); /* location handling */ bool const affinity_from_launcher = _defined_by_launcher() && (_launcher_xml->xml().has_attribute("xpos") || _launcher_xml->xml().has_attribute("ypos")); bool const affinity_from_start = _start_xml->xml().has_attribute("xpos") || _start_xml->xml().has_attribute("ypos"); if (affinity_from_start || affinity_from_launcher) { long xpos = 0, ypos = 0; unsigned width = 1, height = 1; if (affinity_from_launcher) { xpos = _launcher_xml->xml().attribute_value("xpos", xpos); ypos = _launcher_xml->xml().attribute_value("ypos", ypos); width = _launcher_xml->xml().attribute_value("width", width); height = _launcher_xml->xml().attribute_value("height", height); } xpos = _start_xml->xml().attribute_value("xpos", xpos); ypos = _start_xml->xml().attribute_value("ypos", ypos); width = _start_xml->xml().attribute_value("width", width); height = _start_xml->xml().attribute_value("height", height); xml.node("affinity", [&] () { xml.attribute("xpos", xpos); xml.attribute("ypos", ypos); xml.attribute("width", width); xml.attribute("height", height); }); } /* runtime handling */ Xml_node const runtime = _pkg_xml->xml().sub_node("runtime"); /* * Insert inline '' 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 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 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_ */