summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2018-06-01 15:40:35 +0200
committerAndrej Mihajlov <and.mikhaylov@gmail.com>2018-06-04 14:52:04 +0200
commit7318786327ef2d9f14e5814a12bcf43da40feca0 (patch)
treedb3d7ac9376b2805f0102aff9cecf9a9baafb182
parent05405300140d1c20e2a843378a334953ddb1c5bb (diff)
downloadmullvadvpn-7318786327ef2d9f14e5814a12bcf43da40feca0.tar.xz
mullvadvpn-7318786327ef2d9f14e5814a12bcf43da40feca0.zip
Remove local source of windows-service and migrate to mullvad/windows-service-rs
-rw-r--r--Cargo.lock16
-rw-r--r--Cargo.toml1
-rw-r--r--mullvad-daemon/Cargo.toml2
-rw-r--r--mullvad-daemon/src/system_service.rs7
-rw-r--r--windows-service/Cargo.toml15
-rw-r--r--windows-service/examples/simple_service.rs457
-rw-r--r--windows-service/src/lib.rs18
-rw-r--r--windows-service/src/service.rs396
-rw-r--r--windows-service/src/service_control_handler.rs144
-rw-r--r--windows-service/src/service_dispatcher.rs90
-rw-r--r--windows-service/src/service_manager.rs196
-rw-r--r--windows-service/src/shell_escape.rs120
12 files changed, 7 insertions, 1455 deletions
diff --git a/Cargo.lock b/Cargo.lock
index de9aa030a5..efba8d2afc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -782,7 +782,7 @@ dependencies = [
"tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "windows-service 0.1.0",
+ "windows-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1285,15 +1285,6 @@ dependencies = [
]
[[package]]
-name = "simplelog"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
name = "slab"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1811,11 +1802,10 @@ dependencies = [
[[package]]
name = "windows-service"
version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "simplelog 0.5.1 (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.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1987,7 +1977,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum shell-escape 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "170a13e64f2a51b77a45702ba77287f5c6829375b04a69cf2222acd17d0cfab9"
"checksum shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c"
"checksum simple-signal 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "53f7da44adcc42667d57483bd93f81295f27d66897804b757573b61b6f13288b"
-"checksum simplelog 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce595117de34b75e057b41e99079e43e9fcc4e5ec9c7ba5f2fea55321f0c624e"
"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e"
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
"checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d"
@@ -2044,6 +2033,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767"
+"checksum windows-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51cb08429e18f787748343122610b09f50c79f8034627e57faacf37582a709ec"
"checksum ws 0.7.5 (git+https://github.com/tomusdrw/ws-rs)" = "<none>"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
"checksum xdg 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a66b7c2281ebde13cf4391d70d4c7e5946c3c25e72a7b859ca8f677dcd0b0c61"
diff --git a/Cargo.toml b/Cargo.toml
index 8bcae5cce6..5879f8ed18 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,6 @@
[workspace]
members = [
"mullvad-daemon",
- "windows-service",
"mullvad-cli",
"mullvad-ipc-client",
"mullvad-metadata",
diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml
index b09db7e275..da13e356c2 100644
--- a/mullvad-daemon/Cargo.toml
+++ b/mullvad-daemon/Cargo.toml
@@ -41,7 +41,7 @@ simple-signal = "1.1"
[target.'cfg(windows)'.dependencies]
ctrlc = "3.0"
-windows-service = { path = "../windows-service" }
+windows-service = "0.1"
[dev-dependencies]
assert_matches = "1.0"
diff --git a/mullvad-daemon/src/system_service.rs b/mullvad-daemon/src/system_service.rs
index 0cc53f3522..91e245e881 100644
--- a/mullvad-daemon/src/system_service.rs
+++ b/mullvad-daemon/src/system_service.rs
@@ -31,7 +31,7 @@ pub fn run() -> Result<()> {
// Start the service dispatcher.
// This will block current thread until the service stopped and spawn `service_main` on a
// background thread.
- service_dispatcher::start_dispatcher(SERVICE_NAME, service_main)
+ service_dispatcher::start(SERVICE_NAME, service_main)
.chain_err(|| "Failed to start a service dispatcher")
}
@@ -64,9 +64,8 @@ fn run_service(_arguments: Vec<OsString>) -> Result<()> {
_ => ServiceControlHandlerResult::NotImplemented,
}
};
- let status_handle =
- service_control_handler::register_control_handler(SERVICE_NAME, event_handler)
- .chain_err(|| "Failed to register a service control handler")?;
+ let status_handle = service_control_handler::register(SERVICE_NAME, event_handler)
+ .chain_err(|| "Failed to register a service control handler")?;
let mut persistent_service_status = PersistentServiceStatus::new(status_handle);
persistent_service_status
.set_pending_start(Duration::from_secs(1))
diff --git a/windows-service/Cargo.toml b/windows-service/Cargo.toml
deleted file mode 100644
index 0e73261ce8..0000000000
--- a/windows-service/Cargo.toml
+++ /dev/null
@@ -1,15 +0,0 @@
-[package]
-name = "windows-service"
-version = "0.1.0"
-authors = ["Mullvad VPN <admin@mullvad.net>", "Andrej Mihajlov <and@mullvad.net>"]
-
-[target.'cfg(windows)'.dependencies]
-bitflags = "1.0.1"
-error-chain = "0.11"
-winapi = { version = "0.3", features = ["std", "winsvc", "winerror"] }
-widestring = "0.3.0"
-
-[dev-dependencies]
-error-chain = "0.11"
-log = "0.4"
-simplelog = { version = "0.5", default-features = false }
diff --git a/windows-service/examples/simple_service.rs b/windows-service/examples/simple_service.rs
deleted file mode 100644
index ee72ddbe73..0000000000
--- a/windows-service/examples/simple_service.rs
+++ /dev/null
@@ -1,457 +0,0 @@
-// Simple service example.
-//
-// All commands mentioned below shall be executed in Command Prompt with Administrator privileges.
-//
-// Service self-installation: `simple_service.exe --install-service`
-// Service self-removal: `simple_service.exe --remove-service`
-//
-// Start the service: `net start simpleservice`
-// Pause the service: `net pause simpleservice`
-// Resume the service: `net continue simpleservice`
-// Stop the service: `net stop simpleservice`
-//
-// Simple service outputs all logs in C:\Windows\Temp\simple-service.log.
-// If you have GNU tools installed, you can follow the log using:
-// `tail -F C:\Windows\Temp\simple-service.log`
-//
-
-#[cfg(windows)]
-#[macro_use]
-extern crate error_chain;
-#[cfg(windows)]
-#[macro_use]
-extern crate log;
-#[cfg(windows)]
-#[macro_use]
-extern crate windows_service;
-
-#[cfg(not(windows))]
-fn main() {
- panic!("This program is only intended to run on Windows.");
-}
-
-#[cfg(windows)]
-fn main() {
- simple_service::run();
-}
-
-#[cfg(windows)]
-mod simple_service {
- extern crate simplelog;
-
- use std::ffi::OsString;
- use std::fs::OpenOptions;
- use std::path::PathBuf;
- use std::sync::atomic::{AtomicUsize, Ordering};
- use std::sync::mpsc;
- use std::time::Duration;
- use std::{env, io, thread, time};
-
- use self::simplelog::{CombinedLogger, Config, WriteLogger};
- use log::LevelFilter;
-
- use windows_service::service::{
- ServiceAccess, ServiceControl, ServiceControlAccept, ServiceErrorControl, ServiceExitCode,
- ServiceInfo, ServiceStartType, ServiceState, ServiceStatus, ServiceType,
- };
- use windows_service::service_control_handler::{
- self, ServiceControlHandlerResult, ServiceStatusHandle,
- };
- use windows_service::service_dispatcher;
- use windows_service::service_manager::{ServiceManager, ServiceManagerAccess};
- use windows_service::ChainedError;
-
- static SERVICE_NAME: &'static str = "SimpleService";
- static SERVICE_DISPLAY_NAME: &'static str = "Simple Service";
-
- error_chain! {
- errors {
- InstallService {
- description("Failed to install the service")
- }
- RemoveService {
- description("Failed to remove the service")
- }
- OpenLogFile(path: PathBuf) {
- description("Unable to open log file for writing")
- display("Unable to open log file for writing: {}", path.display())
- }
- InitLogger {
- description("Cannot initialize logger")
- }
- }
- foreign_links {
- SetLoggerError(::log::SetLoggerError);
- }
- }
-
- static CHECKPOINT_COUNTER: AtomicUsize = AtomicUsize::new(0);
-
- pub fn update_service_status(
- status_handle: &ServiceStatusHandle,
- next_state: ServiceState,
- exit_code: ServiceExitCode,
- wait_hint: Duration,
- ) -> io::Result<()> {
- // Automatically bump the checkpoint when updating the pending events to tell the system
- // that the service is making a progress in transition from pending to final state.
- // `wait_hint` should reflect the estimated time for transition to complete.
- let checkpoint = match next_state {
- ServiceState::ContinuePending
- | ServiceState::PausePending
- | ServiceState::StartPending
- | ServiceState::StopPending => CHECKPOINT_COUNTER.fetch_add(1, Ordering::SeqCst) + 1,
- _ => 0,
- };
- let service_status = ServiceStatus {
- service_type: ServiceType::OwnProcess,
- current_state: next_state,
- controls_accepted: accepted_controls_by_state(next_state),
- exit_code: exit_code,
- checkpoint: checkpoint as u32,
- wait_hint: wait_hint,
- };
- info!(
- "Update service status: {:?}, checkpoint: {}, wait_hint: {:?}",
- service_status.current_state, service_status.checkpoint, service_status.wait_hint
- );
- status_handle.set_service_status(service_status)
- }
-
- pub fn run() {
- if let Some(command) = env::args().nth(1) {
- match command.as_ref() {
- "--install-service" => {
- if let Err(e) = install_service() {
- println!("{}", e.display_chain());
- } else {
- println!("Installed the service.");
- }
- }
- "--remove-service" => {
- if let Err(e) = remove_service() {
- println!("{}", e.display_chain());
- } else {
- println!("Removed the service.");
- }
- }
- "--run-service" => {
- // Setup file logger since there is no stdout when running as a service.
- if let Err(err) = init_logger() {
- panic!("Unable to initialize logger: {}", err.display_chain());
- }
-
- // Start the service dispatcher.
- // This will block current thread until the service stopped.
- let result = service_dispatcher::start_dispatcher(SERVICE_NAME, service_main);
-
- match result {
- Err(ref e) => {
- error!("Failed to start service dispatcher: {}", e.display_chain());
- }
- Ok(_) => {
- info!("Service dispatcher exited.");
- }
- };
- }
- _ => println!("Unsupported command: {}", command),
- }
- } else {
- println!("Usage:");
- println!("--install-service to install the service");
- println!("--remove-service to uninstall the service");
- println!("--run-service to run the service");
- }
- }
-
- define_windows_service!(service_main, handle_service_main);
-
- pub fn handle_service_main(arguments: Vec<OsString>) {
- // Create an event channel to funnel events to worker.
- let (event_tx, event_rx) = mpsc::channel();
-
- info!("Received arguments: {:?}", arguments);
-
- // Register service event handler
- let event_handler = move |control_event| -> ServiceControlHandlerResult {
- match control_event {
- // Notifies a service to report its current status information to the service
- // control manager. Always return NO_ERROR even if not implemented.
- ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
-
- // Handle primary control events
- ServiceControl::Pause
- | ServiceControl::Continue
- | ServiceControl::Stop
- | ServiceControl::Shutdown => {
- event_tx.send(control_event).unwrap();
- ServiceControlHandlerResult::NoError
- }
-
- _ => ServiceControlHandlerResult::NotImplemented,
- }
- };
-
- let result = service_control_handler::register_control_handler(SERVICE_NAME, event_handler);
- match result {
- Ok(status_handle) => {
- run_service(status_handle, event_rx);
- }
- Err(ref e) => {
- error!("Cannot register a service control handler: {}", e);
- }
- };
-
- info!("Quit service main.");
- }
-
- #[derive(Debug, Copy, Clone)]
- enum DaemonEvent {
- Continue,
- Pause,
- Stop,
- }
-
- fn start_event_monitor(
- service_status_handle: ServiceStatusHandle,
- event_rx: mpsc::Receiver<ServiceControl>,
- daemon_tx: mpsc::Sender<DaemonEvent>,
- ) -> thread::JoinHandle<()> {
- thread::spawn(move || {
- loop {
- match event_rx.recv().unwrap() {
- ServiceControl::Pause => {
- info!("Pausing the service.");
-
- update_service_status(
- &service_status_handle,
- ServiceState::PausePending,
- ServiceExitCode::Win32(0),
- Duration::from_secs(2),
- ).unwrap();
-
- daemon_tx.send(DaemonEvent::Pause).unwrap();
- }
-
- ServiceControl::Continue => {
- info!("Continuing the service.");
-
- update_service_status(
- &service_status_handle,
- ServiceState::ContinuePending,
- ServiceExitCode::Win32(0),
- Duration::from_secs(2),
- ).unwrap();
-
- daemon_tx.send(DaemonEvent::Continue).unwrap();
- }
-
- ServiceControl::Stop => {
- info!("Stopping the service.");
-
- update_service_status(
- &service_status_handle,
- ServiceState::StopPending,
- ServiceExitCode::Win32(0),
- Duration::from_secs(2),
- ).unwrap();
-
- daemon_tx.send(DaemonEvent::Stop).unwrap();
- break; // break the loop
- }
-
- ServiceControl::Shutdown => {
- info!("Exiting due to shutdown.");
-
- update_service_status(
- &service_status_handle,
- ServiceState::StopPending,
- ServiceExitCode::Win32(0),
- Duration::from_secs(1),
- ).unwrap();
-
- daemon_tx.send(DaemonEvent::Stop).unwrap();
- break; // break the loop
- }
-
- _ => (),
- };
- }
- })
- }
-
- fn start_worker(
- service_status_handle: ServiceStatusHandle,
- daemon_rx: mpsc::Receiver<DaemonEvent>,
- ) -> thread::JoinHandle<()> {
- thread::spawn(move || {
- let mut is_running = true;
- let mut is_paused = false;
-
- // Tell Windows that the service is running now
- update_service_status(
- &service_status_handle,
- ServiceState::Running,
- ServiceExitCode::Win32(0),
- Duration::default(),
- ).unwrap();
-
- while is_running {
- // Do some work
- if !is_paused {
- info!("Working...");
- }
-
- // Poll events
- match daemon_rx.recv_timeout(Duration::from_secs(1)) {
- Ok(DaemonEvent::Pause) => {
- is_paused = true;
-
- update_service_status(
- &service_status_handle,
- ServiceState::Paused,
- ServiceExitCode::Win32(0),
- Duration::default(),
- ).unwrap();
- }
- Ok(DaemonEvent::Continue) => {
- is_paused = false;
-
- update_service_status(
- &service_status_handle,
- ServiceState::Running,
- ServiceExitCode::Win32(0),
- Duration::default(),
- ).unwrap();
- }
- Ok(DaemonEvent::Stop) | Err(mpsc::RecvTimeoutError::Disconnected) => {
- is_running = false;
-
- update_service_status(
- &service_status_handle,
- ServiceState::Stopped,
- ServiceExitCode::Win32(0),
- Duration::default(),
- ).unwrap();
- }
- Err(mpsc::RecvTimeoutError::Timeout) => (),
- };
- }
- })
- }
-
- fn run_service(status_handle: ServiceStatusHandle, event_rx: mpsc::Receiver<ServiceControl>) {
- let (daemon_tx, daemon_rx) = mpsc::channel();
-
- // Tell Windows that the service is starting up
- update_service_status(
- &status_handle,
- ServiceState::StartPending,
- ServiceExitCode::Win32(0),
- Duration::from_secs(5),
- ).unwrap();
-
- let event_monitor_handle = start_event_monitor(status_handle, event_rx, daemon_tx);
- let worker_thread_handle = start_worker(status_handle, daemon_rx);
-
- // Block current thread until other threads complete execution
- event_monitor_handle.join().unwrap();
- worker_thread_handle.join().unwrap();
- }
-
- /// Returns the list of accepted service events at each stage of the service lifecycle.
- fn accepted_controls_by_state(state: ServiceState) -> ServiceControlAccept {
- match state {
- ServiceState::StartPending
- | ServiceState::PausePending
- | ServiceState::ContinuePending => ServiceControlAccept::empty(),
- ServiceState::Running => {
- ServiceControlAccept::STOP
- | ServiceControlAccept::PAUSE_CONTINUE
- | ServiceControlAccept::SHUTDOWN
- }
- ServiceState::Paused => {
- ServiceControlAccept::STOP
- | ServiceControlAccept::PAUSE_CONTINUE
- | ServiceControlAccept::SHUTDOWN
- }
- ServiceState::StopPending | ServiceState::Stopped => ServiceControlAccept::empty(),
- }
- }
-
- fn install_service() -> Result<()> {
- let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;
- let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)
- .chain_err(|| ErrorKind::InstallService)?;
- let service_info = get_service_info();
- service_manager
- .create_service(service_info, ServiceAccess::empty())
- .map(|_| ())
- .chain_err(|| ErrorKind::InstallService)
- }
-
- fn remove_service() -> Result<()> {
- let manager_access = ServiceManagerAccess::CONNECT;
- let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)
- .chain_err(|| ErrorKind::RemoveService)?;
-
- let service_access =
- ServiceAccess::QUERY_STATUS | ServiceAccess::STOP | ServiceAccess::DELETE;
- let service = service_manager
- .open_service(SERVICE_NAME, service_access)
- .chain_err(|| ErrorKind::RemoveService)?;
-
- loop {
- let service_status = service
- .query_status()
- .chain_err(|| ErrorKind::RemoveService)?;
-
- match service_status.current_state {
- ServiceState::StopPending => (),
- ServiceState::Stopped => {
- println!("Removing the service...");
- service.delete().chain_err(|| ErrorKind::RemoveService)?;
- return Ok(()); // explicit return
- }
- _ => {
- println!("Stopping the service...");
- service.stop().chain_err(|| ErrorKind::RemoveService)?;
- }
- }
-
- thread::sleep(time::Duration::from_secs(1))
- }
- }
-
- fn get_service_info() -> ServiceInfo {
- ServiceInfo {
- name: OsString::from(SERVICE_NAME),
- display_name: OsString::from(SERVICE_DISPLAY_NAME),
- service_type: ServiceType::OwnProcess,
- start_type: ServiceStartType::OnDemand,
- error_control: ServiceErrorControl::Normal,
- executable_path: env::current_exe().unwrap(),
- launch_arguments: vec![OsString::from("--run-service")],
- account_name: None, // run as System
- account_password: None,
- }
- }
-
- fn init_logger() -> Result<()> {
- let windows_directory = env::var_os("WINDIR").unwrap();
- let log_file_path = PathBuf::from(windows_directory)
- .join("Temp")
- .join("simple-service.log");
-
- let log_file = OpenOptions::new()
- .create(true)
- .append(true)
- .open(log_file_path.as_path())
- .chain_err(|| ErrorKind::OpenLogFile(log_file_path))?;
-
- let file_logger = WriteLogger::new(LevelFilter::Trace, Config::default(), log_file);
-
- CombinedLogger::init(vec![file_logger]).chain_err(|| ErrorKind::InitLogger)
- }
-
-}
diff --git a/windows-service/src/lib.rs b/windows-service/src/lib.rs
deleted file mode 100644
index bae17eb41d..0000000000
--- a/windows-service/src/lib.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-#![cfg(windows)]
-
-#[macro_use]
-extern crate bitflags;
-#[macro_use]
-extern crate error_chain;
-extern crate widestring;
-extern crate winapi;
-
-pub use error_chain::ChainedError;
-
-pub mod service;
-pub mod service_control_handler;
-pub mod service_manager;
-#[macro_use]
-pub mod service_dispatcher;
-
-mod shell_escape;
diff --git a/windows-service/src/service.rs b/windows-service/src/service.rs
deleted file mode 100644
index 1898cf8c1c..0000000000
--- a/windows-service/src/service.rs
+++ /dev/null
@@ -1,396 +0,0 @@
-use std::ffi::OsString;
-use std::path::PathBuf;
-use std::time::Duration;
-use std::{io, mem};
-
-use winapi::shared::winerror::{ERROR_SERVICE_SPECIFIC_ERROR, NO_ERROR};
-use winapi::um::{winnt, winsvc};
-
-mod errors {
- error_chain! {
- errors {
- InvalidServiceType(raw_value: u32) {
- description("Invalid service type value")
- display("Invalid service type value: {}", raw_value)
- }
- InvalidServiceState(raw_value: u32) {
- description("Invalid service state")
- display("Invalid service state value: {}", raw_value)
- }
- InvalidServiceControl(raw_value: u32) {
- description("Invalid service control")
- display("Invalid service control value: {}", raw_value)
- }
- }
- foreign_links {
- System(::std::io::Error);
- }
- }
-}
-pub use self::errors::*;
-
-/// Enum describing types of windows services
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-#[repr(u32)]
-pub enum ServiceType {
- /// Service that runs in its own process.
- OwnProcess = winnt::SERVICE_WIN32_OWN_PROCESS,
-}
-
-impl ServiceType {
- pub fn from_raw(raw_value: u32) -> Result<Self> {
- let service_type = match raw_value {
- x if x == ServiceType::OwnProcess.to_raw() => ServiceType::OwnProcess,
- _ => Err(ErrorKind::InvalidServiceType(raw_value))?,
- };
- Ok(service_type)
- }
-
- pub fn to_raw(&self) -> u32 {
- *self as u32
- }
-}
-
-/// Flags describing the access permissions when working with services
-bitflags! {
- pub struct ServiceAccess: u32 {
- /// Can query the service status
- const QUERY_STATUS = winsvc::SERVICE_QUERY_STATUS;
-
- /// Can start the service
- const START = winsvc::SERVICE_START;
-
- // Can stop the service
- const STOP = winsvc::SERVICE_STOP;
-
- /// Can pause or continue the service execution
- const PAUSE_CONTINUE = winsvc::SERVICE_PAUSE_CONTINUE;
-
- /// Can ask the service to report its status
- const INTERROGATE = winsvc::SERVICE_INTERROGATE;
-
- /// Can delete the service
- const DELETE = winnt::DELETE;
- }
-}
-
-/// Enum describing the start options for windows services
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-#[repr(u32)]
-pub enum ServiceStartType {
- /// Autostart on system startup
- AutoStart = winnt::SERVICE_AUTO_START,
- /// Service is enabled, can be started manually
- OnDemand = winnt::SERVICE_DEMAND_START,
- /// Disabled service
- Disabled = winnt::SERVICE_DISABLED,
-}
-
-impl ServiceStartType {
- pub fn to_raw(&self) -> u32 {
- *self as u32
- }
-}
-
-/// Error handling strategy for service failures.
-/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682450(v=vs.85).aspx
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-#[repr(u32)]
-pub enum ServiceErrorControl {
- Critical = winnt::SERVICE_ERROR_CRITICAL,
- Ignore = winnt::SERVICE_ERROR_IGNORE,
- Normal = winnt::SERVICE_ERROR_NORMAL,
- Severe = winnt::SERVICE_ERROR_SEVERE,
-}
-
-impl ServiceErrorControl {
- pub fn to_raw(&self) -> u32 {
- *self as u32
- }
-}
-
-/// A struct that describes the service
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct ServiceInfo {
- /// Service name
- pub name: OsString,
-
- /// Friendly service name
- pub display_name: OsString,
-
- pub service_type: ServiceType,
- pub start_type: ServiceStartType,
- pub error_control: ServiceErrorControl,
-
- /// Path to the service binary.
- pub executable_path: PathBuf,
-
- /// Launch arguments passed to `main` when system starts the service.
- /// This is not the same as arguments passed to `service_main`.
- pub launch_arguments: Vec<OsString>,
-
- /// Account to use for running the service.
- /// for example: NT Authority\System.
- /// use `None` to run as LocalSystem.
- pub account_name: Option<OsString>,
-
- /// Account password.
- /// For system accounts this should normally be `None`.
- pub account_password: Option<OsString>,
-}
-
-/// Enum describing the service control operations
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-#[repr(u32)]
-pub enum ServiceControl {
- Continue = winsvc::SERVICE_CONTROL_CONTINUE,
- Interrogate = winsvc::SERVICE_CONTROL_INTERROGATE,
- NetBindAdd = winsvc::SERVICE_CONTROL_NETBINDADD,
- NetBindDisable = winsvc::SERVICE_CONTROL_NETBINDDISABLE,
- NetBindEnable = winsvc::SERVICE_CONTROL_NETBINDENABLE,
- NetBindRemove = winsvc::SERVICE_CONTROL_NETBINDREMOVE,
- ParamChange = winsvc::SERVICE_CONTROL_PARAMCHANGE,
- Pause = winsvc::SERVICE_CONTROL_PAUSE,
- Preshutdown = winsvc::SERVICE_CONTROL_PRESHUTDOWN,
- Shutdown = winsvc::SERVICE_CONTROL_SHUTDOWN,
- Stop = winsvc::SERVICE_CONTROL_STOP,
-}
-
-impl ServiceControl {
- pub fn from_raw(raw_value: u32) -> Result<Self> {
- let service_control = match raw_value {
- x if x == ServiceControl::Continue.to_raw() => ServiceControl::Continue,
- x if x == ServiceControl::Interrogate.to_raw() => ServiceControl::Interrogate,
- x if x == ServiceControl::NetBindAdd.to_raw() => ServiceControl::NetBindAdd,
- x if x == ServiceControl::NetBindDisable.to_raw() => ServiceControl::NetBindDisable,
- x if x == ServiceControl::NetBindEnable.to_raw() => ServiceControl::NetBindEnable,
- x if x == ServiceControl::NetBindRemove.to_raw() => ServiceControl::NetBindRemove,
- x if x == ServiceControl::ParamChange.to_raw() => ServiceControl::ParamChange,
- x if x == ServiceControl::Pause.to_raw() => ServiceControl::Pause,
- x if x == ServiceControl::Preshutdown.to_raw() => ServiceControl::Preshutdown,
- x if x == ServiceControl::Shutdown.to_raw() => ServiceControl::Shutdown,
- x if x == ServiceControl::Stop.to_raw() => ServiceControl::Stop,
- other => Err(ErrorKind::InvalidServiceControl(other))?,
- };
- Ok(service_control)
- }
-
- pub fn to_raw(&self) -> u32 {
- *self as u32
- }
-}
-
-/// Service state returned as a part of ServiceStatus
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-#[repr(u32)]
-pub enum ServiceState {
- Stopped = winsvc::SERVICE_STOPPED,
- StartPending = winsvc::SERVICE_START_PENDING,
- StopPending = winsvc::SERVICE_STOP_PENDING,
- Running = winsvc::SERVICE_RUNNING,
- ContinuePending = winsvc::SERVICE_CONTINUE_PENDING,
- PausePending = winsvc::SERVICE_PAUSE_PENDING,
- Paused = winsvc::SERVICE_PAUSED,
-}
-
-impl ServiceState {
- fn from_raw(raw_state: u32) -> Result<Self> {
- let service_state = match raw_state {
- x if x == ServiceState::Stopped.to_raw() => ServiceState::Stopped,
- x if x == ServiceState::StartPending.to_raw() => ServiceState::StartPending,
- x if x == ServiceState::StopPending.to_raw() => ServiceState::StopPending,
- x if x == ServiceState::Running.to_raw() => ServiceState::Running,
- x if x == ServiceState::ContinuePending.to_raw() => ServiceState::ContinuePending,
- x if x == ServiceState::PausePending.to_raw() => ServiceState::PausePending,
- x if x == ServiceState::Paused.to_raw() => ServiceState::Paused,
- other => Err(ErrorKind::InvalidServiceState(other))?,
- };
- Ok(service_state)
- }
-
- fn to_raw(&self) -> u32 {
- *self as u32
- }
-}
-
-/// Service exit code abstraction.
-///
-/// This struct provides a logic around the relationship between `win32_exit_code` and
-/// `service_specific_exit_code`.
-///
-/// The service can either return a win32 error code or a custom error
-/// code. In that case `win32_exit_code` has to be set to `ERROR_SERVICE_SPECIFIC_ERROR` and
-/// the `service_specific_exit_code` assigned with custom error code.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum ServiceExitCode {
- Win32(u32),
- ServiceSpecific(u32),
-}
-
-impl ServiceExitCode {
- fn copy_to(&self, raw_service_status: &mut winsvc::SERVICE_STATUS) {
- match *self {
- ServiceExitCode::Win32(win32_error_code) => {
- raw_service_status.dwWin32ExitCode = win32_error_code;
- raw_service_status.dwServiceSpecificExitCode = 0;
- }
- ServiceExitCode::ServiceSpecific(service_error_code) => {
- raw_service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
- raw_service_status.dwServiceSpecificExitCode = service_error_code;
- }
- }
- }
-}
-
-impl Default for ServiceExitCode {
- fn default() -> Self {
- ServiceExitCode::Win32(NO_ERROR)
- }
-}
-
-impl<'a> From<&'a winsvc::SERVICE_STATUS> for ServiceExitCode {
- fn from(service_status: &'a winsvc::SERVICE_STATUS) -> Self {
- if service_status.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR {
- ServiceExitCode::ServiceSpecific(service_status.dwServiceSpecificExitCode)
- } else {
- ServiceExitCode::Win32(service_status.dwWin32ExitCode)
- }
- }
-}
-
-/// Flags describing accepted types of service control requests
-bitflags! {
- pub struct ServiceControlAccept: u32 {
- /// The service is a network component that can accept changes in its binding without being
- /// stopped and restarted. This allows service to receive `ServiceControl::Netbind*`
- /// family of events.
- const NETBIND_CHANGE = winsvc::SERVICE_ACCEPT_NETBINDCHANGE;
-
- /// The service can reread its startup parameters without being stopped and restarted.
- const PARAM_CHANGE = winsvc::SERVICE_ACCEPT_PARAMCHANGE;
-
- /// The service can be paused and continued.
- const PAUSE_CONTINUE = winsvc::SERVICE_ACCEPT_PAUSE_CONTINUE;
-
- /// The service can perform preshutdown tasks.
- /// Mutually exclusive with shutdown.
- const PRESHUTDOWN = winsvc::SERVICE_ACCEPT_PRESHUTDOWN;
-
- /// The service is notified when system shutdown occurs.
- /// Mutually exclusive with preshutdown.
- const SHUTDOWN = winsvc::SERVICE_ACCEPT_SHUTDOWN;
-
- /// The service can be stopped.
- const STOP = winsvc::SERVICE_ACCEPT_STOP;
- }
-}
-
-/// Service status.
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct ServiceStatus {
- /// Type of service
- pub service_type: ServiceType,
-
- /// Current state of the service
- pub current_state: ServiceState,
-
- /// Control commands that service accepts.
- pub controls_accepted: ServiceControlAccept,
-
- /// Service exit code
- pub exit_code: ServiceExitCode,
-
- /// Service initialization progress value that should be increased during a lengthy start,
- /// stop, pause or continue eration. For example the service should increment the value as
- /// it completes each step of initialization.
- /// This value must be zero if the service does not have any pending start, stop, pause or
- /// continue operations.
- pub checkpoint: u32,
-
- /// Estimated time for pending operation.
- /// This basically works as a timeout until the service manager assumes that the service hung.
- /// This could be either circumvented by updating the `current_state` or incrementing a
- /// `checkpoint` value.
- pub wait_hint: Duration,
-}
-
-impl ServiceStatus {
- pub(super) fn to_raw(&self) -> winsvc::SERVICE_STATUS {
- let mut raw_status = unsafe { mem::zeroed::<winsvc::SERVICE_STATUS>() };
- raw_status.dwServiceType = self.service_type.to_raw();
- raw_status.dwCurrentState = self.current_state.to_raw();
- raw_status.dwControlsAccepted = self.controls_accepted.bits();
-
- self.exit_code.copy_to(&mut raw_status);
-
- raw_status.dwCheckPoint = self.checkpoint;
-
- // we lose precision here but dwWaitHint should never be too big.
- raw_status.dwWaitHint = (self.wait_hint.as_secs() * 1000) as u32;
-
- raw_status
- }
-
- fn from_raw(raw_status: winsvc::SERVICE_STATUS) -> Result<Self> {
- Ok(ServiceStatus {
- service_type: ServiceType::from_raw(raw_status.dwServiceType)?,
- current_state: ServiceState::from_raw(raw_status.dwCurrentState)?,
- controls_accepted: ServiceControlAccept::from_bits_truncate(
- raw_status.dwControlsAccepted,
- ),
- exit_code: ServiceExitCode::from(&raw_status),
- checkpoint: raw_status.dwCheckPoint,
- wait_hint: Duration::from_millis(raw_status.dwWaitHint as u64),
- })
- }
-}
-
-
-pub struct Service(winsvc::SC_HANDLE);
-
-impl Service {
- /// Internal constructor
- pub(super) unsafe fn from_handle(handle: winsvc::SC_HANDLE) -> Self {
- Service(handle)
- }
-
- pub fn stop(&self) -> Result<ServiceStatus> {
- self.send_control_command(ServiceControl::Stop)
- }
-
- pub fn query_status(&self) -> Result<ServiceStatus> {
- let mut raw_status = unsafe { mem::zeroed::<winsvc::SERVICE_STATUS>() };
- let success = unsafe { winsvc::QueryServiceStatus(self.0, &mut raw_status) };
- if success == 1 {
- ServiceStatus::from_raw(raw_status)
- } else {
- Err(io::Error::last_os_error().into())
- }
- }
-
- pub fn delete(self) -> io::Result<()> {
- let success = unsafe { winsvc::DeleteService(self.0) };
- if success == 1 {
- Ok(())
- } else {
- Err(io::Error::last_os_error())
- }
- }
-
- fn send_control_command(&self, command: ServiceControl) -> Result<ServiceStatus> {
- let mut raw_status = unsafe { mem::zeroed::<winsvc::SERVICE_STATUS>() };
- let success = unsafe { winsvc::ControlService(self.0, command.to_raw(), &mut raw_status) };
-
- if success == 1 {
- ServiceStatus::from_raw(raw_status).map_err(|err| err.into())
- } else {
- Err(io::Error::last_os_error().into())
- }
- }
-}
-
-impl Drop for Service {
- fn drop(&mut self) {
- unsafe { winsvc::CloseServiceHandle(self.0) };
- }
-}
diff --git a/windows-service/src/service_control_handler.rs b/windows-service/src/service_control_handler.rs
deleted file mode 100644
index b722e94f52..0000000000
--- a/windows-service/src/service_control_handler.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use std::ffi::OsStr;
-use std::io;
-use widestring::WideCString;
-use winapi::shared::winerror::{ERROR_CALL_NOT_IMPLEMENTED, NO_ERROR};
-use winapi::um::winsvc;
-
-use service::{ServiceControl, ServiceStatus};
-
-mod errors {
- error_chain! {
- errors {
- InvalidServiceName {
- description("Invalid service name")
- }
- }
- foreign_links {
- System(::std::io::Error);
- }
- }
-}
-pub use self::errors::*;
-
-/// Struct that holds unique token for updating the status of the corresponding service.
-#[derive(Debug, Clone, Copy)]
-pub struct ServiceStatusHandle(winsvc::SERVICE_STATUS_HANDLE);
-
-impl ServiceStatusHandle {
- fn from_handle(handle: winsvc::SERVICE_STATUS_HANDLE) -> Self {
- ServiceStatusHandle(handle)
- }
-
- /// Report the new service status to the system
- pub fn set_service_status(&self, service_status: ServiceStatus) -> io::Result<()> {
- let mut raw_service_status = service_status.to_raw();
- let result = unsafe { winsvc::SetServiceStatus(self.0, &mut raw_service_status) };
- if result == 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(())
- }
- }
-}
-
-// Underlying SERVICE_STATUS_HANDLE is thread safe.
-// See remarks section for more info:
-// https://msdn.microsoft.com/en-us/library/windows/desktop/ms686241(v=vs.85).aspx
-unsafe impl Send for ServiceStatusHandle {}
-
-/// Abstraction over the return value of service control handler.
-/// The meaning of each of variants in this enum depends on the type of received event.
-/// See the "Return value" section of corresponding MSDN article for more info:
-/// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683241(v=vs.85).aspx
-#[derive(Debug)]
-pub enum ServiceControlHandlerResult {
- /// Either used to aknowledge the call or grant the permission in advanced events.
- NoError,
- /// The received event is not implemented.
- NotImplemented,
- /// This variant is used to deny permission and return the reason error code in advanced
- /// events.
- Other(u32),
-}
-
-impl ServiceControlHandlerResult {
- pub fn to_raw(&self) -> u32 {
- match *self {
- ServiceControlHandlerResult::NoError => NO_ERROR,
- ServiceControlHandlerResult::NotImplemented => ERROR_CALL_NOT_IMPLEMENTED,
- ServiceControlHandlerResult::Other(code) => code,
- }
- }
-}
-
-/// Register a closure for receiving service events.
-/// Returns `ServiceStatusHandle` that can be used to report the service status back to the system.
-pub fn register_control_handler<
- S: AsRef<OsStr>,
- F: Fn(ServiceControl) -> ServiceControlHandlerResult + 'static,
->(
- service_name: S,
- event_handler: F,
-) -> Result<ServiceStatusHandle> {
- // Move closure data on heap.
- // The Box<HandlerFn> is a trait object and is stored on stack at this point.
- let heap_event_handler = Box::new(event_handler) as Box<HandlerFn>;
-
- // Box again to move trait object to heap.
- let boxed_event_handler: Box<Box<HandlerFn>> = Box::new(heap_event_handler);
-
- // Important: leak the Box<Box<HandlerFn>> which will be released in `service_control_handler`.
- let context = Box::into_raw(boxed_event_handler) as *mut ::std::os::raw::c_void;
-
- let service_name =
- WideCString::from_str(service_name).chain_err(|| ErrorKind::InvalidServiceName)?;
- let status_handle = unsafe {
- winsvc::RegisterServiceCtrlHandlerExW(
- service_name.as_ptr(),
- Some(service_control_handler),
- context,
- )
- };
-
- if status_handle.is_null() {
- Err(io::Error::last_os_error().into())
- } else {
- Ok(ServiceStatusHandle::from_handle(status_handle))
- }
-}
-
-/// Alias for control event handler closure.
-type HandlerFn = Fn(ServiceControl) -> ServiceControlHandlerResult;
-
-/// Static service control handler
-#[allow(dead_code)]
-extern "system" fn service_control_handler(
- control: u32,
- _event_type: u32,
- _event_data: *mut ::std::os::raw::c_void,
- context: *mut ::std::os::raw::c_void,
-) -> u32 {
- // Important: cast context to &mut Box<HandlerFn> without taking ownership.
- let handler_fn = unsafe { &mut *(context as *mut Box<HandlerFn>) };
-
- match ServiceControl::from_raw(control) {
- Ok(service_control) => {
- let return_code = ((handler_fn)(service_control)).to_raw();
-
- // Important: release context upon Stop, Shutdown or Preshutdown at the end of the
- // service lifecycle.
- match service_control {
- ServiceControl::Stop | ServiceControl::Shutdown | ServiceControl::Preshutdown => {
- let _owned_boxed_handler: Box<Box<HandlerFn>> =
- unsafe { Box::from_raw(context as *mut Box<HandlerFn>) };
- }
- _ => (),
- };
-
- return_code
- }
-
- // Report all unknown control commands as unimplemented
- Err(_) => ServiceControlHandlerResult::NotImplemented.to_raw(),
- }
-}
diff --git a/windows-service/src/service_dispatcher.rs b/windows-service/src/service_dispatcher.rs
deleted file mode 100644
index fb9d5df8f3..0000000000
--- a/windows-service/src/service_dispatcher.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-use std::ffi::{OsStr, OsString};
-use std::{io, ptr};
-use widestring::{WideCStr, WideCString};
-use winapi::um::winsvc;
-
-mod errors {
- error_chain! {
- errors {
- InvalidServiceName {
- description("Invalid service name")
- }
- }
- foreign_links {
- System(::std::io::Error);
- }
- }
-}
-pub use self::errors::*;
-
-/// Macro to generate a "service_main" function for Windows service.
-///
-/// The `service_main` function parses service arguments provided by the system
-/// and passes them with a call to `$service_main_handler`.
-///
-/// `$function_name` - name of the "service_main" callback.
-/// `$service_main_handler` - function with a signature `fn(Vec<OsString>)` that's called from
-/// generated `$function_name`. Accepts parsed service arguments as `Vec<OsString>`. Its
-/// responsibility is to create a `ServiceControlHandler`, start processing control events and
-/// report the service status to the system.
-///
-#[macro_export]
-macro_rules! define_windows_service {
- ($function_name:ident, $service_main_handler:ident) => {
- /// Static callback used by the system to bootstrap the service.
- /// Do not call it directly.
- extern "system" fn $function_name(argc: u32, argv: *mut *mut u16) {
- let arguments = unsafe { $crate::service_dispatcher::parse_raw_arguments(argc, argv) };
-
- $service_main_handler(arguments);
- }
- };
-}
-
-/// Start service control dispatcher.
-///
-/// Once started the service control dispatcher blocks the current thread execution
-/// until the service is stopped.
-///
-/// Upon successful initialization, system calls the `service_main` in
-/// background thread which parses service arguments received from the system and
-/// passes them to higher level `$service_main_handler` handler.
-///
-/// On failure: immediately returns an error, no threads are spawned.
-///
-pub fn start_dispatcher<T: AsRef<OsStr>>(
- service_name: T,
- service_main: extern "system" fn(u32, *mut *mut u16),
-) -> Result<()> {
- let service_name =
- WideCString::from_str(service_name).chain_err(|| ErrorKind::InvalidServiceName)?;
- let service_table: &[winsvc::SERVICE_TABLE_ENTRYW] = &[
- winsvc::SERVICE_TABLE_ENTRYW {
- lpServiceName: service_name.as_ptr(),
- lpServiceProc: Some(service_main),
- },
- // the last item has to be { null, null }
- winsvc::SERVICE_TABLE_ENTRYW {
- lpServiceName: ptr::null(),
- lpServiceProc: None,
- },
- ];
-
- let result = unsafe { winsvc::StartServiceCtrlDispatcherW(service_table.as_ptr()) };
- if result == 0 {
- Err(io::Error::last_os_error().into())
- } else {
- Ok(())
- }
-}
-
-/// Parse raw arguments received from `service_main` into Vec.
-pub unsafe fn parse_raw_arguments(argc: u32, argv: *mut *mut u16) -> Vec<OsString> {
- (0..argc)
- .into_iter()
- .map(|i| {
- let array_element_ptr: *mut *mut u16 = argv.offset(i as isize);
- WideCStr::from_ptr_str(*array_element_ptr).to_os_string()
- })
- .collect()
-}
diff --git a/windows-service/src/service_manager.rs b/windows-service/src/service_manager.rs
deleted file mode 100644
index b4dceb9d3e..0000000000
--- a/windows-service/src/service_manager.rs
+++ /dev/null
@@ -1,196 +0,0 @@
-use std::borrow::Cow;
-use std::ffi::OsStr;
-use std::{io, ptr};
-
-use widestring::{NulError, WideCString, WideString};
-use winapi::um::winsvc;
-
-use service::{Service, ServiceAccess, ServiceInfo};
-use shell_escape;
-
-mod errors {
- error_chain! {
- errors {
- InvalidAccountName {
- description("Invalid account name")
- }
- InvalidAccountPassword {
- description("Invalid account password")
- }
- InvalidDisplayName {
- description("Invalid display name")
- }
- InvalidDatabaseName {
- description("Invalid database name")
- }
- InvalidExecutablePath {
- description("Invalid executable path")
- }
- InvalidLaunchArgument {
- description("Invalid launch argument")
- }
- InvalidMachineName {
- description("Invalid machine name")
- }
- InvalidServiceName {
- description("Invalid service name")
- }
- }
- foreign_links {
- System(::std::io::Error);
- }
- }
-}
-pub use self::errors::*;
-
-/// Flags describing access permissions for ServiceManager
-bitflags! {
- pub struct ServiceManagerAccess: u32 {
- /// Can connect to service control manager
- const CONNECT = winsvc::SC_MANAGER_CONNECT;
-
- /// Can create services
- const CREATE_SERVICE = winsvc::SC_MANAGER_CREATE_SERVICE;
-
- /// Can enumerate services
- const ENUMERATE_SERVICE = winsvc::SC_MANAGER_ENUMERATE_SERVICE;
- }
-}
-
-/// Service control manager
-pub struct ServiceManager(winsvc::SC_HANDLE);
-
-impl ServiceManager {
- /// Private initializer
- /// Passing None for machine connects to local machine
- /// Passing None for database connects to active database
- fn new<M: AsRef<OsStr>, D: AsRef<OsStr>>(
- machine: Option<M>,
- database: Option<D>,
- request_access: ServiceManagerAccess,
- ) -> Result<Self> {
- let machine_name = to_wide(machine).chain_err(|| ErrorKind::InvalidMachineName)?;
- let database_name = to_wide(database).chain_err(|| ErrorKind::InvalidDatabaseName)?;
- let handle = unsafe {
- winsvc::OpenSCManagerW(
- machine_name.map_or(ptr::null(), |s| s.as_ptr()),
- database_name.map_or(ptr::null(), |s| s.as_ptr()),
- request_access.bits(),
- )
- };
-
- if handle.is_null() {
- Err(io::Error::last_os_error().into())
- } else {
- Ok(ServiceManager(handle))
- }
- }
-
- /// Passing None for database connects to active database
- pub fn local_computer<D: AsRef<OsStr>>(
- database: Option<D>,
- request_access: ServiceManagerAccess,
- ) -> Result<Self> {
- ServiceManager::new(None::<&OsStr>, database, request_access)
- }
-
- /// Passing None for database connects to active database
- pub fn remote_computer<M: AsRef<OsStr>, D: AsRef<OsStr>>(
- machine: M,
- database: Option<D>,
- request_access: ServiceManagerAccess,
- ) -> Result<Self> {
- ServiceManager::new(Some(machine), database, request_access)
- }
-
- pub fn create_service(
- &self,
- service_info: ServiceInfo,
- service_access: ServiceAccess,
- ) -> Result<Service> {
- let service_name =
- WideCString::from_str(service_info.name).chain_err(|| ErrorKind::InvalidServiceName)?;
- let display_name = WideCString::from_str(service_info.display_name)
- .chain_err(|| ErrorKind::InvalidDisplayName)?;
- let account_name =
- to_wide(service_info.account_name).chain_err(|| ErrorKind::InvalidAccountName)?;
- let account_password =
- to_wide(service_info.account_password).chain_err(|| ErrorKind::InvalidAccountPassword)?;
-
- // escape executable path and arguments and combine them into single command
- let executable_path = escape_wide(service_info.executable_path)
- .chain_err(|| ErrorKind::InvalidExecutablePath)?;
-
- let mut launch_command_buffer = WideString::new();
- launch_command_buffer.push(executable_path);
-
- for launch_argument in service_info.launch_arguments.iter() {
- let wide = escape_wide(launch_argument).chain_err(|| ErrorKind::InvalidLaunchArgument)?;
-
- launch_command_buffer.push_str(" ");
- launch_command_buffer.push(wide);
- }
-
- let launch_command = WideCString::from_wide_str(launch_command_buffer).unwrap();
-
- let service_handle = unsafe {
- winsvc::CreateServiceW(
- self.0,
- service_name.as_ptr(),
- display_name.as_ptr(),
- service_access.bits(),
- service_info.service_type.to_raw(),
- service_info.start_type.to_raw(),
- service_info.error_control.to_raw(),
- launch_command.as_ptr(),
- ptr::null(), // load ordering group
- ptr::null_mut(), // tag id within the load ordering group
- ptr::null(), // service dependencies
- account_name.map_or(ptr::null(), |s| s.as_ptr()),
- account_password.map_or(ptr::null(), |s| s.as_ptr()),
- )
- };
-
- if service_handle.is_null() {
- Err(io::Error::last_os_error().into())
- } else {
- Ok(unsafe { Service::from_handle(service_handle) })
- }
- }
-
- pub fn open_service<T: AsRef<OsStr>>(
- &self,
- name: T,
- request_access: ServiceAccess,
- ) -> Result<Service> {
- let service_name = WideCString::from_str(name).chain_err(|| ErrorKind::InvalidServiceName)?;
- let service_handle =
- unsafe { winsvc::OpenServiceW(self.0, service_name.as_ptr(), request_access.bits()) };
-
- if service_handle.is_null() {
- Err(io::Error::last_os_error().into())
- } else {
- Ok(unsafe { Service::from_handle(service_handle) })
- }
- }
-}
-
-impl Drop for ServiceManager {
- fn drop(&mut self) {
- unsafe { winsvc::CloseServiceHandle(self.0) };
- }
-}
-
-fn to_wide<T: AsRef<OsStr>>(s: Option<T>) -> ::std::result::Result<Option<WideCString>, NulError> {
- if let Some(s) = s {
- Ok(Some(WideCString::from_str(s)?))
- } else {
- Ok(None)
- }
-}
-
-fn escape_wide<T: AsRef<OsStr>>(s: T) -> ::std::result::Result<WideString, NulError> {
- let escaped = shell_escape::escape(Cow::Borrowed(s.as_ref()));
- let wide = WideCString::from_str(escaped)?;
- Ok(wide.to_wide_string())
-}
diff --git a/windows-service/src/shell_escape.rs b/windows-service/src/shell_escape.rs
deleted file mode 100644
index 12014a3b15..0000000000
--- a/windows-service/src/shell_escape.rs
+++ /dev/null
@@ -1,120 +0,0 @@
-use std::borrow::Cow;
-use std::ffi::{OsStr, OsString};
-use std::iter::repeat;
-use std::os::windows::ffi::{OsStrExt, OsStringExt};
-
-/// Common UTF-16 code points.
-mod utf16 {
- pub const DOUBLEQUOTE: u16 = '"' as u16;
- pub const BACKSLASH: u16 = '\\' as u16;
- pub const SPACE: u16 = ' ' as u16;
- pub const LINEFEED: u16 = '\n' as u16;
- pub const HTAB: u16 = '\t' as u16;
- pub const VTAB: u16 = 0x000B; // '\v'
-}
-
-/// Loselessly escape shell arguments on Windows.
-///
-/// Inspired by https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/.
-/// Heavily based on https://github.com/sfackler/shell-escape
-pub fn escape(s: Cow<OsStr>) -> Cow<OsStr> {
- static ESCAPE_CHARS: &'static [u16] = &[
- utf16::DOUBLEQUOTE,
- utf16::SPACE,
- utf16::LINEFEED,
- utf16::HTAB,
- utf16::VTAB,
- ];
- let needs_escape = s.is_empty() || s.encode_wide().any(|ref c| ESCAPE_CHARS.contains(c));
- if !needs_escape {
- return s;
- }
-
- let mut escaped_wide_string: Vec<u16> = Vec::with_capacity(s.len() + 2);
- escaped_wide_string.push(utf16::DOUBLEQUOTE);
-
- let mut chars = s.encode_wide().peekable();
- loop {
- let mut num_slashes = 0;
- while let Some(&utf16::BACKSLASH) = chars.peek() {
- chars.next();
- num_slashes += 1;
- }
-
- match chars.next() {
- Some(utf16::DOUBLEQUOTE) => {
- escaped_wide_string.extend(repeat(utf16::BACKSLASH).take(num_slashes * 2 + 1));
- escaped_wide_string.push(utf16::DOUBLEQUOTE);
- }
- Some(c) => {
- escaped_wide_string.extend(repeat(utf16::BACKSLASH).take(num_slashes));
- escaped_wide_string.push(c);
- }
- None => {
- escaped_wide_string.extend(repeat(utf16::BACKSLASH).take(num_slashes * 2));
- break;
- }
- }
- }
-
- escaped_wide_string.push(utf16::DOUBLEQUOTE);
-
- Cow::Owned(OsString::from_wide(&escaped_wide_string))
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_no_escape() {
- assert_eq!(
- escape(Cow::Borrowed(OsStr::new("--aaa=bbb-ccc"))),
- OsStr::new("--aaa=bbb-ccc")
- );
- }
-
- #[test]
- fn test_escape_empty_argument() {
- assert_eq!(escape(Cow::Borrowed(OsStr::new(""))), OsStr::new(r#""""#));
- }
-
- #[test]
- fn test_escape_argument_with_spaces() {
- assert_eq!(
- escape(Cow::Borrowed(OsStr::new("linker=gcc -L/foo -Wl,bar"))),
- OsStr::new(r#""linker=gcc -L/foo -Wl,bar""#)
- );
- }
-
- #[test]
- fn test_escape_nested_quotes() {
- assert_eq!(
- escape(Cow::Borrowed(OsStr::new(r#"--features="default""#))),
- OsStr::new(r#""--features=\"default\"""#)
- );
- }
-
-
- #[test]
- fn test_escape_multiple_backslashes_and_nested_quotes() {
- assert_eq!(
- escape(Cow::Borrowed(OsStr::new(r#"hello \\\"quote\\\""#))),
- OsStr::new(r#""hello \\\\\\\"quote\\\\\\\"""#)
- );
- }
-
- // Input:
- // child.exe "\some\directory with\spaces\" argument2
- //
- // Parsed as:
- // 0: [child.exe]
- // 1: [\some\directory with\spaces" argument2]
- #[test]
- fn test_escape_trailing_backslash() {
- assert_eq!(
- escape(Cow::Borrowed(OsStr::new(r#"\some\directory with\spaces\"#))),
- OsStr::new(r#""\some\directory with\spaces\\""#)
- );
- }
-}