summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2024-04-22 10:17:45 +0200
committerDavid Lönnhager <david.l@mullvad.net>2024-04-30 16:22:52 +0200
commit82cd7690d983d4b6ae3b8f7583a382a93e21b54f (patch)
tree138391715f01d238542a19e87f2b6805932db542
parentd2cc168d13df8fa2c84c0bf40dc48e67dbcfa675 (diff)
downloadmullvadvpn-82cd7690d983d4b6ae3b8f7583a382a93e21b54f.tar.xz
mullvadvpn-82cd7690d983d4b6ae3b8f7583a382a93e21b54f.zip
Refactor state handling
-rw-r--r--talpid-core/src/split_tunnel/macos/mod.rs114
1 files changed, 64 insertions, 50 deletions
diff --git a/talpid-core/src/split_tunnel/macos/mod.rs b/talpid-core/src/split_tunnel/macos/mod.rs
index 77a81af627..980097d94b 100644
--- a/talpid-core/src/split_tunnel/macos/mod.rs
+++ b/talpid-core/src/split_tunnel/macos/mod.rs
@@ -47,19 +47,22 @@ pub struct SplitTunnel {
state: State,
tunnel_tx: Weak<futures::channel::mpsc::UnboundedSender<TunnelCommand>>,
rx: mpsc::UnboundedReceiver<Message>,
+ shutdown_tx: Option<oneshot::Sender<()>>,
}
enum Message {
+ /// Return the name of the split tunnel interface
GetInterface {
result_tx: oneshot::Sender<Option<String>>,
},
- Shutdown {
- result_tx: oneshot::Sender<()>,
- },
+ /// Shut down split tunnel service
+ Shutdown { result_tx: oneshot::Sender<()> },
+ /// Set paths to exclude from the VPN tunnel
SetExcludePaths {
result_tx: oneshot::Sender<Result<(), Error>>,
paths: HashSet<PathBuf>,
},
+ /// Update VPN tunnel interface
SetTunnel {
result_tx: oneshot::Sender<Result<(), Error>>,
new_vpn_interface: Option<VpnInterface>,
@@ -124,6 +127,7 @@ impl SplitTunnel {
},
tunnel_tx,
rx,
+ shutdown_tx: None,
};
tokio::spawn(Self::run(split_tunnel));
@@ -143,68 +147,79 @@ impl SplitTunnel {
tokio::select! {
// Handle process monitor being stopped
result = process_monitor_stopped => {
- match result {
- Ok(()) => log::error!("Process monitor stopped unexpectedly with no error"),
- Err(error) => {
- log::error!("{}", error.display_chain_with_msg("Process monitor stopped unexpectedly"));
- }
- }
-
- // Enter the error state if split tunneling is active. Otherwise, we might make incorrect
- // decisions for new processes
- if self.state.active() {
- if let Some(tunnel_tx) = self.tunnel_tx.upgrade() {
- let _ = tunnel_tx.unbounded_send(TunnelCommand::Block(ErrorStateCause::SplitTunnelError));
- }
- }
-
- self.state.fail();
+ self.handle_process_monitor_shutdown(result);
}
// Handle messages
message = self.rx.recv() => {
let Some(message) = message else {
- // Shut down split tunnel
break
};
-
- match message {
- Message::GetInterface {
- result_tx,
- } => {
- let _ = result_tx.send(self.interface().map(str::to_owned));
- }
- Message::Shutdown {
- result_tx,
- } => {
- // Shut down; early exit
- self.shutdown().await;
- let _ = result_tx.send(());
- return;
- }
- Message::SetExcludePaths {
- result_tx,
- paths,
- } => {
- let _ = result_tx.send(self.state.set_exclude_paths(paths).await);
- }
- Message::SetTunnel {
- result_tx,
- new_vpn_interface,
- } => {
- let _ = result_tx.send(self.state.set_tunnel(new_vpn_interface).await);
- }
+ if !self.handle_message(message).await {
+ break;
}
}
}
}
self.shutdown().await;
+
+ if let Some(tx) = self.shutdown_tx.take() {
+ let _ = tx.send(());
+ }
+ }
+
+ /// Handle process monitor unexpectedly stopping
+ fn handle_process_monitor_shutdown(&mut self, result: Result<(), process::Error>) {
+ match result {
+ Ok(()) => log::error!("Process monitor stopped unexpectedly with no error"),
+ Err(error) => {
+ log::error!(
+ "{}",
+ error.display_chain_with_msg("Process monitor stopped unexpectedly")
+ );
+ }
+ }
+
+ // Enter the error state if split tunneling is active. Otherwise, we might make incorrect
+ // decisions for new processes
+ if self.state.active() {
+ if let Some(tunnel_tx) = self.tunnel_tx.upgrade() {
+ let _ = tunnel_tx
+ .unbounded_send(TunnelCommand::Block(ErrorStateCause::SplitTunnelError));
+ }
+ }
+
+ self.state.fail();
+ }
+
+ /// Handle an incoming message
+ /// Return whether the actor should continue running
+ async fn handle_message(&mut self, message: Message) -> bool {
+ match message {
+ Message::GetInterface { result_tx } => {
+ let _ = result_tx.send(self.interface().map(str::to_owned));
+ }
+ Message::Shutdown { result_tx } => {
+ self.shutdown_tx = Some(result_tx);
+ return false;
+ }
+ Message::SetExcludePaths { result_tx, paths } => {
+ let _ = result_tx.send(self.state.set_exclude_paths(paths).await);
+ }
+ Message::SetTunnel {
+ result_tx,
+ new_vpn_interface,
+ } => {
+ let _ = result_tx.send(self.state.set_tunnel(new_vpn_interface).await);
+ }
+ }
+ true
}
/// Shut down split tunnel
- async fn shutdown(self) {
- match self.state {
+ async fn shutdown(&mut self) {
+ match self.state.fail() {
State::ProcessMonitorOnly { mut process, .. } => {
process.shutdown().await;
}
@@ -231,7 +246,6 @@ impl SplitTunnel {
}
}
-/// State machine
enum State {
/// The initial state: no paths have been provided
NoExclusions {