From 759e789ddd34bb799288d25ffd123163fdcd86c9 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Wed, 25 Jan 2012 20:19:03 +0100 Subject: [PATCH] Demo device driver manager (d3m) The new d3m component is the designated device-driver manager for the upcoming live CD. It addresses the auto probing of USB storage and ATAPI boot devices (issue #94) and the aggregation of user input coming from USB HID and PS/2. --- gems/src/server/d3m/README | 6 + gems/src/server/d3m/block_service.h | 317 ++++++++++++++++++++++++++++ gems/src/server/d3m/input_service.h | 268 +++++++++++++++++++++++ gems/src/server/d3m/main.cc | 292 +++++++++++++++++++++++++ gems/src/server/d3m/nic_service.h | 68 ++++++ gems/src/server/d3m/target.mk | 4 + 6 files changed, 955 insertions(+) create mode 100644 gems/src/server/d3m/README create mode 100644 gems/src/server/d3m/block_service.h create mode 100644 gems/src/server/d3m/input_service.h create mode 100644 gems/src/server/d3m/main.cc create mode 100644 gems/src/server/d3m/nic_service.h create mode 100644 gems/src/server/d3m/target.mk diff --git a/gems/src/server/d3m/README b/gems/src/server/d3m/README new file mode 100644 index 0000000000..042ad913a5 --- /dev/null +++ b/gems/src/server/d3m/README @@ -0,0 +1,6 @@ +D3m stands for demo device driver manager. It is the service responsible for +implementing device-driver policies such as probing for a boot device or +merging user input coming from different input devices (USB HID and PS/2). + +The actual device drivers are executed as child processes of d3m. At the +current stage, d3m is primarily geared towards bootable live CD images. diff --git a/gems/src/server/d3m/block_service.h b/gems/src/server/d3m/block_service.h new file mode 100644 index 0000000000..c80427f4cd --- /dev/null +++ b/gems/src/server/d3m/block_service.h @@ -0,0 +1,317 @@ +/* + * \brief D3m block service + * \author Norman Feske + * \author Christian Helmuth + * \date 2012-01-25 + */ + +/* + * Copyright (C) 2012 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 _BLOCK_SERVICE_H_ +#define _BLOCK_SERVICE_H_ + +/* Genode includes */ +#include +#include +#include + + +/** + * Facility to probe a block session for the presence of a specific file + * + * The 'Iso9660_boot_probe' utility is used to select an iso9660 formatted + * block device to boot from by checking for the presence of a magic file. + */ +class Iso9660_boot_probe +{ + /** + * Policy for iso9660 server when executed as slave service + * + * The policy supplies a predefined block root capability to the iso9660 + * server. + */ + struct Iso9660_policy : Genode::Slave_policy + { + Genode::Lock &_annouce_lock; + Genode::Capability _block_session; + Genode::Root_capability _rom_root; + + /** + * Pseudo service, handing out a predefined session capability + */ + struct Proxy_service : Genode::Service + { + Genode::Session_capability _session; + + Proxy_service(Genode::Session_capability session) + : Genode::Service("proxy"), _session(session) { } + + Genode::Session_capability session(const char *) { return _session; } + + void upgrade(Genode::Session_capability session, const char *) { } + + } _block_proxy_service; + + Iso9660_policy(Genode::Rpc_entrypoint &entrypoint, + Genode::Lock &annouce_lock, + Genode::Capability block_session) + : + Genode::Slave_policy("iso9660", entrypoint), + _annouce_lock(annouce_lock), + _block_session(block_session), + _block_proxy_service(block_session) + { } + + Genode::Root_capability rom_root() { return _rom_root; } + + + /**************************** + ** Slave_policy interface ** + ****************************/ + + const char **_permitted_services() const + { + static const char *permitted_services[] = { + "CAP", "RM", "LOG", "SIGNAL", 0 }; + return permitted_services; + }; + + Genode::Service *resolve_session_request(const char *service_name, + const char *args) + { + if (Genode::strcmp(service_name, "Block") == 0) + return &_block_proxy_service; + + return Genode::Slave_policy::resolve_session_request(service_name, args); + } + + bool announce_service(char const *service_name, + Genode::Root_capability root, + Genode::Allocator *alloc) + { + if (Genode::strcmp(service_name, "ROM") != 0) + return false; + + _rom_root = root; + _annouce_lock.unlock(); + return true; + } + }; + + Genode::Root_capability _block_root; + Block::Session_capability _block_session; + Genode::Lock _rom_announce_lock; + Genode::Cap_connection _cap; + + enum { STACK_SIZE = 2*4096 }; + Genode::Rpc_entrypoint _entrypoint; + + Iso9660_policy _iso9660_policy; + Genode::Slave _iso9660_slave; + + /** + * RAM quota to assign to the iso9660 service + */ + enum { ISO9660_RAM_QUOTA = 8*1024*1024 }; + + /** + * Obtain block session from specified root interface + */ + Block::Session_capability _init_session() + { + char const *args = "ram_quota=140K, tx_buf_size=128K"; + Genode::Root_client root(_block_root); + return Genode::static_cap_cast(root.session(args)); + } + + /** + * Private constructor + * + * This constructor is used by the 'probe' function below. + */ + Iso9660_boot_probe(Genode::Root_capability root, char const *boot_tag_name) + : + _block_root(root), + _block_session(_init_session()), + _rom_announce_lock(Genode::Lock::LOCKED), + _entrypoint(&_cap, STACK_SIZE, "iso9660_slave"), + _iso9660_policy(_entrypoint, _rom_announce_lock, _block_session), + _iso9660_slave(_entrypoint, _iso9660_policy, ISO9660_RAM_QUOTA) + { + /* wait until the iso9660 server announces the ROM service */ + _rom_announce_lock.lock(); + + /* try to open file with filename 'boot_tag_name' */ + Genode::Root_client rom_root(_iso9660_policy.rom_root()); + char args[Genode::Root::Session_args::MAX_SIZE]; + Genode::snprintf(args, sizeof(args), "ram_quota=4K, filename=\"%s\"", + boot_tag_name); + rom_root.session(args); + } + + public: + + /** + * Probe block service for the presence of a boot tag file + * + * \param root root capability of block service to probe + * \param boot_tag_name name of the file to probe for + * + * \return true if the specified tag file exists at the block session + */ + static bool probe(Genode::Root_capability root, + char const *boot_tag_name) + { + /* + * In the process of creating an 'Iso9660_boot_probe', many steps + * can fail. For example, the binary of the iso9660 server may be + * missing, the block service may not contain an iso9660 file + * system, or the file may be missing. Only if all steps succeed, + * we report the probing to have succeeded. + */ + try { + /* + * Do not allocate the boot probe at the stack because this + * object may too large for the stack size of the context + * calling the probe function. We use the heap instead to + * create and subsequently destroy the boot probe. + */ + Iso9660_boot_probe *boot_probe = new (Genode::env()->heap()) + Iso9660_boot_probe(root, boot_tag_name); + + Genode::destroy(Genode::env()->heap(), boot_probe); + return true; + } catch (...) { } + + PWRN("Could not probe file at iso9660 ROM service"); + return false; + } + + ~Iso9660_boot_probe() + { + /* close session at block service */ + Genode::Root_client(_block_root).close(_block_session); + } +}; + + +namespace Block { + + class Driver : public Genode::List::Element + { + private: + + char const *_name; + Genode::Root_capability _root; + + public: + + Driver() : _name(0) { } + + void init(char const *name, Genode::Root_capability root) + { + _name = name; + _root = root; + } + + char const *name() const { return _name; } + + Genode::Root_capability root() const { return _root; } + }; + + + class Driver_registry + { + private: + + Genode::Lock _lock; + Genode::List _drivers; + Genode::Semaphore _retry_probing_sem; + + public: + + void add_driver(Driver *driver) + { + Genode::Lock::Guard guard(_lock); + + _drivers.insert(driver); + + PDBG("\ninsert new driver %s", driver->name()); + _retry_probing_sem.up(); + } + + /** + * Return root capability of boot device + * + * If the boot device is not available yet, block until a matching + * driver becomes available. + */ + Genode::Root_capability root() + { + for (;;) { + { + Genode::Lock::Guard guard(_lock); + while (_drivers.first()) { + + Driver *driver = _drivers.first(); + PDBG("\nprobing driver %s", driver->name()); + + if (!Iso9660_boot_probe::probe(driver->root(), + "libc.lib.so")) { + PWRN("probing failed, not using %s as boot device", + driver->name()); + + _drivers.remove(driver); + break; + } + + PINF("found boot medium via %s", driver->name()); + return driver->root(); + } + } + + /* + * Block until another driver becomes known via + * 'add_driver' + */ + _retry_probing_sem.down(); + }; + } + }; + + + class Root : public Genode::Rpc_object > + { + private: + + Driver_registry &_driver_registry; + + public: + + Root(Driver_registry &driver_registry) + : _driver_registry(driver_registry) { } + + Genode::Session_capability session(Genode::Root::Session_args const &args) + { + PDBG("\nsession requested args=\"%s\"", args.string()); + Genode::Root_capability root = _driver_registry.root(); + return Genode::Root_client(root).session(args); + } + + void upgrade(Genode::Session_capability, + Genode::Root::Upgrade_args const &) { } + + void close(Genode::Session_capability session) + { + Genode::Root_capability root = _driver_registry.root(); + Genode::Root_client(root).close(session); + } + }; +} + +#endif /* _BLOCK_SERVICE_H_ */ diff --git a/gems/src/server/d3m/input_service.h b/gems/src/server/d3m/input_service.h new file mode 100644 index 0000000000..989fe5c297 --- /dev/null +++ b/gems/src/server/d3m/input_service.h @@ -0,0 +1,268 @@ +/* + * \brief D3m input service + * \author Norman Feske + * \author Christian Helmuth + * \date 2012-01-25 + * + * D3m supports merging the input events of multiple devices into one + * stream of events. Each driver corresponds to an event 'Source'. When the + * driver announces the "Input" session interface, the corresponding + * 'Source' is added to the 'Source_registry'. The d3m input serves queries + * all sources registered at the source registry for input and merges the + * streams of events. + */ + +/* + * Copyright (C) 2012 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 _INPUT_SERVICE_H_ +#define _INPUT_SERVICE_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +namespace Input { + + class Source : public Genode::List::Element + { + public: + + class Source_unavailable { }; + + private: + + Genode::Root_capability _root; + Input::Session_capability _session; + Input::Session_client _client; + Event *_ev_buf; + Genode::size_t _ev_buf_max; /* in events */ + + /** + * Open input session via the specified root capability + */ + static Session_capability + _request_session(Genode::Root_capability root) + { + const char *args = "ram_quota=8K"; + + try { + using namespace Genode; + return static_cap_cast(Root_client(root).session(args)); + } catch (...) { + throw Source_unavailable(); + } + } + + public: + + /** + * Constructor + * + * At construction time, the '_client' gets initialized using the + * default-initialized (invalid) '_session' capability. The + * 'Source::session' function is not usaable before + * re-initializing the object via the 'connect' function. + */ + Source() + : _client(_session), _ev_buf(0), _ev_buf_max(0) { } + + /** + * Called when the driver announces the "Input" service + */ + void connect(Genode::Root_capability root) + { + _root = root; + _session = _request_session(_root); + _client = Session_client(_session); + + /* locally map input-event buffer */ + Genode::Dataspace_capability ds_cap = _client.dataspace(); + _ev_buf = Genode::env()->rm_session()->attach(ds_cap); + _ev_buf_max = Genode::Dataspace_client(ds_cap).size() + / sizeof(Event); + } + + /** + * Return true is 'session()' is ready to use + */ + bool connected() const { return _session.valid(); } + + /** + * Return true if input is pending + */ + bool input_pending() { return connected() && _client.is_pending(); } + + /** + * Return event buffer + */ + Event const *ev_buf() const { return _ev_buf; } + + /** + * Flush input events + */ + Genode::size_t flush() { return _client.flush(); } + }; + + + struct Source_registry + { + private: + + Genode::Lock _lock; + Genode::List _sources; + + public: + + /** + * Register a new source of input events + * + * This function is called once for each driver, when the driver + * announced its "Input" service. By adding the new source, the + * driver's input events become visible to the d3m input session. + */ + void add_source(Source *entry) + { + Genode::Lock::Guard guard(_lock); + _sources.insert(entry); + } + + bool any_source_has_pending_input() + { + Genode::Lock::Guard guard(_lock); + + for (Source *e = _sources.first(); e; e = e->next()) + if (e->connected() && e->input_pending()) + return true; + + return false; + } + + /** + * Flush all input events from all available sources + * + * This function merges the input-event streams of all sources into + * one. + * + * \param dst destination event buffer + * \param dst_max capacility of event buffer, in number of events + * + * \return total number of available input events + */ + Genode::size_t flush_sources(Event *dst, Genode::size_t dst_max) const + { + Genode::size_t dst_count = 0; + + for (Source *e = _sources.first(); e; e = e->next()) { + + if (!e->connected() || !e->input_pending()) + continue; + + /* + * Copy input events from driver to client buffer + */ + Event const *src = e->ev_buf(); + Genode::size_t const src_max = e->flush(); + for (Genode::size_t i = 0; i < src_max; i++, dst_count++) { + + if (dst_count == dst_max) { + PERR("client input-buffer overflow"); + return dst_count; + } + + dst[dst_count] = src[i]; + } + } + return dst_count; + } + }; + + + /***************************** + ** Input service front end ** + *****************************/ + + class Session_component : public Genode::Rpc_object + { + private: + + Source_registry &_source_registry; + + /* + * Input event buffer shared with the client + */ + enum { MAX_EVENTS = 1000 }; + Genode::Attached_ram_dataspace _ev_ds; + + public: + + Session_component(Source_registry &source_registry) + : + _source_registry(source_registry), + _ev_ds(Genode::env()->ram_session(), MAX_EVENTS*sizeof(Event)) + { } + + /***************************** + ** Input-session interface ** + *****************************/ + + Genode::Dataspace_capability dataspace() { return _ev_ds.cap(); } + + bool is_pending() + { + return _source_registry.any_source_has_pending_input(); + } + + int flush() + { + return _source_registry.flush_sources(_ev_ds.local_addr(), + MAX_EVENTS); + } + }; + + /** + * Shortcut for single-client root component + */ + typedef Genode::Root_component Root_component; + + class Root : public Root_component + { + private: + + Source_registry &_source_registry; + + protected: + + Session_component *_create_session(const char *args) + { + try { + return new (md_alloc()) Session_component(_source_registry); + } catch (Genode::Allocator::Out_of_memory()) { + PERR("Out of memory"); + throw Genode::Root::Quota_exceeded(); + } catch (...) { + PERR("Exception in construction of NIC slave"); + throw; + } + } + + public: + + Root(Genode::Rpc_entrypoint *session_ep, + Genode::Allocator *md_alloc, + Source_registry &source_registry) + : + Root_component(session_ep, md_alloc), + _source_registry(source_registry) + { } + }; +} + +#endif /* _INPUT_SERVICE_H_ */ diff --git a/gems/src/server/d3m/main.cc b/gems/src/server/d3m/main.cc new file mode 100644 index 0000000000..f7ae306e6f --- /dev/null +++ b/gems/src/server/d3m/main.cc @@ -0,0 +1,292 @@ +/* + * \brief Demo device-driver manager (d3m) + * \author Christian Helmuth + * \author Norman Feske + * \date 2010-09-21 + * + * D3m is a simple device-driver manager for demo purposes. Currently, it copes + * with the following tasks: + * + * - Merge events of input drivers for PS/2 and USB HID + * - Probe boot device using the ATAPI and USB storage drivers + */ + +/* + * Copyright (C) 2012 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. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include + +/* local includes */ +#include +#include +#include + + +class Ipxe_policy : public Genode::Slave_policy, public Nic::Provider +{ + Genode::Root_capability _cap; + bool _announced; + Genode::Lock _lock; + + protected: + + char const **_permitted_services() const + { + static char const *permitted_services[] = { + "CAP", "RM", "LOG", "SIGNAL", "Timer", "PCI", + "IO_MEM", "IO_PORT", "IRQ", 0 }; + + return permitted_services; + }; + + public: + + Ipxe_policy(Genode::Rpc_entrypoint &entrypoint) + : + Slave_policy("nic_drv", entrypoint), + _announced(false), + _lock(Genode::Lock::LOCKED) + { } + + bool announce_service(const char *service_name, + Genode::Root_capability root, + Genode::Allocator *alloc) + { + if (Genode::strcmp(service_name, "Nic")) + return false; + + _cap = root; + return true; + } + + /** + * Nic::Provider interface + */ + Genode::Root_capability root() { return _cap; } +}; + + +struct Ps2_policy : public Genode::Slave_policy +{ + private: + + Input::Source_registry &_input_source_registry; + Input::Source _input_source_registry_entry; + + protected: + + char const **_permitted_services() const + { + static char const *allowed_services[] = { + "CAP", "RM", "IO_PORT", "IRQ", "LOG", 0 }; + + return allowed_services; + }; + + public: + + Ps2_policy(Genode::Rpc_entrypoint &entrypoint, + Input::Source_registry &input_source_registry) + : + Genode::Slave_policy("ps2_drv", entrypoint), + _input_source_registry(input_source_registry) + { } + + bool announce_service(const char *service_name, + Genode::Root_capability root, + Genode::Allocator *alloc) + { + if (Genode::strcmp(service_name, "Input")) + return false; + + _input_source_registry_entry.connect(root); + _input_source_registry.add_source(&_input_source_registry_entry); + return true; + } +}; + + +struct Usb_policy : public Genode::Slave_policy +{ + private: + + Input::Source_registry &_input_source_registry; + Input::Source _input_source_registry_entry; + + Block::Driver_registry &_block_driver_registry; + Block::Driver _block_driver; + + protected: + + const char **_permitted_services() const + { + static const char *permitted_services[] = { + "CAP", "RM", "IO_PORT", "IO_MEM", "IRQ", "LOG", "PCI", + "Timer", "SIGNAL", 0 }; + + return permitted_services; + }; + + public: + + Usb_policy(Genode::Rpc_entrypoint &entrypoint, + Input::Source_registry &input_source_registry, + Block::Driver_registry &block_driver_registry, + Genode::Dataspace_capability config) + : + Genode::Slave_policy("usb_drv", entrypoint, config), + _input_source_registry(input_source_registry), + _block_driver_registry(block_driver_registry) + { } + + bool announce_service(const char *service_name, + Genode::Root_capability root, + Genode::Allocator *alloc) + { + if (Genode::strcmp(service_name, "Input") == 0) { + _input_source_registry_entry.connect(root); + _input_source_registry.add_source(&_input_source_registry_entry); + return true; + } + + if (Genode::strcmp(service_name, "Block") == 0) { + _block_driver.init("usb_drv", root); + _block_driver_registry.add_driver(&_block_driver); + return true; + } + + return false; + } +}; + + +class Atapi_policy : public Genode::Slave_policy +{ + private: + + Block::Driver_registry &_block_driver_registry; + Block::Driver _block_driver; + + protected: + + const char **_permitted_services() const + { + static const char *permitted_services[] = { + "CAP", "RM", "LOG", "SIGNAL", "Timer", "PCI", "IO_MEM", + "IO_PORT", "IRQ", 0 }; + + return permitted_services; + }; + + public: + + Atapi_policy(Genode::Rpc_entrypoint &entrypoint, + Block::Driver_registry &block_driver_registry) + : + Genode::Slave_policy("atapi_drv", entrypoint), + _block_driver_registry(block_driver_registry) { } + + bool announce_service(char const *service_name, + Genode::Root_capability root, + Genode::Allocator *alloc) + { + if (Genode::strcmp(service_name, "Block")) + return false; + + _block_driver.init(name(), root); + _block_driver_registry.add_driver(&_block_driver); + return true; + } +}; + + +int main(int argc, char **argv) +{ + using namespace Genode; + + enum { STACK_SIZE = 2*4096 }; + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "d3m_ep"); + + /* + * Registry of input-event sources + * + * Upon startup of the USB HID and PS/2 drivers, this registry gets + * populated when each of the drivers announces its respective 'Input' + * service. + */ + static Input::Source_registry input_source_registry; + + /* + * Registry for boot block device + */ + static Block::Driver_registry block_driver_registry; + + /* create PS/2 driver */ + static Rpc_entrypoint ps2_ep(&cap, STACK_SIZE, "ps2_slave"); + static Ps2_policy ps2_policy(ps2_ep, input_source_registry); + static Genode::Slave ps2_slave(ps2_ep, ps2_policy, 512*1024); + + /* + * Create config dataspace for USB driver + */ + enum { USB_CONFIG_MAX_LEN = 4096 }; + Genode::Attached_ram_dataspace usb_config_ds(Genode::env()->ram_session(), + USB_CONFIG_MAX_LEN); + char const *config = ""; + Genode::strncpy(usb_config_ds.local_addr(), config, USB_CONFIG_MAX_LEN); + + /* create USB driver */ + static Rpc_entrypoint usb_ep(&cap, STACK_SIZE, "usb_slave"); + static Usb_policy usb_policy(usb_ep, input_source_registry, + block_driver_registry, usb_config_ds.cap()); + static Genode::Slave usb_slave(usb_ep, usb_policy, 1024*1024); + + /* create ATAPI driver */ + static Rpc_entrypoint atapi_ep(&cap, STACK_SIZE, "atapi_slave"); + static Atapi_policy atapi_policy(atapi_ep, block_driver_registry); + static Genode::Slave atapi_slave(atapi_ep, atapi_policy, 1024*1024); + + /* initialize input service */ + static Input::Root input_root(&ep, env()->heap(), input_source_registry); + env()->parent()->announce(ep.manage(&input_root)); + + /* + * Always announce 'Nic' service, throw 'Unavailable' during session + * request if no appropriate driver could be found. + */ + static Rpc_entrypoint nic_ep(&cap, STACK_SIZE, "nic_slave"); + static Ipxe_policy nic_policy(nic_ep); + static Genode::Slave nic_slave(nic_ep, nic_policy, 2048 * 1024); + + static Nic::Root nic_root(nic_policy); + env()->parent()->announce(ep.manage(&nic_root)); + + /* + * Announce 'Block' service + * + * We use a distinct entrypoint for the block driver because otherwise, a + * long-taking block session request may defer other session requests + * (i.e., input session). By using a different entrypoint, the GUI can + * start even if the boot-device probing failed completely. + */ + { + static Rpc_entrypoint ep(&cap, STACK_SIZE, "d3m_block_ep"); + static Block::Root block_root(block_driver_registry); + env()->parent()->announce(ep.manage(&block_root)); + } + + Genode::sleep_forever(); + return 0; +} diff --git a/gems/src/server/d3m/nic_service.h b/gems/src/server/d3m/nic_service.h new file mode 100644 index 0000000000..d7fc68da20 --- /dev/null +++ b/gems/src/server/d3m/nic_service.h @@ -0,0 +1,68 @@ +/* + * \brief D3m NIC service + * \author Norman Feske + * \author Christian Helmuth + * \date 2012-01-25 + */ + +/* + * Copyright (C) 2012 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 _NIC_SERVICE_H_ +#define _NIC_SERVICE_H_ + +/* Genode includes */ +#include +#include +#include + +namespace Nic { + + struct Provider + { + bool ready_to_use() { return root().valid(); } + + virtual Genode::Root_capability root() = 0; + }; + + /** + * Root interface of NIC service + */ + class Root : public Genode::Rpc_object > + { + private: + + Provider &_nic_provider; + + public: + + Genode::Session_capability session(Session_args const &args) + { + if (!args.is_valid_string()) throw Invalid_args(); + + if (!_nic_provider.ready_to_use()) + throw Unavailable(); + + try { + return Genode::Root_client(_nic_provider.root()).session(args.string()); + } catch (...) { + throw Unavailable(); + } + } + + void upgrade(Genode::Session_capability, Upgrade_args const &) { } + + void close(Genode::Session_capability session) + { + Genode::Root_client(_nic_provider.root()).close(session); + } + + Root(Provider &nic_provider) : _nic_provider(nic_provider) { } + }; +} + +#endif /* _INPUT_SERVICE_H_ */ diff --git a/gems/src/server/d3m/target.mk b/gems/src/server/d3m/target.mk new file mode 100644 index 0000000000..cbf0e1257e --- /dev/null +++ b/gems/src/server/d3m/target.mk @@ -0,0 +1,4 @@ +TARGET = d3m +SRC_CC = main.cc +LIBS = cxx env signal server process +INC_DIR += $(PRG_DIR)