use std::io::ErrorKind;
use std::path::Path;
use anyhow::{bail, Context, Result};
use tokio::net::UnixListener;
use super::client::DaemonClient;
use super::paths;
pub async fn bind_or_reclaim(path: &Path) -> Result<UnixListener> {
match bind_private(path) {
Ok(listener) => {
paths::set_file_0600(path)?;
Ok(listener)
}
Err(e) if e.kind() == ErrorKind::AddrInUse => {
if DaemonClient::new(path).ping().await.is_ok() {
bail!(
"a daemon is already running (socket {}); use `omni-dev daemon status`",
path.display()
);
}
tracing::warn!(
"reclaiming stale daemon socket at {} (no live daemon answered)",
path.display()
);
std::fs::remove_file(path)
.with_context(|| format!("failed to remove stale socket {}", path.display()))?;
let listener = bind_private(path)
.with_context(|| format!("failed to bind daemon socket {}", path.display()))?;
paths::set_file_0600(path)?;
Ok(listener)
}
Err(e) => {
Err(e).with_context(|| format!("failed to bind daemon socket {}", path.display()))
}
}
}
#[doc(hidden)]
pub fn bind_private(path: &Path) -> std::io::Result<UnixListener> {
#[cfg(unix)]
let _umask = UmaskGuard::set(nix::sys::stat::Mode::from_bits_truncate(0o177));
UnixListener::bind(path)
}
#[cfg(unix)]
struct UmaskGuard(nix::sys::stat::Mode);
#[cfg(unix)]
impl UmaskGuard {
fn set(mask: nix::sys::stat::Mode) -> Self {
Self(nix::sys::stat::umask(mask))
}
}
#[cfg(unix)]
impl Drop for UmaskGuard {
fn drop(&mut self) {
nix::sys::stat::umask(self.0);
}
}