1use crate::agent::Agent;
7use anyhow::Context;
8use futures_concurrency::future::Race;
9use pal_async::DefaultDriver;
10use pal_async::DefaultPool;
11use std::ffi::OsString;
12use std::time::Duration;
13use windows_service::define_windows_service;
14use windows_service::service;
15use windows_service::service_control_handler;
16use windows_service::service_control_handler::ServiceControlHandlerResult;
17use windows_service::service_dispatcher;
18
19const SERVICE_NAME: &str = "pipette";
20
21pub fn start_service() -> anyhow::Result<()> {
22 define_windows_service!(ffi_service_main, service_main);
24 service_dispatcher::start(SERVICE_NAME, ffi_service_main).context("failed to start service")?;
25 Ok(())
26}
27
28fn service_main(_args: Vec<OsString>) {
29 DefaultPool::run_with(async |driver| {
30 if let Err(e) = service_main_inner(driver).await {
31 eprintln!("service_main failed: {:#}", e);
32 }
33 })
34}
35
36async fn service_main_inner(driver: DefaultDriver) -> anyhow::Result<()> {
37 let (send, recv) = mesh::oneshot::<()>();
38 let mut send = Some(send);
39 let event_handler = move |control_event| match control_event {
40 service::ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
41 service::ServiceControl::Stop => {
42 let _ = send.take();
43 ServiceControlHandlerResult::NoError
44 }
45 _ => ServiceControlHandlerResult::NotImplemented,
46 };
47
48 let status_handle = service_control_handler::register(SERVICE_NAME, event_handler)?;
49 let set_status = |current_state| {
50 status_handle
51 .set_service_status(service::ServiceStatus {
52 service_type: service::ServiceType::OWN_PROCESS,
53 current_state,
54 controls_accepted: service::ServiceControlAccept::STOP,
55 exit_code: service::ServiceExitCode::Win32(0),
56 checkpoint: 0,
57 wait_hint: Duration::ZERO,
58 process_id: None,
59 })
60 .context("failed to set service status")
61 };
62
63 set_status(service::ServiceState::StartPending)?;
64
65 let run = async {
66 let agent = Agent::new(driver).await?;
67 set_status(service::ServiceState::Running)?;
68 agent.run().await
69 };
70
71 let stop = async {
72 recv.await.ok();
73 set_status(service::ServiceState::StopPending)
74 };
75
76 let r = (run, stop).race().await;
77
78 let exit_code = match r {
79 Ok(()) => 0,
80 Err(_) => 1,
81 };
82
83 status_handle
84 .set_service_status(service::ServiceStatus {
85 service_type: service::ServiceType::OWN_PROCESS,
86 current_state: service::ServiceState::Stopped,
87 controls_accepted: service::ServiceControlAccept::STOP,
88 exit_code: service::ServiceExitCode::Win32(exit_code),
89 checkpoint: 0,
90 wait_hint: Duration::ZERO,
91 process_id: None,
92 })
93 .ok();
94
95 r
96}