2
0
Fork 0
genodepkgs/packages/genodelabs/patches/sandbox.patch

702 lines
26 KiB
Diff

From dc4594b0bb0effdd261c70147a47ff0f0b8ee1f3 Mon Sep 17 00:00:00 2001
From: Emery Hemingway <ehmry@posteo.net>
Date: Sat, 25 Apr 2020 17:10:03 +0530
Subject: [PATCH 1/3] init/sandbox: <routes> support
Apply routing rules to a child from a <routes> node at the top-level of
a sandbox config, unless the corresponding start node has as <route>
node. If neither are present routes are taken from <default-route> as a
fallback.
Unlike the <route> and <default-route> the <routes> rules are checked by
labels prefixed by child name, so <routes> may contain child-specific
rules.
---
repos/os/src/lib/sandbox/child.cc | 12 +++++++++++-
repos/os/src/lib/sandbox/child.h | 10 ++++++++++
repos/os/src/lib/sandbox/library.cc | 14 +++++++++++++-
repos/os/src/lib/sandbox/utils.h | 8 +++++---
4 files changed, 39 insertions(+), 5 deletions(-)
diff --git a/repos/os/src/lib/sandbox/child.cc b/repos/os/src/lib/sandbox/child.cc
index e321df61fd..d25e3d9683 100644
--- a/repos/os/src/lib/sandbox/child.cc
+++ b/repos/os/src/lib/sandbox/child.cc
@@ -487,17 +487,25 @@ Sandbox::Child::resolve_session_request(Service::Name const &service_name,
Session::Label(), Session::Diag{false} };
try {
+ /* Lookup route in <default-route>… */
Xml_node route_node = _default_route_accessor.default_route();
+ /* …unless <routes> is present… */
+ route_node = _routes_accessor.routes(route_node);
try {
+ /* …otherwise use <child><route>. */
route_node = _start_node->xml().sub_node("route"); }
catch (...) { }
+
Xml_node service_node = route_node.sub_node();
+ /* <routes> is processed with the "«child» -> " prefix */
+ bool skip_prefix = route_node.type() != "routes";
+
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))
+ if (!service_node_matches(service_node, label, name(), service_name, skip_prefix))
continue;
Xml_node target = service_node.sub_node();
@@ -705,6 +713,7 @@ Sandbox::Child::Child(Env &env,
Report_update_trigger &report_update_trigger,
Xml_node start_node,
Default_route_accessor &default_route_accessor,
+ Routes_accessor &routes_accessor,
Default_caps_accessor &default_caps_accessor,
Name_registry &name_registry,
Ram_quota ram_limit,
@@ -722,6 +731,7 @@ Sandbox::Child::Child(Env &env,
_list_element(this),
_start_node(_alloc, start_node),
_default_route_accessor(default_route_accessor),
+ _routes_accessor(routes_accessor),
_default_caps_accessor(default_caps_accessor),
_ram_limit_accessor(ram_limit_accessor),
_cap_limit_accessor(cap_limit_accessor),
diff --git a/repos/os/src/lib/sandbox/child.h b/repos/os/src/lib/sandbox/child.h
index 2c213e662c..81836a2045 100644
--- a/repos/os/src/lib/sandbox/child.h
+++ b/repos/os/src/lib/sandbox/child.h
@@ -52,6 +52,14 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
struct Default_route_accessor : Interface { virtual Xml_node default_route() = 0; };
struct Default_caps_accessor : Interface { virtual Cap_quota default_caps() = 0; };
+ struct Routes_accessor : Interface
+ {
+ virtual Xml_node routes(Xml_node _default)
+ {
+ return _default;
+ }
+ };
+
template <typename QUOTA>
struct Resource_limit_accessor : Interface
{
@@ -98,6 +106,7 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
bool const _use_ld = _start_node->xml().attribute_value("ld", true);
Default_route_accessor &_default_route_accessor;
+ Routes_accessor &_routes_accessor;
Default_caps_accessor &_default_caps_accessor;
Ram_limit_accessor &_ram_limit_accessor;
Cap_limit_accessor &_cap_limit_accessor;
@@ -473,6 +482,7 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
Report_update_trigger &report_update_trigger,
Xml_node start_node,
Default_route_accessor &default_route_accessor,
+ Routes_accessor &route_accessor,
Default_caps_accessor &default_caps_accessor,
Name_registry &name_registry,
Ram_quota ram_limit,
diff --git a/repos/os/src/lib/sandbox/library.cc b/repos/os/src/lib/sandbox/library.cc
index 28b60c491f..30d0f2dfc1 100644
--- a/repos/os/src/lib/sandbox/library.cc
+++ b/repos/os/src/lib/sandbox/library.cc
@@ -23,6 +23,7 @@
struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
::Sandbox::Child::Default_route_accessor,
+ ::Sandbox::Child::Routes_accessor,
::Sandbox::Child::Default_caps_accessor,
::Sandbox::Child::Ram_limit_accessor,
::Sandbox::Child::Cap_limit_accessor
@@ -52,6 +53,8 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
Constructible<Buffered_xml> _default_route { };
+ Constructible<Buffered_xml> _routes { };
+
Cap_quota _default_caps { 0 };
unsigned _child_cnt = 0;
@@ -140,6 +143,12 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
: Xml_node("<empty/>");
}
+ /**
+ * Routes_accessor interface
+ */
+ Xml_node routes(Xml_node _default) override {
+ return _routes.constructed() ? _routes->xml() : _default; }
+
/**
* Default_caps_accessor interface
*/
@@ -314,6 +323,9 @@ void Genode::Sandbox::Library::apply_config(Xml_node const &config)
_default_route.construct(_heap, config.sub_node("default-route")); }
catch (...) { }
+ try { _routes.construct(_heap, config.sub_node("routes")); }
+ catch (...) { }
+
_default_caps = Cap_quota { 0 };
try {
_default_caps = Cap_quota { config.sub_node("default")
@@ -404,7 +416,7 @@ void Genode::Sandbox::Library::apply_config(Xml_node const &config)
Child &child = *new (_heap)
Child(_env, _heap, *_verbose,
Child::Id { ++_child_cnt }, _state_reporter,
- start_node, *this, *this, _children,
+ start_node, *this, *this, *this, _children,
Ram_quota { avail_ram.value - used_ram.value },
Cap_quota { avail_caps.value - used_caps.value },
*this, *this, prio_levels, affinity_space,
diff --git a/repos/os/src/lib/sandbox/utils.h b/repos/os/src/lib/sandbox/utils.h
index 7afcaebf00..36aab737f2 100644
--- a/repos/os/src/lib/sandbox/utils.h
+++ b/repos/os/src/lib/sandbox/utils.h
@@ -59,7 +59,8 @@ namespace Sandbox {
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)
+ Service::Name const &service_name,
+ bool skip_child_prefix = true)
{
bool const service_matches =
service_node.has_type("any-service") ||
@@ -98,8 +99,9 @@ namespace Sandbox {
if (!route_depends_on_child_provided_label)
return true;
- char const * const scoped_label = skip_label_prefix(
- child_name.string(), label.string());
+ char const * const scoped_label = skip_child_prefix
+ ? skip_label_prefix(child_name.string(), label.string())
+ : label.string();
if (!scoped_label)
return false;
--
2.28.0
From 13f2b3357f0e366a61d8b440fb56a52e5d2dfaff Mon Sep 17 00:00:00 2001
From: Emery Hemingway <ehmry@posteo.net>
Date: Wed, 4 Nov 2020 11:03:49 +0100
Subject: [PATCH 2/3] init/sandbox: do not parse <parent-provides> if <routes>
is present
---
repos/os/src/lib/sandbox/library.cc | 124 +++++++++++++++++++---------
1 file changed, 85 insertions(+), 39 deletions(-)
diff --git a/repos/os/src/lib/sandbox/library.cc b/repos/os/src/lib/sandbox/library.cc
index 30d0f2dfc1..caa9840ea6 100644
--- a/repos/os/src/lib/sandbox/library.cc
+++ b/repos/os/src/lib/sandbox/library.cc
@@ -184,47 +184,93 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
void Genode::Sandbox::Library::_update_parent_services_from_config(Xml_node const &config)
{
- Xml_node const node = config.has_sub_node("parent-provides")
- ? config.sub_node("parent-provides")
- : Xml_node("<empty/>");
-
- /* 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;
+ if (config.has_sub_node("routes")) {
+ if (config.has_sub_node("parent-provides"))
+ warning("ignoring <parent-provides> and parsing <routes> instead");
+
+ Xml_node const node = config.sub_node("routes");
+
+ /* 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 (obsolete && name == service.attribute_value("name", Service::Name())) {
+ obsolete = !service.has_sub_node("parent"); }});
+
+ if (obsolete)
+ service.abandon();
+ });
+
+ /* used to prepend the list of new parent services with title */
+ bool first_log = true;
+
+ /* register new services */
node.for_each_sub_node("service", [&] (Xml_node service) {
- if (name == service.attribute_value("name", Service::Name())) {
- obsolete = false; }});
-
- if (obsolete)
- service.abandon();
- });
-
- /* used to prepend the list of new parent services with title */
- bool first_log = true;
-
- /* 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) ::Sandbox::Parent_service(_parent_services, _env, name);
- if (_verbose->enabled()) {
- if (first_log)
- log("parent provides");
- log(" service \"", name, "\"");
- first_log = false;
+ if (service.has_sub_node("child")) return;
+
+ 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) ::Sandbox::Parent_service(_parent_services, _env, name);
+ if (_verbose->enabled()) {
+ if (first_log)
+ log("parent provides");
+ log(" service \"", name, "\"");
+ first_log = false;
+ }
}
- }
- });
+ });
+ } else {
+ Xml_node const node = config.has_sub_node("parent-provides")
+ ? config.sub_node("parent-provides")
+ : Xml_node("<empty/>");
+
+ /* 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();
+ });
+
+ /* used to prepend the list of new parent services with title */
+ bool first_log = true;
+
+ /* 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) ::Sandbox::Parent_service(_parent_services, _env, name);
+ if (_verbose->enabled()) {
+ if (first_log)
+ log("parent provides");
+ log(" service \"", name, "\"");
+ first_log = false;
+ }
+ }
+ });
+ }
}
--
2.28.0
From f89d8cbc3aa5fe9f7162ad66819fd2cedd76ba02 Mon Sep 17 00:00:00 2001
From: Emery Hemingway <ehmry@posteo.net>
Date: Wed, 4 Nov 2020 20:02:03 +0100
Subject: [PATCH 3/3] init/sandbox: simplify routing
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Remove <default-route>, <any-service>, and <any-child> configuration
options.
Allow label rewriting with <parent prefix="…" suffix="…">
and <child name="…" prefix="…" suffix="…">.
Routes are now selected by longest match rather than first match.
---
repos/os/src/lib/sandbox/child.cc | 131 ++++++++++------------------
repos/os/src/lib/sandbox/child.h | 3 -
repos/os/src/lib/sandbox/library.cc | 12 +--
repos/os/src/lib/sandbox/utils.h | 57 ++++++------
4 files changed, 76 insertions(+), 127 deletions(-)
diff --git a/repos/os/src/lib/sandbox/child.cc b/repos/os/src/lib/sandbox/child.cc
index d25e3d9683..46aa22411c 100644
--- a/repos/os/src/lib/sandbox/child.cc
+++ b/repos/os/src/lib/sandbox/child.cc
@@ -11,6 +11,7 @@
* under the terms of the GNU Affero General Public License version 3.
*/
+#include <os/session_policy.h>
#include <vm_session/vm_session.h>
/* local includes */
@@ -486,105 +487,65 @@ Sandbox::Child::resolve_session_request(Service::Name const &service_name,
return Route { _session_requester.service(),
Session::Label(), Session::Diag{false} };
- try {
- /* Lookup route in <default-route>… */
- Xml_node route_node = _default_route_accessor.default_route();
- /* …unless <routes> is present… */
- route_node = _routes_accessor.routes(route_node);
- try {
- /* …otherwise use <child><route>. */
- route_node = _start_node->xml().sub_node("route"); }
- catch (...) { }
-
- Xml_node service_node = route_node.sub_node();
-
- /* <routes> is processed with the "«child» -> " prefix */
- bool skip_prefix = route_node.type() != "routes";
-
- 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, skip_prefix))
- 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<Session_label::capacity()> Label;
- Label const target_label =
- target.attribute_value("label", Label(label.string()));
-
- Session::Diag const
- target_diag { target.attribute_value("diag", false) };
-
- auto no_filter = [] (Service &) -> bool { return false; };
-
- if (target.has_type("parent")) {
-
- try {
- return Route { find_service(_parent_services, service_name, no_filter),
- target_label, target_diag };
- } catch (Service_denied) { }
+ {
+ Xml_node service_node("<deny/>");
+ Xml_node routes_node = _routes_accessor.routes(service_node);
+ Xml_node_label_score best_score;
+
+ routes_node.for_each_sub_node([&] (Xml_node const &node) {
+ if (service_node_matches(node, label, service_name)) {
+ Xml_node_label_score score(node, label);
+ if (score.stronger(best_score)
+ || service_node.has_type("deny")) {
+ best_score = score;
+ service_node = node;
}
+ }
+ });
- if (target.has_type("local")) {
+ if (service_node.has_type("deny")) {
+ warning(name(), ": no route to service \"", service_name, "\" (label=\"", label, "\")");
+ throw Service_denied();
+ }
- try {
- return Route { find_service(_local_services, service_name, no_filter),
- target_label, target_diag };
- } catch (Service_denied) { }
- }
+ for (Xml_node target_node = service_node.sub_node(); ;
+ target_node = target_node.next()) {
- if (target.has_type("child")) {
+ Session_label the_label_part_of_label(skip_label_prefix(name().string(), label.string()));
+ if (the_label_part_of_label == "") the_label_part_of_label = label;
- typedef Name_registry::Name Name;
- Name server_name = target.attribute_value("name", Name());
- server_name = _name_registry.deref_alias(server_name);
+ auto target_label = Sandbox::target_label(
+ target_node, name(), the_label_part_of_label);
- auto filter_server_name = [&] (Routed_service &s) -> bool {
- return s.child_name() != server_name; };
+ Session::Diag const
+ target_diag { target_node.attribute_value("diag", false) };
- try {
- return Route { find_service(_child_services, service_name, filter_server_name),
- target_label, target_diag };
+ auto no_filter = [] (Service &) -> bool { return false; };
- } catch (Service_denied) { }
- }
+ if (target_node.has_type("parent"))
+ return Route { find_service(_parent_services, service_name, no_filter),
+ target_label, target_diag };
- if (target.has_type("any-child")) {
+ if (target_node.has_type("local"))
+ return Route { find_service(_local_services, service_name, no_filter),
+ target_label, target_diag };
- if (is_ambiguous(_child_services, service_name)) {
- error(name(), ": ambiguous routes to "
- "service \"", service_name, "\"");
- throw Service_denied();
- }
- try {
- return Route { find_service(_child_services, service_name, no_filter),
- target_label, target_diag };
+ if (target_node.has_type("child")) {
- } catch (Service_denied) { }
- }
+ typedef Name_registry::Name Name;
+ Name server_name = target_node.attribute_value("name", Name());
+ server_name = _name_registry.deref_alias(server_name);
- if (!service_wildcard) {
- warning(name(), ": lookup for service \"", service_name, "\" failed");
- throw Service_denied();
- }
+ auto filter_server_name = [&] (Routed_service &s) -> bool {
+ return s.child_name() != server_name; };
- if (target.last())
- break;
+ return Route { find_service(_child_services, service_name, filter_server_name),
+ target_label, target_diag };
}
+
+ if (target_node.last()) break;
}
- } catch (Xml_node::Nonexistent_sub_node) { }
+ }
warning(name(), ": no route to service \"", service_name, "\" (label=\"", label, "\")");
throw Service_denied();
@@ -712,7 +673,6 @@ Sandbox::Child::Child(Env &env,
Id id,
Report_update_trigger &report_update_trigger,
Xml_node start_node,
- Default_route_accessor &default_route_accessor,
Routes_accessor &routes_accessor,
Default_caps_accessor &default_caps_accessor,
Name_registry &name_registry,
@@ -730,7 +690,6 @@ Sandbox::Child::Child(Env &env,
_report_update_trigger(report_update_trigger),
_list_element(this),
_start_node(_alloc, start_node),
- _default_route_accessor(default_route_accessor),
_routes_accessor(routes_accessor),
_default_caps_accessor(default_caps_accessor),
_ram_limit_accessor(ram_limit_accessor),
diff --git a/repos/os/src/lib/sandbox/child.h b/repos/os/src/lib/sandbox/child.h
index 81836a2045..f9d04cfdaf 100644
--- a/repos/os/src/lib/sandbox/child.h
+++ b/repos/os/src/lib/sandbox/child.h
@@ -49,7 +49,6 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
*/
struct Id { unsigned value; };
- struct Default_route_accessor : Interface { virtual Xml_node default_route() = 0; };
struct Default_caps_accessor : Interface { virtual Cap_quota default_caps() = 0; };
struct Routes_accessor : Interface
@@ -105,7 +104,6 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
*/
bool const _use_ld = _start_node->xml().attribute_value("ld", true);
- Default_route_accessor &_default_route_accessor;
Routes_accessor &_routes_accessor;
Default_caps_accessor &_default_caps_accessor;
Ram_limit_accessor &_ram_limit_accessor;
@@ -481,7 +479,6 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
Id id,
Report_update_trigger &report_update_trigger,
Xml_node start_node,
- Default_route_accessor &default_route_accessor,
Routes_accessor &route_accessor,
Default_caps_accessor &default_caps_accessor,
Name_registry &name_registry,
diff --git a/repos/os/src/lib/sandbox/library.cc b/repos/os/src/lib/sandbox/library.cc
index caa9840ea6..7b4e72c60e 100644
--- a/repos/os/src/lib/sandbox/library.cc
+++ b/repos/os/src/lib/sandbox/library.cc
@@ -22,7 +22,6 @@
#include <heartbeat.h>
struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
- ::Sandbox::Child::Default_route_accessor,
::Sandbox::Child::Routes_accessor,
::Sandbox::Child::Default_caps_accessor,
::Sandbox::Child::Ram_limit_accessor,
@@ -134,15 +133,6 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
_children.report_state(xml, detail);
}
- /**
- * Default_route_accessor interface
- */
- Xml_node default_route() override
- {
- return _default_route.constructed() ? _default_route->xml()
- : Xml_node("<empty/>");
- }
-
/**
* Routes_accessor interface
*/
@@ -462,7 +452,7 @@ void Genode::Sandbox::Library::apply_config(Xml_node const &config)
Child &child = *new (_heap)
Child(_env, _heap, *_verbose,
Child::Id { ++_child_cnt }, _state_reporter,
- start_node, *this, *this, *this, _children,
+ start_node, *this, *this, _children,
Ram_quota { avail_ram.value - used_ram.value },
Cap_quota { avail_caps.value - used_caps.value },
*this, *this, prio_levels, affinity_space,
diff --git a/repos/os/src/lib/sandbox/utils.h b/repos/os/src/lib/sandbox/utils.h
index 36aab737f2..639a4be4dd 100644
--- a/repos/os/src/lib/sandbox/utils.h
+++ b/repos/os/src/lib/sandbox/utils.h
@@ -58,12 +58,9 @@ namespace Sandbox {
*/
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 skip_child_prefix = true)
+ Service::Name const &service_name)
{
bool const service_matches =
- service_node.has_type("any-service") ||
(service_node.has_type("service") &&
service_node.attribute_value("name", Service::Name()) == service_name);
@@ -72,7 +69,6 @@ namespace Sandbox {
typedef String<Session_label::capacity()> Label;
- char const *unscoped_attr = "unscoped_label";
char const *label_last_attr = "label_last";
bool const route_depends_on_child_provided_label =
@@ -81,34 +77,13 @@ namespace Sandbox {
service_node.has_attribute("label_suffix") ||
service_node.has_attribute(label_last_attr);
- 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");
-
- return label == service_node.attribute_value(unscoped_attr, Label());
- }
-
if (service_node.has_attribute(label_last_attr))
return service_node.attribute_value(label_last_attr, Label()) == label.last_element();
if (!route_depends_on_child_provided_label)
return true;
- char const * const scoped_label = skip_child_prefix
- ? skip_label_prefix(child_name.string(), label.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();
+ return !Xml_node_label_score(service_node, label).conflict();
}
@@ -131,6 +106,34 @@ namespace Sandbox {
return cnt > 1;
}
+
+ /*
+ * 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 or
+ * by specifying a 'prefix' and 'suffix attributes.
+ */
+ typedef String<Session_label::capacity()> Label;
+ inline Label target_label(Xml_node const node,
+ Child_policy::Name const &child_name,
+ Session_label const &label)
+ {
+ if (node.has_attribute("label"))
+ return Session_label(node.attribute_value("label", Label()).string());
+
+ Label head = node.attribute_value("prefix", Label(child_name.string()));
+ Label tail = node.attribute_value("suffix", Label(child_name == label ? "" : label));
+
+ if (head == "") return tail;
+ if (tail == "") return head;
+
+ return Label(prefixed_label(head, tail).string());
+ }
+
+
/**
* Find service with certain values in given registry
*
--
2.28.0