/* * \brief Representation used for children of the init process * \author Norman Feske * \date 2010-05-04 */ /* * Copyright (C) 2010-2017 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 _INCLUDE__INIT__CHILD_H_ #define _INCLUDE__INIT__CHILD_H_ /* Genode includes */ #include #include #include #include /* init includes */ #include #include #include namespace Init { class Abandonable; class Parent_service; class Buffered_xml; class Routed_service; class Name_registry; class Child_registry; class Child; using namespace Genode; using Genode::size_t; using Genode::strlen; } /*************** ** Utilities ** ***************/ namespace Init { static void warn_insuff_quota(size_t const avail) { warning("specified quota exceeds available quota, " "proceeding with a quota of ", avail); } inline long read_priority(Xml_node start_node, long prio_levels) { long priority = Cpu_session::DEFAULT_PRIORITY; try { start_node.attribute("priority").value(&priority); } catch (...) { } /* * All priority declarations in the config file are * negative because child priorities can never be higher * than parent priorities. To simplify priority * calculations, we use inverted values. Lower values * correspond to higher priorities. */ priority = -priority; if (priority && (priority >= prio_levels)) { long new_prio = prio_levels ? prio_levels-1 : 0; char name[Service::Name::capacity()]; start_node.attribute("name").value(name, sizeof(name)); warning(Cstring(name), ": invalid priority, upgrading " "from ", -priority, " to ", -new_prio); return new_prio; } return priority; } inline Affinity::Location read_affinity_location(Affinity::Space const &space, Xml_node start_node) { typedef Affinity::Location Location; try { Xml_node node = start_node.sub_node("affinity"); /* if no position value is specified, select the whole row/column */ unsigned long const default_width = node.has_attribute("xpos") ? 1 : space.width(), default_height = node.has_attribute("ypos") ? 1 : space.height(); unsigned long const width = node.attribute_value("width", default_width), height = node.attribute_value("height", default_height); long const x1 = node.attribute_value("xpos", 0), y1 = node.attribute_value("ypos", 0), x2 = x1 + width - 1, y2 = y1 + height - 1; /* clip location to space boundary */ return Location(max(x1, 0L), max(y1, 0L), min((unsigned)(x2 - x1 + 1), space.width()), min((unsigned)(y2 - y1 + 1), space.height())); } catch (...) { return Location(0, 0, space.width(), space.height()); } } /** * Return amount of RAM that is currently unused */ static inline size_t avail_slack_ram_quota(size_t ram_avail) { size_t const preserve = 148*1024; return ram_avail > preserve ? ram_avail - preserve : 0; } /** * Return sub string of label with the leading child name stripped out * * \return character pointer to the scoped part of the label, * or nullptr if the label is not correctly prefixed with the child's * name */ inline char const *skip_label_prefix(char const *child_name, char const *label) { size_t const child_name_len = strlen(child_name); if (strcmp(child_name, label, child_name_len) != 0) return nullptr; label += child_name_len; /* * Skip label separator. This condition should be always satisfied. */ if (strcmp(" -> ", label, 4) != 0) return nullptr; return label + 4; } /** * Return true if service XML node matches service request * * \param args session arguments, inspected for the session label * \param child_name name of the originator of the session request * \param service_name name of the requested service */ inline bool service_node_matches(Xml_node const service_node, Session_label const &label, Child_policy::Name const &child_name, Service::Name const &service_name) { bool const service_matches = service_node.has_type("any-service") || (service_node.has_type("service") && service_node.attribute("name").has_value(service_name.string())); if (!service_matches) return false; bool const route_depends_on_child_provided_label = service_node.has_attribute("label") || service_node.has_attribute("label_prefix") || service_node.has_attribute("label_suffix"); char const *unscoped_attr = "unscoped_label"; if (service_node.has_attribute(unscoped_attr)) { /* * If an 'unscoped_label' attribute is provided, don't consider any * scoped label attribute. */ if (route_depends_on_child_provided_label) warning("service node contains both scoped and unscoped label attributes"); typedef String Label; return label == service_node.attribute_value(unscoped_attr, Label()); } if (!route_depends_on_child_provided_label) return true; char const * const scoped_label = skip_label_prefix( child_name.string(), label.string()); if (!scoped_label) return false; Session_label const session_label(scoped_label); return !Xml_node_label_score(service_node, session_label).conflict(); } /** * Check if service name is ambiguous * * \return true if the same service is provided multiple * times * * \deprecated */ template inline bool is_ambiguous(Registry const &services, Service::Name const &name) { /* count number of services with the specified name */ unsigned cnt = 0; services.for_each([&] (T const &service) { cnt += (service.name() == name); }); return cnt > 1; } template inline T *find_service(Registry &services, Service::Name const &name) { T *service = nullptr; services.for_each([&] (T &s) { if (!service && (s.name() == name)) service = &s; }); return service; } inline void generate_ram_info(Xml_generator &xml, Ram_session const &ram) { /* * The const cast is needed because the 'Ram_session' accessors are * non-const methods. */ Ram_session &ram_nonconst = const_cast(ram); typedef String<32> Value; xml.attribute("quota", Value(Number_of_bytes(ram_nonconst.quota()))); xml.attribute("used", Value(Number_of_bytes(ram_nonconst.used()))); xml.attribute("avail", Value(Number_of_bytes(ram_nonconst.avail()))); } } 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: Allocator &_alloc; char const * const _ptr; /* pointer to dynamically allocated buffer */ Xml_node const _xml; /* referring to buffer of '_ptr' */ /** * \throw Allocator::Out_of_memory */ static char const *_init_ptr(Allocator &alloc, Xml_node node) { char *ptr = (char *)alloc.alloc(node.size()); Genode::memcpy(ptr, node.addr(), node.size()); return ptr; } public: /** * Constructor * * \throw Allocator::Out_of_memory */ Buffered_xml(Allocator &alloc, Xml_node node) : _alloc(alloc), _ptr(_init_ptr(alloc, node)), _xml(_ptr, node.size()) { } ~Buffered_xml() { _alloc.free(const_cast(_ptr), _xml.size()); } Xml_node xml() const { return _xml; } }; /** * Init-specific representation of a 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: /** * Constructor * * \param services registry of all services provides by children * \param child_name child name of server, used for session routing * * The other arguments correspond to the arguments of '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, Child_service::Wakeup &wakeup) : 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(); } }; /** * Interface for name database */ struct Init::Name_registry { virtual ~Name_registry() { } typedef Child_policy::Name Name; /** * Check if specified name is unique * * \return false if name already exists */ virtual bool unique(const char *name) const = 0; /** * Return child name for a given alias name * * If there is no alias, the function returns the original name. */ virtual Name deref_alias(Name const &) = 0; }; class Init::Child : Child_policy, Child_service::Wakeup { public: /** * Exception types */ struct Child_name_is_not_unique : Exception { }; struct Missing_name_attribute : Exception { }; /** * Unique ID of the child, solely used for diagnostic purposes */ struct Id { unsigned value; }; struct Default_route_accessor { virtual Xml_node default_route() = 0; }; private: friend class Child_registry; Env &_env; Allocator &_alloc; Verbose const &_verbose; 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; Reconstructible _start_node; Default_route_accessor &_default_route_accessor; Name_registry &_name_registry; typedef String<64> Name; struct Unique_name : Name { /** * Read name from XML and check for name confict with other children * * \throw Missing_name_attribute */ static Name _checked(Xml_node start_node, Name_registry const ®istry) { Name const name = start_node.attribute_value("name", Name()); if (!name.valid()) { warning("missing 'name' attribute in '' entry"); throw Missing_name_attribute(); } if (registry.unique(name.string())) return name; error("child name \"", name, "\" is not unique"); throw Child_name_is_not_unique(); } /** * Constructor * * Obtains file name and unique process name from XML node * * \param start_node XML start node * \param registry registry tracking unique names * * \throw Missing_name_attribute */ Unique_name(Xml_node start_node, Name_registry const ®istry) : Name(_checked(start_node, registry)) { } } _unique_name; static Binary_name _binary_name_from_xml(Xml_node start_node, Unique_name const &unique_name) { if (!start_node.has_sub_node("binary")) return unique_name; return start_node.sub_node("binary").attribute_value("name", Name()); } Binary_name const _binary_name; struct Read_quota { Read_quota(Xml_node start_node, size_t &ram_quota, size_t &cpu_quota_pc, bool &constrain_phys, size_t const ram_avail, Verbose const &verbose) { cpu_quota_pc = 0; constrain_phys = false; Number_of_bytes ram_bytes = 0; try { Xml_node rsc = start_node.sub_node("resource"); for (;; rsc = rsc.next("resource")) { try { if (rsc.attribute("name").has_value("RAM")) { rsc.attribute("quantum").value(&ram_bytes); constrain_phys = rsc.attribute_value("constrain_phys", false); } else if (rsc.attribute("name").has_value("CPU")) { rsc.attribute("quantum").value(&cpu_quota_pc); } } catch (...) { } } } catch (...) { } ram_quota = ram_bytes; /* * If the configured RAM quota exceeds our own quota, we donate * all remaining quota to the child. */ if (ram_quota > ram_avail) { ram_quota = ram_avail; if (verbose.enabled()) warn_insuff_quota(ram_avail); } } }; /** * Resources assigned to the child */ struct Resources : Read_quota { long prio_levels_log2; long priority; Affinity affinity; size_t ram_quota; size_t cpu_quota_pc; bool constrain_phys; Resources(Xml_node start_node, long prio_levels, Affinity::Space const &affinity_space, size_t ram_avail, Verbose const &verbose) : Read_quota(start_node, ram_quota, cpu_quota_pc, constrain_phys, ram_avail, verbose), prio_levels_log2(log2(prio_levels)), priority(read_priority(start_node, prio_levels)), affinity(affinity_space, read_affinity_location(affinity_space, start_node)) { /* deduce session costs from usable ram quota */ ram_quota = Genode::Child::effective_ram_quota(ram_quota); } } _resources; Genode::Parent_service _env_ram_service { _env, Ram_session::service_name() }; Genode::Parent_service _env_cpu_service { _env, Cpu_session::service_name() }; Genode::Parent_service _env_pd_service { _env, Pd_session::service_name() }; Genode::Parent_service _env_log_service { _env, Log_session::service_name() }; Genode::Parent_service _env_rom_service { _env, Rom_session::service_name() }; Registry &_parent_services; Registry &_child_services; 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; /** * Policy helpers */ Init::Child_policy_handle_cpu_priorities _priority_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 */ void wakeup_child_service() override { _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: /** * Constructor * * \param alloc allocator solely used for configuration-dependent * allocations. It is not used for allocations on behalf * of the child's behavior. * * * \throw Ram_session::Alloc_failed allocation of config buffer failed * \throw Region_map::Attach_failed failed to temporarily attach * config dataspace to local address * space * \throw Allocator::Out_of_memory could not buffer the XML start node */ Child(Env &env, Allocator &alloc, Verbose const &verbose, Id id, Report_update_trigger &report_update_trigger, Xml_node start_node, Default_route_accessor &default_route_accessor, Name_registry &name_registry, long prio_levels, Affinity::Space const &affinity_space, Registry &parent_services, Registry &child_services) : _env(env), _alloc(alloc), _verbose(verbose), _id(id), _report_update_trigger(report_update_trigger), _list_element(this), _start_node(_alloc, start_node), _default_route_accessor(default_route_accessor), _name_registry(name_registry), _unique_name(start_node, name_registry), _binary_name(_binary_name_from_xml(start_node, _unique_name)), _resources(start_node, prio_levels, affinity_space, avail_slack_ram_quota(_env.ram().avail()), _verbose), _parent_services(parent_services), _child_services(child_services), _session_requester(_env.ep().rpc_ep(), _env.ram(), _env.rm()), _priority_policy(_resources.prio_levels_log2, _resources.priority), _ram_session_policy(_resources.constrain_phys) { if (_resources.ram_quota == 0) warning("no valid RAM resource for child " "\"", _unique_name, "\""); if (_verbose.enabled()) { log("child \"", _unique_name, "\""); log(" RAM quota: ", _resources.ram_quota); log(" ELF binary: ", _binary_name); log(" priority: ", _resources.priority); } /* * Determine services provided by the child */ try { Xml_node service_node = start_node.sub_node("provides").sub_node("service"); for (; ; service_node = service_node.next("service")) { char name[Service::Name::capacity()]; service_node.attribute("name").value(name, sizeof(name)); if (_verbose.enabled()) log(" provides service ", Cstring(name)); new (_alloc) Routed_service(child_services, this->name(), _ram_accessor, _session_requester.id_space(), _child.session_factory(), 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() { _child_services.for_each([&] (Routed_service &service) { if (service.has_id_space(_session_requester.id_space())) destroy(_alloc, &service); }); } /** * Return true if the child has the specified name */ 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", [&] () { xml.attribute("name", _unique_name); xml.attribute("binary", _binary_name); if (detail.ids()) xml.attribute("id", _id.value); if (detail.child_ram() && _child.ram_session_cap().valid()) { xml.node("ram", [&] () { /* * The const cast is needed because there is no const * accessor for the RAM session of the child. */ auto &nonconst_child = const_cast(_child); generate_ram_info(xml, nonconst_child.ram()); }); } Session_state::Detail const session_detail { detail.session_args() ? Session_state::Detail::ARGS : Session_state::Detail::NO_ARGS}; if (detail.requested()) { xml.node("requested", [&] () { _child.for_each_session([&] (Session_state const &session) { xml.node("session", [&] () { session.generate_client_side_info(xml, session_detail); }); }); }); } if (detail.provided()) { xml.node("provided", [&] () { auto fn = [&] (Session_state const &session) { xml.node("session", [&] () { session.generate_server_side_info(xml, session_detail); }); }; server_id_space().for_each(fn); }); } }); } /**************************** ** Child-policy interface ** ****************************/ Child_policy::Name name() const override { return _unique_name; } Binary_name binary_name() const override { return _binary_name; } Ram_session &ref_ram() override { return _env.ram(); } Ram_session_capability ref_ram_cap() const override { return _env.ram_session_cap(); } void init(Ram_session &session, Ram_session_capability cap) override { session.ref_account(_env.ram_session_cap()); _env.ram().transfer_quota(cap, _resources.ram_quota); } void init(Cpu_session &session, Cpu_session_capability cap) override { static size_t avail = Cpu_session::quota_lim_upscale( 100, 100); size_t const need = Cpu_session::quota_lim_upscale(_resources.cpu_quota_pc, 100); size_t need_adj = 0; if (need > avail || avail == 0) { warn_insuff_quota(Cpu_session::quota_lim_downscale(avail, 100)); need_adj = Cpu_session::quota_lim_upscale(100, 100); avail = 0; } else { need_adj = Cpu_session::quota_lim_upscale(need, avail); avail -= need; } session.ref_account(_env.cpu_session_cap()); _env.cpu().transfer_quota(cap, need_adj); } Id_space &server_id_space() override { return _session_requester.id_space(); } Route resolve_session_request(Service::Name const &service_name, Session_label const &label) override { /* check for "config" ROM request */ 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() && label.last_element() == Session_requester::rom_name()) return Route { _session_requester.service() }; try { Xml_node route_node = _default_route_accessor.default_route(); try { route_node = _start_node->xml().sub_node("route"); } catch (...) { } Xml_node service_node = route_node.sub_node(); for (; ; service_node = service_node.next()) { bool service_wildcard = service_node.has_type("any-service"); if (!service_node_matches(service_node, label, name(), service_name)) continue; Xml_node target = service_node.sub_node(); for (; ; target = target.next()) { /* * Determine session label to be provided to the server. * * By default, the client's identity (accompanied with * the a client-provided label) is presented as session * label to the server. However, the target node can * explicitly override the client's identity by a * custom label via the 'label' attribute. */ typedef String Label; Label const target_label = target.attribute_value("label", Label(label.string())); 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"); throw Parent::Service_denied(); } } if (target.has_type("child")) { typedef Name_registry::Name Name; 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 }; if (!service_wildcard) { warning(name(), ": lookup to child " "server \"", server_name, "\" failed"); throw Parent::Service_denied(); } } 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 }; if (!service_wildcard) { warning(name(), ": lookup for service " "\"", service_name, "\" failed"); throw Parent::Service_denied(); } } if (target.last()) break; } } } catch (Xml_node::Nonexistent_sub_node) { } 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); _ram_session_policy.filter_session_args(service.string(), args, args_len); } Affinity filter_session_affinity(Affinity const &session_affinity) override { Affinity::Space const &child_space = _resources.affinity.space(); Affinity::Location const &child_location = _resources.affinity.location(); /* check if no valid affinity space was specified */ if (session_affinity.space().total() == 0) return Affinity(child_space, child_location); Affinity::Space const &session_space = session_affinity.space(); Affinity::Location const &session_location = session_affinity.location(); /* scale resolution of resulting space */ Affinity::Space space(child_space.multiply(session_space)); /* subordinate session affinity to child affinity subspace */ Affinity::Location location(child_location .multiply_position(session_space) .transpose(session_location.xpos(), session_location.ypos())); return Affinity(space, location); } void announce_service(Service::Name const &service_name) override { log("child \"", name(), "\" announces service \"", service_name, "\""); bool found = false; _child_services.for_each([&] (Routed_service &service) { if (service.has_id_space(_session_requester.id_space()) && service.name() == service_name) found = true; }); if (!found) error(name(), ": illegal announcement of " "service \"", service_name, "\""); } void resource_request(Parent::Resource_args const &args) override { log("child \"", name(), "\" requests resources: ", args.string()); size_t const requested_ram_quota = Arg_string::find_arg(args.string(), "ram_quota") .ulong_value(0); if (avail_slack_ram_quota(_env.ram().avail()) < requested_ram_quota) { warning("cannot respond to resource request - out of memory"); return; } _env.ram().transfer_quota(_child.ram_session_cap(), requested_ram_quota); /* wake up child that was starved for resources */ _child.notify_resource_avail(); } void exit(int exit_value) override { try { if (_start_node->xml().sub_node("exit").attribute_value("propagate", false)) { _env.parent().exit(exit_value); return; } } catch (...) { } /* * Print a message as the exit is not handled otherwise. There are * a number of automated tests that rely on this message. It is * printed by the default implementation of 'Child_policy::exit'. */ Child_policy::exit(exit_value); } void session_state_changed() override { _report_update_trigger.trigger_report_update(); } bool initiate_env_sessions() const override { return false; } }; #endif /* _INCLUDE__INIT__CHILD_H_ */