cli/tunnels/
service.rs

1/*---------------------------------------------------------------------------------------------
2 *  Copyright (c) Microsoft Corporation. All rights reserved.
3 *  Licensed under the MIT License. See License.txt in the project root for license information.
4 *--------------------------------------------------------------------------------------------*/
5
6use 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	/// Registers the current executable as a service to run with the given set
29	/// of arguments.
30	async fn register(&self, exe: PathBuf, args: &[&str]) -> Result<(), AnyError>;
31
32	/// Runs the service using the given handle. The executable *must not* take
33	/// any action which may fail prior to calling this to ensure service
34	/// states may update.
35	async fn run(
36		self,
37		launcher_paths: LauncherPaths,
38		handle: impl 'static + ServiceContainer,
39	) -> Result<(), AnyError>;
40
41	/// Show logs from the running service to standard out.
42	async fn show_logs(&self) -> Result<(), AnyError>;
43
44	/// Gets whether the tunnel service is installed.
45	async fn is_installed(&self) -> Result<bool, AnyError>;
46
47	/// Unregisters the current executable as a service.
48	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)] // unused on Linux
78pub(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}