summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--Cargo.lock32
-rw-r--r--mullvad-daemon/Cargo.toml2
-rw-r--r--mullvad-daemon/src/account_history.rs1
-rw-r--r--mullvad-daemon/src/lib.rs145
-rw-r--r--mullvad-daemon/src/management_interface.rs20
-rw-r--r--mullvad-daemon/src/system_service.rs63
7 files changed, 215 insertions, 49 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 008daea001..113c7571b0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,6 +33,7 @@ Line wrap the file at 100 chars. Th
### Fixed
- Mark CLI `bridge set state` argument as required to avoid a crash.
+- The VPN service on Windows will now be restarted when it crashes.
## [2019.6] - 2019-07-15
diff --git a/Cargo.lock b/Cargo.lock
index 1caa605063..79ed38fae9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1186,7 +1186,7 @@ dependencies = [
"tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "windows-service 0.1.0 (git+https://github.com/mullvad/windows-service-rs.git?rev=aab5b26beae364253802b6e5554c9ecdc6285454)",
+ "windows-service 0.2.0 (git+https://github.com/mullvad/windows-service-rs.git?rev=a5eb1dcbbcee4ec2c6479256305c64b25640c799)",
"winres 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2809,6 +2809,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.7"
+source = "git+https://github.com/mullvad/winapi-rs.git?rev=4bcf5cab87124bbeef8c1a445137494d874f8082#4bcf5cab87124bbeef8c1a445137494d874f8082"
+dependencies = [
+ "winapi-i686-pc-windows-gnu 0.4.0 (git+https://github.com/mullvad/winapi-rs.git?rev=4bcf5cab87124bbeef8c1a445137494d874f8082)",
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (git+https://github.com/mullvad/winapi-rs.git?rev=4bcf5cab87124bbeef8c1a445137494d874f8082)",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2823,6 +2832,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
+source = "git+https://github.com/mullvad/winapi-rs.git?rev=4bcf5cab87124bbeef8c1a445137494d874f8082#4bcf5cab87124bbeef8c1a445137494d874f8082"
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -2836,6 +2850,11 @@ dependencies = [
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
+source = "git+https://github.com/mullvad/winapi-rs.git?rev=4bcf5cab87124bbeef8c1a445137494d874f8082#4bcf5cab87124bbeef8c1a445137494d874f8082"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -2860,13 +2879,13 @@ dependencies = [
[[package]]
name = "windows-service"
-version = "0.1.0"
-source = "git+https://github.com/mullvad/windows-service-rs.git?rev=aab5b26beae364253802b6e5554c9ecdc6285454#aab5b26beae364253802b6e5554c9ecdc6285454"
+version = "0.2.0"
+source = "git+https://github.com/mullvad/windows-service-rs.git?rev=a5eb1dcbbcee4ec2c6479256305c64b25640c799#a5eb1dcbbcee4ec2c6479256305c64b25640c799"
dependencies = [
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"err-derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"widestring 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.7 (git+https://github.com/mullvad/winapi-rs.git?rev=4bcf5cab87124bbeef8c1a445137494d874f8082)",
]
[[package]]
@@ -3185,14 +3204,17 @@ dependencies = [
"checksum widestring 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a212922ea58fbf5044f83663aa4fc6281ff890f1fd7546c0c3f52f5290831781"
"checksum widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effc0e4ff8085673ea7b9b2e3c73f6bd4d118810c9009ed8f1e16bd96c331db6"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
+"checksum winapi 0.3.7 (git+https://github.com/mullvad/winapi-rs.git?rev=4bcf5cab87124bbeef8c1a445137494d874f8082)" = "<none>"
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
+"checksum winapi-i686-pc-windows-gnu 0.4.0 (git+https://github.com/mullvad/winapi-rs.git?rev=4bcf5cab87124bbeef8c1a445137494d874f8082)" = "<none>"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
+"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (git+https://github.com/mullvad/winapi-rs.git?rev=4bcf5cab87124bbeef8c1a445137494d874f8082)" = "<none>"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba"
"checksum winconsole 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ef84b96d10db72dd980056666d7f1e7663ce93d82fa33b63e71c966f4cf5032"
-"checksum windows-service 0.1.0 (git+https://github.com/mullvad/windows-service-rs.git?rev=aab5b26beae364253802b6e5554c9ecdc6285454)" = "<none>"
+"checksum windows-service 0.2.0 (git+https://github.com/mullvad/windows-service-rs.git?rev=a5eb1dcbbcee4ec2c6479256305c64b25640c799)" = "<none>"
"checksum winreg 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "daf67b95d0b1bf421c4f11048d63110ca3719977169eec86396b614c8942b6e0"
"checksum winres 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ff4fb510bbfe5b8992ff15f77a2e6fe6cf062878f0eda00c0f44963a807ca5dc"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml
index bbd1de43b2..511e52b060 100644
--- a/mullvad-daemon/Cargo.toml
+++ b/mullvad-daemon/Cargo.toml
@@ -52,7 +52,7 @@ simple-signal = "1.1"
[target.'cfg(windows)'.dependencies]
ctrlc = "3.0"
-windows-service = { git = "https://github.com/mullvad/windows-service-rs.git", rev = "aab5b26beae364253802b6e5554c9ecdc6285454" }
+windows-service = { git = "https://github.com/mullvad/windows-service-rs.git", rev = "a5eb1dcbbcee4ec2c6479256305c64b25640c799" }
winapi = "0.3"
[target.'cfg(windows)'.build-dependencies]
diff --git a/mullvad-daemon/src/account_history.rs b/mullvad-daemon/src/account_history.rs
index faecc5b5e7..8455c060d2 100644
--- a/mullvad-daemon/src/account_history.rs
+++ b/mullvad-daemon/src/account_history.rs
@@ -148,6 +148,7 @@ impl AccountHistory {
}
/// Remove account history
+ #[cfg(not(target_os = "android"))]
pub fn clear(&mut self) -> Result<()> {
self.accounts = VecDeque::new();
self.save_to_disk()
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index 5fb1f11a89..27594c8adf 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -46,7 +46,9 @@ use mullvad_types::{
wireguard::KeygenEvent,
};
use settings::Settings;
-use std::{fs, io, mem, path::PathBuf, sync::mpsc, thread, time::Duration};
+#[cfg(not(target_os = "android"))]
+use std::path::Path;
+use std::{io, mem, path::PathBuf, sync::mpsc, thread, time::Duration};
use talpid_core::{
mpsc::IntoSender,
tunnel::tun_provider::{PlatformTunProvider, TunProvider},
@@ -102,14 +104,26 @@ pub enum Error {
#[error(display = "Tunnel state machine error")]
TunnelError(#[error(cause)] tunnel_state_machine::Error),
- #[error(display = "Failed to remove a directory")]
- RemovalError(#[error(cause)] io::Error),
+ #[error(display = "Failed to remove directory {}", _0)]
+ RemoveDirError(String, #[error(cause)] io::Error),
- #[error(display = "Failed to create a directory")]
- CreateDirError(#[error(cause)] io::Error),
+ #[error(display = "Failed to create directory {}", _0)]
+ CreateDirError(String, #[error(cause)] io::Error),
#[error(display = "Failed to get path")]
PathError(#[error(cause)] mullvad_paths::Error),
+
+ #[cfg(target_os = "windows")]
+ #[error(display = "Failed to get file type info")]
+ FileTypeError(#[error(cause)] io::Error),
+
+ #[cfg(target_os = "windows")]
+ #[error(display = "Failed to get dir entry")]
+ FileEntryError(#[error(cause)] io::Error),
+
+ #[cfg(target_os = "windows")]
+ #[error(display = "Failed to read dir entries")]
+ ReadDirError(#[error(cause)] io::Error),
}
type SyncUnboundedSender<T> = ::futures::sink::Wait<UnboundedSender<T>>;
@@ -238,6 +252,7 @@ pub struct Daemon<L: EventListener = ManagementInterfaceEventBroadcaster> {
last_generated_relay: Option<Relay>,
last_generated_bridge_relay: Option<Relay>,
version: String,
+ shutdown_callbacks: Vec<Box<dyn FnOnce()>>,
}
impl Daemon<ManagementInterfaceEventBroadcaster> {
@@ -416,6 +431,7 @@ where
last_generated_bridge_relay: None,
version,
wireguard_key_manager,
+ shutdown_callbacks: vec![],
};
daemon.ensure_wireguard_keys_for_current_account();
@@ -442,9 +458,31 @@ where
break;
}
}
+
+ self.finalize();
Ok(())
}
+ fn finalize(self) {
+ let (event_listener, shutdown_callbacks) = self.shutdown();
+ for cb in shutdown_callbacks {
+ cb();
+ }
+ mem::drop(event_listener);
+ }
+
+ /// Shuts down the daemon without shutting down the underlying management interface event
+ /// listener and the shutdown callbacks
+ fn shutdown(self) -> (L, Vec<Box<dyn FnOnce()>>) {
+ let Daemon {
+ event_listener,
+ shutdown_callbacks,
+ ..
+ } = self;
+ (event_listener, shutdown_callbacks)
+ }
+
+
fn handle_event(&mut self, event: InternalDaemonEvent) -> Result<()> {
use self::InternalDaemonEvent::*;
match event {
@@ -456,7 +494,7 @@ where
ManagementInterfaceExited => {
return Err(Error::ManagementInterfaceExited);
}
- TriggerShutdown => self.handle_trigger_shutdown_event(),
+ TriggerShutdown => self.trigger_shutdown_event(),
WgKeyEvent(key_event) => self.handle_wireguard_key_event(key_event),
}
Ok(())
@@ -677,6 +715,10 @@ where
fn handle_management_interface_event(&mut self, event: ManagementCommand) {
use self::ManagementCommand::*;
+ if !self.state.is_running() {
+ log::trace!("Dropping management command because the daemon is shutting down",);
+ return;
+ }
match event {
SetTargetState(tx, state) => self.on_set_target_state(tx, state),
GetState(tx) => self.on_get_state(tx),
@@ -708,8 +750,9 @@ where
VerifyWireguardKey(tx) => self.on_verify_wireguard_key(tx),
GetVersionInfo(tx) => self.on_get_version_info(tx),
GetCurrentVersion(tx) => self.on_get_current_version(tx),
+ #[cfg(not(target_os = "android"))]
FactoryReset(tx) => self.on_factory_reset(tx),
- Shutdown => self.handle_trigger_shutdown_event(),
+ Shutdown => self.trigger_shutdown_event(),
}
}
@@ -942,19 +985,10 @@ where
Self::oneshot_send(tx, self.version.clone(), "get_current_version response");
}
+ #[cfg(not(target_os = "android"))]
fn on_factory_reset(&mut self, tx: oneshot::Sender<()>) {
- self.set_target_state(TargetState::Unsecured);
let mut failed = false;
- if let Err(e) = self.clear_cache_directory() {
- log::error!("Failed to clear cache directory - {}", e);
- failed = true;
- }
-
- if let Err(e) = self.clear_log_directory() {
- log::error!("Failed to clear log directory - {}", e);
- failed = true;
- }
if let Err(e) = self.settings.reset() {
log::error!("Failed to reset settings - {}", e);
@@ -966,9 +1000,29 @@ where
failed = true;
}
- if !failed {
- Self::oneshot_send(tx, (), "factory_reset response");
- }
+ // Shut the daemon down.
+ self.trigger_shutdown_event();
+
+ self.shutdown_callbacks.push(Box::new(move || {
+ if let Err(e) = Self::clear_cache_directory() {
+ log::error!(
+ "{}",
+ e.display_chain_with_msg("Failed to clear cache directory")
+ );
+ failed = true;
+ }
+
+ if let Err(e) = Self::clear_log_directory() {
+ log::error!(
+ "{}",
+ e.display_chain_with_msg("Failed to clear log directory")
+ );
+ failed = true;
+ }
+ if !failed {
+ Self::oneshot_send(tx, (), "factory_reset response");
+ }
+ }));
}
fn on_update_relay_settings(&mut self, tx: oneshot::Sender<()>, update: RelaySettingsUpdate) {
@@ -1293,7 +1347,7 @@ where
}
}
- fn handle_trigger_shutdown_event(&mut self) {
+ fn trigger_shutdown_event(&mut self) {
self.state.shutdown(&self.tunnel_state);
self.disconnect_tunnel();
}
@@ -1332,18 +1386,55 @@ where
.expect("Tunnel state machine has stopped");
}
- fn clear_log_directory(&self) -> Result<()> {
+ #[cfg(not(target_os = "android"))]
+ fn clear_log_directory() -> Result<()> {
let log_dir = mullvad_paths::get_log_dir().map_err(Error::PathError)?;
- fs::remove_dir_all(&log_dir).map_err(Error::RemovalError)?;
- fs::create_dir_all(&log_dir).map_err(Error::CreateDirError)
+ Self::clear_directory(&log_dir)
}
- fn clear_cache_directory(&self) -> Result<()> {
+ #[cfg(not(target_os = "android"))]
+ fn clear_cache_directory() -> Result<()> {
let cache_dir = mullvad_paths::cache_dir().map_err(Error::PathError)?;
- fs::remove_dir_all(&cache_dir).map_err(Error::RemovalError)?;
- fs::create_dir_all(&cache_dir).map_err(Error::CreateDirError)
+ Self::clear_directory(&cache_dir)
}
+ #[cfg(not(target_os = "android"))]
+ fn clear_directory(path: &Path) -> Result<()> {
+ use std::fs;
+ #[cfg(not(target_os = "windows"))]
+ {
+ fs::remove_dir_all(path)
+ .map_err(|e| Error::RemoveDirError(path.display().to_string(), e))?;
+ fs::create_dir_all(path)
+ .map_err(|e| Error::CreateDirError(path.display().to_string(), e))
+ }
+ #[cfg(target_os = "windows")]
+ {
+ fs::read_dir(&path)
+ .map_err(Error::ReadDirError)
+ .and_then(|dir_entries| {
+ dir_entries
+ .into_iter()
+ .map(|entry| {
+ let entry = entry.map_err(Error::FileEntryError)?;
+ let entry_type = entry.file_type().map_err(Error::FileTypeError)?;
+
+
+ let removal = if entry_type.is_file() || entry_type.is_symlink() {
+ fs::remove_file(entry.path())
+ } else {
+ fs::remove_dir_all(entry.path())
+ };
+ removal.map_err(|e| {
+ Error::RemoveDirError(entry.path().display().to_string(), e)
+ })
+ })
+ .collect::<Result<()>>()
+ })
+ }
+ }
+
+
pub fn shutdown_handle(&self) -> DaemonShutdownHandle {
DaemonShutdownHandle {
tx: self.tx.clone(),
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index 04193ca9b5..0940780ea3 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -226,6 +226,7 @@ pub enum ManagementCommand {
GetVersionInfo(OneshotSender<BoxFuture<version::AppVersionInfo, mullvad_rpc::Error>>),
/// Get current version of the app
GetCurrentVersion(OneshotSender<version::AppVersion>),
+ #[cfg(not(target_os = "android"))]
/// Remove settings and clear the cache
FactoryReset(OneshotSender<()>),
/// Makes the daemon exit the main loop and quit.
@@ -686,13 +687,20 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi
}
fn factory_reset(&self, _: Self::Metadata) -> BoxFuture<(), Error> {
- log::debug!("factory_reset");
- let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(ManagementCommand::FactoryReset(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
+ #[cfg(not(target_os = "android"))]
+ {
+ log::debug!("factory_reset");
+ let (tx, rx) = sync::oneshot::channel();
+ let future = self
+ .send_command_to_daemon(ManagementCommand::FactoryReset(tx))
+ .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ Box::new(future)
+ }
+ #[cfg(target_os = "android")]
+ {
+ Box::new(future::ok(()))
+ }
}
diff --git a/mullvad-daemon/src/system_service.rs b/mullvad-daemon/src/system_service.rs
index 62c997022a..c7007ae666 100644
--- a/mullvad-daemon/src/system_service.rs
+++ b/mullvad-daemon/src/system_service.rs
@@ -4,7 +4,7 @@ use std::{
env,
ffi::OsString,
sync::{
- atomic::{AtomicUsize, Ordering},
+ atomic::{AtomicBool, AtomicUsize, Ordering},
mpsc, Arc,
},
thread,
@@ -13,9 +13,10 @@ use std::{
use talpid_types::ErrorExt;
use windows_service::{
service::{
- ServiceAccess, ServiceControl, ServiceControlAccept, ServiceDependency,
- ServiceErrorControl, ServiceExitCode, ServiceInfo, ServiceStartType, ServiceState,
- ServiceStatus, ServiceType,
+ ServiceAccess, ServiceAction, ServiceActionType, ServiceControl, ServiceControlAccept,
+ ServiceDependency, ServiceErrorControl, ServiceExitCode, ServiceFailureActions,
+ ServiceFailureResetPeriod, ServiceInfo, ServiceStartType, ServiceState, ServiceStatus,
+ ServiceType,
},
service_control_handler::{self, ServiceControlHandlerResult, ServiceStatusHandle},
service_dispatcher,
@@ -69,26 +70,42 @@ fn run_service() -> Result<(), String> {
.set_pending_start(Duration::from_secs(1))
.unwrap();
+ let clean_shutdown = Arc::new(AtomicBool::new(false));
+
let log_dir = crate::get_log_dir(cli::get_config()).expect("Log dir should be available here");
let result = crate::create_daemon(log_dir).and_then(|daemon| {
let shutdown_handle = daemon.shutdown_handle();
// Register monitor that translates `ServiceControl` to Daemon events
- start_event_monitor(persistent_service_status.clone(), shutdown_handle, event_rx);
+ start_event_monitor(
+ persistent_service_status.clone(),
+ shutdown_handle,
+ event_rx,
+ clean_shutdown.clone(),
+ );
persistent_service_status.set_running().unwrap();
daemon.run().map_err(|e| e.display_chain())
});
+
let exit_code = match result {
- Ok(()) => ServiceExitCode::default(),
+ Ok(()) => {
+ // check if shutdown signal was sent from the system
+ if clean_shutdown.load(Ordering::Acquire) {
+ ServiceExitCode::default()
+ } else {
+ // otherwise return a non-zero code so that the daemon gets restarted
+ ServiceExitCode::ServiceSpecific(1)
+ }
+ }
Err(_) => ServiceExitCode::ServiceSpecific(1),
};
persistent_service_status.set_stopped(exit_code).unwrap();
- result
+ result.map(|_| ())
}
/// Start event monitor thread that polls for `ServiceControl` and translates them into calls to
@@ -97,6 +114,7 @@ fn start_event_monitor(
mut persistent_service_status: PersistentServiceStatus,
shutdown_handle: DaemonShutdownHandle,
event_rx: mpsc::Receiver<ServiceControl>,
+ clean_shutdown: Arc<AtomicBool>,
) -> thread::JoinHandle<()> {
thread::spawn(move || {
for event in event_rx {
@@ -106,6 +124,7 @@ fn start_event_monitor(
.set_pending_stop(Duration::from_secs(10))
.unwrap();
+ clean_shutdown.store(true, Ordering::Release);
shutdown_handle.shutdown();
}
_ => (),
@@ -227,9 +246,33 @@ pub fn install_service() -> Result<(), InstallError> {
let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)
.map_err(InstallError::ConnectServiceManager)?;
- service_manager
- .create_service(get_service_info(), ServiceAccess::empty())
- .map(|_| ())
+ let service_access = ServiceAccess::QUERY_CONFIG
+ | ServiceAccess::CHANGE_CONFIG
+ | ServiceAccess::START
+ | ServiceAccess::DELETE;
+
+ let service = service_manager
+ .create_service(get_service_info(), service_access)
+ .or(service_manager.open_service(SERVICE_NAME, service_access))
+ .map_err(InstallError::CreateService)?;
+
+ let recovery_actions = vec![ServiceAction {
+ action_type: ServiceActionType::Restart,
+ delay: Duration::from_secs(3),
+ }];
+
+ let failure_actions = ServiceFailureActions {
+ reset_period: ServiceFailureResetPeriod::After(Duration::from_secs(2)),
+ reboot_msg: None,
+ command: None,
+ actions: Some(recovery_actions),
+ };
+
+ service
+ .update_failure_actions(failure_actions)
+ .map_err(InstallError::CreateService)?;
+ service
+ .set_failure_actions_on_non_crash_failures(true)
.map_err(InstallError::CreateService)
}