Skip to content

Instantly share code, notes, and snippets.

@leiless
Last active June 20, 2023 09:12
Show Gist options
  • Save leiless/e4af121675eead74f59d4506aefa5110 to your computer and use it in GitHub Desktop.
Save leiless/e4af121675eead74f59d4506aefa5110 to your computer and use it in GitHub Desktop.
UDP ping service: windows_service + tokio async runtime
use windows_service::service::{
ServiceType,
ServiceControl,
ServiceStatus,
ServiceState,
ServiceControlAccept,
ServiceExitCode,
};
use windows_service::service_control_handler::ServiceControlHandlerResult;
const SERVICE_NAME: &str = "ping_service";
const SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS;
pub fn run() -> anyhow::Result<()> {
// Register generated `ffi_service_main` with the system and start the service
// blocking this thread until the service is stopped.
windows_service::service_dispatcher::start(SERVICE_NAME, ffi_service_main)?;
Ok(())
}
windows_service::define_windows_service!(ffi_service_main, ping_service_main);
fn ping_service_main(args: Vec<std::ffi::OsString>) {
if let Err(err) = run_service(args) {
eprintln!("service error: {}", err);
}
}
fn run_service(_args: Vec<std::ffi::OsString>) -> anyhow::Result<()> {
let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel();
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 NoError even if not implemented.
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
// Handle stop
ServiceControl::Stop => {
shutdown_tx.send(()).unwrap();
ServiceControlHandlerResult::NoError
}
_ => ServiceControlHandlerResult::NotImplemented,
}
};
// Register system service event handler.
// The returned status handle should be used to report service status changes to the system.
let status_handle = windows_service::service_control_handler::register(SERVICE_NAME, event_handler)?;
// Tell the system that service is running
status_handle.set_service_status(ServiceStatus {
service_type: SERVICE_TYPE,
current_state: ServiceState::Running,
controls_accepted: ServiceControlAccept::STOP,
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: std::time::Duration::default(),
process_id: None,
})?;
let result = app_main(shutdown_rx);
// Tell the system that service has stopped.
status_handle.set_service_status(ServiceStatus {
service_type: SERVICE_TYPE,
current_state: ServiceState::Stopped,
controls_accepted: ServiceControlAccept::empty(),
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: std::time::Duration::default(),
process_id: None,
})?;
result
}
fn app_main(shutdown_rx: std::sync::mpsc::Receiver<()>) -> anyhow::Result<()> {
let rt = tokio::runtime::Runtime::new()?;
rt.block_on(async move {
// 0 means bind to any available port
let bind_addr = "127.0.0.1:0";
let receiver_addr = "127.0.0.1:1235";
let ping_msg = "ping\n";
let socket = tokio::net::UdpSocket::bind(bind_addr).await?;
loop {
let len = socket.send_to(ping_msg.as_bytes(), receiver_addr).await?;
assert_eq!(len, ping_msg.len());
match shutdown_rx.try_recv() {
Ok(_) => break,
Err(_) => {}
}
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
Ok::<(), anyhow::Error>(())
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment