Skip to content

Commit

Permalink
Use rustix instead of libc (additive only approach) (#892)
Browse files Browse the repository at this point in the history
* use rustix instead of libc

* make rustix the default feature

* bump msrv to 1.63.0

* fix remaining libc issues

- use rustix version of sigwinch signal
- add a lifetime to FileDesc and replace FileDesc::Static to
  FileDesc::Borrowed. This made it necessary to either add a lifetime to
  the libc version of FileDesc or replace all the callers with multiple
  paths (libc, rustix). Changing FileDesc was more straightforward.
  There are no usages of FileDesc found in any repo on github, so this
  change should be reasonably safe.

* add changelog entry for rustix / filedesc change
  • Loading branch information
joshka committed Jun 16, 2024
1 parent be8cb8c commit fe44028
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 25 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Unreleased

- Use Rustix by default instead of libc. Libc can be re-enabled if necessary with the libc feature flag.
- `FileDesc` now requires a lifetime annotation.

# Version 0.27.1

## Added ⭐
Expand Down
11 changes: 9 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ keywords = ["event", "color", "cli", "input", "terminal"]
exclude = ["target", "Cargo.lock"]
readme = "README.md"
edition = "2021"
rust-version = "1.58.0"
rust-version = "1.63.0"
categories = ["command-line-interface", "command-line-utilities"]

[lib]
Expand Down Expand Up @@ -71,7 +71,14 @@ crossterm_winapi = { version = "0.9.1", optional = true }
# UNIX dependencies
#
[target.'cfg(unix)'.dependencies]
libc = "0.2"
# Default to using rustix for UNIX systems, but provide an option to use libc for backwards
# compatibility.
libc = { version = "0.2", default-features = false, optional = true }
rustix = { version = "0.38.34", default-features = false, features = [
"std",
"stdio",
"termios",
] }
signal-hook = { version = "0.3.17", optional = true }
filedescriptor = { version = "0.8", optional = true }
mio = { version = "0.8", features = ["os-poll"], optional = true }
Expand Down
4 changes: 2 additions & 2 deletions src/event/source/unix/mio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub(crate) struct UnixInternalEventSource {
events: Events,
parser: Parser,
tty_buffer: [u8; TTY_BUFFER_SIZE],
tty_fd: FileDesc,
tty_fd: FileDesc<'static>,
signals: Signals,
#[cfg(feature = "event-stream")]
waker: Waker,
Expand All @@ -37,7 +37,7 @@ impl UnixInternalEventSource {
UnixInternalEventSource::from_file_descriptor(tty_fd()?)
}

pub(crate) fn from_file_descriptor(input_fd: FileDesc) -> io::Result<Self> {
pub(crate) fn from_file_descriptor(input_fd: FileDesc<'static>) -> io::Result<Self> {
let poll = Poll::new()?;
let registry = poll.registry();

Expand Down
14 changes: 12 additions & 2 deletions src/event/source/unix/tty.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#[cfg(feature = "libc")]
use std::os::unix::prelude::AsRawFd;
use std::{collections::VecDeque, io, os::unix::net::UnixStream, time::Duration};

#[cfg(not(feature = "libc"))]
use rustix::fd::{AsFd, AsRawFd};

use signal_hook::low_level::pipe;

use crate::event::timeout::PollTimeout;
Expand Down Expand Up @@ -38,7 +42,7 @@ const TTY_BUFFER_SIZE: usize = 1_024;
pub(crate) struct UnixInternalEventSource {
parser: Parser,
tty_buffer: [u8; TTY_BUFFER_SIZE],
tty: FileDesc,
tty: FileDesc<'static>,
winch_signal_receiver: UnixStream,
#[cfg(feature = "event-stream")]
wake_pipe: WakePipe,
Expand All @@ -56,15 +60,18 @@ impl UnixInternalEventSource {
UnixInternalEventSource::from_file_descriptor(tty_fd()?)
}

pub(crate) fn from_file_descriptor(input_fd: FileDesc) -> io::Result<Self> {
pub(crate) fn from_file_descriptor(input_fd: FileDesc<'static>) -> io::Result<Self> {
Ok(UnixInternalEventSource {
parser: Parser::default(),
tty_buffer: [0u8; TTY_BUFFER_SIZE],
tty: input_fd,
winch_signal_receiver: {
let (receiver, sender) = nonblocking_unix_pair()?;
// Unregistering is unnecessary because EventSource is a singleton
#[cfg(feature = "libc")]
pipe::register(libc::SIGWINCH, sender)?;
#[cfg(not(feature = "libc"))]
pipe::register(rustix::process::Signal::Winch as i32, sender)?;
receiver
},
#[cfg(feature = "event-stream")]
Expand Down Expand Up @@ -157,7 +164,10 @@ impl EventSource for UnixInternalEventSource {
}
}
if fds[1].revents & POLLIN != 0 {
#[cfg(feature = "libc")]
let fd = FileDesc::new(self.winch_signal_receiver.as_raw_fd(), false);
#[cfg(not(feature = "libc"))]
let fd = FileDesc::Borrowed(self.winch_signal_receiver.as_fd());
// drain the pipe
while read_complete(&fd, &mut [0; 1024])? != 0 {}
// TODO Should we remove tput?
Expand Down
85 changes: 75 additions & 10 deletions src/terminal/sys/file_descriptor.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,51 @@
use std::io;

#[cfg(feature = "libc")]
use libc::size_t;
#[cfg(not(feature = "libc"))]
use rustix::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
#[cfg(feature = "libc")]
use std::{
fs, io,
fs,
marker::PhantomData,
os::unix::{
io::{IntoRawFd, RawFd},
prelude::AsRawFd,
},
};

use libc::size_t;

/// A file descriptor wrapper.
///
/// It allows to retrieve raw file descriptor, write to the file descriptor and
/// mainly it closes the file descriptor once dropped.
#[derive(Debug)]
pub struct FileDesc {
#[cfg(feature = "libc")]
pub struct FileDesc<'a> {
fd: RawFd,
close_on_drop: bool,
phantom: PhantomData<&'a ()>,
}

impl FileDesc {
#[cfg(not(feature = "libc"))]
pub enum FileDesc<'a> {
Owned(OwnedFd),
Borrowed(BorrowedFd<'a>),
}

#[cfg(feature = "libc")]
impl FileDesc<'_> {
/// Constructs a new `FileDesc` with the given `RawFd`.
///
/// # Arguments
///
/// * `fd` - raw file descriptor
/// * `close_on_drop` - specify if the raw file descriptor should be closed once the `FileDesc` is dropped
pub fn new(fd: RawFd, close_on_drop: bool) -> FileDesc {
FileDesc { fd, close_on_drop }
pub fn new(fd: RawFd, close_on_drop: bool) -> FileDesc<'static> {
FileDesc {
fd,
close_on_drop,
phantom: PhantomData,
}
}

pub fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
Expand All @@ -51,7 +70,27 @@ impl FileDesc {
}
}

impl Drop for FileDesc {
#[cfg(not(feature = "libc"))]
impl FileDesc<'_> {
pub fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
let fd = match self {
FileDesc::Owned(fd) => fd.as_fd(),
FileDesc::Borrowed(fd) => fd.as_fd(),
};
let result = rustix::io::read(fd, buffer)?;
Ok(result)
}

pub fn raw_fd(&self) -> RawFd {
match self {
FileDesc::Owned(fd) => fd.as_raw_fd(),
FileDesc::Borrowed(fd) => fd.as_raw_fd(),
}
}
}

#[cfg(feature = "libc")]
impl Drop for FileDesc<'_> {
fn drop(&mut self) {
if self.close_on_drop {
// Note that errors are ignored when closing a file descriptor. The
Expand All @@ -64,14 +103,25 @@ impl Drop for FileDesc {
}
}

impl AsRawFd for FileDesc {
impl AsRawFd for FileDesc<'_> {
fn as_raw_fd(&self) -> RawFd {
self.raw_fd()
}
}

#[cfg(not(feature = "libc"))]
impl AsFd for FileDesc<'_> {
fn as_fd(&self) -> BorrowedFd<'_> {
match self {
FileDesc::Owned(fd) => fd.as_fd(),
FileDesc::Borrowed(fd) => fd.as_fd(),
}
}
}

#[cfg(feature = "libc")]
/// Creates a file descriptor pointing to the standard input or `/dev/tty`.
pub fn tty_fd() -> io::Result<FileDesc> {
pub fn tty_fd() -> io::Result<FileDesc<'static>> {
let (fd, close_on_drop) = if unsafe { libc::isatty(libc::STDIN_FILENO) == 1 } {
(libc::STDIN_FILENO, false)
} else {
Expand All @@ -87,3 +137,18 @@ pub fn tty_fd() -> io::Result<FileDesc> {

Ok(FileDesc::new(fd, close_on_drop))
}

#[cfg(not(feature = "libc"))]
/// Creates a file descriptor pointing to the standard input or `/dev/tty`.
pub fn tty_fd() -> io::Result<FileDesc<'static>> {
use std::fs::File;

let stdin = rustix::stdio::stdin();
let fd = if rustix::termios::isatty(stdin) {
FileDesc::Borrowed(stdin)
} else {
let dev_tty = File::options().read(true).write(true).open("/dev/tty")?;
FileDesc::Owned(dev_tty.into())
};
Ok(fd)
}
Loading

0 comments on commit fe44028

Please sign in to comment.