use crate::errors::OpenVTCError;
use std::{
fs::{self, OpenOptions},
io::Write,
path::{Path, PathBuf},
process,
str::FromStr,
};
use sysinfo::{Pid, ProcessRefreshKind, RefreshKind, System};
pub fn check_duplicate_instance(profile: &str) -> Result<PathBuf, OpenVTCError> {
let lock_file = get_lock_file(profile)?;
if let Some(parent) = lock_file.parent()
&& !parent.exists()
{
fs::create_dir_all(parent)
.map_err(|e| OpenVTCError::LockFile(format!("couldn't create lock directory: {e}")))?;
}
match OpenOptions::new()
.write(true)
.create_new(true)
.open(&lock_file)
{
Ok(mut file) => {
file.write_all(process::id().to_string().as_bytes())
.map_err(|e| {
OpenVTCError::LockFile(format!("couldn't write PID to lock file: {e}"))
})?;
return Ok(lock_file);
}
Err(ref e) if e.kind() == std::io::ErrorKind::AlreadyExists => {
}
Err(e) => {
return Err(OpenVTCError::LockFile(format!(
"couldn't create lock file: {e}"
)));
}
}
let pid_str = fs::read_to_string(&lock_file)
.map_err(|e| OpenVTCError::LockFile(format!("couldn't read lock file: {e}")))?;
let pid_str = pid_str.trim_end();
let system = System::new_with_specifics(
RefreshKind::nothing().with_processes(ProcessRefreshKind::nothing()),
);
let pid = Pid::from_str(pid_str)
.map_err(|e| OpenVTCError::LockFile(format!("invalid PID in lock file: {e}")))?;
if system.process(pid).is_some() {
return Err(OpenVTCError::DuplicateInstance(profile.to_string()));
}
create_lock_file(&lock_file)?;
Ok(lock_file)
}
pub(crate) fn get_lock_file(profile: &str) -> Result<PathBuf, OpenVTCError> {
let mut path = if let Ok(config_path) = std::env::var("OPENVTC_CONFIG_PATH") {
PathBuf::from(config_path)
} else {
#[cfg(windows)]
{
dirs::config_dir()
.map(|p| p.join("openvtc"))
.ok_or_else(|| {
OpenVTCError::LockFile("couldn't determine configuration directory".to_string())
})?
}
#[cfg(not(windows))]
{
dirs::home_dir()
.map(|p| p.join(".config").join("openvtc"))
.ok_or_else(|| {
OpenVTCError::LockFile("couldn't determine home directory".to_string())
})?
}
};
if profile == "default" {
path.push("config.lock");
} else {
path.push(format!("config-{profile}.lock"));
}
Ok(path)
}
pub(crate) fn create_lock_file<P: AsRef<Path>>(lock_file: P) -> Result<(), OpenVTCError> {
let lock_file = lock_file.as_ref();
if let Some(parent) = lock_file.parent()
&& !parent.exists()
{
fs::create_dir_all(parent)
.map_err(|e| OpenVTCError::LockFile(format!("couldn't create lock directory: {e}")))?;
}
fs::write(lock_file, process::id().to_string()).map_err(|e| {
OpenVTCError::LockFile(format!(
"couldn't write lock file '{}': {e}",
lock_file.to_string_lossy()
))
})?;
Ok(())
}
pub fn remove_lock_file<P: AsRef<Path>>(lock_file: P) {
let _ = fs::remove_file(lock_file);
}