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::>(); 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::(); 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> { 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::( &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) }