genode/repos/os/include/os/slave.h

338 lines
8.7 KiB
C++

/*
* \brief Convenience helper for running a service as child process
* \author Norman Feske
* \author Christian Helmuth
* \date 2012-01-25
*/
/*
* Copyright (C) 2012-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__OS__SLAVE_H_
#define _INCLUDE__OS__SLAVE_H_
/* Genode includes */
#include <base/rpc_server.h>
#include <base/local_connection.h>
#include <base/child.h>
#include <init/child_policy.h>
#include <os/child_policy_dynamic_rom.h>
#include <os/session_requester.h>
namespace Genode { struct Slave; }
struct Genode::Slave
{
typedef Session_state::Args Args;
class Policy;
template <typename>
class Connection_base;
template <typename>
struct Connection;
};
class Genode::Slave::Policy : public Child_policy
{
public:
typedef Child_policy::Name Name;
typedef Session_label Label;
typedef Registered<Genode::Parent_service> Parent_service;
typedef Registry<Parent_service> Parent_services;
private:
Label const _label;
Binary_name const _binary_name;
Pd_session &_ref_pd;
Pd_session_capability _ref_pd_cap;
Genode::Parent_service _binary_service;
Cap_quota const _cap_quota;
Ram_quota const _ram_quota;
Parent_services &_parent_services;
Rpc_entrypoint &_ep;
Child_policy_dynamic_rom_file _config_policy;
Session_requester _session_requester;
Service &_matching_service(Service::Name const &service_name,
Session_label const &label)
{
/* check for config file request */
if (Service *s = _config_policy.resolve_session_request(service_name, label))
return *s;
if (service_name == "ROM") {
Session_label const rom_name(label.last_element());
if (rom_name == _binary_name) return _binary_service;
if (rom_name == "session_requests") return _session_requester.service();
}
/* fill parent service registry on demand */
Parent_service *service = nullptr;
_parent_services.for_each([&] (Parent_service &s) {
if (!service && s.name() == service_name)
service = &s; });
if (!service) {
error(name(), ": illegal session request of "
"service \"", service_name, "\" (", label, ")");
throw Service_denied();
}
return *service;
}
public:
class Connection;
/**
* Slave-policy constructor
*
* \param ep entrypoint used to provide local services
* such as the config ROM service
*
* \throw Out_of_ram by 'Child_policy_dynamic_rom_file'
* \throw Out_of_caps by 'Child_policy_dynamic_rom_file'
*/
Policy(Env &env,
Label const &label,
Name const &binary_name,
Parent_services &parent_services,
Rpc_entrypoint &ep,
Cap_quota cap_quota,
Ram_quota ram_quota)
:
_label(label), _binary_name(binary_name),
_ref_pd(env.pd()), _ref_pd_cap(env.pd_session_cap()),
_binary_service(env, Rom_session::service_name()),
_cap_quota(cap_quota), _ram_quota(ram_quota),
_parent_services(parent_services), _ep(ep),
_config_policy(env.rm(), "config", _ep, &env.pd()),
_session_requester(ep, env.pd(), env.rm())
{
configure("<config/>");
}
/**
* Assign new configuration to slave
*
* \param config new configuration as null-terminated string
*/
void configure(char const *config)
{
_config_policy.load(config, strlen(config) + 1);
}
void configure(char const *config, size_t len)
{
_config_policy.load(config, len);
}
void trigger_session_requests()
{
_session_requester.trigger_update();
}
/****************************
** Child_policy interface **
****************************/
Name name() const override { return _label; }
Binary_name binary_name() const override { return _binary_name; }
Pd_session &ref_pd() override { return _ref_pd; }
Pd_session_capability ref_pd_cap() const override { return _ref_pd_cap; }
void init(Pd_session &session, Pd_session_capability cap) override
{
session.ref_account(_ref_pd_cap);
_ref_pd.transfer_quota(cap, _cap_quota);
_ref_pd.transfer_quota(cap, _ram_quota);
}
Route resolve_session_request(Service::Name const &name,
Session_label const &label) override
{
return Route { .service = _matching_service(name, label),
.label = label,
.diag = Session::Diag() };
}
Id_space<Parent::Server> &server_id_space() override {
return _session_requester.id_space(); }
};
template <typename CONNECTION>
class Genode::Slave::Connection_base
{
protected:
/* each connection appears as a separate client */
Id_space<Parent::Client> _id_space;
Policy &_policy;
struct Service : Genode::Service, Session_state::Ready_callback,
Session_state::Closed_callback
{
Policy &_policy;
Lock _lock { Lock::LOCKED };
bool _alive = false;
Service(Policy &policy)
:
Genode::Service(CONNECTION::service_name()), _policy(policy)
{ }
void initiate_request(Session_state &session) override
{
switch (session.phase) {
case Session_state::CREATE_REQUESTED:
if (!session.id_at_server.constructed())
session.id_at_server.construct(session,
_policy.server_id_space());
session.ready_callback = this;
session.async_client_notify = true;
break;
case Session_state::UPGRADE_REQUESTED:
warning("upgrading slaves is not implemented");
session.phase = Session_state::CAP_HANDED_OUT;
break;
case Session_state::CLOSE_REQUESTED:
warning("closing slave connections is not implemented");
session.phase = Session_state::CLOSED;
break;
case Session_state::SERVICE_DENIED:
case Session_state::INSUFFICIENT_RAM_QUOTA:
case Session_state::INSUFFICIENT_CAP_QUOTA:
case Session_state::AVAILABLE:
case Session_state::CAP_HANDED_OUT:
case Session_state::CLOSED:
break;
}
}
void wakeup() override { }
/**
* Session_state::Ready_callback
*/
void session_ready(Session_state &session) override
{
_alive = session.alive();
_lock.unlock();
}
/**
* Session_state::Closed_callback
*/
void session_closed(Session_state &s) override { _lock.unlock(); }
/**
* Service ('Ram_transfer::Account') interface
*/
void transfer(Pd_session_capability to, Ram_quota amount) override
{
if (to.valid()) _policy.ref_pd().transfer_quota(to, amount);
}
/**
* Service ('Ram_transfer::Account') interface
*/
Pd_session_capability cap(Ram_quota) const override
{
return _policy.ref_pd_cap();
}
/**
* Service ('Cap_transfer::Account') interface
*/
void transfer(Pd_session_capability to, Cap_quota amount) override
{
if (to.valid()) _policy.ref_pd().transfer_quota(to, amount);
}
/**
* Service ('Cap_transfer::Account') interface
*/
Pd_session_capability cap(Cap_quota) const override
{
return _policy.ref_pd_cap();
}
} _service;
Local_connection<CONNECTION> _connection;
Connection_base(Policy &policy, Args const &args, Affinity const &affinity)
:
_policy(policy), _service(_policy),
_connection(_service, _id_space, { 1 }, args, affinity)
{
_policy.trigger_session_requests();
_service._lock.lock();
if (!_service._alive)
throw Service_denied();
}
~Connection_base()
{
_policy.trigger_session_requests();
_service._lock.lock();
}
typedef typename CONNECTION::Session_type SESSION;
Capability<SESSION> _cap() const { return _connection.cap(); }
};
template <typename CONNECTION>
struct Genode::Slave::Connection : private Connection_base<CONNECTION>,
public CONNECTION::Client
{
/**
* Constructor
*
* \throw Service_denied parent denies session request
* \throw Out_of_ram our own quota does not suffice for
* the creation of the new session
* \throw Out_of_caps
*/
Connection(Slave::Policy &policy, Args const &args,
Affinity const &affinity = Affinity())
:
Connection_base<CONNECTION>(policy, args, affinity),
CONNECTION::Client(Connection_base<CONNECTION>::_cap())
{ }
Connection(Region_map &rm, Slave::Policy &policy, Args const &args,
Affinity const &affinity = Affinity())
:
Connection_base<CONNECTION>(policy, args, affinity),
CONNECTION::Client(rm, Connection_base<CONNECTION>::_cap())
{ }
};
#endif /* _INCLUDE__OS__SLAVE_H_ */