aboutsummaryrefslogtreecommitdiff
path: root/src/windows_util.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/windows_util.rs')
-rw-r--r--src/windows_util.rs129
1 files changed, 129 insertions, 0 deletions
diff --git a/src/windows_util.rs b/src/windows_util.rs
new file mode 100644
index 0000000..348ea91
--- /dev/null
+++ b/src/windows_util.rs
@@ -0,0 +1,129 @@
+use windows::{
+ core::HSTRING,
+ Win32::{
+ Foundation,
+ System::Services,
+ },
+};
+
+lazy_static::lazy_static! {
+ static ref PSQL_REGEX: regex::Regex = regex::Regex::new(r#"^(?:postgresql|psql|postgres)-.*([\d\.]*)"#).unwrap();
+}
+
+pub unsafe fn ensure_postgres_started() -> windows::core::Result<()> {
+ let sc_manager = Services::OpenSCManagerW(
+ None,
+ None,
+ (Foundation::GENERIC_READ | Foundation::GENERIC_WRITE).0,
+ )?;
+
+ let services = list_services(sc_manager)?;
+
+ let mut psql_services =
+ services.into_iter().filter(|status| PSQL_REGEX.is_match(&status.name)).collect::<Vec<_>>();
+
+ match psql_services.len() {
+ 1 => {},
+
+ 0 => {
+ log::warn!("unable to identify an installed postgres service");
+ },
+
+ other => {
+ log::warn!("unable to identify postgres service: {other} potential matches found");
+ return Ok(());
+ },
+ }
+
+ let svc = psql_services.remove(0);
+ log::debug!("identified postgres service: {}", svc.name);
+
+ match svc.status.dwCurrentState {
+ Services::SERVICE_RUNNING => {
+ log::info!("postgres was already running, done");
+ return Ok(());
+ },
+
+ Services::SERVICE_START_PENDING => {
+ log::info!("postgres was already in startup, done");
+ return Ok(());
+ },
+
+ _ => {
+ log::info!("postgres is not running: starting it")
+ },
+ }
+
+ let service =
+ Services::OpenServiceW(sc_manager, &HSTRING::from(&svc.name), Services::SERVICE_START)?;
+
+ Services::StartServiceW(service, None)?;
+
+ log::info!("started postgres service");
+
+ Services::CloseServiceHandle(service)?;
+ Services::CloseServiceHandle(sc_manager)?;
+
+ Ok(())
+}
+
+const RESULT_SIZE: usize = size_of::<Services::ENUM_SERVICE_STATUS_PROCESSW>();
+
+pub struct ServiceStatus {
+ pub display_name: String,
+ pub name: String,
+ pub status: Services::SERVICE_STATUS_PROCESS,
+}
+
+pub unsafe fn list_services(
+ sc_manager: Services::SC_HANDLE,
+) -> windows::core::Result<Vec<ServiceStatus>> {
+ let mut bytes_needed: u32 = 0;
+ let mut services_returned: u32 = 0;
+ let mut resume = 0;
+
+ let mut svcs = vec![];
+
+ loop {
+ let mut data = [0u8; 256 * 1024];
+
+ let result = Services::EnumServicesStatusExW(
+ sc_manager,
+ Services::SC_ENUM_PROCESS_INFO,
+ Services::SERVICE_WIN32,
+ Services::SERVICE_STATE_ALL,
+ Some(&mut data),
+ &mut bytes_needed as *mut _,
+ &mut services_returned as *mut _,
+ Some(&mut resume),
+ None,
+ );
+
+ if let Err(ref e) = result
+ && e.code() != Foundation::ERROR_MORE_DATA.to_hresult()
+ {
+ return result.map(|_| unreachable!());
+ }
+
+ let data = bytemuck::cast_slice::<u8, [u8; RESULT_SIZE]>(
+ &data[..services_returned as usize * RESULT_SIZE],
+ );
+
+ let elems = data
+ .into_iter()
+ .map(|x| core::mem::transmute::<_, Services::ENUM_SERVICE_STATUS_PROCESSW>(*x))
+ .map(|x| ServiceStatus {
+ display_name: x.lpDisplayName.to_string().unwrap(),
+ name: x.lpServiceName.to_string().unwrap(),
+ status: x.ServiceStatusProcess,
+ });
+
+ svcs.extend(elems);
+
+ if result.is_ok() {
+ break;
+ }
+ }
+
+ Ok(svcs)
+}