cloudpub_client/service/
mod.rs

1use anyhow::Result;
2use std::path::PathBuf;
3use std::{env, fs};
4use tracing::debug;
5
6#[cfg(target_os = "windows")]
7mod windows;
8#[cfg(target_os = "windows")]
9pub use windows::*;
10
11#[cfg(target_os = "linux")]
12mod linux;
13#[cfg(target_os = "linux")]
14pub use linux::*;
15
16#[cfg(target_os = "macos")]
17mod macos;
18#[cfg(target_os = "macos")]
19pub use macos::*;
20
21// Common service trait
22pub trait ServiceManager {
23    fn install(&self) -> Result<()>;
24    fn uninstall(&self) -> Result<()>;
25    fn start(&self) -> Result<()>;
26    fn stop(&self) -> Result<()>;
27    fn status(&self) -> Result<ServiceStatus>;
28}
29
30#[allow(dead_code)]
31#[derive(Debug)]
32pub enum ServiceStatus {
33    Running,
34    Stopped,
35    NotInstalled,
36    Unknown,
37}
38
39#[allow(dead_code)]
40#[derive(Debug)]
41pub struct ServiceConfig {
42    pub name: String,
43    pub display_name: String,
44    pub description: String,
45    pub executable_path: PathBuf,
46    pub args: Vec<String>,
47    pub config_path: PathBuf,
48}
49
50impl ServiceConfig {
51    /// Get the system config path for the service based on the OS
52    pub fn get_system_config_path() -> PathBuf {
53        #[cfg(target_os = "windows")]
54        {
55            // For Windows service running as SYSTEM
56            PathBuf::from(r"C:\ProgramData\cloudpub\client.toml")
57        }
58        #[cfg(target_os = "linux")]
59        {
60            // For Linux service running as root
61            PathBuf::from("/root/.config/cloudpub/client.toml")
62        }
63        #[cfg(target_os = "macos")]
64        {
65            // For macOS service running as root
66            PathBuf::from("/var/root/.config/cloudpub/client.toml")
67        }
68        #[cfg(target_os = "android")]
69        {
70            // For Android/Termux
71            PathBuf::from("/data/data/com.termux/files/home/.config/cloudpub/client.toml")
72        }
73    }
74
75    /// Get the original user's config path when running under sudo
76    #[cfg(any(target_os = "linux", target_os = "macos"))]
77    pub fn get_sudo_user_config_path() -> Option<PathBuf> {
78        // Check if we're running under sudo
79        if let Ok(sudo_user) = env::var("SUDO_USER") {
80            debug!("Detected SUDO_USER: {}", sudo_user);
81
82            // Get the home directory for the sudo user
83            #[cfg(target_os = "linux")]
84            let home_base = "/home";
85            #[cfg(target_os = "macos")]
86            let home_base = "/Users";
87
88            let user_home = PathBuf::from(home_base).join(&sudo_user);
89            let config_path = user_home.join(".config/cloudpub/client.toml");
90
91            debug!("Check for sudo user config location: {:?}", config_path);
92            if config_path.exists() {
93                debug!("Found config at sudo user's location: {:?}", config_path);
94                return Some(config_path);
95            }
96        }
97        debug!("No SUDO_USER detected or config file does not exist");
98        None
99    }
100
101    /// Copy the provided config to the system location
102    pub fn copy_config_to_system(&self) -> Result<()> {
103        let system_config_path = Self::get_system_config_path();
104
105        // Create parent directory if it doesn't exist
106        if let Some(parent) = system_config_path.parent() {
107            fs::create_dir_all(parent)?;
108        }
109
110        // Copy the config file
111        if self.config_path != system_config_path {
112            fs::copy(&self.config_path, &system_config_path)?;
113            debug!(
114                "Copied config from {:?} to {:?}",
115                self.config_path, system_config_path
116            );
117        }
118
119        Ok(())
120    }
121}
122
123pub fn create_service_manager(config_path: PathBuf) -> Box<dyn ServiceManager> {
124    // Get the current executable path
125    let exe_path = env::current_exe().expect("Failed to get current executable path");
126
127    // Prepare config file argument using system config path
128    let mut args = Vec::new();
129
130    // Use system config path for service
131    let system_config_path = ServiceConfig::get_system_config_path();
132    let path_str = system_config_path.to_str().unwrap();
133    args.push("--conf".to_string());
134    args.push(path_str.to_string());
135
136    // Add the run command for the service
137    args.push("run".to_string());
138    args.push("--run-as-service".to_string());
139
140    // Create service configuration
141    let config = ServiceConfig {
142        #[cfg(target_os = "macos")]
143        name: "ru.cloudpub.clo".to_string(),
144        #[cfg(not(target_os = "macos"))]
145        name: "cloudpub".to_string(),
146        display_name: "CloudPub Client".to_string(),
147        description: "CloudPub Client Service".to_string(),
148        executable_path: exe_path,
149        args,
150        config_path,
151    };
152
153    debug!("Service configuration: {:?}", config);
154
155    #[cfg(target_os = "windows")]
156    {
157        Box::new(WindowsServiceManager::new(config))
158    }
159    #[cfg(target_os = "linux")]
160    {
161        Box::new(LinuxServiceManager::new(config))
162    }
163    #[cfg(target_os = "macos")]
164    {
165        Box::new(MacOSServiceManager::new(config))
166    }
167    #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
168    {
169        panic!("Unsupported platform for service management");
170    }
171}