Skip to content

Commit

Permalink
Merge branch 'break-apart-leaf-errors'
Browse files Browse the repository at this point in the history
  • Loading branch information
faern committed Mar 25, 2019
2 parents 3799354 + 8f1b537 commit aab5b26
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 92 deletions.
54 changes: 9 additions & 45 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,56 +219,20 @@ pub enum Error {
InvalidStartArgument(#[error(cause)] widestring::NulError),

/// Invalid raw representation of [`ServiceState`].
#[error(display = "Invalid service state value: {}", _0)]
InvalidServiceState(u32),

/// Invalid raw representation of [`ServiceControl`].
#[error(display = "Invalid service control value: {}", _0)]
InvalidServiceControl(u32),
#[error(display = "Invalid service state value")]
InvalidServiceState(#[error(cause)] service::ParseRawError),

/// Invalid raw representation of [`ServiceStartType`].
#[error(display = "Invalid service start type: {}", _0)]
InvalidServiceStartType(u32),
#[error(display = "Invalid service start value")]
InvalidServiceStartType(#[error(cause)] service::ParseRawError),

/// Invalid raw representation of [`ServiceErrorControl`].
#[error(display = "Invalid service error control type: {}", _0)]
InvalidServiceErrorControl(u32),

/// Failed to send command to service Service deletion to start
#[error(display = "Could not send commands to service")]
ServiceControlFailed(#[error(cause)] std::io::Error),

/// Failed to create service
#[error(display = "Could not create service")]
ServiceCreateFailed(#[error(cause)] std::io::Error),

/// Service deletion failed
#[error(display = "Could not delete service")]
ServiceDeleteFailed(#[error(cause)] std::io::Error),

/// Failed to connect to service manager
#[error(display = "Could not connect to service manager")]
ServiceManagerConnectFailed(#[error(cause)] std::io::Error),

/// Failed to open service
#[error(display = "Could not open service")]
ServiceOpenFailed(#[error(cause)] std::io::Error),

/// Failed to query Service
#[error(display = "Could not query service")]
ServiceQueryFailed(#[error(cause)] std::io::Error),

/// Failed to register service
#[error(display = "Could not register service")]
ServiceRegisterFailed(#[error(cause)] std::io::Error),

/// Service failed to start
#[error(display = "Could not start service")]
ServiceStartFailed(#[error(cause)] std::io::Error),
#[error(display = "Invalid service error control value")]
InvalidServiceErrorControl(#[error(cause)] service::ParseRawError),

/// Failed to set service status
#[error(display = "Could not set service status")]
ServiceStatusFailed(#[error(cause)] std::io::Error),
/// IO error when calling winapi
#[error(display = "IO error in winapi call")]
Winapi(#[error(cause)] std::io::Error),
}

mod sc_handle;
Expand Down
88 changes: 51 additions & 37 deletions src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use winapi::shared::winerror::{ERROR_SERVICE_SPECIFIC_ERROR, NO_ERROR};
use winapi::um::{winnt, winsvc};

use crate::sc_handle::ScHandle;
use crate::{double_nul_terminated, Error, Result};
use crate::{double_nul_terminated, Error};

bitflags::bitflags! {
/// Enum describing the types of Windows services.
Expand Down Expand Up @@ -82,12 +82,12 @@ impl ServiceStartType {
*self as u32
}

pub fn from_raw(raw: u32) -> Result<ServiceStartType> {
pub fn from_raw(raw: u32) -> Result<ServiceStartType, ParseRawError> {
match raw {
x if x == ServiceStartType::AutoStart.to_raw() => Ok(ServiceStartType::AutoStart),
x if x == ServiceStartType::OnDemand.to_raw() => Ok(ServiceStartType::OnDemand),
x if x == ServiceStartType::Disabled.to_raw() => Ok(ServiceStartType::Disabled),
_ => Err(Error::InvalidServiceStartType(raw)),
_ => Err(ParseRawError(raw)),
}
}
}
Expand All @@ -109,13 +109,13 @@ impl ServiceErrorControl {
*self as u32
}

pub fn from_raw(raw: u32) -> Result<ServiceErrorControl> {
pub fn from_raw(raw: u32) -> Result<ServiceErrorControl, ParseRawError> {
match raw {
x if x == ServiceErrorControl::Critical.to_raw() => Ok(ServiceErrorControl::Critical),
x if x == ServiceErrorControl::Ignore.to_raw() => Ok(ServiceErrorControl::Ignore),
x if x == ServiceErrorControl::Normal.to_raw() => Ok(ServiceErrorControl::Normal),
x if x == ServiceErrorControl::Severe.to_raw() => Ok(ServiceErrorControl::Severe),
_ => Err(Error::InvalidServiceErrorControl(raw)),
_ => Err(ParseRawError(raw)),
}
}
}
Expand Down Expand Up @@ -233,7 +233,12 @@ pub struct ServiceConfig {
}

impl ServiceConfig {
pub unsafe fn from_raw(raw: winsvc::QUERY_SERVICE_CONFIGW) -> Result<ServiceConfig> {
/// Tries to parse a `QUERY_SERVICE_CONFIGW` into Rust [`ServiceConfig`].
///
/// # Errors
///
/// Returns an error if a field inside the `QUERY_SERVICE_CONFIGW` does not have a valid value.
pub unsafe fn from_raw(raw: winsvc::QUERY_SERVICE_CONFIGW) -> crate::Result<ServiceConfig> {
let dependencies = double_nul_terminated::parse_str_ptr(raw.lpDependencies)
.iter()
.map(ServiceDependency::from_system_identifier)
Expand All @@ -255,8 +260,10 @@ impl ServiceConfig {

Ok(ServiceConfig {
service_type: ServiceType::from_bits_truncate(raw.dwServiceType),
start_type: ServiceStartType::from_raw(raw.dwStartType)?,
error_control: ServiceErrorControl::from_raw(raw.dwErrorControl)?,
start_type: ServiceStartType::from_raw(raw.dwStartType)
.map_err(Error::InvalidServiceStartType)?,
error_control: ServiceErrorControl::from_raw(raw.dwErrorControl)
.map_err(Error::InvalidServiceErrorControl)?,
executable_path: PathBuf::from(
WideCStr::from_ptr_str(raw.lpBinaryPathName).to_os_string(),
),
Expand Down Expand Up @@ -287,8 +294,8 @@ pub enum ServiceControl {
}

impl ServiceControl {
pub fn from_raw(raw_value: u32) -> Result<Self> {
match raw_value {
pub fn from_raw(raw: u32) -> Result<Self, ParseRawError> {
match raw {
x if x == ServiceControl::Continue.to_raw() => Ok(ServiceControl::Continue),
x if x == ServiceControl::Interrogate.to_raw() => Ok(ServiceControl::Interrogate),
x if x == ServiceControl::NetBindAdd.to_raw() => Ok(ServiceControl::NetBindAdd),
Expand All @@ -300,7 +307,7 @@ impl ServiceControl {
x if x == ServiceControl::Preshutdown.to_raw() => Ok(ServiceControl::Preshutdown),
x if x == ServiceControl::Shutdown.to_raw() => Ok(ServiceControl::Shutdown),
x if x == ServiceControl::Stop.to_raw() => Ok(ServiceControl::Stop),
other => Err(Error::InvalidServiceControl(other)),
_ => Err(ParseRawError(raw)),
}
}

Expand All @@ -323,16 +330,16 @@ pub enum ServiceState {
}

impl ServiceState {
fn from_raw(raw_state: u32) -> Result<Self> {
match raw_state {
fn from_raw(raw: u32) -> Result<Self, ParseRawError> {
match raw {
x if x == ServiceState::Stopped.to_raw() => Ok(ServiceState::Stopped),
x if x == ServiceState::StartPending.to_raw() => Ok(ServiceState::StartPending),
x if x == ServiceState::StopPending.to_raw() => Ok(ServiceState::StopPending),
x if x == ServiceState::Running.to_raw() => Ok(ServiceState::Running),
x if x == ServiceState::ContinuePending.to_raw() => Ok(ServiceState::ContinuePending),
x if x == ServiceState::PausePending.to_raw() => Ok(ServiceState::PausePending),
x if x == ServiceState::Paused.to_raw() => Ok(ServiceState::Paused),
other => Err(Error::InvalidServiceState(other)),
_ => Err(ParseRawError(raw)),
}
}

Expand Down Expand Up @@ -477,16 +484,19 @@ impl ServiceStatus {
raw_status
}

fn from_raw(raw_status: winsvc::SERVICE_STATUS) -> Result<Self> {
/// Tries to parse a `SERVICE_STATUS` into a Rust [`ServiceStatus`].
///
/// # Errors
///
/// Returns an error if the `dwCurrentState` field does not represent a valid [`ServiceState`].
fn from_raw(raw: winsvc::SERVICE_STATUS) -> Result<Self, ParseRawError> {
Ok(ServiceStatus {
service_type: ServiceType::from_bits_truncate(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),
service_type: ServiceType::from_bits_truncate(raw.dwServiceType),
current_state: ServiceState::from_raw(raw.dwCurrentState)?,
controls_accepted: ServiceControlAccept::from_bits_truncate(raw.dwControlsAccepted),
exit_code: ServiceExitCode::from(&raw),
checkpoint: raw.dwCheckPoint,
wait_hint: Duration::from_millis(raw.dwWaitHint as u64),
})
}
}
Expand Down Expand Up @@ -521,11 +531,11 @@ impl Service {
/// # Ok(())
/// # }
/// ```
pub fn start<S: AsRef<OsStr>>(&self, service_arguments: &[S]) -> Result<()> {
pub fn start<S: AsRef<OsStr>>(&self, service_arguments: &[S]) -> crate::Result<()> {
let wide_service_arguments = service_arguments
.iter()
.map(|s| WideCString::from_str(s).map_err(Error::InvalidStartArgument))
.collect::<Result<Vec<WideCString>>>()?;
.collect::<crate::Result<Vec<WideCString>>>()?;

let mut raw_service_arguments: Vec<*const u16> =
wide_service_arguments.iter().map(|s| s.as_ptr()).collect();
Expand All @@ -539,42 +549,42 @@ impl Service {
};

if success == 0 {
Err(Error::ServiceStartFailed(io::Error::last_os_error()))
Err(Error::Winapi(io::Error::last_os_error()))
} else {
Ok(())
}
}

/// Stop the service.
pub fn stop(&self) -> Result<ServiceStatus> {
pub fn stop(&self) -> crate::Result<ServiceStatus> {
self.send_control_command(ServiceControl::Stop)
}

/// Get the service status from the system.
pub fn query_status(&self) -> Result<ServiceStatus> {
pub fn query_status(&self) -> crate::Result<ServiceStatus> {
let mut raw_status = unsafe { mem::zeroed::<winsvc::SERVICE_STATUS>() };
let success = unsafe {
winsvc::QueryServiceStatus(self.service_handle.raw_handle(), &mut raw_status)
};
if success == 0 {
Err(Error::ServiceQueryFailed(io::Error::last_os_error()))
Err(Error::Winapi(io::Error::last_os_error()))
} else {
ServiceStatus::from_raw(raw_status)
ServiceStatus::from_raw(raw_status).map_err(Error::InvalidServiceState)
}
}

/// Delete the service from system registry.
pub fn delete(self) -> Result<()> {
pub fn delete(self) -> crate::Result<()> {
let success = unsafe { winsvc::DeleteService(self.service_handle.raw_handle()) };
if success == 0 {
Err(Error::ServiceDeleteFailed(io::Error::last_os_error()))
Err(Error::Winapi(io::Error::last_os_error()))
} else {
Ok(())
}
}

/// Get the service config from the system.
pub fn query_config(&self) -> Result<ServiceConfig> {
pub fn query_config(&self) -> crate::Result<ServiceConfig> {
// As per docs, the maximum size of data buffer used by QueryServiceConfigW is 8K
let mut data = [0u8; 8096];
let mut bytes_written: u32 = 0;
Expand All @@ -589,7 +599,7 @@ impl Service {
};

if success == 0 {
Err(Error::ServiceQueryFailed(io::Error::last_os_error()))
Err(Error::Winapi(io::Error::last_os_error()))
} else {
unsafe {
let raw_config = data.as_ptr() as *const winsvc::QUERY_SERVICE_CONFIGW;
Expand All @@ -599,7 +609,7 @@ impl Service {
}

/// Private helper to send the control commands to the system.
fn send_control_command(&self, command: ServiceControl) -> Result<ServiceStatus> {
fn send_control_command(&self, command: ServiceControl) -> crate::Result<ServiceStatus> {
let mut raw_status = unsafe { mem::zeroed::<winsvc::SERVICE_STATUS>() };
let success = unsafe {
winsvc::ControlService(
Expand All @@ -610,13 +620,17 @@ impl Service {
};

if success == 0 {
Err(Error::ServiceControlFailed(io::Error::last_os_error()))
Err(Error::Winapi(io::Error::last_os_error()))
} else {
ServiceStatus::from_raw(raw_status)
ServiceStatus::from_raw(raw_status).map_err(Error::InvalidServiceState)
}
}
}

#[derive(err_derive::Error, Debug)]
#[error(display = "Invalid integer value for the target type: {}", _0)]
pub struct ParseRawError(u32);

#[cfg(test)]
mod tests {
use super::*;
Expand Down
6 changes: 3 additions & 3 deletions src/service_control_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ impl ServiceStatusHandle {
}

/// Report the new service status to the system.
pub fn set_service_status(&self, service_status: ServiceStatus) -> Result<()> {
pub fn set_service_status(&self, service_status: ServiceStatus) -> crate::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(Error::ServiceStatusFailed(io::Error::last_os_error()))
Err(Error::Winapi(io::Error::last_os_error()))
} else {
Ok(())
}
Expand Down Expand Up @@ -115,7 +115,7 @@ where
if status_handle.is_null() {
// Release the `event_handler` in case of an error.
let _: Box<F> = unsafe { Box::from_raw(context) };
Err(Error::ServiceRegisterFailed(io::Error::last_os_error()))
Err(Error::Winapi(io::Error::last_os_error()))
} else {
Ok(ServiceStatusHandle::from_handle(status_handle))
}
Expand Down
2 changes: 1 addition & 1 deletion src/service_dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ pub fn start<T: AsRef<OsStr>>(

let result = unsafe { winsvc::StartServiceCtrlDispatcherW(service_table.as_ptr()) };
if result == 0 {
Err(Error::ServiceStartFailed(io::Error::last_os_error()))
Err(Error::Winapi(io::Error::last_os_error()))
} else {
Ok(())
}
Expand Down
11 changes: 5 additions & 6 deletions src/service_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@ impl ServiceManager {
};

if handle.is_null() {
Err(Error::ServiceManagerConnectFailed(
io::Error::last_os_error(),
))
Err(Error::Winapi(io::Error::last_os_error()))
} else {
Ok(ServiceManager {
manager_handle: unsafe { ScHandle::new(handle) },
Expand Down Expand Up @@ -161,7 +159,8 @@ impl ServiceManager {
launch_command_buffer.push(wide);
}

let launch_command = WideCString::from_wide_str(launch_command_buffer).unwrap();
let launch_command = WideCString::from_wide_str(launch_command_buffer)
.expect("launch_command_buffer invalidly formatted");

let dependency_identifiers: Vec<OsString> = service_info
.dependencies
Expand Down Expand Up @@ -194,7 +193,7 @@ impl ServiceManager {
};

if service_handle.is_null() {
Err(Error::ServiceCreateFailed(io::Error::last_os_error()))
Err(Error::Winapi(io::Error::last_os_error()))
} else {
Ok(Service::new(unsafe { ScHandle::new(service_handle) }))
}
Expand Down Expand Up @@ -234,7 +233,7 @@ impl ServiceManager {
};

if service_handle.is_null() {
Err(Error::ServiceOpenFailed(io::Error::last_os_error()))
Err(Error::Winapi(io::Error::last_os_error()))
} else {
Ok(Service::new(unsafe { ScHandle::new(service_handle) }))
}
Expand Down

0 comments on commit aab5b26

Please sign in to comment.