308 lines
7.8 KiB
C++
308 lines
7.8 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-2016 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.
|
|
*/
|
|
|
|
#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;
|
|
|
|
protected:
|
|
|
|
typedef Registered<Genode::Parent_service> Parent_service;
|
|
typedef Registry<Parent_service> Parent_services;
|
|
|
|
/**
|
|
* Return white list of services the slave is permitted to use
|
|
*
|
|
* The list is terminated via a null pointer.
|
|
*/
|
|
virtual char const **_permitted_services() const = 0;
|
|
|
|
private:
|
|
|
|
Label const _label;
|
|
Binary_name const _binary_name;
|
|
Ram_session_client _ram;
|
|
Genode::Parent_service _binary_service;
|
|
size_t _ram_quota;
|
|
Parent_services _parent_services;
|
|
Rpc_entrypoint &_ep;
|
|
Child_policy_dynamic_rom_file _config_policy;
|
|
|
|
bool _service_permitted(Service::Name const &service_name) const
|
|
{
|
|
for (const char **s = _permitted_services(); *s; ++s)
|
|
if (service_name == *s)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
Session_requester _session_requester;
|
|
|
|
public:
|
|
|
|
class Connection;
|
|
|
|
/**
|
|
* Slave-policy constructor
|
|
*
|
|
* \param ep entrypoint used to provide local services
|
|
* such as the config ROM service
|
|
* \param local_rm local address space, needed to populate dataspaces
|
|
* provided to the child (config, session_requests)
|
|
*
|
|
* \throw Ram_session::Alloc_failed by 'Child_policy_dynamic_rom_file'
|
|
* \throw Rm_session::Attach_failed by 'Child_policy_dynamic_rom_file'
|
|
*/
|
|
Policy(Label const &label,
|
|
Name const &binary_name,
|
|
Rpc_entrypoint &ep,
|
|
Region_map &rm,
|
|
Ram_session_capability ram_cap,
|
|
size_t ram_quota)
|
|
:
|
|
_label(label), _binary_name(binary_name), _ram(ram_cap),
|
|
_binary_service(Rom_session::service_name()),
|
|
_ram_quota(ram_quota), _ep(ep),
|
|
_config_policy("config", _ep, &_ram),
|
|
_session_requester(ep, _ram, 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; }
|
|
|
|
Ram_session &ref_ram() override { return _ram; }
|
|
Ram_session_capability ref_ram_cap() const override { return _ram; }
|
|
|
|
void init(Ram_session &session, Ram_session_capability cap) override
|
|
{
|
|
session.ref_account(_ram);
|
|
_ram.transfer_quota(cap, _ram_quota);
|
|
}
|
|
|
|
Service &resolve_session_request(Service::Name const &service_name,
|
|
Session_state::Args const &args)
|
|
{
|
|
/* check for config file request */
|
|
if (Service *s = _config_policy.resolve_session_request(service_name.string(), args.string()))
|
|
return *s;
|
|
|
|
if (service_name == "ROM") {
|
|
Session_label const rom_name(label_from_args(args.string()).last_element());
|
|
if (rom_name == _binary_name) return _binary_service;
|
|
if (rom_name == "session_requests") return _session_requester.service();
|
|
}
|
|
|
|
if (!_service_permitted(service_name)) {
|
|
error(name(), ": illegal session request of "
|
|
"service \"", service_name, "\" (", args, ")");
|
|
throw Parent::Service_denied();
|
|
}
|
|
|
|
/* 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)
|
|
service = new (env()->heap())
|
|
Parent_service(_parent_services, service_name);
|
|
|
|
return *service;
|
|
}
|
|
|
|
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
|
|
{
|
|
Id_space<Parent::Server> &_id_space;
|
|
|
|
Lock _lock { Lock::LOCKED };
|
|
bool _alive = false;
|
|
|
|
Service(Policy &policy)
|
|
:
|
|
Genode::Service(CONNECTION::service_name(), policy.ref_ram_cap()),
|
|
_id_space(policy.server_id_space())
|
|
{ }
|
|
|
|
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, _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::INVALID_ARGS:
|
|
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;
|
|
|
|
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 Parent::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 Parent::Service_denied parent denies session request
|
|
* \throw Parent::Quota_exceeded our own quota does not suffice for
|
|
* the creation of the new session
|
|
*/
|
|
Connection(Slave::Policy &policy, Args const &args,
|
|
Affinity const &affinity = Affinity())
|
|
:
|
|
Connection_base<CONNECTION>(policy, args, affinity),
|
|
CONNECTION::Client(Connection_base<CONNECTION>::_cap())
|
|
{ }
|
|
};
|
|
|
|
#endif /* _INCLUDE__OS__SLAVE_H_ */
|