Skip to main content

minifly_litefs/
process.rs

1use std::path::PathBuf;
2use std::process::{Child, Command, Stdio};
3use std::sync::Arc;
4use tokio::sync::Mutex;
5use tracing::{info, warn, error};
6use crate::config::LiteFSConfig;
7use minifly_core::Error;
8use crate::Result;
9
10pub struct LiteFSProcess {
11    child: Option<Child>,
12    config_path: PathBuf,
13    binary_path: PathBuf,
14    machine_id: String,
15}
16
17impl LiteFSProcess {
18    pub fn new(machine_id: String, binary_path: PathBuf, config_path: PathBuf) -> Self {
19        Self {
20            child: None,
21            config_path,
22            binary_path,
23            machine_id,
24        }
25    }
26    
27    pub fn start(&mut self) -> Result<()> {
28        if self.child.is_some() {
29            return Err(Error::LiteFSError("LiteFS process already running".to_string()));
30        }
31        
32        info!("Starting LiteFS for machine {}", self.machine_id);
33        
34        let mut cmd = Command::new(&self.binary_path);
35        cmd.arg("mount")
36            .arg("-config")
37            .arg(&self.config_path)
38            .stdout(Stdio::piped())
39            .stderr(Stdio::piped());
40        
41        match cmd.spawn() {
42            Ok(child) => {
43                info!("LiteFS process started with PID: {:?}", child.id());
44                self.child = Some(child);
45                Ok(())
46            }
47            Err(e) => {
48                error!("Failed to start LiteFS: {}", e);
49                Err(Error::LiteFSError(format!("Failed to start LiteFS: {}", e)))
50            }
51        }
52    }
53    
54    pub fn stop(&mut self) -> Result<()> {
55        if let Some(mut child) = self.child.take() {
56            info!("Stopping LiteFS process for machine {}", self.machine_id);
57            
58            match child.kill() {
59                Ok(_) => {
60                    match child.wait() {
61                        Ok(status) => {
62                            info!("LiteFS process stopped with status: {:?}", status);
63                            Ok(())
64                        }
65                        Err(e) => {
66                            warn!("Error waiting for LiteFS process to stop: {}", e);
67                            Ok(())
68                        }
69                    }
70                }
71                Err(e) => {
72                    error!("Failed to stop LiteFS process: {}", e);
73                    Err(Error::LiteFSError(format!("Failed to stop LiteFS: {}", e)))
74                }
75            }
76        } else {
77            Ok(())
78        }
79    }
80    
81    pub fn is_running(&mut self) -> bool {
82        if let Some(ref mut child) = self.child {
83            match child.try_wait() {
84                Ok(Some(_)) => {
85                    // Process has exited
86                    self.child = None;
87                    false
88                }
89                Ok(None) => {
90                    // Process is still running
91                    true
92                }
93                Err(_) => {
94                    // Error checking status, assume not running
95                    self.child = None;
96                    false
97                }
98            }
99        } else {
100            false
101        }
102    }
103}
104
105impl Drop for LiteFSProcess {
106    fn drop(&mut self) {
107        if let Err(e) = self.stop() {
108            error!("Error stopping LiteFS process in drop: {}", e);
109        }
110    }
111}
112
113pub struct LiteFSProcessManager {
114    processes: Arc<Mutex<std::collections::HashMap<String, LiteFSProcess>>>,
115    binary_path: PathBuf,
116}
117
118impl LiteFSProcessManager {
119    pub fn new(binary_path: PathBuf) -> Self {
120        Self {
121            processes: Arc::new(Mutex::new(std::collections::HashMap::new())),
122            binary_path,
123        }
124    }
125    
126    pub async fn start_litefs(&self, machine_id: &str, config: &LiteFSConfig, config_dir: &PathBuf) -> Result<()> {
127        let config_path = config_dir.join(format!("{}.yml", machine_id));
128        
129        // Write config file
130        let yaml = config.to_yaml()
131            .map_err(|e| Error::LiteFSError(format!("Failed to serialize config: {}", e)))?;
132        
133        tokio::fs::write(&config_path, yaml).await
134            .map_err(|e| Error::LiteFSError(format!("Failed to write config: {}", e)))?;
135        
136        let mut processes = self.processes.lock().await;
137        
138        let mut process = LiteFSProcess::new(
139            machine_id.to_string(),
140            self.binary_path.clone(),
141            config_path,
142        );
143        
144        process.start()?;
145        processes.insert(machine_id.to_string(), process);
146        
147        Ok(())
148    }
149    
150    pub async fn stop_litefs(&self, machine_id: &str) -> Result<()> {
151        let mut processes = self.processes.lock().await;
152        
153        if let Some(mut process) = processes.remove(machine_id) {
154            process.stop()?;
155        }
156        
157        Ok(())
158    }
159    
160    pub async fn is_running(&self, machine_id: &str) -> bool {
161        let mut processes = self.processes.lock().await;
162        
163        if let Some(process) = processes.get_mut(machine_id) {
164            process.is_running()
165        } else {
166            false
167        }
168    }
169    
170    pub async fn stop_all(&self) -> Result<()> {
171        let mut processes = self.processes.lock().await;
172        
173        for (machine_id, mut process) in processes.drain() {
174            info!("Stopping LiteFS for machine {}", machine_id);
175            if let Err(e) = process.stop() {
176                error!("Failed to stop LiteFS for machine {}: {}", machine_id, e);
177            }
178        }
179        
180        Ok(())
181    }
182}