use std::path::Path;
pub fn bind(path: &Path) -> std::io::Result<tokio::net::UnixListener> {
use std::os::unix::fs::FileTypeExt;
match std::fs::metadata(path) {
Err(_) => {}
Ok(meta) if meta.file_type().is_socket() => {
if std::os::unix::net::UnixStream::connect(path).is_ok() {
return Err(std::io::Error::new(
std::io::ErrorKind::AddrInUse,
"a server is already listening on it",
));
}
std::fs::remove_file(path)?;
}
Ok(_) => {
return Err(std::io::Error::new(
std::io::ErrorKind::AlreadyExists,
"the path already exists and is not a socket",
))
}
}
tokio::net::UnixListener::bind(path)
}
pub fn cleanup(path: &Path) {
let _ = std::fs::remove_file(path);
}
pub fn peer_addr(path: &Path) -> String {
format!("{}:0", path.display())
}
#[cfg(test)]
mod tests {
use super::*;
struct TempDir(std::path::PathBuf);
impl TempDir {
fn new(tag: &str) -> TempDir {
let dir = std::env::temp_dir().join(format!(
"meebis-unixsocket-{}-{tag}-{}",
std::process::id(),
crate::db::now_ms()
));
std::fs::create_dir_all(&dir).unwrap();
TempDir(dir)
}
fn join(&self, name: &str) -> std::path::PathBuf {
self.0.join(name)
}
}
impl Drop for TempDir {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.0);
}
}
#[tokio::test]
async fn binds_a_fresh_path() {
let dir = TempDir::new("fresh");
let path = dir.join("redis.sock");
let listener = bind(&path).expect("a fresh path should bind");
assert!(path.exists(), "the socket should exist once bound");
drop(listener);
cleanup(&path);
assert!(!path.exists(), "cleanup should remove the socket");
}
#[tokio::test]
async fn a_stale_socket_is_replaced() {
let dir = TempDir::new("stale");
let path = dir.join("redis.sock");
let listener = std::os::unix::net::UnixListener::bind(&path).unwrap();
drop(listener);
assert!(path.exists(), "the stale file should still be there");
bind(&path).expect("a stale socket should be cleared, not fatal");
}
#[test]
fn a_live_socket_is_refused() {
let dir = TempDir::new("live");
let path = dir.join("redis.sock");
let _held = std::os::unix::net::UnixListener::bind(&path).unwrap();
let err = bind(&path).expect_err("a live socket must not be stolen");
assert_eq!(err.kind(), std::io::ErrorKind::AddrInUse);
assert!(path.exists(), "the live socket must survive the attempt");
}
#[test]
fn a_regular_file_is_never_deleted() {
let dir = TempDir::new("file");
let path = dir.join("not-a-socket");
std::fs::write(&path, b"precious").unwrap();
let err = bind(&path).expect_err("a regular file is not ours to remove");
assert_eq!(err.kind(), std::io::ErrorKind::AlreadyExists);
assert_eq!(std::fs::read(&path).unwrap(), b"precious");
}
#[test]
fn peer_addr_matches_redis_spelling() {
assert_eq!(peer_addr(Path::new("/tmp/redis.sock")), "/tmp/redis.sock:0");
}
}