os: improved label matching

This patch unifies the mechanism of selecting server-side policies and
taking session-routing decisions based on session labels. In both cases,
XML nodes are scored against session labels. The score depends on the
XML attributes 'label' (exact match), 'label_prefix', and
'label_suffix'.

Issue #1766
This commit is contained in:
Norman Feske 2015-11-24 16:53:10 +01:00 committed by Christian Helmuth
parent b766387069
commit 3f6ff39d76
2 changed files with 138 additions and 32 deletions

View File

@ -178,17 +178,12 @@ namespace Init {
if (!service_matches)
return false;
if (!service_node.has_attribute("label"))
return true;
typedef Genode::String<160> Label;
Label const expected = service_node.attribute_value("label", Label());
typedef Genode::String<128> Label;
Label const session_label =
Label(skip_label_prefix(child_name, Genode::Session_label(args).string()));
return session_label == expected;
return !Genode::Xml_node_label_score(service_node, session_label).conflict();
}

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright (C) 2011-2013 Genode Labs GmbH
* Copyright (C) 2011-2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
@ -19,11 +19,136 @@
namespace Genode {
struct Xml_node_label_score;
struct Session_label;
class Session_policy;
}
/**
* Score for matching an Xml_node against a label
*
* The score is based on the attributes 'label', 'label_prefix', and
* 'label_suffix'.
*/
struct Genode::Xml_node_label_score
{
bool label_present = true;
bool prefix_present = true;
bool suffix_present = true;
bool label_match = false;
/*
* The match values contain the number of matching characters + 1.
* If 0, there is a conflict. If 1, an empty string matched.
*/
enum { CONFLICT = 0 };
unsigned prefix_match = CONFLICT;
unsigned suffix_match = CONFLICT;
Xml_node_label_score() { }
template <size_t N>
Xml_node_label_score(Xml_node node, String<N> const &label)
:
label_present (node.has_attribute("label")),
prefix_present(node.has_attribute("label_prefix")),
suffix_present(node.has_attribute("label_suffix"))
{
if (label_present)
label_match = node.attribute_value("label", String<N>()) == label;
if (prefix_present) {
typedef String<N> Prefix;
Prefix const prefix = node.attribute_value("label_prefix", Prefix());
if (!strcmp(label.string(), prefix.string(), prefix.length() - 1))
prefix_match = prefix.length();
}
if (suffix_present) {
typedef String<N> Suffix;
Suffix const suffix = node.attribute_value("label_suffix", Suffix());
if (label.length() >= suffix.length()) {
unsigned const offset = label.length() - suffix.length();
if (!strcmp(label.string() + offset, suffix.string()))
suffix_match = suffix.length();
}
}
}
bool conflict() const
{
return (label_present && !label_match)
|| (prefix_present && !prefix_match)
|| (suffix_present && !suffix_match);
}
/**
* Return true if this node's score is higher than 'other'
*/
bool stronger(Xml_node_label_score const &other) const
{
/* if we are in conflict, we have a lower score than any other node */
if (conflict())
return false;
/* there are no conflicts */
/* we have a higher score than another conflicting node */
if (other.conflict())
return true;
if (label_present && !other.label_present)
return true;
if (other.label_present)
return false;
/* labels are equally good */
if (prefix_present && !other.prefix_present)
return true;
if (!prefix_present && other.prefix_present)
return false;
if (prefix_present && other.prefix_present) {
if (prefix_match > other.prefix_match)
return true;
if (prefix_match < other.prefix_match)
return false;
}
/* prefixes are equally good */
if (suffix_present && !other.suffix_present)
return true;
if (!suffix_present && other.suffix_present)
return false;
if (suffix_present && other.suffix_present) {
if (suffix_match > other.suffix_match)
return true;
if (suffix_match < other.suffix_match)
return false;
}
/* nodes are equally good */
return false;
}
};
struct Genode::Session_label : String<128>
{
Session_label() { }
@ -62,41 +187,27 @@ class Genode::Session_policy : public Xml_node
private:
/**
* Returns true if the start of the label matches the specified
* match string
*/
static bool _label_matches(char const *label, char const *match) {
return strcmp(label, match, strlen(match)) == 0; }
/**
* Query session policy from session label
*/
static Xml_node _query_policy(char const *label, Xml_node config)
template <size_t N>
static Xml_node _query_policy(String<N> const &label, Xml_node config)
{
/*
* Find policy node that matches best
*/
Xml_node best_match("<none/>");
unsigned label_len = 0;
Xml_node best_match("<none/>");
Xml_node_label_score best_score;
/*
* Functor to be applied to each policy node
*/
auto lambda = [&] (Xml_node policy) {
/* label attribute from policy node */
char policy_label[Session_label::capacity()];
policy.attribute("label").value(policy_label,
sizeof(policy_label));
if (!_label_matches(label, policy_label)
|| strlen(policy_label) < label_len)
return;
label_len = strlen(policy_label);
best_match = policy;
Xml_node_label_score const score(policy, label);
if (score.stronger(best_score)) {
best_match = policy;
best_score = score;
}
};
config.for_each_sub_node("policy", lambda);
@ -132,7 +243,7 @@ class Genode::Session_policy : public Xml_node
explicit Session_policy(String<N> const &label,
Xml_node config = Genode::config()->xml_node())
:
Xml_node(_query_policy(label.string(), config))
Xml_node(_query_policy(label, config))
{ }
};