diff --git a/src/lib.rs b/src/lib.rs index ba4877c..fe5ea4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/service.rs b/src/service.rs index 97fe44a..01b3835 100644 --- a/src/service.rs +++ b/src/service.rs @@ -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. @@ -82,12 +82,12 @@ impl ServiceStartType { *self as u32 } - pub fn from_raw(raw: u32) -> Result { + pub fn from_raw(raw: u32) -> Result { 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)), } } } @@ -109,13 +109,13 @@ impl ServiceErrorControl { *self as u32 } - pub fn from_raw(raw: u32) -> Result { + pub fn from_raw(raw: u32) -> Result { 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)), } } } @@ -233,7 +233,12 @@ pub struct ServiceConfig { } impl ServiceConfig { - pub unsafe fn from_raw(raw: winsvc::QUERY_SERVICE_CONFIGW) -> Result { + /// 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 { let dependencies = double_nul_terminated::parse_str_ptr(raw.lpDependencies) .iter() .map(ServiceDependency::from_system_identifier) @@ -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(), ), @@ -287,8 +294,8 @@ pub enum ServiceControl { } impl ServiceControl { - pub fn from_raw(raw_value: u32) -> Result { - match raw_value { + pub fn from_raw(raw: u32) -> Result { + 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), @@ -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)), } } @@ -323,8 +330,8 @@ pub enum ServiceState { } impl ServiceState { - fn from_raw(raw_state: u32) -> Result { - match raw_state { + fn from_raw(raw: u32) -> Result { + 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), @@ -332,7 +339,7 @@ impl ServiceState { 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)), } } @@ -477,16 +484,19 @@ impl ServiceStatus { raw_status } - fn from_raw(raw_status: winsvc::SERVICE_STATUS) -> Result { + /// 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 { 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), }) } } @@ -521,11 +531,11 @@ impl Service { /// # Ok(()) /// # } /// ``` - pub fn start>(&self, service_arguments: &[S]) -> Result<()> { + pub fn start>(&self, service_arguments: &[S]) -> crate::Result<()> { let wide_service_arguments = service_arguments .iter() .map(|s| WideCString::from_str(s).map_err(Error::InvalidStartArgument)) - .collect::>>()?; + .collect::>>()?; let mut raw_service_arguments: Vec<*const u16> = wide_service_arguments.iter().map(|s| s.as_ptr()).collect(); @@ -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 { + pub fn stop(&self) -> crate::Result { self.send_control_command(ServiceControl::Stop) } /// Get the service status from the system. - pub fn query_status(&self) -> Result { + pub fn query_status(&self) -> crate::Result { let mut raw_status = unsafe { mem::zeroed::() }; 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 { + pub fn query_config(&self) -> crate::Result { // 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; @@ -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; @@ -599,7 +609,7 @@ impl Service { } /// Private helper to send the control commands to the system. - fn send_control_command(&self, command: ServiceControl) -> Result { + fn send_control_command(&self, command: ServiceControl) -> crate::Result { let mut raw_status = unsafe { mem::zeroed::() }; let success = unsafe { winsvc::ControlService( @@ -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::*; diff --git a/src/service_control_handler.rs b/src/service_control_handler.rs index 3762056..61b7a0c 100644 --- a/src/service_control_handler.rs +++ b/src/service_control_handler.rs @@ -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(()) } @@ -115,7 +115,7 @@ where if status_handle.is_null() { // Release the `event_handler` in case of an error. let _: Box = 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)) } diff --git a/src/service_dispatcher.rs b/src/service_dispatcher.rs index 38b0081..b6624f5 100644 --- a/src/service_dispatcher.rs +++ b/src/service_dispatcher.rs @@ -106,7 +106,7 @@ pub fn start>( 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(()) } diff --git a/src/service_manager.rs b/src/service_manager.rs index 58d9d05..f39564d 100644 --- a/src/service_manager.rs +++ b/src/service_manager.rs @@ -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) }, @@ -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 = service_info .dependencies @@ -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) })) } @@ -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) })) }