| |
GitVersion(#[from] git::VersionError),
|
| |
}
|
| |
|
| + |
/// Wraps a [`UnixListener`] but tracks its origin.
|
| + |
pub enum ControlSocket {
|
| + |
/// The listener was created by binding to it.
|
| + |
Bound(UnixListener, PathBuf),
|
| + |
/// The listener was received via socket activation.
|
| + |
Received(UnixListener),
|
| + |
}
|
| + |
|
| |
/// Holds join handles to the client threads, as well as a client handle.
|
| |
pub struct Runtime {
|
| |
pub id: NodeId,
|
| |
pub home: Home,
|
| - |
pub control: UnixListener,
|
| + |
pub control: ControlSocket,
|
| |
pub handle: Handle,
|
| |
pub storage: Storage,
|
| |
pub reactor: Reactor<wire::Control, popol::Poller>,
|
| |
|
| |
pub fn run(self) -> Result<(), Error> {
|
| |
let home = self.home;
|
| + |
let (listener, remove) = match self.control {
|
| + |
ControlSocket::Bound(listener, path) => (listener, Some(path)),
|
| + |
ControlSocket::Received(listener) => (listener, None),
|
| + |
};
|
| |
|
| |
log::info!(target: "node", "Running node {} in {}..", self.id, home.path().display());
|
| - |
log::info!(target: "node", "Binding control socket {}..", home.socket().display());
|
| |
|
| |
thread::spawn(&self.id, "control", {
|
| |
let handle = self.handle.clone();
|
| - |
|| control::listen(self.control, handle)
|
| + |
|| control::listen(listener, handle)
|
| |
});
|
| |
let _signals = thread::spawn(&self.id, "signals", move || {
|
| |
if let Ok(Signal::Terminate | Signal::Interrupt) = self.signals.recv() {
|
| |
// node is shutting down.
|
| |
|
| |
// Remove control socket file, but don't freak out if it's not there anymore.
|
| - |
fs::remove_file(home.socket()).ok();
|
| + |
remove.map(|path| fs::remove_file(path).ok());
|
| |
|
| |
log::debug!(target: "node", "Node shutdown completed for {}", self.id);
|
| |
|
| |
Ok(())
|
| |
}
|
| + |
|
| + |
#[cfg(all(feature = "systemd", target_family = "unix"))]
|
| + |
fn receive_listener() -> Option<UnixListener> {
|
| + |
use libsystemd::activation::IsType;
|
| + |
use std::os::fd::FromRawFd;
|
| + |
use std::os::fd::IntoRawFd;
|
| + |
|
| + |
const NAME: &str = "control";
|
| + |
|
| + |
match libsystemd::activation::receive_descriptors_with_names(true) {
|
| + |
Ok(descriptors) => match descriptors
|
| + |
.into_iter()
|
| + |
.find_map(|(fd, name)| (name == NAME).then_some(fd))
|
| + |
{
|
| + |
Some(fd) if fd.is_unix() => {
|
| + |
let raw_fd = fd.into_raw_fd();
|
| + |
return Some(unsafe {
|
| + |
// SAFETY: We take ownership of this FD from systemd, which guarantees that it is open.
|
| + |
UnixListener::from_raw_fd(raw_fd)
|
| + |
});
|
| + |
}
|
| + |
Some(_) => {
|
| + |
log::debug!(target: "node", "Unexpected type of file descriptor from systemd.")
|
| + |
}
|
| + |
None => {
|
| + |
log::debug!(target: "node", "No file descriptor named '{NAME}' from systemd.")
|
| + |
}
|
| + |
},
|
| + |
Err(err) => log::trace!(target: "node", "No file descriptors received: {err}"),
|
| + |
}
|
| + |
|
| + |
None
|
| + |
}
|
| + |
|
| + |
fn bind(path: PathBuf) -> Result<ControlSocket, Error> {
|
| + |
if cfg!(all(feature = "systemd", target_family = "unix")) {
|
| + |
if let Some(listener) = Self::receive_listener() {
|
| + |
log::info!(target: "node", "Received control socket.");
|
| + |
return Ok(ControlSocket::Received(listener));
|
| + |
}
|
| + |
}
|
| + |
|
| + |
log::info!(target: "node", "Binding control socket {}..", &path.display());
|
| + |
match UnixListener::bind(&path) {
|
| + |
Ok(sock) => Ok(ControlSocket::Bound(sock, path)),
|
| + |
Err(err) if err.kind() == io::ErrorKind::AddrInUse => Err(Error::AlreadyRunning(path)),
|
| + |
Err(err) => Err(err.into()),
|
| + |
}
|
| + |
}
|
| |
}
|