diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2017-05-23 13:07:18 +0200 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2017-05-23 13:07:18 +0200 |
| commit | 54754e78fa41244f661bf76d921b35edeb6b9be8 (patch) | |
| tree | dc1e0156f6019a2b7f2d37cc7325ac3794b1bef1 /talpid_core | |
| parent | 649a0d5bae81ac6b1b1385fd9ad013f4c3134515 (diff) | |
| parent | 26d77b11f01276023683ac0701ef2e190b367dc6 (diff) | |
| download | mullvadvpn-54754e78fa41244f661bf76d921b35edeb6b9be8.tar.xz mullvadvpn-54754e78fa41244f661bf76d921b35edeb6b9be8.zip | |
Merge branch 'new-openvpn-monitor'
Diffstat (limited to 'talpid_core')
| -rw-r--r-- | talpid_core/src/process/openvpn.rs | 79 | ||||
| -rw-r--r-- | talpid_core/src/tunnel/openvpn.rs | 117 |
2 files changed, 116 insertions, 80 deletions
diff --git a/talpid_core/src/process/openvpn.rs b/talpid_core/src/process/openvpn.rs index 5d40d8e42c..df1e4c7682 100644 --- a/talpid_core/src/process/openvpn.rs +++ b/talpid_core/src/process/openvpn.rs @@ -1,7 +1,5 @@ extern crate openvpn_ffi; -use super::monitor::ChildMonitor; - use duct; use net::{RemoteAddr, ToRemoteAddrs}; @@ -9,25 +7,8 @@ use net::{RemoteAddr, ToRemoteAddrs}; use std::ffi::{OsStr, OsString}; use std::fmt; use std::io; -use std::ops::DerefMut; use std::path::{Path, PathBuf}; -use std::process; -use std::sync::{Arc, Mutex}; - -use talpid_ipc; -error_chain!{ - errors { - /// Error while communicating with the OpenVPN plugin. - PluginCommunicationError { - description("Error while communicating with the OpenVPN plugin") - } - /// Error while trying to spawn OpenVPN process. - ChildSpawnError { - description("Error while trying to spawn OpenVPN process") - } - } -} /// An OpenVPN process builder, providing control over the different arguments that the OpenVPN /// binary accepts. @@ -123,66 +104,8 @@ fn write_argument(fmt: &mut fmt::Formatter, arg: &str) -> fmt::Result { } -/// Possible events from OpenVPN -pub enum OpenVpnEvent { - /// An event from the plugin loaded into OpenVPN. - PluginEvent(talpid_ipc::Result<(openvpn_ffi::OpenVpnPluginEvent, openvpn_ffi::OpenVpnEnv)>), - /// The OpenVPN process exited. Containing the result of waiting for the process. - Shutdown(io::Result<process::ExitStatus>), -} - -/// A struct able to start and monitor OpenVPN processes. -pub struct OpenVpnMonitor { - child: ChildMonitor, -} - -impl OpenVpnMonitor { - /// Spawns a new OpenVPN process and monitors it for exit and events. - pub fn start<P, L>(mut cmd: OpenVpnCommand, plugin_path: P, listener: L) -> Result<Self> - where P: AsRef<Path>, - L: FnMut(OpenVpnEvent) + Send + 'static - { - let shared_listener = Arc::new(Mutex::new(listener)); - let server_id = Self::start_plugin_listener(shared_listener.clone())?; - cmd.plugin(plugin_path, vec![server_id]); - let child = Self::start_child_monitor(&cmd, shared_listener)?; - Ok(OpenVpnMonitor { child }) - } - - fn start_plugin_listener<L>(shared_listener: Arc<Mutex<L>>) -> Result<String> - where L: FnMut(OpenVpnEvent) + Send + 'static - { - talpid_ipc::start_new_server( - move |msg| { - let mut listener = shared_listener.lock().unwrap(); - (listener.deref_mut())(OpenVpnEvent::PluginEvent(msg)); - }, - ) - .chain_err(|| ErrorKind::PluginCommunicationError) - } - - fn start_child_monitor<L>(cmd: &OpenVpnCommand, - shared_listener: Arc<Mutex<L>>) - -> Result<ChildMonitor> - where L: FnMut(OpenVpnEvent) + Send + 'static - { - let on_exit = move |result: io::Result<&process::Output>| { - let status = result.map(|out: &process::Output| out.status.clone()); - let mut listener = shared_listener.lock().unwrap(); - (listener.deref_mut())(OpenVpnEvent::Shutdown(status)); - }; - ChildMonitor::start(&cmd.build(), on_exit).chain_err(|| ErrorKind::ChildSpawnError) - } - - /// Send a kill signal to the OpenVPN process. - pub fn kill(&self) -> io::Result<()> { - self.child.kill() - } -} - - #[cfg(test)] -mod openvpn_command_tests { +mod tests { use super::OpenVpnCommand; use net::RemoteAddr; use std::ffi::OsString; diff --git a/talpid_core/src/tunnel/openvpn.rs b/talpid_core/src/tunnel/openvpn.rs index c0d65ce462..bcf89e3eb0 100644 --- a/talpid_core/src/tunnel/openvpn.rs +++ b/talpid_core/src/tunnel/openvpn.rs @@ -1,8 +1,121 @@ use jsonrpc_core::{Error, IoHandler}; use openvpn_ffi; +use process::monitor::ChildMonitor; +use process::openvpn::OpenVpnCommand; +use std::io; + +use std::path::{Path, PathBuf}; +use std::process; +use std::result::Result as StdResult; +use std::sync::{Arc, Mutex}; use talpid_ipc; +mod errors { + error_chain!{ + errors { + /// The `OpenVpnMonitor` is in an invalid state for the requested operation. + InvalidState { + description("Invalid state. OpenVPN is already running") + } + /// Unable to start or kill the OpenVPN process. + ChildProcessError { + description("Unable to start or kill the OpenVPN process") + } + /// Unable to start or manage the IPC server listening for events from OpenVPN + IpcServerError { + description("Unable to start or manage the IPC server") + } + } + } +} +pub use self::errors::*; + + +/// Possible events from OpenVPN +pub enum OpenVpnEvent { + /// An event from the plugin loaded into OpenVPN. + PluginEvent(openvpn_ffi::OpenVpnPluginEvent, openvpn_ffi::OpenVpnEnv), + /// The OpenVPN process exited. Containing the result of waiting for the process. + Shutdown(io::Result<process::ExitStatus>), +} + +/// Struct for monitoring OpenVPN processes. +pub struct OpenVpnMonitor { + on_event: Arc<Fn(OpenVpnEvent) + Send + Sync + 'static>, + plugin_path: PathBuf, + child: Arc<Mutex<Option<ChildMonitor>>>, + event_dispatcher: OpenVpnEventDispatcher, +} + +impl OpenVpnMonitor { + /// Creates a new `OpenVpnMonitor` with the given listener and using the plugin at the given + /// path. + pub fn new<L, P>(on_event: L, plugin_path: P) -> Result<Self> + where L: Fn(OpenVpnEvent) + Send + Sync + 'static, + P: AsRef<Path> + { + let on_event = Arc::new(on_event); + let event_dispatcher = Self::start_event_dispatcher(on_event.clone())?; + Ok( + OpenVpnMonitor { + on_event, + plugin_path: plugin_path.as_ref().to_owned(), + child: Arc::new(Mutex::new(None)), + event_dispatcher, + }, + ) + } + + fn start_event_dispatcher(on_event: Arc<Fn(OpenVpnEvent) + Send + Sync + 'static>) + -> Result<OpenVpnEventDispatcher> { + let on_plugin_event = move |event, env| (*on_event)(OpenVpnEvent::PluginEvent(event, env)); + OpenVpnEventDispatcher::start(on_plugin_event).chain_err(|| ErrorKind::IpcServerError) + } + + /// Tries to start a new OpenVPN process if one is not already running. + /// If this `OpenVpnMonitor is already monitoring a running process it will return an + /// `InvalidState` error. + pub fn start(&self, cmd: OpenVpnCommand) -> Result<()> { + let mut child_lock = self.child.lock().unwrap(); + if child_lock.is_some() { + bail!(ErrorKind::InvalidState); + } + *child_lock = Some(self.start_child_monitor(cmd)?); + Ok(()) + } + + fn start_child_monitor(&self, mut cmd: OpenVpnCommand) -> Result<ChildMonitor> { + self.set_plugin(&mut cmd); + + let child = self.child.clone(); + let on_event = self.on_event.clone(); + + let on_exit = move |exit_status: io::Result<&process::Output>| { + *child.lock().unwrap() = None; + (*on_event)(OpenVpnEvent::Shutdown(exit_status.map(|output| output.status)),) + }; + ChildMonitor::start(&cmd.build(), on_exit).chain_err(|| ErrorKind::ChildProcessError) + } + + fn set_plugin(&self, cmd: &mut OpenVpnCommand) { + let event_dispatcher_address = self.event_dispatcher.address().to_string(); + cmd.plugin(&self.plugin_path, vec![event_dispatcher_address]); + } + + /// Tries to kill the OpenVPN process if it is running. If it is already dead, this does + /// nothing. + pub fn kill(&self) -> Result<()> { + if let Some(ref child) = *self.child.lock().unwrap() { + child.kill().chain_err(|| ErrorKind::ChildProcessError)?; + } + Ok(()) + } +} + + + + /// IPC server for listening to events coming from plugin loaded into OpenVPN. pub struct OpenVpnEventDispatcher { server: talpid_ipc::IpcServer, @@ -41,7 +154,7 @@ mod api { fn openvpn_event(&self, openvpn_ffi::OpenVpnPluginEvent, openvpn_ffi::OpenVpnEnv) - -> Result<(), Error>; + -> StdResult<(), Error>; } } } @@ -59,7 +172,7 @@ impl<L> OpenVpnEventApi for OpenVpnEventApiImpl<L> fn openvpn_event(&self, event: openvpn_ffi::OpenVpnPluginEvent, env: openvpn_ffi::OpenVpnEnv) - -> Result<(), Error> { + -> StdResult<(), Error> { debug!("OpenVPN event {:?}", event); (self.on_event)(event, env); Ok(()) |
