diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml
index 329b58f6..091faafb 100644
--- a/.github/workflows/build-and-release.yml
+++ b/.github/workflows/build-and-release.yml
@@ -2,7 +2,7 @@ name: Build and Release
on:
push:
branches:
- - "**"
+ - main
tags:
- "v*.*.*"
@@ -631,7 +631,7 @@ jobs:
cd runtime
export TAURI_PRIVATE_KEY="$PRIVATE_PUBLISH_KEY"
export TAURI_KEY_PASSWORD="$PRIVATE_PUBLISH_KEY_PASSWORD"
- cargo tauri build --target ${{ matrix.rust_target }} --bundles none
+ cargo tauri build --target ${{ matrix.rust_target }} --bundles ${{ matrix.tauri_bundle }}
- name: Build Tauri project (Windows)
if: matrix.platform == 'windows-latest'
@@ -642,7 +642,7 @@ jobs:
cd runtime
$env:TAURI_PRIVATE_KEY="$env:PRIVATE_PUBLISH_KEY"
$env:TAURI_KEY_PASSWORD="$env:PRIVATE_PUBLISH_KEY_PASSWORD"
- cargo tauri build --target ${{ matrix.rust_target }} --bundles none
+ cargo tauri build --target ${{ matrix.rust_target }} --bundles ${{ matrix.tauri_bundle }}
- name: Upload artifact (macOS)
if: startsWith(matrix.platform, 'macos') && startsWith(github.ref, 'refs/tags/v')
diff --git a/app/MindWork AI Studio/Pages/Information.razor b/app/MindWork AI Studio/Pages/Information.razor
index 54087f16..8d33571f 100644
--- a/app/MindWork AI Studio/Pages/Information.razor
+++ b/app/MindWork AI Studio/Pages/Information.razor
@@ -238,6 +238,8 @@
+
+
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index 9fc375cd..b9a7dfda 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -7,7 +7,6 @@ authors = ["Thorsten Sommer"]
[build-dependencies]
tauri-build = { version = "1.5", features = [] }
-dirs = "6.0.0"
[dependencies]
tauri = { version = "1.8", features = [ "http-all", "updater", "shell-sidecar", "shell-open", "dialog", "global-shortcut"] }
@@ -42,6 +41,7 @@ cfg-if = "1.0.4"
pptx-to-md = "0.4.0"
tempfile = "3.8"
strum_macros = "0.27"
+sysinfo = "0.38.0"
# Fixes security vulnerability downstream, where the upstream is not fixed yet:
url = "2.5.8"
@@ -50,6 +50,7 @@ crossbeam-channel = "0.5.15"
tracing-subscriber = "0.3.20"
dirs = "6.0.0"
+
[target.'cfg(target_os = "linux")'.dependencies]
# See issue https://github.com/tauri-apps/tauri/issues/4470
reqwest = { version = "0.13.1", features = ["native-tls-vendored"] }
diff --git a/runtime/build.rs b/runtime/build.rs
index e90dbed8..c4d1f749 100644
--- a/runtime/build.rs
+++ b/runtime/build.rs
@@ -1,32 +1,6 @@
-use std::{env, fs};
use std::path::{PathBuf};
-use std::process::Command;
-use std::io::{Error, ErrorKind};
fn main() {
- match env::var("MINDWORK_START_DEV_ENV") {
- Ok(val) => {
- let is_started_manually = match val.parse::() {
- Ok(b) => b,
- Err(_) => {
- println!("cargo: warning= Invalid value for MINDWORK_START_DEV_ENV: expected 'true' or 'false'");
- return;
- }
- };
- if is_started_manually {
- if let Err(e) = kill_zombie_qdrant_process(){
- println!("cargo:warning=Error: {e}");
- return;
- };
- if let Err(e) = delete_old_certificates() {
- println!("cargo: warning= Failed to delete old certificates: {e}");
- }
- }
- },
- Err(_) => {
- println!("cargo: warning= The environment variable 'MINDWORK_START_DEV_ENV' was not found.");
- }
- }
tauri_build::build();
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
@@ -109,167 +83,4 @@ fn update_tauri_conf(tauri_conf_path: &str, version: &str) {
}
std::fs::write(tauri_conf_path, new_tauri_conf).unwrap();
-}
-
-#[cfg(unix)]
-pub fn ensure_process_killed(pid: u32, expected_name: &str) -> Result<(), Error> {
- //
- // Check if PID exists and name matches
- //
- let ps_output = Command::new("ps")
- .arg("-p")
- .arg(pid.to_string())
- .arg("-o")
- .arg("comm=")
- .output()?;
-
- let output = String::from_utf8_lossy(&ps_output.stdout).trim().to_string();
-
- if output.is_empty() {
- // Process doesn't exist
- return Ok(());
- }
-
- let name = output;
- if name != expected_name {
- return Err(Error::new(ErrorKind::InvalidInput, "Process name does not match"));
- }
-
- //
- // Kill the process
- //
- let kill_output = Command::new("kill")
- .arg("-9")
- .arg(pid.to_string())
- .output()?;
-
- if !kill_output.status.success() {
- return Err(Error::new(ErrorKind::Other, "Failed to kill process"));
- }
-
- //
- // Verify process is killed
- //
- let ps_check = Command::new("ps")
- .arg("-p")
- .arg(pid.to_string())
- .output()?;
-
- let output = String::from_utf8_lossy(&ps_check.stdout).trim().to_string();
- if output.is_empty() {
- Ok(())
- } else {
- Err(Error::new(ErrorKind::Other, "Process still running after kill attempt"))
- }
-}
-
-#[cfg(windows)]
-pub fn ensure_process_killed(pid: u32, expected_name: &str) -> Result<(), Error> {
- //
- // Check if PID exists and name matches
- //
- let tasklist_output = Command::new("tasklist")
- .arg("/FI")
- .arg(format!("PID eq {}", pid))
- .arg("/FO")
- .arg("CSV")
- .arg("/NH")
- .output()?;
-
- let output = String::from_utf8_lossy(&tasklist_output.stdout).trim().to_string();
-
- if output.is_empty() || !output.starts_with('"') {
- println!("cargo:warning= Pid file was found, but process was not.");
- return Ok(())
- }
-
- let name = output.split(',').next().unwrap_or("").trim_matches('"');
- if name != expected_name {
- return Err(Error::new(ErrorKind::InvalidInput, format!("Process name does not match. Expected:{}, got:{}",expected_name,name)));
- }
-
- //
- // Kill the process
- //
- let kill_output = Command::new("taskkill")
- .arg("/PID")
- .arg(pid.to_string())
- .arg("/F")
- .arg("/T")
- .output()?;
-
- if !kill_output.status.success() {
- return Err(Error::new(ErrorKind::Other, "Failed to kill process"));
- }
-
- //
- // Verify process is killed
- //
- let tasklist_check = Command::new("tasklist")
- .arg("/FI")
- .arg(format!("PID eq {}", pid))
- .output()?;
-
- let output = String::from_utf8_lossy(&tasklist_check.stdout).trim().to_string();
- if output.is_empty() || !output.starts_with('"') {
- Ok(())
- }
- else {
- Err(Error::new(ErrorKind::Other, "Process still running after kill attempt"))
- }
-}
-
-
-pub fn kill_zombie_qdrant_process() -> Result<(), Error> {
- let pid_file = dirs::data_local_dir()
- .expect("Local appdata was not found")
- .join("com.github.mindwork-ai.ai-studio")
- .join("data")
- .join("databases")
- .join("qdrant")
- .join("qdrant.pid");
-
- if !pid_file.exists() {
- return Ok(());
- }
-
- let pid_str = fs::read_to_string(&pid_file)?;
- let pid: u32 = pid_str.trim().parse().map_err(|_| {Error::new(ErrorKind::InvalidData, "Invalid PID in file")})?;
- if let Err(e) = ensure_process_killed(pid, "qdrant.exe".as_ref()){
- return Err(e);
- }
-
- fs::remove_file(&pid_file)?;
- println!("cargo:warning= Killed qdrant process and deleted redundant Pid file: {}", pid_file.display());
-
- Ok(())
-}
-
-pub fn delete_old_certificates() -> Result<(), Box> {
- let dir_path = dirs::data_local_dir()
- .expect("Local appdata was not found")
- .join("com.github.mindwork-ai.ai-studio")
- .join("data")
- .join("databases")
- .join("qdrant");
-
- if !dir_path.exists() {
- return Ok(());
- }
-
- for entry in fs::read_dir(dir_path)? {
- let entry = entry?;
- let path = entry.path();
-
- if path.is_dir() {
- let file_name = entry.file_name();
- let folder_name = file_name.to_string_lossy();
-
- if folder_name.starts_with("cert-") {
- fs::remove_dir_all(&path)?;
- println!("cargo: warning= Removed old certificates in: {}", path.display());
- }
- }
- }
- Ok(())
-}
+}
\ No newline at end of file
diff --git a/runtime/src/app_window.rs b/runtime/src/app_window.rs
index df48fc2b..c7e7bd74 100644
--- a/runtime/src/app_window.rs
+++ b/runtime/src/app_window.rs
@@ -15,11 +15,13 @@ use tauri::api::dialog::blocking::FileDialogBuilder;
use tokio::sync::broadcast;
use tokio::time;
use crate::api_token::APIToken;
-use crate::dotnet::{cleanup_dotnet_server, stop_dotnet_server};
+use crate::dotnet::{cleanup_dotnet_server, start_dotnet_server, stop_dotnet_server};
use crate::environment::{is_prod, is_dev, CONFIG_DIRECTORY, DATA_DIRECTORY};
use crate::log::switch_to_file_logging;
use crate::pdfium::PDFIUM_LIB_PATH;
use crate::qdrant::{cleanup_qdrant, start_qdrant_server, stop_qdrant_server};
+#[cfg(debug_assertions)]
+use crate::dotnet::create_startup_env_file;
/// The Tauri main window.
static MAIN_WINDOW: Lazy>> = Lazy::new(|| Mutex::new(None));
@@ -102,20 +104,24 @@ pub fn start_tauri() {
let data_path = data_path.join("data");
// Get and store the data and config directories:
- DATA_DIRECTORY.set(data_path.to_str().unwrap().to_string()).map_err(|_| error!("Was not abe to set the data directory.")).unwrap();
+ DATA_DIRECTORY.set(data_path.to_str().unwrap().to_string()).map_err(|_| error!("Was not able to set the data directory.")).unwrap();
CONFIG_DIRECTORY.set(app.path_resolver().app_config_dir().unwrap().to_str().unwrap().to_string()).map_err(|_| error!("Was not able to set the config directory.")).unwrap();
- if is_prod() {
- cleanup_qdrant().expect("Zombie processes of Qdrant were not killed");
- cleanup_dotnet_server();
+ cleanup_qdrant();
+ cleanup_dotnet_server();
+
+ if is_dev() {
+ #[cfg(debug_assertions)]
+ create_startup_env_file();
+ } else {
+ start_dotnet_server();
}
+ start_qdrant_server();
info!(Source = "Bootloader Tauri"; "Reconfigure the file logger to use the app data directory {data_path:?}");
switch_to_file_logging(data_path).map_err(|e| error!("Failed to switch logging to file: {e}")).unwrap();
set_pdfium_path(app.path_resolver());
- start_qdrant_server();
-
Ok(())
})
.plugin(tauri_plugin_window_state::Builder::default().build())
@@ -164,6 +170,7 @@ pub fn start_tauri() {
if is_prod() {
stop_dotnet_server();
+ stop_qdrant_server();
} else {
warn!(Source = "Tauri"; "Development environment detected; do not stop the .NET server.");
}
@@ -193,6 +200,10 @@ pub fn start_tauri() {
RunEvent::ExitRequested { .. } => {
warn!(Source = "Tauri"; "Run event: exit was requested.");
stop_qdrant_server();
+ if is_prod() {
+ warn!("Try to stop the .NET server as well...");
+ stop_dotnet_server();
+ }
}
RunEvent::Ready => {
@@ -204,10 +215,6 @@ pub fn start_tauri() {
});
warn!(Source = "Tauri"; "Tauri app was stopped.");
- if is_prod() {
- warn!("Try to stop the .NET server as well...");
- stop_dotnet_server();
- }
}
/// Our event API endpoint for Tauri events. We try to send an endless stream of events to the client.
@@ -458,7 +465,6 @@ pub async fn install_update(_token: APIToken) {
let cloned_response_option = CHECK_UPDATE_RESPONSE.lock().unwrap().clone();
match cloned_response_option {
Some(update_response) => {
- stop_qdrant_server();
update_response.download_and_install().await.unwrap();
},
diff --git a/runtime/src/dotnet.rs b/runtime/src/dotnet.rs
index 944e1d95..c1e0b560 100644
--- a/runtime/src/dotnet.rs
+++ b/runtime/src/dotnet.rs
@@ -16,7 +16,7 @@ use crate::encryption::ENCRYPTION;
use crate::environment::{is_dev, DATA_DIRECTORY};
use crate::network::get_available_port;
use crate::runtime_api::API_SERVER_PORT;
-use crate::zombie_process_remover::{kill_zombie_process, log_potential_zombie_process};
+use crate::stale_process_cleanup::{kill_stale_process, log_potential_stale_process};
// The .NET server is started in a separate process and communicates with this
// runtime process via IPC. However, we do net start the .NET server in
@@ -100,7 +100,7 @@ pub fn start_dotnet_server() {
.expect("Failed to spawn .NET server process.");
let server_pid = child.pid();
info!(Source = "Bootloader .NET"; "The .NET server process started with PID={server_pid}.");
- log_potential_zombie_process(Path::new(DATA_DIRECTORY.get().unwrap()).join(PID_FILE_NAME), server_pid.to_string().as_str());
+ log_potential_stale_process(Path::new(DATA_DIRECTORY.get().unwrap()).join(PID_FILE_NAME), server_pid);
// Save the server process to stop it later:
*server_spawn_clone.lock().unwrap() = Some(child);
@@ -158,13 +158,14 @@ pub fn stop_dotnet_server() {
} else {
warn!("The .NET server process was not started or is already stopped.");
}
+ info!("Start dotnet server cleanup");
cleanup_dotnet_server();
}
/// Remove old Pid files and kill the corresponding processes
pub fn cleanup_dotnet_server() {
let pid_path = Path::new(DATA_DIRECTORY.get().unwrap()).join(PID_FILE_NAME);
- if let Err(e) = kill_zombie_process(pid_path, "mindworkAIStudioServer.exe"){
+ if let Err(e) = kill_stale_process(pid_path) {
warn!(Source = ".NET"; "Error during the cleanup of .NET: {}", e);
}
}
\ No newline at end of file
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
index 65b6abdf..5447395b 100644
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -16,4 +16,4 @@ pub mod pandoc;
pub mod qdrant;
pub mod certificate_factory;
pub mod runtime_api_token;
-pub mod zombie_process_remover;
\ No newline at end of file
+pub mod stale_process_cleanup;
\ No newline at end of file
diff --git a/runtime/src/main.rs b/runtime/src/main.rs
index 91427472..00a7ba90 100644
--- a/runtime/src/main.rs
+++ b/runtime/src/main.rs
@@ -7,14 +7,11 @@ extern crate core;
use log::{info, warn};
use mindwork_ai_studio::app_window::start_tauri;
use mindwork_ai_studio::runtime_certificate::{generate_runtime_certificate};
-use mindwork_ai_studio::dotnet::start_dotnet_server;
use mindwork_ai_studio::environment::is_dev;
use mindwork_ai_studio::log::init_logging;
use mindwork_ai_studio::metadata::MetaData;
use mindwork_ai_studio::runtime_api::start_runtime_api;
-#[cfg(debug_assertions)]
-use mindwork_ai_studio::dotnet::create_startup_env_file;
#[tokio::main]
async fn main() {
@@ -49,12 +46,5 @@ async fn main() {
generate_runtime_certificate();
start_runtime_api();
- if is_dev() {
- #[cfg(debug_assertions)]
- create_startup_env_file();
- } else {
- start_dotnet_server();
- }
-
start_tauri();
}
\ No newline at end of file
diff --git a/runtime/src/qdrant.rs b/runtime/src/qdrant.rs
index 523a3c2a..c562dc75 100644
--- a/runtime/src/qdrant.rs
+++ b/runtime/src/qdrant.rs
@@ -16,7 +16,7 @@ use crate::environment::DATA_DIRECTORY;
use crate::certificate_factory::generate_certificate;
use std::path::PathBuf;
use tempfile::{TempDir, Builder};
-use crate::zombie_process_remover::{kill_zombie_process, log_potential_zombie_process};
+use crate::stale_process_cleanup::{kill_stale_process, log_potential_stale_process};
// Qdrant server process started in a separate process and can communicate
// via HTTP or gRPC with the .NET server and the runtime process
@@ -106,7 +106,7 @@ pub fn start_qdrant_server(){
let server_pid = child.pid();
info!(Source = "Bootloader Qdrant"; "Qdrant server process started with PID={server_pid}.");
- log_potential_zombie_process(path.join(PID_FILE_NAME), server_pid.to_string().as_str());
+ log_potential_stale_process(path.join(PID_FILE_NAME), server_pid);
// Save the server process to stop it later:
*server_spawn_clone.lock().unwrap() = Some(child);
@@ -150,9 +150,7 @@ pub fn stop_qdrant_server() {
}
drop_tmpdir();
- if let Err(e) = cleanup_qdrant(){
- warn!(Source = "Qdrant"; "Error during the cleanup of Qdrant: {}", e);
- }
+ cleanup_qdrant();
}
/// Create temporary directory with TLS relevant files
@@ -193,11 +191,15 @@ pub fn drop_tmpdir() {
}
/// Remove old Pid files and kill the corresponding processes
-pub fn cleanup_qdrant() -> Result<(), Box> {
+pub fn cleanup_qdrant() {
let pid_path = Path::new(DATA_DIRECTORY.get().unwrap()).join("databases").join("qdrant").join(PID_FILE_NAME);
- kill_zombie_process(pid_path, "qdrant.exe")?;
- delete_old_certificates()?;
- Ok(())
+ if let Err(e) = kill_stale_process(pid_path) {
+ warn!(Source = "Qdrant"; "Error during the cleanup of Qdrant: {}", e);
+ }
+ if let Err(e) = delete_old_certificates() {
+ warn!(Source = "Qdrant"; "Error during the cleanup of Qdrant: {}", e);
+ }
+
}
pub fn delete_old_certificates() -> Result<(), Box> {
diff --git a/runtime/src/stale_process_cleanup.rs b/runtime/src/stale_process_cleanup.rs
new file mode 100644
index 00000000..124f576c
--- /dev/null
+++ b/runtime/src/stale_process_cleanup.rs
@@ -0,0 +1,94 @@
+use std::fs;
+use std::fs::File;
+use std::io::{Error, ErrorKind, Write};
+use std::path::{PathBuf};
+use log::{info, warn};
+use sysinfo::{Pid, ProcessesToUpdate, Signal, System};
+
+fn parse_pid_file(content: &str) -> Result<(u32, String), Error> {
+ let mut lines = content
+ .lines()
+ .map(|line| line.trim())
+ .filter(|line| !line.is_empty());
+ let pid_str = lines
+ .next()
+ .ok_or_else(|| Error::new(ErrorKind::InvalidData, "Missing PID in file"))?;
+ let pid: u32 = pid_str
+ .parse()
+ .map_err(|_| Error::new(ErrorKind::InvalidData, "Invalid PID in file"))?;
+ let name = lines
+ .next()
+ .ok_or_else(|| Error::new(ErrorKind::InvalidData, "Missing process name in file"))?
+ .to_string();
+ Ok((pid, name))
+}
+
+pub fn kill_stale_process(pid_file_path: PathBuf) -> Result<(), Error> {
+ if !pid_file_path.exists() {
+ return Ok(());
+ }
+
+ let pid_file_content = fs::read_to_string(&pid_file_path)?;
+ let (pid, expected_name) = parse_pid_file(&pid_file_content)?;
+
+ let mut system = System::new_all();
+
+ let pid = Pid::from_u32(pid);
+ system.refresh_processes(ProcessesToUpdate::Some(&[pid]), true);
+ if let Some(process) = system.process(pid){
+ let name = process.name().to_string_lossy();
+ if name != expected_name {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ format!(
+ "Process name does not match: expected '{}' but found '{}'",
+ expected_name, name
+ ),
+ ));
+ }
+
+ let killed = process.kill_with(Signal::Kill).unwrap_or_else(|| process.kill());
+ if !killed {
+ return Err(Error::new(ErrorKind::Other, "Failed to kill process"));
+ }
+
+ system.refresh_processes(ProcessesToUpdate::Some(&[pid]), true);
+ if !system.process(pid).is_none() {
+ return Err(Error::new(ErrorKind::Other, "Process still running after kill attempt"))
+ }
+ info!("Killed process: {}", pid_file_path.display());
+ } else {
+ info!("Pid file {} was found, but process was not.", pid);
+ };
+
+ fs::remove_file(&pid_file_path)?;
+ info!("Deleted redundant Pid file: {}", pid_file_path.display());
+ Ok(())
+}
+
+pub fn log_potential_stale_process(pid_file_path: PathBuf, pid: u32) {
+ let mut system = System::new_all();
+ let pid_u32 = pid;
+ let pid = Pid::from_u32(pid_u32);
+ system.refresh_processes(ProcessesToUpdate::Some(&[pid]), true);
+ let Some(process) = system.process(pid) else {
+ warn!(
+ "Pid file {} was not created because the process was not found.",
+ pid_u32
+ );
+ return;
+ };
+
+ match File::create(&pid_file_path) {
+ Ok(mut file) => {
+ let name = process.name().to_string_lossy();
+ let content = format!("{pid_u32}\n{name}\n");
+ if let Err(e) = file.write_all(content.as_bytes()) {
+ warn!("Failed to write to {}: {}", pid_file_path.display(), e);
+ }
+ }
+ Err(e) => {
+ warn!("Failed to create {}: {}", pid_file_path.display(), e);
+ }
+ }
+}
diff --git a/runtime/src/zombie_process_remover.rs b/runtime/src/zombie_process_remover.rs
deleted file mode 100644
index 33d2ba74..00000000
--- a/runtime/src/zombie_process_remover.rs
+++ /dev/null
@@ -1,145 +0,0 @@
-use std::fs;
-use std::fs::File;
-use std::io::{Error, ErrorKind, Write};
-use std::path::PathBuf;
-use std::process::Command;
-use log::{info, warn};
-
-#[cfg(unix)]
-pub fn ensure_process_killed(pid: u32, expected_name: &str) -> Result<(), Error> {
- //
- // Check if PID exists and name matches
- //
- let ps_output = Command::new("ps")
- .arg("-p")
- .arg(pid.to_string())
- .arg("-o")
- .arg("comm=")
- .output()?;
-
- let output = String::from_utf8_lossy(&ps_output.stdout).trim().to_string();
-
- if output.is_empty() {
- info!("Pid file {} was found, but process was not.", pid);
- return Ok(());
- }
-
- let name = output;
- if name != expected_name {
- return Err(Error::new(ErrorKind::InvalidInput, "Process name does not match"));
- }
-
- //
- // Kill the process
- //
- let kill_output = Command::new("kill")
- .arg("-9")
- .arg(pid.to_string())
- .output()?;
-
- if !kill_output.status.success() {
- return Err(Error::new(ErrorKind::Other, "Failed to kill process"));
- }
-
- //
- // Verify process is killed
- //
- let ps_check = Command::new("ps")
- .arg("-p")
- .arg(pid.to_string())
- .output()?;
-
- let output = String::from_utf8_lossy(&ps_check.stdout).trim().to_string();
- if output.is_empty() {
- Ok(())
- } else {
- Err(Error::new(ErrorKind::Other, "Process still running after kill attempt"))
- }
-}
-
-#[cfg(windows)]
-pub fn ensure_process_killed(pid: u32, expected_name: &str) -> Result<(), Error> {
- //
- // Check if PID exists and name matches
- //
- let tasklist_output = Command::new("tasklist")
- .arg("/FI")
- .arg(format!("PID eq {}", pid))
- .arg("/FO")
- .arg("CSV")
- .arg("/NH")
- .output()?;
-
- let output = String::from_utf8_lossy(&tasklist_output.stdout).trim().to_string();
-
- if output.is_empty() || !output.starts_with('"') {
- info!("Pid file {} was found, but process was not.", pid);
- return Ok(())
- }
-
- let name = output.split(',').next().unwrap_or("").trim_matches('"');
- if name != expected_name {
- return Err(Error::new(ErrorKind::InvalidInput, format!("Process name does not match. Expected:{}, got:{}",expected_name,name)));
- }
-
- //
- // Kill the process
- //
- let kill_output = Command::new("taskkill")
- .arg("/PID")
- .arg(pid.to_string())
- .arg("/F")
- .arg("/T")
- .output()?;
-
- if !kill_output.status.success() {
- return Err(Error::new(ErrorKind::Other, "Failed to kill process"));
- }
-
- //
- // Verify process is killed
- //
- let tasklist_check = Command::new("tasklist")
- .arg("/FI")
- .arg(format!("PID eq {}", pid))
- .output()?;
-
- let output = String::from_utf8_lossy(&tasklist_check.stdout).trim().to_string();
- if output.is_empty() || !output.starts_with('"') {
- Ok(())
- }
- else {
- Err(Error::new(ErrorKind::Other, "Process still running after kill attempt"))
- }
-}
-
-
-pub fn kill_zombie_process(pid_file_path: PathBuf, process_name: &str) -> Result<(), Error> {
- if !pid_file_path.exists() {
- return Ok(());
- }
-
- let pid_str = fs::read_to_string(&pid_file_path)?;
- let pid: u32 = pid_str.trim().parse().map_err(|_| {Error::new(ErrorKind::InvalidData, "Invalid PID in file")})?;
- if let Err(e) = ensure_process_killed(pid, process_name){
- return Err(e);
- }
-
- fs::remove_file(&pid_file_path)?;
- info!("Killed qdrant process and deleted redundant Pid file: {}", pid_file_path.display());
-
- Ok(())
-}
-
-pub fn log_potential_zombie_process(pid_file_path: PathBuf, content: &str) {
- match File::create(&pid_file_path) {
- Ok(mut file) => {
- if let Err(e) = file.write_all(content.as_bytes()) {
- warn!("Failed to write to {}: {}", pid_file_path.display(), e);
- }
- }
- Err(e) => {
- warn!("Failed to create {}: {}", pid_file_path.display(), e);
- }
- }
-}
\ No newline at end of file