diff --git a/crates/owlry/src/main.rs b/crates/owlry/src/main.rs index 83ac9b2..90a37aa 100644 --- a/crates/owlry/src/main.rs +++ b/crates/owlry/src/main.rs @@ -10,10 +10,43 @@ mod ui; use app::OwlryApp; use cli::{CliArgs, Command}; use log::{info, warn}; +use std::os::unix::io::AsRawFd; #[cfg(feature = "dev-logging")] use log::debug; +/// Try to acquire an exclusive lock on the UI lock file. +/// +/// Returns `Some(File)` if the lock was acquired (no other instance running), +/// or `None` if another instance already holds the lock. +/// The returned `File` must be kept alive for the duration of the process. +fn try_acquire_lock() -> Option { + use std::os::unix::fs::OpenOptionsExt; + + let lock_path = owlry_core::paths::socket_path() + .parent() + .unwrap() + .join("owlry-ui.lock"); + + // Ensure the parent directory exists + if let Some(parent) = lock_path.parent() { + let _ = std::fs::create_dir_all(parent); + } + + std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .mode(0o600) + .open(&lock_path) + .ok() + .and_then(|f| { + let fd = f.as_raw_fd(); + let ret = unsafe { libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) }; + if ret == 0 { Some(f) } else { None } + }) +} + fn main() { let args = CliArgs::parse_args(); @@ -46,6 +79,27 @@ fn main() { debug!("CLI args: {:?}", args); } + // Toggle behavior: if another instance is already running, tell the daemon + // to toggle visibility and exit immediately. + let _lock_guard = match try_acquire_lock() { + Some(file) => file, + None => { + // Another instance holds the lock — send toggle to daemon and exit + info!("Another owlry instance detected, sending toggle"); + let socket_path = client::CoreClient::socket_path(); + if let Ok(mut client) = client::CoreClient::connect(&socket_path) { + if let Err(e) = client.toggle() { + eprintln!("Failed to toggle existing instance: {}", e); + std::process::exit(1); + } + } else { + eprintln!("Another instance is running but daemon is unreachable"); + std::process::exit(1); + } + std::process::exit(0); + } + }; + info!("Starting Owlry launcher"); // Diagnostic: log critical environment variables