daemon: allow TCP sockets as well as unix

This commit is contained in:
Qyriad 2026-03-26 12:44:48 +01:00
parent d57f605d42
commit 7cfb07241c
4 changed files with 53 additions and 11 deletions

View file

@ -4,6 +4,7 @@
use std::{ use std::{
env, env,
net::SocketAddr,
sync::{Arc, LazyLock}, sync::{Arc, LazyLock},
}; };
@ -35,6 +36,12 @@ pub struct DaemonCmd {
#[arg(long)] #[arg(long)]
#[arg(conflicts_with = "stdin")] #[arg(conflicts_with = "stdin")]
pub socket: Option<PathBuf>, pub socket: Option<PathBuf>,
/// Use a TCP port instead of a Unix socket or stdin. e.g.: 0.0.0.0:42420
#[arg(long)]
#[arg(conflicts_with = "socket")]
#[arg(conflicts_with = "stdin")]
pub tcp: Option<SocketAddr>,
} }
#[derive(Debug, Clone, PartialEq, clap::Subcommand)] #[derive(Debug, Clone, PartialEq, clap::Subcommand)]

View file

@ -1,5 +1,7 @@
use std::{ use std::{
borrow::Cow,
env, io, env, io,
net::SocketAddr,
os::fd::{AsFd, BorrowedFd, IntoRawFd, OwnedFd, RawFd}, os::fd::{AsFd, BorrowedFd, IntoRawFd, OwnedFd, RawFd},
process::{Command, Stdio}, process::{Command, Stdio},
sync::{ sync::{
@ -11,7 +13,12 @@ use std::{
use iddqd::{BiHashMap, IdOrdMap}; use iddqd::{BiHashMap, IdOrdMap};
use mio::{Events, Interest, Poll, Token, event::Event, net::UnixListener, unix::SourceFd}; use mio::{
Events, Interest, Poll, Token,
event::Event,
net::{TcpListener, UnixListener},
unix::SourceFd,
};
use rustix::{ use rustix::{
buffer::spare_capacity, buffer::spare_capacity,
@ -56,14 +63,6 @@ pub static TMPDIR: LazyLock<&'static Path> = LazyLock::new(|| {
Box::leak(dir) Box::leak(dir)
}); });
pub static NIXOS_REBUILD: LazyLock<&'static Path> = LazyLock::new(|| {
which::which("nixos-rebuild")
.inspect_err(|e| error!("couldn't find `nixos-rebuild` in PATH: {e}"))
.map(PathBuf::into_boxed_path)
.map(|boxed| &*Box::leak(boxed))
.unwrap_or(Path::new("/run/current-system/sw/bin/nixos-rebuild"))
});
pub static NIX: LazyLock<&'static Path> = LazyLock::new(|| { pub static NIX: LazyLock<&'static Path> = LazyLock::new(|| {
which::which("nix") which::which("nix")
.inspect_err(|e| error!("couldn't find `nix` in PATH: {e}")) .inspect_err(|e| error!("couldn't find `nix` in PATH: {e}"))
@ -243,7 +242,11 @@ impl Daemon {
.insert_unique(FdInfo::new(fd.as_raw_fd(), kind)) .insert_unique(FdInfo::new(fd.as_raw_fd(), kind))
.unwrap_or_else(|e| unreachable!("{e}")); .unwrap_or_else(|e| unreachable!("{e}"));
debug!("opened daemon to {:?} file descriptor {fd:?}", name); if let Some(name) = &name {
info!("opened daemon to {}", name.to_string_lossy());
} else {
debug!("opened daemon to {name:?} (fd {fd:?})");
}
let path = name let path = name
.as_ref() .as_ref()
@ -260,6 +263,24 @@ impl Daemon {
} }
} }
pub fn from_tcp_socket_addr(config_path: Arc<Path>, addr: SocketAddr) -> Result<Self, IoError> {
let listener = TcpListener::bind(addr.clone())
.inspect_err(|e| error!("failed to bind to '{addr}': {e}"))?;
let listener_owned_fd = OwnedFd::from(listener);
// FIXME: should we KEEP_ALIVE?
rustix::net::sockopt::set_socket_keepalive(&listener_owned_fd, true).unwrap();
let name = OsString::from(addr.to_string()).into_boxed_os_str();
Ok(Self::new(
config_path,
listener_owned_fd,
FdKind::Socket,
Some(name),
))
}
pub fn from_unix_socket_path(config_path: Arc<Path>, path: &Path) -> Result<Self, IoError> { pub fn from_unix_socket_path(config_path: Arc<Path>, path: &Path) -> Result<Self, IoError> {
// We unconditionally unlink() `path` before binding, but completely ignore the result. // We unconditionally unlink() `path` before binding, but completely ignore the result.
let _ = rustix::fs::unlink(path); let _ = rustix::fs::unlink(path);

View file

@ -93,7 +93,7 @@ impl<'a> Display for FdInfoDisplay<'a> {
write!( write!(
f, f,
"{} fd {} ({})", "{} fd {} ({})",
self.inner.kind.name_str(), self.inner.kind,
self.inner.fd, self.inner.fd,
self.inner.name().to_string_lossy(), self.inner.name().to_string_lossy(),
)?; )?;
@ -136,6 +136,19 @@ impl FdKind {
} }
} }
impl Display for FdKind {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
use FdKind::*;
let name = self.name_str();
match self {
ChildStdout(pid) | ChildStderr(pid) | Pid(pid) => write!(f, "{name} for {pid}")?,
_ => write!(f, "{name}")?,
};
Ok(())
}
}
#[derive(Copy)] #[derive(Copy)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TokenFd { pub struct TokenFd {

View file

@ -162,6 +162,7 @@ pub fn do_daemon(args: Arc<Args>, daemon_args: DaemonCmd) -> Result<(), BoxDynEr
let mut daemon = match daemon_args { let mut daemon = match daemon_args {
DaemonCmd { stdin: true, .. } => Daemon::from_stdin(config_file), DaemonCmd { stdin: true, .. } => Daemon::from_stdin(config_file),
DaemonCmd { tcp: Some(tcp), .. } => Daemon::from_tcp_socket_addr(config_file, tcp)?,
DaemonCmd { socket: None, .. } => Daemon::open_default_socket(config_file)?, DaemonCmd { socket: None, .. } => Daemon::open_default_socket(config_file)?,
DaemonCmd { DaemonCmd {
socket: Some(socket), socket: Some(socket),