diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-03 17:11:20 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-03 17:11:20 +0300 |
| commit | 5ed262a43ac7f264a1a7523b6d4507ba4a976604 (patch) | |
| tree | 8de14a54cf67bb8ee656c3536035f68c7703a01a /src | |
| parent | f159ef69ffe59fd327a4a4f3c4a53aaea567b32a (diff) | |
fix: isolate debug runtime paths and block duplicate herdr instances
Diffstat (limited to 'src')
| -rw-r--r-- | src/api/mod.rs | 42 | ||||
| -rw-r--r-- | src/config.rs | 26 | ||||
| -rw-r--r-- | src/main.rs | 23 | ||||
| -rw-r--r-- | src/persist.rs | 8 |
4 files changed, 66 insertions, 33 deletions
diff --git a/src/api/mod.rs b/src/api/mod.rs index 2e7692a4..1a713323 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -71,19 +71,17 @@ pub fn socket_path() -> PathBuf { return PathBuf::from(path); } - if let Ok(dir) = std::env::var("XDG_RUNTIME_DIR") { - return PathBuf::from(dir).join("herdr.sock"); - } - - if let Ok(dir) = std::env::var("XDG_CONFIG_HOME") { - return PathBuf::from(dir).join("herdr/herdr.sock"); - } + let socket_name = if crate::config::app_dir_name() == "herdr" { + "herdr.sock" + } else { + "herdr-dev.sock" + }; - if let Ok(home) = std::env::var("HOME") { - return PathBuf::from(home).join(".config/herdr/herdr.sock"); + if let Ok(dir) = std::env::var("XDG_RUNTIME_DIR") { + return PathBuf::from(dir).join(socket_name); } - PathBuf::from("/tmp/herdr.sock") + crate::config::config_dir().join("herdr.sock") } pub struct ServerHandle { @@ -143,6 +141,30 @@ fn prepare_socket_path(path: &Path) -> std::io::Result<()> { fs::create_dir_all(parent)?; } + if !path.exists() { + return Ok(()); + } + + match UnixStream::connect(path) { + Ok(_) => { + return Err(std::io::Error::new( + std::io::ErrorKind::AddrInUse, + format!( + "herdr is already running (socket busy at {})", + path.display() + ), + )); + } + Err(err) + if matches!( + err.kind(), + std::io::ErrorKind::ConnectionRefused + | std::io::ErrorKind::NotFound + | std::io::ErrorKind::TimedOut + ) => {} + Err(err) => return Err(err), + } + if let Err(err) = fs::remove_file(path) { if err.kind() != std::io::ErrorKind::NotFound { return Err(err); diff --git a/src/config.rs b/src/config.rs index c901c2d6..d5417421 100644 --- a/src/config.rs +++ b/src/config.rs @@ -6,6 +6,24 @@ use serde::Deserialize; pub const CONFIG_PATH_ENV_VAR: &str = "HERDR_CONFIG_PATH"; use tracing::warn; +pub fn app_dir_name() -> &'static str { + if cfg!(debug_assertions) { + "herdr-dev" + } else { + "herdr" + } +} + +pub fn config_dir() -> PathBuf { + if let Ok(dir) = std::env::var("XDG_CONFIG_HOME") { + PathBuf::from(dir).join(app_dir_name()) + } else if let Ok(home) = std::env::var("HOME") { + PathBuf::from(home).join(format!(".config/{}", app_dir_name())) + } else { + PathBuf::from(format!("/tmp/{}", app_dir_name())) + } +} + use crate::detect::Agent; #[derive(Debug, Clone, Deserialize)] @@ -705,13 +723,7 @@ pub fn config_path() -> PathBuf { if let Ok(path) = std::env::var(CONFIG_PATH_ENV_VAR) { return PathBuf::from(path); } - if let Ok(dir) = std::env::var("XDG_CONFIG_HOME") { - PathBuf::from(dir).join("herdr/config.toml") - } else if let Ok(home) = std::env::var("HOME") { - PathBuf::from(home).join(".config/herdr/config.toml") - } else { - PathBuf::from("/tmp/herdr/config.toml") - } + config_dir().join("config.toml") } fn upsert_top_level_bool(content: &str, key: &str, value: bool) -> String { diff --git a/src/main.rs b/src/main.rs index 385f6f5f..ee72e49b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,13 +43,7 @@ fn init_logging() { use std::fs::{self, OpenOptions}; use tracing_subscriber::EnvFilter; - let log_dir = if let Ok(dir) = std::env::var("XDG_CONFIG_HOME") { - std::path::PathBuf::from(dir).join("herdr") - } else if let Ok(home) = std::env::var("HOME") { - std::path::PathBuf::from(home).join(".config/herdr") - } else { - std::path::PathBuf::from("/tmp/herdr") - }; + let log_dir = crate::config::config_dir(); let _ = fs::create_dir_all(&log_dir); let log_path = log_dir.join("herdr.log"); @@ -218,7 +212,10 @@ fn main() -> io::Result<()> { println!(" --help, -h Show this help"); println!(); println!("Config: {}", config::config_path().display()); - println!("Logs: ~/.config/herdr/herdr.log"); + println!( + "Logs: {}", + config::config_dir().join("herdr.log").display() + ); println!("Env: HERDR_CONFIG_PATH overrides config file path"); println!("Home: https://herdr.dev"); return Ok(()); @@ -272,7 +269,15 @@ fn main() -> io::Result<()> { let (api_tx, api_rx) = tokio::sync::mpsc::unbounded_channel(); let event_hub = api::EventHub::default(); - let _api_server = api::start_server(api_tx, event_hub.clone())?; + let _api_server = match api::start_server(api_tx, event_hub.clone()) { + Ok(server) => server, + Err(err) if err.kind() == io::ErrorKind::AddrInUse => { + eprintln!("error: herdr is already running"); + eprintln!("socket: {}", api::socket_path().display()); + std::process::exit(1); + } + Err(err) => return Err(err), + }; let no_session = std::env::args().any(|a| a == "--no-session"); let show_changelog = std::env::args().any(|a| a == "--show-changelog"); diff --git a/src/persist.rs b/src/persist.rs index c296b0ac..fb8ab2d8 100644 --- a/src/persist.rs +++ b/src/persist.rs @@ -467,13 +467,7 @@ fn collect_ids_inner(node: &Node, ids: &mut Vec<PaneId>) { // --- File I/O --- fn session_path() -> PathBuf { - if let Ok(dir) = std::env::var("XDG_CONFIG_HOME") { - PathBuf::from(dir).join("herdr/session.json") - } else if let Ok(home) = std::env::var("HOME") { - PathBuf::from(home).join(".config/herdr/session.json") - } else { - PathBuf::from("/tmp/herdr/session.json") - } + crate::config::config_dir().join("session.json") } pub fn save(snapshot: &SessionSnapshot) { |
