1use std::path::{Path, PathBuf};
7
8use async_trait::async_trait;
9
10use crate::log;
11use crate::state::LauncherPaths;
12use crate::util::errors::{wrap, AnyError};
13use crate::util::io::{tailf, TailEvent};
14
15pub const SERVICE_LOG_FILE_NAME: &str = "tunnel-service.log";
16
17#[async_trait]
18pub trait ServiceContainer: Send {
19 async fn run_service(
20 &mut self,
21 log: log::Logger,
22 launcher_paths: LauncherPaths,
23 ) -> Result<(), AnyError>;
24}
25
26#[async_trait]
27pub trait ServiceManager {
28 async fn register(&self, exe: PathBuf, args: &[&str]) -> Result<(), AnyError>;
31
32 async fn run(
36 self,
37 launcher_paths: LauncherPaths,
38 handle: impl 'static + ServiceContainer,
39 ) -> Result<(), AnyError>;
40
41 async fn show_logs(&self) -> Result<(), AnyError>;
43
44 async fn is_installed(&self) -> Result<bool, AnyError>;
46
47 async fn unregister(&self) -> Result<(), AnyError>;
49}
50
51#[cfg(target_os = "windows")]
52pub type ServiceManagerImpl = super::service_windows::WindowsService;
53
54#[cfg(target_os = "linux")]
55pub type ServiceManagerImpl = super::service_linux::SystemdService;
56
57#[cfg(target_os = "macos")]
58pub type ServiceManagerImpl = super::service_macos::LaunchdService;
59
60#[allow(unreachable_code)]
61#[allow(unused_variables)]
62pub fn create_service_manager(log: log::Logger, paths: &LauncherPaths) -> ServiceManagerImpl {
63 #[cfg(target_os = "macos")]
64 {
65 super::service_macos::LaunchdService::new(log, paths)
66 }
67 #[cfg(target_os = "windows")]
68 {
69 super::service_windows::WindowsService::new(log, paths)
70 }
71 #[cfg(target_os = "linux")]
72 {
73 super::service_linux::SystemdService::new(log, paths.clone())
74 }
75}
76
77#[allow(dead_code)] pub(crate) async fn tail_log_file(log_file: &Path) -> Result<(), AnyError> {
79 if !log_file.exists() {
80 println!("The tunnel service has not started yet.");
81 return Ok(());
82 }
83
84 let file = std::fs::File::open(log_file).map_err(|e| wrap(e, "error opening log file"))?;
85 let mut rx = tailf(file, 20);
86 while let Some(line) = rx.recv().await {
87 match line {
88 TailEvent::Line(l) => print!("{}", l),
89 TailEvent::Reset => println!("== Tunnel service restarted =="),
90 TailEvent::Err(e) => return Err(wrap(e, "error reading log file").into()),
91 }
92 }
93
94 Ok(())
95}