Compare commits
6 commits
eb5dc84ca8
...
099a22fb98
Author | SHA1 | Date | |
---|---|---|---|
Mathieu Trossevin | 099a22fb98 | ||
Mathieu Trossevin | 10c4028f49 | ||
Mathieu Trossevin | c25149382b | ||
Mathieu Trossevin | f61df0da1b | ||
Mathieu Trossevin | 42aa8b285a | ||
Mathieu Trossevin | 0ce9662f52 |
15
Cargo.toml
15
Cargo.toml
|
@ -17,5 +17,18 @@ notify = ["dep:libc", "dep:rustix", "rustix/pipe", "rustix/event"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = { version = "0.2.150", optional = true }
|
libc = { version = "0.2.150", optional = true }
|
||||||
|
log = "0.4.20"
|
||||||
rustix = { version = "0.38.26", optional = true, features = ["net"] }
|
rustix = { version = "0.38.26", optional = true, features = ["net"] }
|
||||||
tracing = { version = "0.1.40", default-features = false, features = ["std", "log"] }
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
features = ["listenfd", "notify"]
|
||||||
|
rustdoc-args = ["--cfg", "doc_cfg"]
|
||||||
|
targets = [
|
||||||
|
"x86_64-unknown-linux-gnu",
|
||||||
|
"i686-unknown-linux-gnu",
|
||||||
|
"x86_64-apple-darwin",
|
||||||
|
"x86_64-unknown-freebsd",
|
||||||
|
"x86_64-unknown-openbsd",
|
||||||
|
"x86_64-unknown-netbsd",
|
||||||
|
"wasm32-unknown-emscripten"
|
||||||
|
]
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
|
//! A library permitting interaction with the LISTEN_FDS and NOTIFY_SOCKET protocol.
|
||||||
|
|
||||||
|
#![cfg_attr(doc_cfg, feature(doc_cfg))]
|
||||||
|
|
||||||
#[cfg(feature = "listenfd")]
|
#[cfg(feature = "listenfd")]
|
||||||
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "listenfd")))]
|
||||||
pub mod listen;
|
pub mod listen;
|
||||||
#[cfg(feature = "notify")]
|
#[cfg(feature = "notify")]
|
||||||
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "notify")))]
|
||||||
pub mod notify;
|
pub mod notify;
|
||||||
|
|
|
@ -104,13 +104,11 @@ impl FileDescriptor {
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// This function will fail if no file descriptor colud be received which might not actually be an error. See [`ReceiveError`] for details.
|
/// This function will fail if no file descriptor could be received which might not actually be an error. See [`ReceiveError`] for details.
|
||||||
pub fn receive(
|
pub fn receive(
|
||||||
unset_env: bool,
|
unset_env: bool,
|
||||||
) -> Result<impl IntoIterator<Item = Result<Self, DupError>>, ReceiveError> {
|
) -> Result<impl IntoIterator<Item = Result<Self, DupError>>, ReceiveError> {
|
||||||
let span = tracing::info_span!("receive", f_unset_env = ?unset_env);
|
log::trace!("Receiving file descriptors.");
|
||||||
let _enter = span.enter();
|
|
||||||
|
|
||||||
let fds = Self::inner_receive(unset_env)?;
|
let fds = Self::inner_receive(unset_env)?;
|
||||||
|
|
||||||
match Self::from_fds(fds) {
|
match Self::from_fds(fds) {
|
||||||
|
@ -127,7 +125,7 @@ impl FileDescriptor {
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// This function will fail if no file descriptor colud be received which might not actually be an error. See [`ReceiveError`] for details.
|
/// This function will fail if no file descriptor could be received which might not actually be an error. See [`ReceiveError`] for details.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
|
@ -135,9 +133,7 @@ impl FileDescriptor {
|
||||||
/// (In short it needs to follow the safety constraints of [`OwnedFd::from_raw_fd`](std::os::fd::FromRawFd)).
|
/// (In short it needs to follow the safety constraints of [`OwnedFd::from_raw_fd`](std::os::fd::FromRawFd)).
|
||||||
/// The simplest way to insure that it is so is to only use functions from this crate to get these file descriptors.
|
/// The simplest way to insure that it is so is to only use functions from this crate to get these file descriptors.
|
||||||
pub unsafe fn receive_no_dup() -> Result<impl IntoIterator<Item = Self>, ReceiveError> {
|
pub unsafe fn receive_no_dup() -> Result<impl IntoIterator<Item = Self>, ReceiveError> {
|
||||||
let span = tracing::info_span!("receive_no_dup");
|
log::trace!("Receiving file descriptors without duplication.");
|
||||||
let _enter = span.enter();
|
|
||||||
|
|
||||||
let fds = Self::inner_receive(true)?;
|
let fds = Self::inner_receive(true)?;
|
||||||
|
|
||||||
Self::from_fds_no_dup(fds).map_err(ReceiveError::GetFds)
|
Self::from_fds_no_dup(fds).map_err(ReceiveError::GetFds)
|
||||||
|
@ -146,7 +142,7 @@ impl FileDescriptor {
|
||||||
fn inner_receive(unset_env: bool) -> Result<usize, ReceiveError> {
|
fn inner_receive(unset_env: bool) -> Result<usize, ReceiveError> {
|
||||||
let pid = env::var_os(PID_VAR).ok_or(ReceiveError::NoListenPID)?;
|
let pid = env::var_os(PID_VAR).ok_or(ReceiveError::NoListenPID)?;
|
||||||
let fds = env::var_os(FD_NUMBER_VAR).ok_or(ReceiveError::NoListenFD)?;
|
let fds = env::var_os(FD_NUMBER_VAR).ok_or(ReceiveError::NoListenFD)?;
|
||||||
tracing::trace!("{PID_VAR} = {pid:?}; {FD_NUMBER_VAR} = {fds:?}");
|
log::trace!("{PID_VAR} = {pid:?}; {FD_NUMBER_VAR} = {fds:?}");
|
||||||
|
|
||||||
if unset_env {
|
if unset_env {
|
||||||
env::remove_var(PID_VAR);
|
env::remove_var(PID_VAR);
|
||||||
|
@ -189,8 +185,6 @@ impl FileDescriptor {
|
||||||
unset_env: bool,
|
unset_env: bool,
|
||||||
) -> Result<impl IntoIterator<Item = (OsString, Result<Self, DupError>)>, ReceiveNameError>
|
) -> Result<impl IntoIterator<Item = (OsString, Result<Self, DupError>)>, ReceiveNameError>
|
||||||
{
|
{
|
||||||
let span = tracing::info_span!("receive_with_names", f_unset_env = ?unset_env);
|
|
||||||
let _enter = span.enter();
|
|
||||||
let ret = Self::get_names()?
|
let ret = Self::get_names()?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(Self::receive(unset_env)?);
|
.zip(Self::receive(unset_env)?);
|
||||||
|
@ -214,15 +208,13 @@ impl FileDescriptor {
|
||||||
/// The simplest way to insure that it is so is to only use functions from this crate to get these file descriptors.
|
/// The simplest way to insure that it is so is to only use functions from this crate to get these file descriptors.
|
||||||
pub unsafe fn receive_with_names_no_dup(
|
pub unsafe fn receive_with_names_no_dup(
|
||||||
) -> Result<impl IntoIterator<Item = (OsString, Self)>, ReceiveNameError> {
|
) -> Result<impl IntoIterator<Item = (OsString, Self)>, ReceiveNameError> {
|
||||||
let span = tracing::info_span!("receive_with_names_no_dups");
|
|
||||||
let _enter = span.enter();
|
|
||||||
let ret = Self::get_names()?.into_iter().zip(Self::receive_no_dup()?);
|
let ret = Self::get_names()?.into_iter().zip(Self::receive_no_dup()?);
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_names() -> Result<impl IntoIterator<Item = OsString>, ReceiveNameError> {
|
fn get_names() -> Result<impl IntoIterator<Item = OsString>, ReceiveNameError> {
|
||||||
let fd_names = env::var_os(FD_NAMES_VAR).ok_or(ReceiveNameError::NoListenFDName)?;
|
let fd_names = env::var_os(FD_NAMES_VAR).ok_or(ReceiveNameError::NoListenFDName)?;
|
||||||
tracing::trace!("{FD_NAMES_VAR} = {fd_names:?}");
|
log::trace!("{FD_NAMES_VAR} = {fd_names:?}");
|
||||||
Ok(fd_names
|
Ok(fd_names
|
||||||
.as_encoded_bytes()
|
.as_encoded_bytes()
|
||||||
.split(|b| *b == b':')
|
.split(|b| *b == b':')
|
||||||
|
@ -236,6 +228,18 @@ impl FileDescriptor {
|
||||||
.collect::<Vec<_>>())
|
.collect::<Vec<_>>())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn from_fd_offset(fd_offset: usize) -> Result<Self, DupError> {
|
||||||
|
SD_LISTEN_FDS_START
|
||||||
|
.checked_add(fd_offset as RawFd)
|
||||||
|
.map(|fd| {
|
||||||
|
// SAFETY: The file descriptor won't be closed by the time we duplicate it.
|
||||||
|
let fd = unsafe { BorrowedFd::borrow_raw(fd) };
|
||||||
|
rustix::fs::fcntl_dupfd_cloexec(fd, 0).map_err(DupError::from)
|
||||||
|
})
|
||||||
|
.expect("Already checked against overflow.")
|
||||||
|
.map(Self::from_fd)
|
||||||
|
}
|
||||||
|
|
||||||
fn from_fds(
|
fn from_fds(
|
||||||
num_fds: usize,
|
num_fds: usize,
|
||||||
) -> Result<impl IntoIterator<Item = Result<Self, DupError>>, GetFdsError> {
|
) -> Result<impl IntoIterator<Item = Result<Self, DupError>>, GetFdsError> {
|
||||||
|
@ -243,17 +247,7 @@ impl FileDescriptor {
|
||||||
return Err(GetFdsError::TooManyFDs(num_fds));
|
return Err(GetFdsError::TooManyFDs(num_fds));
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret = (0..num_fds).map(|fd_offset| {
|
let ret = (0..num_fds).map(Self::from_fd_offset);
|
||||||
SD_LISTEN_FDS_START
|
|
||||||
.checked_add(fd_offset as RawFd)
|
|
||||||
.map(|fd| {
|
|
||||||
// SAFETY: The file descriptor won't be closed by the time we duplicate it.
|
|
||||||
let fd = unsafe { BorrowedFd::borrow_raw(fd) };
|
|
||||||
rustix::fs::fcntl_dupfd_cloexec(fd, 0).map_err(DupError::from)
|
|
||||||
})
|
|
||||||
.expect("Already checked against overflow.")
|
|
||||||
.map(Self::from_fd)
|
|
||||||
});
|
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,18 +48,15 @@ impl Notifier {
|
||||||
///
|
///
|
||||||
/// This function error out if the socket couldn't be opened.
|
/// This function error out if the socket couldn't be opened.
|
||||||
pub fn new(unset_env: bool) -> Result<Option<Self>, NewNotifierError> {
|
pub fn new(unset_env: bool) -> Result<Option<Self>, NewNotifierError> {
|
||||||
let span = tracing::info_span!("new", f_unset_env = ?unset_env);
|
log::info!("Opening NOTIFY_SOCKET if available.");
|
||||||
let _enter = span.enter();
|
|
||||||
|
|
||||||
tracing::info!("Opening NOTIFY_SOCKET if available.");
|
|
||||||
let env_sock = match env::var_os("NOTIFY_SOCKET") {
|
let env_sock = match env::var_os("NOTIFY_SOCKET") {
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
};
|
};
|
||||||
tracing::debug!("NOTIFY_SOCKET = {env_sock:?}");
|
log::debug!("NOTIFY_SOCKET = {env_sock:?}");
|
||||||
|
|
||||||
if unset_env {
|
if unset_env {
|
||||||
tracing::trace!("Removing NOTIFY_SOCKET from environment.");
|
log::trace!("Removing NOTIFY_SOCKET from environment.");
|
||||||
env::remove_var("NOTIFY_SOCKET");
|
env::remove_var("NOTIFY_SOCKET");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,12 +70,12 @@ impl Notifier {
|
||||||
unsafe { OsString::from_encoded_bytes_unchecked(v.to_vec()) }
|
unsafe { OsString::from_encoded_bytes_unchecked(v.to_vec()) }
|
||||||
}) {
|
}) {
|
||||||
Some(stripped_addr) => {
|
Some(stripped_addr) => {
|
||||||
tracing::trace!("Opening abstract socket {stripped_addr:?}.");
|
log::trace!("Opening abstract socket {stripped_addr:?}.");
|
||||||
rustix::net::SocketAddrUnix::new_abstract_name(stripped_addr.as_encoded_bytes())
|
rustix::net::SocketAddrUnix::new_abstract_name(stripped_addr.as_encoded_bytes())
|
||||||
.map_err(NewNotifierError::InvalidAbstractSocket)?
|
.map_err(NewNotifierError::InvalidAbstractSocket)?
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
tracing::trace!("Opening named socket {env_sock:?}.");
|
log::trace!("Opening named socket {env_sock:?}.");
|
||||||
rustix::net::SocketAddrUnix::new(env_sock)
|
rustix::net::SocketAddrUnix::new(env_sock)
|
||||||
.map_err(NewNotifierError::InvalidSocketPath)?
|
.map_err(NewNotifierError::InvalidSocketPath)?
|
||||||
}
|
}
|
||||||
|
@ -104,10 +101,6 @@ impl Notifier {
|
||||||
state: &[NotifyState<'_>],
|
state: &[NotifyState<'_>],
|
||||||
fds: &[BorrowedFd],
|
fds: &[BorrowedFd],
|
||||||
) -> Result<(), NotifyError> {
|
) -> Result<(), NotifyError> {
|
||||||
let span =
|
|
||||||
tracing::info_span!("notify_with_fds", f_self = ?self, f_state = ?state, f_fds = ?fds);
|
|
||||||
let _enter = span.enter();
|
|
||||||
|
|
||||||
let msg = state
|
let msg = state
|
||||||
.iter()
|
.iter()
|
||||||
.fold(String::new(), |acc, state| format!("{acc}{state}\n"))
|
.fold(String::new(), |acc, state| format!("{acc}{state}\n"))
|
||||||
|
@ -116,10 +109,10 @@ impl Notifier {
|
||||||
let msg_iov = IoSlice::new(&msg);
|
let msg_iov = IoSlice::new(&msg);
|
||||||
|
|
||||||
let mut ancillary = if fds.is_empty() {
|
let mut ancillary = if fds.is_empty() {
|
||||||
tracing::trace!("No file descriptors provided, not sending ancillary messages.");
|
log::trace!("No file descriptors provided, not sending ancillary messages.");
|
||||||
rustix::net::SendAncillaryBuffer::default()
|
rustix::net::SendAncillaryBuffer::default()
|
||||||
} else {
|
} else {
|
||||||
tracing::trace!(
|
log::trace!(
|
||||||
"{} file descriptors provided, sending through ancillary messages",
|
"{} file descriptors provided, sending through ancillary messages",
|
||||||
fds.len()
|
fds.len()
|
||||||
);
|
);
|
||||||
|
@ -131,8 +124,7 @@ impl Notifier {
|
||||||
ancillary
|
ancillary
|
||||||
};
|
};
|
||||||
|
|
||||||
span.record("expected_length", msg_len);
|
log::debug!("Sending notification messages.");
|
||||||
tracing::debug!("Sending notification messages.");
|
|
||||||
let sent_len = rustix::net::sendmsg_unix(
|
let sent_len = rustix::net::sendmsg_unix(
|
||||||
self.socket.as_fd(),
|
self.socket.as_fd(),
|
||||||
&self.sock_addr,
|
&self.sock_addr,
|
||||||
|
@ -141,11 +133,10 @@ impl Notifier {
|
||||||
rustix::net::SendFlags::empty(),
|
rustix::net::SendFlags::empty(),
|
||||||
)
|
)
|
||||||
.map_err(NotifyError::SendMsg)?;
|
.map_err(NotifyError::SendMsg)?;
|
||||||
span.record("sent_length", sent_len);
|
log::debug!("Notification message sent. {sent_len} bytes sent.");
|
||||||
tracing::debug!("Notification message sent. {sent_len} bytes sent.");
|
|
||||||
|
|
||||||
if sent_len != msg_len {
|
if sent_len != msg_len {
|
||||||
tracing::error!("The notification message couldn't be completely sent!");
|
log::error!("The notification message couldn't be completely sent!");
|
||||||
return Err(NotifyError::PartialSend);
|
return Err(NotifyError::PartialSend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue