mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-02 16:52:52 +00:00
This patch extracts the child-management functionality from the init component into a new library called "sandbox". The library API is located at 'os/include/os/sandbox.h'. The sandbox API allows for the interaction of the component with the sandboxed children by providing locally implemented services. This mechanism is illustrated by the new test at os/src/test/sandbox. Issue #3601
274 lines
7.8 KiB
C++
274 lines
7.8 KiB
C++
/*
|
|
* \brief Utilities
|
|
* \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 _LIB__SANDBOX__UTILS_H_
|
|
#define _LIB__SANDBOX__UTILS_H_
|
|
|
|
namespace Sandbox {
|
|
|
|
static inline void warn_insuff_quota(size_t const avail)
|
|
{
|
|
warning("specified quota exceeds available quota, "
|
|
"proceeding with a quota of ", avail);
|
|
}
|
|
|
|
|
|
/**
|
|
* 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_value("name", Service::Name()) == service_name);
|
|
|
|
if (!service_matches)
|
|
return false;
|
|
|
|
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 =
|
|
service_node.has_attribute("label") ||
|
|
service_node.has_attribute("label_prefix") ||
|
|
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_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 <typename T>
|
|
inline bool is_ambiguous(Registry<T> 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;
|
|
}
|
|
|
|
/**
|
|
* Find service with certain values in given registry
|
|
*
|
|
* \param services service registry
|
|
* \param name name of wanted service
|
|
* \param filter_fn function that applies additional filters
|
|
*
|
|
* \throw Service_denied
|
|
*/
|
|
template <typename T, typename FILTER_FN>
|
|
inline T &find_service(Registry<T> &services,
|
|
Service::Name const &name,
|
|
FILTER_FN const &filter_fn)
|
|
{
|
|
T *service = nullptr;
|
|
services.for_each([&] (T &s) {
|
|
|
|
if (service || s.name() != name || filter_fn(s))
|
|
return;
|
|
|
|
service = &s;
|
|
});
|
|
|
|
if (!service)
|
|
throw Service_denied();
|
|
|
|
if (service->abandoned())
|
|
throw Service_denied();
|
|
|
|
return *service;
|
|
}
|
|
|
|
|
|
inline void generate_ram_info(Xml_generator &xml, Pd_session const &pd)
|
|
{
|
|
typedef String<32> Value;
|
|
xml.attribute("quota", Value(pd.ram_quota()));
|
|
xml.attribute("used", Value(pd.used_ram()));
|
|
xml.attribute("avail", Value(pd.avail_ram()));
|
|
}
|
|
|
|
inline void generate_caps_info(Xml_generator &xml, Pd_session const &pd)
|
|
{
|
|
typedef String<32> Value;
|
|
xml.attribute("quota", Value(pd.cap_quota()));
|
|
xml.attribute("used", Value(pd.used_caps()));
|
|
xml.attribute("avail", Value(pd.avail_caps()));
|
|
}
|
|
|
|
/**
|
|
* Read priority-levels declaration from config
|
|
*/
|
|
inline Prio_levels prio_levels_from_xml(Xml_node config)
|
|
{
|
|
long const prio_levels = config.attribute_value("prio_levels", 0UL);
|
|
|
|
if (prio_levels && ((prio_levels >= (long)sizeof(prio_levels)*8) ||
|
|
(prio_levels != (1L << log2(prio_levels))))) {
|
|
warning("prio levels is not power of two, priorities are disabled");
|
|
return Prio_levels { 0 };
|
|
}
|
|
return Prio_levels { prio_levels };
|
|
}
|
|
|
|
|
|
inline long priority_from_xml(Xml_node start_node, Prio_levels prio_levels)
|
|
{
|
|
long const default_priority = Cpu_session::DEFAULT_PRIORITY;
|
|
|
|
long priority = start_node.attribute_value("priority", default_priority);
|
|
|
|
/*
|
|
* 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.value)) {
|
|
long new_prio = prio_levels.value ? prio_levels.value - 1 : 0;
|
|
|
|
Service::Name const name = start_node.attribute_value("name", Service::Name());
|
|
warning(name, ": invalid priority, upgrading "
|
|
"from ", -priority, " to ", -new_prio);
|
|
return new_prio;
|
|
}
|
|
|
|
return priority;
|
|
}
|
|
|
|
|
|
inline Affinity::Location
|
|
affinity_location_from_xml(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<unsigned long>("width", default_width),
|
|
height = node.attribute_value<unsigned long>("height", default_height);
|
|
|
|
long const x1 = node.attribute_value<long>("xpos", 0),
|
|
y1 = node.attribute_value<long>("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()); }
|
|
}
|
|
|
|
|
|
/**
|
|
* Read affinity-space parameters from config
|
|
*
|
|
* If no affinity space is declared, construct a space with a single element,
|
|
* width and height being 1. If only one of both dimensions is specified, the
|
|
* other dimension is set to 1.
|
|
*/
|
|
inline Affinity::Space affinity_space_from_xml(Xml_node config)
|
|
{
|
|
try {
|
|
Xml_node node = config.sub_node("affinity-space");
|
|
return Affinity::Space(node.attribute_value<unsigned long>("width", 1),
|
|
node.attribute_value<unsigned long>("height", 1));
|
|
} catch (...) {
|
|
return Affinity::Space(1, 1); }
|
|
}
|
|
}
|
|
|
|
#endif /* _LIB__SANDBOX__UTILS_H_ */
|