(&self)
| 172 | } |
| 173 | |
| 174 | pub fn start(&self) -> Result<()> { |
| 175 | if self.lock().is_running() { |
| 176 | bail!("Process is already running"); |
| 177 | } |
| 178 | |
| 179 | // Create command and spawn process |
| 180 | let mut command = Command::new(&self.config.command); |
| 181 | command |
| 182 | .stdout(Stdio::piped()) |
| 183 | .stderr(Stdio::piped()) |
| 184 | .args(&self.config.args) |
| 185 | .envs(&self.config.env) |
| 186 | .kill_on_drop(true); |
| 187 | if !self.config.cwd.is_empty() { |
| 188 | command.current_dir(&self.config.cwd); |
| 189 | } |
| 190 | if !self.config.stdout.is_empty() { |
| 191 | command.stdout(Stdio::piped()); |
| 192 | } else { |
| 193 | command.stdout(Stdio::null()); |
| 194 | } |
| 195 | if !self.config.stderr.is_empty() { |
| 196 | command.stderr(Stdio::piped()); |
| 197 | } else { |
| 198 | command.stderr(Stdio::null()); |
| 199 | } |
| 200 | |
| 201 | let mut process = command.spawn()?; |
| 202 | let pid = process.id(); |
| 203 | |
| 204 | let (kill_tx, kill_rx) = oneshot::channel(); |
| 205 | |
| 206 | // Update process state |
| 207 | { |
| 208 | let mut state = self.lock(); |
| 209 | state.started_at = Some(SystemTime::now()); |
| 210 | state.status = ProcessStatus::Running; |
| 211 | state.pid = pid; |
| 212 | state.kill_tx = Some(kill_tx); |
| 213 | state.started = true; |
| 214 | } |
| 215 | |
| 216 | // Handle IO redirection |
| 217 | { |
| 218 | let pidfile_path = self.config.pidfile.clone(); |
| 219 | if !pidfile_path.is_empty() { |
| 220 | if let Err(err) = |
| 221 | fs_err::write(&pidfile_path, format!("{}", process.id().unwrap_or(0))) |
| 222 | { |
| 223 | error!("Failed to write pidfile: {err}"); |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | let stdout = process.stdout.take(); |
| 228 | let stderr = process.stderr.take(); |
| 229 | let stdout_path = self.config.stdout.clone(); |
| 230 | let stderr_path = self.config.stderr.clone(); |
| 231 |
no test coverage detected