diff --git a/repos/os/include/init/child.h b/repos/os/include/init/child.h index 8a42415fe8..0275211408 100644 --- a/repos/os/include/init/child.h +++ b/repos/os/include/init/child.h @@ -22,12 +22,13 @@ /* init includes */ #include -#include #include #include namespace Init { + class Abandonable; + class Parent_service; class Buffered_xml; class Routed_service; class Name_registry; @@ -37,8 +38,6 @@ namespace Init { using namespace Genode; using Genode::size_t; using Genode::strlen; - - typedef Genode::Registered Parent_service; } @@ -226,8 +225,8 @@ namespace Init { template - inline Service *find_service(Registry &services, - Service::Name const &name) + inline T *find_service(Registry &services, + Service::Name const &name) { T *service = nullptr; services.for_each([&] (T &s) { @@ -253,6 +252,36 @@ namespace Init { } +class Init::Abandonable +{ + private: + + bool _abandoned = false; + + public: + + void abandon() { _abandoned = true; } + + bool abandoned() const { return _abandoned; } +}; + + +class Init::Parent_service : public Genode::Parent_service, public Abandonable +{ + private: + + Registry::Element _reg_elem; + + public: + + Parent_service(Registry ®istry, Env &env, + Service::Name const &name) + : + Genode::Parent_service(env, name), _reg_elem(registry, *this) + { } +}; + + class Init::Buffered_xml { private: @@ -292,16 +321,20 @@ class Init::Buffered_xml /** * Init-specific representation of a child service */ -class Init::Routed_service : public Child_service +class Init::Routed_service : public Child_service, public Abandonable { public: typedef Child_policy::Name Child_name; + struct Ram_accessor { virtual Ram_session_capability ram() const = 0; }; + private: Child_name _child_name; + Ram_accessor &_ram_accessor; + Registry::Element _registry_element; public: @@ -316,17 +349,21 @@ class Init::Routed_service : public Child_service */ Routed_service(Registry &services, Child_name const &child_name, + Ram_accessor &ram_accessor, Id_space &server_id_space, Session_state::Factory &factory, Service::Name const &name, - Ram_session_capability ram, Child_service::Wakeup &wakeup) : - Child_service(server_id_space, factory, name, ram, wakeup), - _child_name(child_name), _registry_element(services, *this) + Child_service(server_id_space, factory, name, + Ram_session_capability(), wakeup), + _child_name(child_name), _ram_accessor(ram_accessor), + _registry_element(services, *this) { } Child_name const &child_name() const { return _child_name; } + + Ram_session_capability ram() const { return _ram_accessor.ram(); } }; @@ -387,6 +424,11 @@ class Init::Child : Child_policy, Child_service::Wakeup Id const _id; + enum State { STATE_INITIAL, STATE_RAM_INITIALIZED, STATE_ALIVE, + STATE_ABANDONED }; + + State _state = STATE_INITIAL; + Report_update_trigger &_report_update_trigger; List_element _list_element; @@ -525,10 +567,52 @@ class Init::Child : Child_policy, Child_service::Wakeup Registry &_parent_services; Registry &_child_services; - /** - * Private child configuration - */ - Init::Child_config _config; + struct Inline_config_rom_service : Abandonable, Dynamic_rom_session::Content_producer + { + typedef Local_service Service; + + Child &_child; + + Dynamic_rom_session _session { _child._env.ep().rpc_ep(), + _child.ref_ram(), _child._env.rm(), + *this }; + + Service::Single_session_factory _factory { _session }; + Service _service { _factory }; + + Inline_config_rom_service(Child &child) : _child(child) { } + + /** + * Dynamic_rom_session::Content_producer interface + */ + void produce_content(char *dst, Genode::size_t dst_len) override + { + Xml_node config = _child._start_node->xml().has_sub_node("config") + ? _child._start_node->xml().sub_node("config") + : Xml_node(""); + + size_t const config_len = config.size(); + + if (config_len + 1 /* null termination */ >= dst_len) + throw Buffer_capacity_exceeded(); + + /* + * The 'config.size()' method returns the number of bytes of + * the config-node content, which is not null-terminated. Since + * 'Genode::strncpy' always null-terminates the result, the + * last byte of the source string is not copied. Hence, it is + * safe to add '1' to 'config_len' and thereby include the + * last actual config-content character in the result. + */ + Genode::strncpy(dst, config.addr(), config_len + 1); + } + + void trigger_update() { _session.trigger_update(); } + + Service &service() { return _service; } + }; + + Constructible _config_rom_service; Session_requester _session_requester; @@ -536,12 +620,18 @@ class Init::Child : Child_policy, Child_service::Wakeup * Policy helpers */ Init::Child_policy_handle_cpu_priorities _priority_policy; - Init::Child_policy_provide_rom_file _config_policy; - Init::Child_policy_redirect_rom_file _configfile_policy; Init::Child_policy_ram_phys _ram_session_policy; Genode::Child _child { _env.rm(), _env.ep().rpc_ep(), *this }; + struct Ram_accessor : Routed_service::Ram_accessor + { + Genode::Child &_child; + Ram_accessor(Genode::Child &child) : _child(child) { } + Ram_session_capability ram() const override { + return _child.ram_session_cap(); } + } _ram_accessor { _child }; + /** * Child_service::Wakeup callback */ @@ -550,6 +640,26 @@ class Init::Child : Child_policy, Child_service::Wakeup _session_requester.trigger_update(); } + /** + * Return true if the policy results in the current route of the session + * + * This method is used to check if a policy change affects an existing + * client session of a child, i.e., to determine whether the child must + * be restarted. + */ + bool _route_valid(Session_state const &session) + { + try { + Route const route = + resolve_session_request(session.service().name(), + session.client_label()); + + return (session.service() == route.service) + && (route.label == session.label()); + } + catch (Parent::Service_denied) { return false; } + } + public: /** @@ -591,11 +701,8 @@ class Init::Child : Child_policy, Child_service::Wakeup avail_slack_ram_quota(_env.ram().avail()), _verbose), _parent_services(parent_services), _child_services(child_services), - _config(_env.ram(), _env.rm(), start_node), _session_requester(_env.ep().rpc_ep(), _env.ram(), _env.rm()), _priority_policy(_resources.prio_levels_log2, _resources.priority), - _config_policy("config", _config.dataspace(), &_env.ep().rpc_ep()), - _configfile_policy("config", _config.filename()), _ram_session_policy(_resources.constrain_phys) { if (_resources.ram_quota == 0) @@ -624,13 +731,19 @@ class Init::Child : Child_policy, Child_service::Wakeup log(" provides service ", Cstring(name)); new (_alloc) - Routed_service(child_services, this->name(), + Routed_service(child_services, this->name(), _ram_accessor, _session_requester.id_space(), _child.session_factory(), - name, _child.ram_session_cap(), *this); + name, *this); } } catch (Xml_node::Nonexistent_sub_node) { } + + /* + * Construct inline config ROM service if "config" node is present. + */ + if (start_node.has_sub_node("config")) + _config_rom_service.construct(*this); } virtual ~Child() @@ -645,6 +758,125 @@ class Init::Child : Child_policy, Child_service::Wakeup */ bool has_name(Child_policy::Name const &str) const { return str == name(); } + void initiate_env_ram_session() + { + if (_state == STATE_INITIAL) { + _child.initiate_env_ram_session(); + _state = STATE_RAM_INITIALIZED; + } + } + + void initiate_env_sessions() + { + if (_state == STATE_RAM_INITIALIZED) { + + _child.initiate_env_sessions(); + + /* check for completeness of the child's environment */ + if (_verbose.enabled()) + _child.for_each_session([&] (Session_state const &session) { + if (session.phase != Session_state::AVAILABLE) + warning(name(), ": incomplete environment ", + session.service().name(), " session " + "(", session.label(), ")"); }); + + _state = STATE_ALIVE; + } + } + + void abandon() + { + _state = STATE_ABANDONED; + + _child_services.for_each([&] (Routed_service &service) { + if (service.has_id_space(_session_requester.id_space())) + service.abandon(); }); + } + + bool abandoned() const { return _state == STATE_ABANDONED; } + + enum Apply_config_result { MAY_HAVE_SIDE_EFFECTS, NO_SIDE_EFFECTS }; + + /** + * Apply new configuration to child + * + * \throw Allocator::Out_of_memory unable to allocate buffer for new + * config + */ + Apply_config_result apply_config(Xml_node start_node) + { + Child_policy &policy = *this; + + if (_state == STATE_ABANDONED) + return NO_SIDE_EFFECTS; + + enum Config_update { CONFIG_APPEARED, CONFIG_VANISHED, + CONFIG_CHANGED, CONFIG_UNCHANGED }; + + Config_update config_update = CONFIG_UNCHANGED; + + /* import new start node if new version differs */ + if (start_node.size() != _start_node->xml().size() || + Genode::memcmp(start_node.addr(), _start_node->xml().addr(), + start_node.size()) != 0) + { + /* + * Start node changed + * + * Determine how the inline config is affected. + */ + char const * const tag = "config"; + bool const config_was_present = _start_node->xml().has_sub_node(tag); + bool const config_is_present = start_node.has_sub_node(tag); + + if (config_was_present && !config_is_present) + config_update = CONFIG_VANISHED; + + if (!config_was_present && config_is_present) + config_update = CONFIG_APPEARED; + + if (config_was_present && config_is_present) { + + Xml_node old_config = _start_node->xml().sub_node(tag); + Xml_node new_config = start_node.sub_node(tag); + + if (Genode::memcmp(old_config.addr(), new_config.addr(), + min(old_config.size(), new_config.size()))) + config_update = CONFIG_CHANGED; + } + + /* import new start node */ + _start_node.construct(_alloc, start_node); + } + + /* + * Apply change to '_config_rom_service'. This will + * potentially result in a change of the "config" ROM route, which + * may in turn prompt the routing-check below to abandon (restart) + * the child. + */ + switch (config_update) { + case CONFIG_UNCHANGED: break; + case CONFIG_CHANGED: _config_rom_service->trigger_update(); break; + case CONFIG_APPEARED: _config_rom_service.construct(*this); break; + case CONFIG_VANISHED: _config_rom_service->abandon(); break; + } + + /* validate that the routes of all existing sessions remain intact */ + { + bool routing_changed = false; + _child.for_each_session([&] (Session_state const &session) { + if (!_route_valid(session)) + routing_changed = true; }); + + if (routing_changed) { + abandon(); + return MAY_HAVE_SIDE_EFFECTS; + } + } + return NO_SIDE_EFFECTS; + } + void report_state(Xml_generator &xml, Report_detail const &detail) const { xml.node("child", [&] () { @@ -731,11 +963,41 @@ class Init::Child : Child_policy, Child_service::Wakeup Route resolve_session_request(Service::Name const &service_name, Session_label const &label) override { - Service *service = nullptr; - /* check for "config" ROM request */ - if ((service = _config_policy.resolve_session_request_with_label(service_name, label))) - return Route { *service, label }; + if (service_name == Rom_session::service_name() && + label.last_element() == "config") { + + if (_config_rom_service.constructed() && + !_config_rom_service->abandoned()) + return Route { _config_rom_service->service(), label }; + + /* + * \deprecated the support for the tag will + * be removed + */ + if (_start_node->xml().has_sub_node("configfile")) { + + typedef String<50> Name; + Name const rom = + _start_node->xml().sub_node("configfile") + .attribute_value("name", Name()); + + /* prevent infinite recursion */ + if (rom == "config") { + error("configfile must not be named 'config'"); + throw Parent::Service_denied(); + } + + return resolve_session_request(service_name, + prefixed_label(name(), rom)); + } + + /* + * If there is neither an inline '' nor a + * '' node present, we apply the regular session + * routing to the "config" ROM request. + */ + } /* check for "session_requests" ROM request */ if (service_name == Rom_session::service_name() @@ -774,9 +1036,14 @@ class Init::Child : Child_policy, Child_service::Wakeup if (target.has_type("parent")) { + Parent_service *service = nullptr; + if ((service = find_service(_parent_services, service_name))) return Route { *service, target_label }; + if (service && service->abandoned()) + throw Parent::Service_denied(); + if (!service_wildcard) { warning(name(), ": service lookup for " "\"", service_name, "\" at parent failed"); @@ -790,10 +1057,16 @@ class Init::Child : Child_policy, Child_service::Wakeup Name server_name = target.attribute_value("name", Name()); server_name = _name_registry.deref_alias(server_name); + Routed_service *service = nullptr; + _child_services.for_each([&] (Routed_service &s) { if (s.name() == Service::Name(service_name) && s.child_name() == server_name) service = &s; }); + + if (service && service->abandoned()) + throw Parent::Service_denied(); + if (service) return Route { *service, target_label }; @@ -805,12 +1078,15 @@ class Init::Child : Child_policy, Child_service::Wakeup } if (target.has_type("any-child")) { + if (is_ambiguous(_child_services, service_name)) { error(name(), ": ambiguous routes to " "service \"", service_name, "\""); throw Parent::Service_denied(); } + Routed_service *service = nullptr; + if ((service = find_service(_child_services, service_name))) return Route { *service, target_label }; @@ -825,21 +1101,16 @@ class Init::Child : Child_policy, Child_service::Wakeup break; } } - } catch (...) { - warning(name(), ": no route to service \"", service_name, "\""); - } + } catch (Xml_node::Nonexistent_sub_node) { } - if (!service) - throw Parent::Service_denied(); - - return Route { *service }; + warning(name(), ": no route to service \"", service_name, "\""); + throw Parent::Service_denied(); } void filter_session_args(Service::Name const &service, char *args, size_t args_len) override { _priority_policy. filter_session_args(service.string(), args, args_len); - _configfile_policy. filter_session_args(service.string(), args, args_len); _ram_session_policy.filter_session_args(service.string(), args, args_len); } @@ -923,6 +1194,8 @@ class Init::Child : Child_policy, Child_service::Wakeup { _report_update_trigger.trigger_report_update(); } + + bool initiate_env_sessions() const override { return false; } }; #endif /* _INCLUDE__INIT__CHILD_H_ */ diff --git a/repos/os/include/init/child_config.h b/repos/os/include/init/child_config.h index 5f49b953bb..410b755425 100644 --- a/repos/os/include/init/child_config.h +++ b/repos/os/include/init/child_config.h @@ -14,6 +14,8 @@ #ifndef _INCLUDE__INIT__CHILD_CONFIG_H_ #define _INCLUDE__INIT__CHILD_CONFIG_H_ +#warning header is deprecated, used os/dynamic_rom_session.h instead + #include #include #include diff --git a/repos/os/run/init.run b/repos/os/run/init.run index 42ae61f89e..2cab3b803f 100644 --- a/repos/os/run/init.run +++ b/repos/os/run/init.run @@ -49,7 +49,7 @@ append config { - + @@ -89,6 +89,151 @@ append config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/os/src/app/dummy/main.cc b/repos/os/src/app/dummy/main.cc index beba60f62e..a549019e47 100644 --- a/repos/os/src/app/dummy/main.cc +++ b/repos/os/src/app/dummy/main.cc @@ -16,17 +16,68 @@ #include #include #include +#include #include #include namespace Dummy { + struct Log_service; struct Log_connections; struct Main; using namespace Genode; } +struct Dummy::Log_service +{ + Env &_env; + + Heap _heap { _env.ram(), _env.rm() }; + + struct Session_component : Rpc_object + { + Session_label const _label; + + Session_component(Session_label const &label) : _label(label) { } + + size_t write(String const &string) override + { + /* strip known line delimiter from incoming message */ + unsigned n = 0; + Genode::String<16> const pattern("\033[0m\n"); + for (char const *s = string.string(); s[n] && pattern != s + n; n++); + + typedef Genode::String<100> Message; + Message const message("[", _label, "] ", Cstring(string.string(), n)); + log(message); + + return strlen(string.string()); + } + }; + + struct Root : Root_component + { + Root(Entrypoint &ep, Allocator &alloc) : Root_component(ep, alloc) { } + + Session_component *_create_session(const char *args, Affinity const &) override + { + return new (md_alloc()) Session_component(label_from_args(args)); + } + }; + + Root _root { _env.ep(), _heap }; + + Log_service(Env &env) : _env(env) + { + _env.parent().announce(_env.ep().manage(_root)); + log("created LOG service"); + } + + ~Log_service() { _env.ep().dissolve(_root); } +}; + + struct Dummy::Log_connections { Env &_env; @@ -66,10 +117,28 @@ struct Dummy::Main Attached_rom_dataspace _config { _env, "config" }; - Constructible _log_connections; + unsigned _config_cnt = 0; - Main(Env &env) : _env(env) + typedef String<50> Version; + + Version _config_version; + + Signal_handler
_config_handler { _env.ep(), *this, &Main::_handle_config }; + + void _handle_config() { + _config.update(); + + Version const version = _config.xml().attribute_value("version", Version()); + if (_config_cnt > 0 && version == _config_version) + return; + + _config_cnt++; + _config_version = version; + + if (_config_version.valid()) + log("config ", _config_cnt, ": ", _config_version); + _config.xml().for_each_sub_node([&] (Xml_node node) { if (node.type() == "create_log_connections") @@ -78,6 +147,9 @@ struct Dummy::Main if (node.type() == "destroy_log_connections") _log_connections.destruct(); + if (node.type() == "log_service") + _log_service.construct(_env); + if (node.type() == "sleep") { if (!_timer.constructed()) @@ -90,9 +162,17 @@ struct Dummy::Main log(node.attribute_value("string", String<50>())); }); } + + Constructible _log_connections; + + Constructible _log_service; + + Main(Env &env) : _env(env) + { + _config.sigh(_config_handler); + _handle_config(); + } }; - - void Component::construct(Genode::Env &env) { static Dummy::Main main(env); } diff --git a/repos/os/src/init/main.cc b/repos/os/src/init/main.cc index 62002f33eb..475aea64c1 100644 --- a/repos/os/src/init/main.cc +++ b/repos/os/src/init/main.cc @@ -55,28 +55,6 @@ namespace Init { } 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, "\""); - }); - } } @@ -213,11 +191,27 @@ class Init::Child_registry : public Name_registry, Child_list return _aliases.first() ? _aliases.first() : 0; } - void report_state(Xml_generator &xml, Report_detail const &detail) const + template + void for_each_child(FN const &fn) const { Genode::List_element const *curr = first(); for (; curr; curr = curr->next()) - curr->object()->report_state(xml, detail); + fn(*curr->object()); + } + + template + void for_each_child(FN const &fn) + { + Genode::List_element *curr = first(), *next = nullptr; + for (; curr; curr = next) { + next = curr->next(); + fn(*curr->object()); + } + } + + void report_state(Xml_generator &xml, Report_detail const &detail) const + { + for_each_child([&] (Child &child) { child.report_state(xml, detail); }); /* check for name clash with an existing alias */ for (Alias const *a = _aliases.first(); a; a = a->next()) { @@ -226,7 +220,6 @@ class Init::Child_registry : public Name_registry, Child_list xml.attribute("child", a->child); }); } - } @@ -427,6 +420,11 @@ struct Init::Main : State_reporter::Producer, Child::Default_route_accessor Signal_handler
_resource_avail_handler { _env.ep(), *this, &Main::_handle_resource_avail }; + void _update_aliases_from_config(); + void _update_parent_services_from_config(); + void _abandon_obsolete_children(); + void _update_children_config(); + void _destroy_abandoned_parent_services(); void _handle_config(); Signal_handler
_config_handler { @@ -444,15 +442,58 @@ struct Init::Main : State_reporter::Producer, Child::Default_route_accessor }; -void Init::Main::_handle_config() +void Init::Main::_update_parent_services_from_config() { - /* kill all currently running children */ - while (_children.any()) { - Init::Child *child = _children.any(); - _children.remove(child); - destroy(_heap, child); - } + Xml_node const node = _config.xml().has_sub_node("parent-provides") + ? _config.xml().sub_node("parent-provides") + : Xml_node(""); + /* remove services that are no longer present in config */ + _parent_services.for_each([&] (Parent_service &service) { + + Service::Name const name = service.name(); + + bool obsolete = true; + node.for_each_sub_node("service", [&] (Xml_node service) { + if (name == service.attribute_value("name", Service::Name())) { + obsolete = false; }}); + + if (obsolete) + service.abandon(); + }); + + if (_verbose->enabled()) + log("parent provides"); + + /* register new services */ + node.for_each_sub_node("service", [&] (Xml_node service) { + + Service::Name const name = service.attribute_value("name", Service::Name()); + + bool registered = false; + _parent_services.for_each([&] (Parent_service const &service) { + if (service.name() == name) + registered = true; }); + + if (!registered) { + new (_heap) Init::Parent_service(_parent_services, _env, name); + if (_verbose->enabled()) + log(" service \"", name, "\""); + } + }); +} + + +void Init::Main::_destroy_abandoned_parent_services() +{ + _parent_services.for_each([&] (Parent_service &service) { + if (service.abandoned()) + destroy(_heap, &service); }); +} + + +void Init::Main::_update_aliases_from_config() +{ /* remove all known aliases */ while (_children.any_alias()) { Init::Alias *alias = _children.any_alias(); @@ -460,24 +501,6 @@ void Init::Main::_handle_config() 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 */ - try { - _default_route.construct(_heap, _config.xml().sub_node("default-route")); } - catch (...) { } - /* create aliases */ _config.xml().for_each_sub_node("alias", [&] (Xml_node alias_node) { @@ -488,11 +511,99 @@ void Init::Main::_handle_config() catch (Alias::Child_is_missing) { warning("missing 'child' attribute in '' entry"); } }); +} - /* create children */ + +void Init::Main::_abandon_obsolete_children() +{ + _children.for_each_child([&] (Child &child) { + + Child_policy::Name const name = child.name(); + + bool obsolete = true; + _config.xml().for_each_sub_node("start", [&] (Xml_node node) { + if (node.attribute_value("name", Child_policy::Name()) == name) + obsolete = false; }); + + if (obsolete) + child.abandon(); + }); +} + + +void Init::Main::_update_children_config() +{ + for (;;) { + + /* + * Children are abandoned if any of their client sessions can no longer + * be routed or result in a different route. As each child may be a + * service, an avalanche effect may occur. It stops if no update causes + * a potential side effect in one iteration over all chilren. + */ + bool side_effects = false; + + _config.xml().for_each_sub_node("start", [&] (Xml_node node) { + + Child_policy::Name const start_node_name = + node.attribute_value("name", Child_policy::Name()); + + _children.for_each_child([&] (Child &child) { + if (child.name() == start_node_name) { + switch (child.apply_config(node)) { + case Child::NO_SIDE_EFFECTS: break; + case Child::MAY_HAVE_SIDE_EFFECTS: side_effects = true; break; + }; + } + }); + }); + + if (!side_effects) + break; + } +} + + +void Init::Main::_handle_config() +{ + _config.update(); + + _verbose.construct(_config.xml()); + _state_reporter.apply_config(_config.xml()); + + /* determine default route for resolving service requests */ + try { + _default_route.construct(_heap, _config.xml().sub_node("default-route")); } + catch (...) { } + + _update_aliases_from_config(); + _update_parent_services_from_config(); + _abandon_obsolete_children(); + _update_children_config(); + + /* kill abandoned children */ + _children.for_each_child([&] (Child &child) { + if (child.abandoned()) { + _children.remove(&child); + destroy(_heap, &child); + } + }); + + _destroy_abandoned_parent_services(); + + /* create new children */ try { _config.xml().for_each_sub_node("start", [&] (Xml_node start_node) { + /* skip start node if corresponding child already exists */ + bool exists = false; + _children.for_each_child([&] (Child const &child) { + if (child.name() == start_node.attribute_value("name", Child_policy::Name())) + exists = true; }); + if (exists) { + return; + } + try { _children.insert(new (_heap) Init::Child(_env, _heap, *_verbose, @@ -526,6 +637,18 @@ void Init::Main::_handle_config() 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) { } + + /* + * Initiate RAM sessions of all new children + */ + _children.for_each_child([&] (Child &child) { + child.initiate_env_ram_session(); }); + + /* + * Initiate remaining environment sessions of all new children + */ + _children.for_each_child([&] (Child &child) { + child.initiate_env_sessions(); }); }