diff --git a/Cargo.lock b/Cargo.lock index 509e60aa4..eef290a2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,7 @@ name = "parity" version = "1.1.0" dependencies = [ + "bincode 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.63 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 1.1.1 (git+https://github.com/tomusdrw/rust-ctrlc.git)", "daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -9,6 +10,9 @@ dependencies = [ "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore 1.1.0", "ethcore-devtools 1.1.0", + "ethcore-ipc 1.1.0", + "ethcore-ipc-codegen 1.1.0", + "ethcore-ipc-nano 1.1.0", "ethcore-rpc 1.1.0", "ethcore-util 1.1.0", "ethcore-webapp 1.1.0", @@ -23,6 +27,9 @@ dependencies = [ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -63,6 +70,17 @@ dependencies = [ "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bincode" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bitflags" version = "0.3.3" @@ -78,6 +96,11 @@ name = "blastfig" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "byteorder" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bytes" version = "0.3.0" @@ -254,6 +277,36 @@ dependencies = [ "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ethcore-ipc" +version = "1.1.0" +dependencies = [ + "ethcore-devtools 1.1.0", + "nanomsg 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)", + "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ethcore-ipc-codegen" +version = "1.1.0" +dependencies = [ + "aster 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-ipc 1.1.0", + "quasi 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quasi_codegen 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_syntax 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ethcore-ipc-nano" +version = "1.1.0" +dependencies = [ + "ethcore-ipc 1.1.0", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "nanomsg 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)", +] + [[package]] name = "ethcore-rpc" version = "1.1.0" @@ -659,6 +712,24 @@ name = "modifier" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "nanomsg" +version = "0.5.0" +source = "git+https://github.com/ethcore/nanomsg.rs.git#26449b15f29b850bcf62a577f1ee3a56474a0bc9" +dependencies = [ + "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "nanomsg-sys 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)", +] + +[[package]] +name = "nanomsg-sys" +version = "0.5.0" +source = "git+https://github.com/ethcore/nanomsg.rs.git#26449b15f29b850bcf62a577f1ee3a56474a0bc9" +dependencies = [ + "gcc 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "net2" version = "0.2.23" diff --git a/Cargo.toml b/Cargo.toml index 6f1a70d76..5dc5afcb3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,9 @@ build = "build.rs" [build-dependencies] rustc_version = "0.1" +syntex = "*" +serde_codegen = "0.7.0" +"ethcore-ipc-codegen" = { path = "ipc/codegen" } [dependencies] log = "0.3" @@ -30,6 +33,10 @@ ethcore-devtools = { path = "devtools" } ethcore-rpc = { path = "rpc", optional = true } ethcore-webapp = { path = "webapp", optional = true } semver = "0.2" +ethcore-ipc-nano = { path = "ipc/nano" } +"ethcore-ipc" = { path = "ipc/rpc" } +bincode = "*" +serde = "0.7.0" [dependencies.hyper] version = "0.8" diff --git a/build.rs b/build.rs index 41b9a1b3e..9bb9ec249 100644 --- a/build.rs +++ b/build.rs @@ -15,11 +15,36 @@ // along with Parity. If not, see . extern crate rustc_version; +extern crate syntex; +extern crate ethcore_ipc_codegen as codegen; +extern crate serde_codegen; +use std::env; +use std::path::Path; use rustc_version::{version_meta, Channel}; fn main() { if let Channel::Nightly = version_meta().channel { println!("cargo:rustc-cfg=nightly"); } + + let out_dir = env::var_os("OUT_DIR").unwrap(); + + // ipc pass + { + let src = Path::new("parity/hypervisor/service.rs.in"); + let dst = Path::new(&out_dir).join("hypervisor_service_ipc.rs"); + let mut registry = syntex::Registry::new(); + codegen::register(&mut registry); + registry.expand("", &src, &dst).unwrap(); + } + + // serde pass + { + let src = Path::new(&out_dir).join("hypervisor_service_ipc.rs"); + let dst = Path::new(&out_dir).join("hypervisor_service_cg.rs"); + let mut registry = syntex::Registry::new(); + serde_codegen::register(&mut registry); + registry.expand("", &src, &dst).unwrap(); + } } diff --git a/ipc/codegen/src/codegen.rs b/ipc/codegen/src/codegen.rs index 85ea86f16..c20514097 100644 --- a/ipc/codegen/src/codegen.rs +++ b/ipc/codegen/src/codegen.rs @@ -188,7 +188,6 @@ fn implement_dispatch_arm_invoke_stmt( ) -> ast::Stmt { let function_name = builder.id(dispatch.function_name.as_str()); - let output_type_id = builder.id(dispatch.return_type_name.clone().unwrap().as_str()); let input_args_exprs = dispatch.input_arg_names.iter().enumerate().map(|(arg_index, arg_name)| { let arg_ident = builder.id(arg_name); @@ -216,10 +215,6 @@ fn implement_dispatch_arm_invoke_stmt( tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serialize"), ::syntax::parse::token::Plain))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And))); - tt.extend(::quasi::ToTokens::to_tokens(&output_type_id, ext_cx).into_iter()); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace))); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("payload"), ::syntax::parse::token::Plain))); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Colon)); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self"), ::syntax::parse::token::Plain))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot)); tt.extend(::quasi::ToTokens::to_tokens(&function_name, ext_cx).into_iter()); @@ -231,7 +226,6 @@ fn implement_dispatch_arm_invoke_stmt( } tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren))); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Comma)); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("bincode"), ::syntax::parse::token::ModName))); @@ -729,6 +723,7 @@ fn implement_interface( Err(e) => { panic!("ipc read error: {:?}, aborting", e); } _ => { } } + // method_num is a 16-bit little-endian unsigned number match method_num[1] as u16 + (method_num[0] as u16)*256 { // handshake diff --git a/ipc/nano/src/lib.rs b/ipc/nano/src/lib.rs index 18a5b0ffe..624de776b 100644 --- a/ipc/nano/src/lib.rs +++ b/ipc/nano/src/lib.rs @@ -54,7 +54,7 @@ impl Deref for GuardedSocket where S: WithSocket { /// Spawns client <`S`> over specified address /// creates socket and connects endpoint to it /// for duplex (paired) connections with the service -pub fn init_client(socket_addr: &str) -> Result, SocketError> where S: WithSocket { +pub fn init_duplex_client(socket_addr: &str) -> Result, SocketError> where S: WithSocket { let mut socket = try!(Socket::new(Protocol::Pair).map_err(|e| { warn!(target: "ipc", "Failed to create ipc socket: {:?}", e); SocketError::DuplexLink @@ -71,16 +71,38 @@ pub fn init_client(socket_addr: &str) -> Result, SocketError }) } +/// Spawns client <`S`> over specified address +/// creates socket and connects endpoint to it +/// for request-reply connections to the service +pub fn init_client(socket_addr: &str) -> Result, SocketError> where S: WithSocket { + let mut socket = try!(Socket::new(Protocol::Req).map_err(|e| { + warn!(target: "ipc", "Failed to create ipc socket: {:?}", e); + SocketError::RequestLink + })); + + let endpoint = try!(socket.connect(socket_addr).map_err(|e| { + warn!(target: "ipc", "Failed to bind socket to address '{}': {:?}", socket_addr, e); + SocketError::RequestLink + })); + + Ok(GuardedSocket { + client: Arc::new(S::init(socket)), + _endpoint: endpoint, + }) +} + /// Error occured while establising socket or endpoint #[derive(Debug)] pub enum SocketError { /// Error establising duplex (paired) socket and/or endpoint - DuplexLink + DuplexLink, + /// Error establising duplex (paired) socket and/or endpoint + RequestLink, } impl Worker where S: IpcInterface { /// New worker over specified `service` - pub fn new(service: Arc) -> Worker { + pub fn new(service: &Arc) -> Worker { Worker:: { service: service.clone(), sockets: Vec::new(), @@ -103,7 +125,7 @@ impl Worker where S: IpcInterface { if method_sign_len >= 2 { // method_num - let method_num = self.buf[1] as u16 * 256 + self.buf[0] as u16; + let method_num = self.buf[0] as u16 * 256 + self.buf[1] as u16; // payload let payload = &self.buf[2..]; @@ -155,6 +177,26 @@ impl Worker where S: IpcInterface { Ok(()) } + + /// Add generic socket for request-reply style communications + /// with multiple clients + pub fn add_reqrep(&mut self, addr: &str) -> Result<(), SocketError> { + let mut socket = try!(Socket::new(Protocol::Rep).map_err(|e| { + warn!(target: "ipc", "Failed to create ipc socket: {:?}", e); + SocketError::DuplexLink + })); + + let endpoint = try!(socket.bind(addr).map_err(|e| { + warn!(target: "ipc", "Failed to bind socket to address '{}': {:?}", addr, e); + SocketError::DuplexLink + })); + + self.sockets.push((socket, endpoint)); + + self.rebuild_poll_request(); + + Ok(()) + } } #[cfg(test)] @@ -206,13 +248,13 @@ mod service_tests { #[test] fn can_create_worker() { - let worker = Worker::::new(Arc::new(DummyService::new())); + let worker = Worker::::new(&Arc::new(DummyService::new())); assert_eq!(0, worker.sockets.len()); } #[test] fn can_add_duplex_socket_to_worker() { - let mut worker = Worker::::new(Arc::new(DummyService::new())); + let mut worker = Worker::::new(&Arc::new(DummyService::new())); worker.add_duplex("ipc:///tmp/parity-test10.ipc").unwrap(); assert_eq!(1, worker.sockets.len()); } @@ -220,7 +262,7 @@ mod service_tests { #[test] fn worker_can_poll_empty() { let service = Arc::new(DummyService::new()); - let mut worker = Worker::::new(service.clone()); + let mut worker = Worker::::new(&service); worker.add_duplex("ipc:///tmp/parity-test20.ipc").unwrap(); worker.poll(); assert_eq!(0, service.methods_stack.read().unwrap().len()); @@ -230,7 +272,7 @@ mod service_tests { fn worker_can_poll() { let url = "ipc:///tmp/parity-test30.ipc"; - let mut worker = Worker::::new(Arc::new(DummyService::new())); + let mut worker = Worker::::new(&Arc::new(DummyService::new())); worker.add_duplex(url).unwrap(); let (_socket, _endpoint) = dummy_write(url, &vec![0, 0, 7, 7, 6, 6]); @@ -245,7 +287,7 @@ mod service_tests { fn worker_can_poll_long() { let url = "ipc:///tmp/parity-test40.ipc"; - let mut worker = Worker::::new(Arc::new(DummyService::new())); + let mut worker = Worker::::new(&Arc::new(DummyService::new())); worker.add_duplex(url).unwrap(); let message = [0u8; 1024*1024]; diff --git a/ipc/tests/over_nano.rs b/ipc/tests/over_nano.rs index 720dff81d..0c0d40372 100644 --- a/ipc/tests/over_nano.rs +++ b/ipc/tests/over_nano.rs @@ -32,14 +32,14 @@ mod tests { fn init_worker(addr: &str) -> nanoipc::Worker { - let mut worker = nanoipc::Worker::::new(Arc::new(Service::new())); + let mut worker = nanoipc::Worker::::new(&Arc::new(Service::new())); worker.add_duplex(addr).unwrap(); worker } #[test] fn can_create_client() { - let client = nanoipc::init_client::>("ipc:///tmp/parity-nano-test10.ipc"); + let client = nanoipc::init_duplex_client::>("ipc:///tmp/parity-nano-test10.ipc"); assert!(client.is_ok()); } @@ -60,7 +60,7 @@ mod tests { }); while !worker_is_ready.load(Ordering::Relaxed) { } - let client = nanoipc::init_client::>(url).unwrap(); + let client = nanoipc::init_duplex_client::>(url).unwrap(); let hs = client.handshake(); @@ -105,5 +105,4 @@ mod tests { worker_should_exit.store(true, Ordering::Relaxed); } - } diff --git a/parity/hypervisor/mod.rs b/parity/hypervisor/mod.rs new file mode 100644 index 000000000..bbc95b150 --- /dev/null +++ b/parity/hypervisor/mod.rs @@ -0,0 +1,158 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Parity interprocess hypervisor module + +// while not included in binary +#![allow(dead_code)] + +pub mod service; + +/// Default value for hypervisor ipc listener +pub const HYPERVISOR_IPC_URL: &'static str = "ipc:///tmp/parity-internal-hyper-status.ipc"; + +use nanoipc; +use std::sync::{Arc,RwLock}; +use hypervisor::service::*; +use std::process::{Command,Child}; +use std::collections::HashMap; + +type BinaryId = &'static str; + +const BLOCKCHAIN_DB_BINARY: BinaryId = "blockchain"; + +pub struct Hypervisor { + ipc_addr: String, + service: Arc, + ipc_worker: RwLock>, + processes: RwLock>, +} + +impl Hypervisor { + /// initializes the Hypervisor service with the open ipc socket for incoming clients + pub fn new() -> Hypervisor { + Hypervisor::with_url(HYPERVISOR_IPC_URL) + } + + /// Starts on the specified address for ipc listener + fn with_url(addr: &str) -> Hypervisor{ + Hypervisor::with_url_and_service(addr, HypervisorService::new()) + } + + /// Starts with the specified address for the ipc listener and + /// the specified list of modules in form of created service + fn with_url_and_service(addr: &str, service: Arc) -> Hypervisor { + let worker = nanoipc::Worker::new(&service); + Hypervisor{ + ipc_addr: addr.to_owned(), + service: service, + ipc_worker: RwLock::new(worker), + processes: RwLock::new(HashMap::new()), + } + } + + /// Since one binary can host multiple modules + /// we match binaries + fn match_module(module_id: &IpcModuleId) -> Option { + match *module_id { + BLOCKCHAIN_MODULE_ID => Some(BLOCKCHAIN_DB_BINARY), + // none means the module is inside the main binary + _ => None + } + } + + /// Creates IPC listener and starts all binaries + fn start(&self) { + let mut worker = self.ipc_worker.write().unwrap(); + worker.add_reqrep(&self.ipc_addr).unwrap_or_else(|e| panic!("Hypervisor ipc worker can not start - critical! ({:?})", e)); + + for module_id in self.service.module_ids() { + self.start_module(module_id); + } + } + + /// Start binary for the specified module + /// Does nothing when it is already started on module is inside the + /// main binary + fn start_module(&self, module_id: IpcModuleId) { + Self::match_module(&module_id).map(|binary_id| { + let mut processes = self.processes.write().unwrap(); + { + if processes.get(binary_id).is_some() { + // already started for another module + return; + } + } + let child = Command::new(binary_id).spawn().unwrap_or_else( + |e| panic!("Hypervisor cannot start binary: {}", e)); + processes.insert(binary_id, child); + }); + } + + /// Reports if all modules are checked in + pub fn modules_ready(&self) -> bool { + self.service.unchecked_count() == 0 + } + + /// Waits for every required module to check in + pub fn wait_for_startup(&self) { + let mut worker = self.ipc_worker.write().unwrap(); + while !self.modules_ready() { + worker.poll() + } + } +} + +mod tests { + use super::*; + use std::sync::atomic::{AtomicBool,Ordering}; + use std::sync::Arc; + use super::service::*; + use nanoipc; + + #[test] + fn can_init() { + let url = "ipc:///tmp/test-parity-hypervisor-10.ipc"; + let test_module_id = 8080u64; + + let hypervisor = Hypervisor::with_url_and_service(url, HypervisorService::with_modules(vec![test_module_id])); + assert_eq!(false, hypervisor.modules_ready()); + } + + #[test] + fn can_wait_for_startup() { + let url = "ipc:///tmp/test-parity-hypervisor-20.ipc"; + let test_module_id = 8080u64; + + let hypervisor_ready = Arc::new(AtomicBool::new(false)); + let hypervisor_ready_local = hypervisor_ready.clone(); + + ::std::thread::spawn(move || { + while !hypervisor_ready.load(Ordering::Relaxed) { } + + let client = nanoipc::init_client::>(url).unwrap(); + client.handshake().unwrap(); + client.module_ready(test_module_id); + }); + + let hypervisor = Hypervisor::with_url_and_service(url, HypervisorService::with_modules(vec![test_module_id])); + hypervisor.start(); + hypervisor_ready_local.store(true, Ordering::Relaxed); + hypervisor.wait_for_startup(); + + assert_eq!(true, hypervisor.modules_ready()); + } +} diff --git a/parity/hypervisor/service.rs b/parity/hypervisor/service.rs new file mode 100644 index 000000000..3171fa11c --- /dev/null +++ b/parity/hypervisor/service.rs @@ -0,0 +1,19 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Parity interprocess hypervisor IPC service + +include!(concat!(env!("OUT_DIR"), "/hypervisor_service_cg.rs")); diff --git a/parity/hypervisor/service.rs.in b/parity/hypervisor/service.rs.in new file mode 100644 index 000000000..2efef6ffd --- /dev/null +++ b/parity/hypervisor/service.rs.in @@ -0,0 +1,69 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::sync::{RwLock,Arc}; +use std::ops::*; +use ipc::IpcConfig; +use std::collections::HashMap; + +pub type IpcModuleId = u64; + +/// Blockhain database module id +pub const BLOCKCHAIN_MODULE_ID: IpcModuleId = 2000; + +/// IPC service that handles module management +pub struct HypervisorService { + check_list: RwLock>, +} + +#[derive(Ipc)] +impl HypervisorService { + fn module_ready(&self, module_id: u64) -> bool { + let mut check_list = self.check_list.write().unwrap(); + check_list.get_mut(&module_id).map(|mut status| *status = true); + check_list.iter().any(|(_, status)| !status) + } +} + +impl HypervisorService { + /// New service with the default list of modules + pub fn new() -> Arc { + HypervisorService::with_modules(vec![]) + } + + /// New service with list of modules that will report for being ready + pub fn with_modules(module_ids: Vec) -> Arc { + let mut check_list = HashMap::new(); + for module_id in module_ids { + check_list.insert(module_id, false); + } + Arc::new(HypervisorService { + check_list: RwLock::new(check_list), + }) + } + + /// Number of modules still being waited for check-in + pub fn unchecked_count(&self) -> usize { + self.check_list.read().unwrap().iter().filter(|&(_, status)| !status).count() + } + + /// List of all modules within this service + pub fn module_ids(&self) -> Vec { + self.check_list.read().unwrap().iter().map(|(module_id, _)| module_id).cloned().collect() + } +} + +impl ::ipc::IpcConfig for HypervisorService {} diff --git a/parity/main.rs b/parity/main.rs index 94466e0e2..bb3693bb7 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -37,6 +37,10 @@ extern crate time; extern crate number_prefix; extern crate rpassword; extern crate semver; +extern crate ethcore_ipc as ipc; +extern crate ethcore_ipc_nano as nanoipc; +extern crate serde; +extern crate bincode; // for price_info.rs #[macro_use] extern crate hyper; @@ -73,6 +77,7 @@ use webapp::Listening as WebappServer; mod price_info; mod upgrade; +mod hypervisor; fn die_with_message(msg: &str) -> ! { println!("ERROR: {}", msg);