diff --git a/repos/gems/run/depot_query.run b/repos/gems/run/depot_query.run index 5d1f1731ae..81d8db046e 100644 --- a/repos/gems/run/depot_query.run +++ b/repos/gems/run/depot_query.run @@ -43,7 +43,8 @@ install_config { - + + @@ -64,20 +65,18 @@ install_config { - + - - - - - - + + + + - + @@ -95,6 +94,7 @@ install_config { + @@ -102,7 +102,7 @@ install_config { - + diff --git a/repos/gems/src/app/depot_deploy/child.h b/repos/gems/src/app/depot_deploy/child.h new file mode 100644 index 0000000000..c460030dca --- /dev/null +++ b/repos/gems/src/app/depot_deploy/child.h @@ -0,0 +1,294 @@ +/* + * \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; + + private: + + Allocator &_alloc; + + Reconstructible _start_xml; /* from config */ + Constructible _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 { }; + + 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); + } + + 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"); + + _ram_quota = Ram_quota { runtime.attribute_value("ram", Number_of_bytes()) }; + _cap_quota = Cap_quota { runtime.attribute_value("caps", 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 gen_query(Xml_generator &xml) const + { + if (_configured()) + 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); + + 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 '' 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 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"); + 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_ */ diff --git a/repos/gems/src/app/depot_deploy/main.cc b/repos/gems/src/app/depot_deploy/main.cc index df8445bab8..5b947421d1 100644 --- a/repos/gems/src/app/depot_deploy/main.cc +++ b/repos/gems/src/app/depot_deploy/main.cc @@ -12,16 +12,83 @@ */ /* Genode includes */ +#include #include #include +#include #include +/* local includes */ +#include "child.h" + namespace Depot_deploy { - using namespace Genode; + struct Children; struct Main; } +class Depot_deploy::Children +{ + private: + + Allocator &_alloc; + + List_model _children { }; + + struct Model_update_policy : List_model::Update_policy + { + Allocator &_alloc; + + Model_update_policy(Allocator &alloc) : _alloc(alloc) { } + + void destroy_element(Child &c) { destroy(_alloc, &c); } + + Child &create_element(Xml_node node) + { + return *new (_alloc) Child(_alloc, node); + } + + void update_element(Child &c, Xml_node node) { c.apply_config(node); } + + static bool element_matches_xml_node(Child const &child, Xml_node node) + { + return node.attribute_value("name", Child::Name()) == child.name(); + } + + static bool node_is_element(Xml_node node) { return node.has_type("start"); } + + } _model_update_policy { _alloc }; + + public: + + Children(Allocator &alloc) : _alloc(alloc) { } + + void apply_config(Xml_node config) + { + _children.update_from_xml(_model_update_policy, config); + } + + void apply_blueprint(Xml_node blueprint) + { + blueprint.for_each_sub_node("pkg", [&] (Xml_node pkg) { + _children.for_each([&] (Child &child) { + child.apply_blueprint(pkg); }); }); + } + + void gen_start_nodes(Xml_generator &xml, Xml_node common) + { + _children.for_each([&] (Child const &child) { + child.gen_start_node(xml, common); }); + } + + void gen_queries(Xml_generator &xml) + { + _children.for_each([&] (Child const &child) { + child.gen_query(xml); }); + } +}; + + struct Depot_deploy::Main { Env &_env; @@ -29,67 +96,55 @@ struct Depot_deploy::Main Attached_rom_dataspace _config { _env, "config" }; Attached_rom_dataspace _blueprint { _env, "blueprint" }; - Reporter _init_config_reporter { _env, "config", "init.config", 16*1024 }; + Expanding_reporter _query_reporter { _env, "query" , "query"}; + Expanding_reporter _init_config_reporter { _env, "config", "init.config"}; + + size_t _query_buffer_size = 4096; + size_t _init_config_buffer_size = 4096; + + Heap _heap { _env.ram(), _env.rm() }; + + Children _children { _heap }; Signal_handler
_config_handler { _env.ep(), *this, &Main::_handle_config }; typedef String<128> Name; - typedef String<80> Binary; - typedef String<80> Config; - - /** - * Generate start node of init configuration - * - * \param pkg pkg node of the subsystem blueprint - * \param common session routes to be added in addition to the ones - * found in the pkg blueprint - */ - static void _gen_start_node(Xml_generator &, Xml_node pkg, Xml_node common); void _handle_config() { _config.update(); _blueprint.update(); - Xml_node const config = _config.xml(); - Xml_node const blueprint = _blueprint.xml(); + Xml_node const config = _config.xml(); - Reporter::Xml_generator xml(_init_config_reporter, [&] () { + _children.apply_config(config); + _children.apply_blueprint(_blueprint.xml()); + /* determine CPU architecture of deployment */ + typedef String<16> Arch; + Arch const arch = config.attribute_value("arch", Arch()); + if (!arch.valid()) + warning("config lacks 'arch' attribute"); + + /* generate init config containing all configured start nodes */ + _init_config_reporter.generate([&] (Xml_generator &xml) { Xml_node static_config = config.sub_node("static"); xml.append(static_config.content_base(), static_config.content_size()); - - blueprint.for_each_sub_node("pkg", [&] (Xml_node pkg) { - - /* - * Check preconditions for generating a '' node. - */ - Name const name = pkg.attribute_value("name", Name()); - - if (!pkg.has_sub_node("runtime")) { - warning(" node for '", name, "' lacks node"); - return; - } - - Xml_node const runtime = pkg.sub_node("runtime"); - - if (!runtime.has_attribute("binary")) { - warning(" node for '", name, "' lacks 'binary' attribute"); - return; - } - - xml.node("start", [&] () { - _gen_start_node(xml, pkg, config.sub_node("common_routes")); - }); - }); + _children.gen_start_nodes(xml, config.sub_node("common_routes")); }); + + /* update query for blueprints of all unconfigured start nodes */ + if (arch.valid()) { + _query_reporter.generate([&] (Xml_generator &xml) { + xml.attribute("arch", arch); + _children.gen_queries(xml); + }); + } } Main(Env &env) : _env(env) { - _init_config_reporter.enabled(true); - _config .sigh(_config_handler); _blueprint.sigh(_config_handler); @@ -98,80 +153,5 @@ struct Depot_deploy::Main }; -void Depot_deploy::Main::_gen_start_node(Xml_generator &xml, Xml_node pkg, Xml_node common) -{ - typedef String<80> Name; - - Name const name = pkg.attribute_value("name", Name()); - Xml_node const runtime = pkg.sub_node("runtime"); - size_t const caps = runtime.attribute_value("caps", 0UL); - Number_of_bytes const ram = runtime.attribute_value("ram", Number_of_bytes()); - Binary const binary = runtime.attribute_value("binary", Binary()); - Config const config = runtime.attribute_value("config", Config()); - - xml.attribute("name", name); - xml.attribute("caps", caps); - - xml.node("binary", [&] () { xml.attribute("name", binary); }); - - xml.node("resource", [&] () { - xml.attribute("name", "RAM"); - xml.attribute("quantum", String<32>(ram)); - }); - - /* - * Insert inline '' node if provided by the blueprint. - */ - if (runtime.has_sub_node("config")) { - Xml_node config = runtime.sub_node("config"); - xml.node("config", [&] () { - xml.append(config.content_base(), config.content_size()); }); - }; - - xml.node("route", [&] () { - - /* - * Redirect config ROM request to label given in the 'config' - * attribute. - */ - if (config.valid()) { - xml.node("service", [&] () { - xml.attribute("name", "ROM"); - xml.attribute("label", "config"); - xml.node("parent", [&] () { - xml.attribute("label", config); }); - }); - } - - /* - * 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.for_each_sub_node("rom", [&] (Xml_node rom) { - - if (!rom.has_attribute("path")) - return; - - typedef String<160> Path; - 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); }); - }); - }); - }); -} - - void Component::construct(Genode::Env &env) { static Depot_deploy::Main main(env); } diff --git a/repos/gems/src/app/depot_deploy/target.mk b/repos/gems/src/app/depot_deploy/target.mk index 5064cc00a6..cb84c2cd49 100644 --- a/repos/gems/src/app/depot_deploy/target.mk +++ b/repos/gems/src/app/depot_deploy/target.mk @@ -1,5 +1,3 @@ TARGET = depot_deploy SRC_CC = main.cc -LIBS += base vfs - -CC_CXX_WARN_STRICT = +LIBS += base diff --git a/repos/gems/src/app/depot_query/main.cc b/repos/gems/src/app/depot_query/main.cc index 7e58ded975..0a4e78883e 100644 --- a/repos/gems/src/app/depot_query/main.cc +++ b/repos/gems/src/app/depot_query/main.cc @@ -161,10 +161,25 @@ struct Depot_query::Main Signal_handler
_config_handler { _env.ep(), *this, &Main::_handle_config }; - Reporter _directory_reporter { _env, "directory" }; - Reporter _blueprint_reporter { _env, "blueprint" }; - Reporter _dependencies_reporter { _env, "dependencies" }; - Reporter _user_reporter { _env, "user" }; + Signal_handler
_query_handler { + _env.ep(), *this, &Main::_handle_config }; + + typedef Constructible Constructible_reporter; + + Constructible_reporter _directory_reporter { _env, "directory" }; + Constructible_reporter _blueprint_reporter { _env, "blueprint" }; + Constructible_reporter _dependencies_reporter { _env, "dependencies" }; + Constructible_reporter _user_reporter { _env, "user" }; + + template + static void _construct_if(bool condition, Constructible &obj, ARGS &&... args) + { + if (condition && !obj.constructed()) + obj.construct(args...); + + if (!condition && obj.constructed()) + obj.destruct(); + } typedef String<64> Rom_label; typedef String<16> Architecture; @@ -205,7 +220,7 @@ struct Depot_query::Main if (query_from_rom && !_query_rom.constructed()) { _query_rom.construct(_env, "query"); - _query_rom->sigh(_config_handler); + _query_rom->sigh(_query_handler); } if (!query_from_rom && _query_rom.constructed()) @@ -216,20 +231,31 @@ struct Depot_query::Main Xml_node const query = (query_from_rom ? _query_rom->xml() : config); - _directory_reporter .enabled(query.has_sub_node("scan")); - _blueprint_reporter .enabled(query.has_sub_node("blueprint")); - _dependencies_reporter.enabled(query.has_sub_node("dependencies")); - _user_reporter .enabled(query.has_sub_node("user")); + _construct_if(query.has_sub_node("scan"), + _directory_reporter, _env, "directory", "directory"); + + _construct_if(query.has_sub_node("blueprint"), + _blueprint_reporter, _env, "blueprint", "blueprint"); + + _construct_if(query.has_sub_node("dependencies"), + _dependencies_reporter, _env, "dependencies", "dependencies"); + + _construct_if(query.has_sub_node("user"), + _user_reporter, _env, "user", "user"); _root.apply_config(config.sub_node("vfs")); + /* ignore incomplete queries that may occur at the startup */ + if (query.has_type("empty")) + return; + if (!query.has_attribute("arch")) warning("query lacks 'arch' attribute"); _architecture = query.attribute_value("arch", Architecture()); - if (_directory_reporter.enabled()) { - Reporter::Xml_generator xml(_directory_reporter, [&] () { + if (_directory_reporter.constructed()) { + _directory_reporter->generate([&] (Xml_generator &xml) { query.for_each_sub_node("scan", [&] (Xml_node node) { Archive::User const user = node.attribute_value("user", Archive::User()); Directory::Path path("depot/", user, "/pkg"); @@ -239,11 +265,13 @@ struct Depot_query::Main }); } - if (_blueprint_reporter.enabled()) { - Reporter::Xml_generator xml(_blueprint_reporter, [&] () { + if (_blueprint_reporter.constructed()) { + _blueprint_reporter->generate([&] (Xml_generator &xml) { query.for_each_sub_node("blueprint", [&] (Xml_node node) { Archive::Path pkg = node.attribute_value("pkg", Archive::Path()); try { _query_blueprint(pkg, xml); } + catch (Xml_generator::Buffer_exceeded) { + throw; /* handled by 'generate' */ } catch (...) { warning("could not obtain blueprint for '", pkg, "'"); } @@ -251,8 +279,8 @@ struct Depot_query::Main }); } - if (_dependencies_reporter.enabled()) { - Reporter::Xml_generator xml(_dependencies_reporter, [&] () { + if (_dependencies_reporter.constructed()) { + _dependencies_reporter->generate([&] (Xml_generator &xml) { Dependencies dependencies(_heap, _depot_dir); query.for_each_sub_node("dependencies", [&] (Xml_node node) { @@ -268,8 +296,8 @@ struct Depot_query::Main }); } - if (_user_reporter.enabled()) { - Reporter::Xml_generator xml(_user_reporter, [&] () { + if (_user_reporter.constructed()) { + _user_reporter->generate([&] (Xml_generator &xml) { query.for_each_sub_node("user", [&] (Xml_node node) { _query_user(node.attribute_value("name", Archive::User()), xml); }); });