add types in preparation for non-blocking stdin

This commit is contained in:
Qyriad 2026-03-11 14:26:59 +01:00
parent a9068f86f3
commit a72a48f92b
4 changed files with 111 additions and 1 deletions

1
Cargo.lock generated
View file

@ -160,6 +160,7 @@ dependencies = [
"libc", "libc",
"regex", "regex",
"regex-lite", "regex-lite",
"rustix",
"serde", "serde",
"serde_json", "serde_json",
"tap", "tap",

View file

@ -29,6 +29,7 @@ itertools = "0.14.0"
libc = { version = "0.2.180", features = ["extra_traits"] } libc = { version = "0.2.180", features = ["extra_traits"] }
regex = { version = "1.12.3", optional = true } regex = { version = "1.12.3", optional = true }
regex-lite = { version = "0.1.9", optional = true } regex-lite = { version = "0.1.9", optional = true }
rustix = { version = "1.1.4", features = ["event", "fs"] }
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149" serde_json = "1.0.149"
tap = "1.0.1" tap = "1.0.1"

106
src/daemon_io.rs Normal file
View file

@ -0,0 +1,106 @@
use std::{
mem::ManuallyDrop,
os::{
fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd},
unix::prelude::RawFd,
},
};
use rustix::{
fs::{OFlags, fcntl_getfl, fcntl_setfl},
io::Errno,
};
use crate::prelude::*;
/// An [`OwnedFd`] that captures file status flags on init and restores them on:
///
/// - [`IntoRawFd`]
/// - [`drop()`]
#[derive(Debug)]
pub struct OwnedFdWithFlags {
fd: OwnedFd,
oflags: OFlags,
}
impl OwnedFdWithFlags {
pub fn new(fd: OwnedFd) -> Result<Self, Errno> {
let oflags = fcntl_getfl(&fd)?;
Ok(Self { fd, oflags })
}
/// If, for some ungodly reason, `fcntl(F_GETFL)` fails, then empty flags are used.
/// Empty flags should not be restored on close.
///
/// I'm pretty sure this should never happen if the program is IO-safe.
pub fn new_with_fallback(fd: OwnedFd) -> Self {
let oflags = Self::get_flags_or_log(fd.as_fd());
Self { fd, oflags }
}
}
impl Drop for OwnedFdWithFlags {
fn drop(&mut self) {
self.restore_flags_or_log();
}
}
/// Private helpers.
impl OwnedFdWithFlags {
fn restore_flags_or_log(&self) {
// As far as we can tell there's no such thing as a file with entirely empty
// flags (at minimum, it needs read/write mode, right?).
// So empty `oflags` is our sentinel that `fcntl(F_GETFL)` somehow failed.
// Empty oflags is our sentinel that
if self.oflags.is_empty() {
return;
}
if let Err(e) = fcntl_setfl(&self.fd, self.oflags) {
error!(
"fcntl(F_SETFL) to restore flags {:?} on fd {:?} failed: {e}\nIO safety violation?",
self.oflags, self.fd,
);
};
}
fn get_flags_or_log(fd: BorrowedFd) -> OFlags {
fcntl_getfl(fd)
.tap_err(|e| error!("fcntl(F_GETFL) failed on fd {fd:?}: {e}\nIO-safety violation?"))
.unwrap_or(OFlags::empty())
}
}
impl AsRawFd for OwnedFdWithFlags {
fn as_raw_fd(&self) -> RawFd {
AsRawFd::as_raw_fd(&self.fd)
}
}
impl IntoRawFd for OwnedFdWithFlags {
fn into_raw_fd(self) -> RawFd {
self.restore_flags_or_log();
ManuallyDrop::new(self).fd.as_raw_fd()
}
}
impl FromRawFd for OwnedFdWithFlags {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
let fd = unsafe { OwnedFd::from_raw_fd(fd) };
let oflags = Self::get_flags_or_log(fd.as_fd());
Self { fd, oflags }
}
}
impl AsFd for OwnedFdWithFlags {
fn as_fd(&self) -> BorrowedFd<'_> {
AsFd::as_fd(&self.fd)
}
}
impl From<OwnedFd> for OwnedFdWithFlags {
fn from(fd: OwnedFd) -> Self {
let oflags = Self::get_flags_or_log(fd.as_fd());
Self { fd, oflags }
}
}

View file

@ -33,7 +33,7 @@ pub(crate) mod prelude {
#[cfg(unix)] #[cfg(unix)]
pub use fs_err::os::unix::fs::{FileExt, OpenOptionsExt}; pub use fs_err::os::unix::fs::{FileExt, OpenOptionsExt};
pub use tap::{Pipe, Tap}; pub use tap::{Pipe, Tap, TapFallible};
pub use tracing::{Level, debug, error, info, trace, warn}; pub use tracing::{Level, debug, error, info, trace, warn};
} }
@ -44,6 +44,8 @@ pub mod args;
pub use args::{AppendCmd, Args}; pub use args::{AppendCmd, Args};
mod color; mod color;
pub use color::{_CLI_ENABLE_COLOR, SHOULD_COLOR}; pub use color::{_CLI_ENABLE_COLOR, SHOULD_COLOR};
mod daemon_io;
pub use daemon_io::OwnedFdWithFlags;
pub mod line; pub mod line;
mod nixcmd; mod nixcmd;
pub use line::Line; pub use line::Line;