use std::path::PathBuf;
use anyhow::Context;
use mcvm_core::io::{json_from_file, json_to_file_pretty};
use serde::{Deserialize, Serialize};
use sysinfo::{Pid, System};
use crate::io::paths::Paths;
pub struct RunningInstanceRegistry {
data: RunningInstanceRegistryDeser,
is_dirty: bool,
system: System,
path: PathBuf,
}
impl RunningInstanceRegistry {
fn get_path(paths: &Paths) -> PathBuf {
paths.internal.join("running_instances.json")
}
pub fn open(paths: &Paths) -> anyhow::Result<Self> {
let path = Self::get_path(paths);
let data = if path.exists() {
json_from_file(&path).context("Failed to open registry file")?
} else {
RunningInstanceRegistryDeser::default()
};
let system = System::new_all();
let mut out = Self {
data,
is_dirty: false,
system,
path,
};
out.remove_dead_instances();
Ok(out)
}
pub fn write(&mut self) -> anyhow::Result<()> {
if !self.is_dirty {
return Ok(());
}
json_to_file_pretty(&self.path, &self.data).context("Failed to write to registry file")?;
self.is_dirty = false;
Ok(())
}
pub fn remove_dead_instances(&mut self) {
let original_lenth = self.data.instances.len();
self.data
.instances
.retain(|x| is_process_alive(x.pid, &self.system).unwrap_or(true));
if original_lenth != self.data.instances.len() {
self.is_dirty = true;
}
}
pub fn add_instance(&mut self, pid: u32, instance: &str) {
let entry = RunningInstanceEntry {
pid,
parent_pid: std::process::id(),
instance_id: instance.to_string(),
};
self.data.instances.push(entry);
self.is_dirty = true;
}
pub fn remove_instance(&mut self, pid: u32, instance: &str) {
let index = self
.data
.instances
.iter()
.position(|x| x.pid == pid && x.instance_id == instance);
if let Some(index) = index {
self.data.instances.remove(index);
}
self.is_dirty = true;
}
pub fn is_instance_alive(&self, entry: &RunningInstanceEntry) -> anyhow::Result<bool> {
is_process_alive(entry.pid, &self.system)
}
}
impl Drop for RunningInstanceRegistry {
fn drop(&mut self) {
let _ = self.write();
}
}
#[derive(Deserialize, Serialize, Default, Debug)]
struct RunningInstanceRegistryDeser {
instances: Vec<RunningInstanceEntry>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct RunningInstanceEntry {
pub pid: u32,
pub instance_id: String,
pub parent_pid: u32,
}
fn is_process_alive(pid: u32, system: &System) -> anyhow::Result<bool> {
let pid = Pid::from_u32(pid);
let process = system.process(pid);
let Some(process) = process else {
return Ok(false);
};
if !process.name().to_string_lossy().contains("java")
&& !process
.cmd()
.iter()
.any(|x| x.to_string_lossy().contains("java"))
{
return Ok(false);
}
Ok(true)
}