mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-29 06:13:20 +00:00
Use nix & Results
This commit is contained in:
parent
971d774e67
commit
b9ba9e57e8
5 changed files with 49 additions and 43 deletions
|
@ -26,11 +26,11 @@ pub fn eval(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Opt
|
|||
let mut stdout_fill = None;
|
||||
if streams.out_is_piped {
|
||||
match IoBufferfill::create_opts(parser.libdata().pods.read_limit, STDOUT_FILENO) {
|
||||
None => {
|
||||
Err(_) => {
|
||||
// We were unable to create a pipe, probably fd exhaustion.
|
||||
return STATUS_CMD_ERROR;
|
||||
}
|
||||
Some(buffer) => {
|
||||
Ok(buffer) => {
|
||||
stdout_fill = Some(buffer.clone());
|
||||
ios.push(buffer);
|
||||
}
|
||||
|
@ -41,11 +41,11 @@ pub fn eval(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Opt
|
|||
let mut stderr_fill = None;
|
||||
if streams.err_is_piped {
|
||||
match IoBufferfill::create_opts(parser.libdata().pods.read_limit, STDERR_FILENO) {
|
||||
None => {
|
||||
Err(_) => {
|
||||
// We were unable to create a pipe, probably fd exhaustion.
|
||||
return STATUS_CMD_ERROR;
|
||||
}
|
||||
Some(buffer) => {
|
||||
Ok(buffer) => {
|
||||
stderr_fill = Some(buffer.clone());
|
||||
ios.push(buffer);
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@ pub fn exec_job(parser: &Parser, job: &Job, block_io: IoChain) -> bool {
|
|||
let mut proc_pipes = PartialPipes::default();
|
||||
std::mem::swap(&mut proc_pipes.read, &mut pipe_next_read);
|
||||
if !p.is_last_in_job {
|
||||
let Some(pipes) = make_autoclose_pipes() else {
|
||||
let Ok(pipes) = make_autoclose_pipes() else {
|
||||
FLOGF!(warning, "%ls", wgettext!(PIPE_ERROR));
|
||||
perror("pipe");
|
||||
aborted_pipeline = true;
|
||||
|
@ -1048,12 +1048,12 @@ fn exec_block_or_func_process(
|
|||
if piped_output_needs_buffering {
|
||||
// Be careful to handle failure, e.g. too many open fds.
|
||||
match IoBufferfill::create() {
|
||||
Some(tmp) => {
|
||||
Ok(tmp) => {
|
||||
// Teach the job about its bufferfill, and add it to our io chain.
|
||||
io_chain.push(tmp.clone());
|
||||
block_output_bufferfill = Some(tmp);
|
||||
}
|
||||
None => return Err(()),
|
||||
Err(_) => return Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1477,8 +1477,7 @@ fn exec_subshell_internal(
|
|||
|
||||
// IO buffer creation may fail (e.g. if we have too many open files to make a pipe), so this may
|
||||
// be null.
|
||||
let Some(bufferfill) =
|
||||
IoBufferfill::create_opts(parser.libdata().pods.read_limit, STDOUT_FILENO)
|
||||
let Ok(bufferfill) = IoBufferfill::create_opts(parser.libdata().pods.read_limit, STDOUT_FILENO)
|
||||
else {
|
||||
*break_expand = true;
|
||||
return STATUS_CMD_ERROR.unwrap();
|
||||
|
|
|
@ -62,7 +62,7 @@ impl FdEventSignaller {
|
|||
#[cfg(not(HAVE_EVENTFD))]
|
||||
{
|
||||
// Implementation using pipes.
|
||||
let Some(pipes) = make_autoclose_pipes() else {
|
||||
let Ok(pipes) = make_autoclose_pipes() else {
|
||||
perror("pipe");
|
||||
exit_without_destructors(1);
|
||||
};
|
||||
|
|
64
src/fds.rs
64
src/fds.rs
|
@ -6,9 +6,8 @@ use crate::tests::prelude::*;
|
|||
use crate::wchar::prelude::*;
|
||||
use crate::wutil::perror;
|
||||
use errno::{errno, set_errno};
|
||||
use libc::{
|
||||
c_int, EINTR, FD_CLOEXEC, F_DUPFD_CLOEXEC, F_GETFD, F_GETFL, F_SETFD, F_SETFL, O_NONBLOCK,
|
||||
};
|
||||
use libc::{c_int, EINTR, FD_CLOEXEC, F_GETFD, F_GETFL, F_SETFD, F_SETFL, O_NONBLOCK};
|
||||
use nix::fcntl::FcntlArg;
|
||||
use nix::{fcntl::OFlag, unistd};
|
||||
use std::ffi::CStr;
|
||||
use std::io::{self, Read, Write};
|
||||
|
@ -130,35 +129,39 @@ pub struct AutoClosePipes {
|
|||
|
||||
/// Construct a pair of connected pipes, set to close-on-exec.
|
||||
/// \return None on fd exhaustion.
|
||||
pub fn make_autoclose_pipes() -> Option<AutoClosePipes> {
|
||||
let mut pipes: [c_int; 2] = [-1, -1];
|
||||
|
||||
pub fn make_autoclose_pipes() -> nix::Result<AutoClosePipes> {
|
||||
#[allow(unused_mut, unused_assignments)]
|
||||
let mut already_cloexec = false;
|
||||
#[cfg(HAVE_PIPE2)]
|
||||
{
|
||||
if unsafe { libc::pipe2(&mut pipes[0], libc::O_CLOEXEC) } < 0 {
|
||||
let pipes = match nix::unistd::pipe2(OFlag::O_CLOEXEC) {
|
||||
Ok(pipes) => {
|
||||
already_cloexec = true;
|
||||
pipes
|
||||
}
|
||||
Err(err) => {
|
||||
FLOG!(warning, PIPE_ERROR);
|
||||
perror("pipe2");
|
||||
return None;
|
||||
return Err(err);
|
||||
}
|
||||
already_cloexec = true;
|
||||
}
|
||||
};
|
||||
#[cfg(not(HAVE_PIPE2))]
|
||||
if unsafe { libc::pipe(&mut pipes[0]) } < 0 {
|
||||
FLOG!(warning, PIPE_ERROR);
|
||||
perror("pipe2");
|
||||
return None;
|
||||
}
|
||||
let pipes = match nix::unistd::pipe() {
|
||||
Ok(pipes) => pipes,
|
||||
Err(err) => {
|
||||
FLOG!(warning, PIPE_ERROR);
|
||||
perror("pipe2");
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
let readp = unsafe { OwnedFd::from_raw_fd(pipes[0]) };
|
||||
let writep = unsafe { OwnedFd::from_raw_fd(pipes[1]) };
|
||||
let readp = unsafe { OwnedFd::from_raw_fd(pipes.0) };
|
||||
let writep = unsafe { OwnedFd::from_raw_fd(pipes.1) };
|
||||
|
||||
// Ensure our fds are out of the user range.
|
||||
let readp = heightenize_fd(readp, already_cloexec)?;
|
||||
let writep = heightenize_fd(writep, already_cloexec)?;
|
||||
|
||||
Some(AutoClosePipes {
|
||||
Ok(AutoClosePipes {
|
||||
read: readp,
|
||||
write: writep,
|
||||
})
|
||||
|
@ -170,23 +173,26 @@ pub fn make_autoclose_pipes() -> Option<AutoClosePipes> {
|
|||
/// setting it again.
|
||||
/// \return the fd, which always has CLOEXEC set; or an invalid fd on failure, in
|
||||
/// which case an error will have been printed, and the input fd closed.
|
||||
fn heightenize_fd(fd: OwnedFd, input_has_cloexec: bool) -> Option<OwnedFd> {
|
||||
fn heightenize_fd(fd: OwnedFd, input_has_cloexec: bool) -> nix::Result<OwnedFd> {
|
||||
let raw_fd = fd.as_raw_fd();
|
||||
|
||||
if raw_fd >= FIRST_HIGH_FD {
|
||||
if !input_has_cloexec {
|
||||
set_cloexec(raw_fd, true);
|
||||
}
|
||||
return Some(fd);
|
||||
}
|
||||
// Here we are asking the kernel to give us a cloexec fd.
|
||||
let newfd = unsafe { libc::fcntl(raw_fd, F_DUPFD_CLOEXEC, FIRST_HIGH_FD) };
|
||||
if newfd < 0 {
|
||||
perror("fcntl");
|
||||
return None;
|
||||
return Ok(fd);
|
||||
}
|
||||
|
||||
Some(unsafe { OwnedFd::from_raw_fd(newfd) })
|
||||
// Here we are asking the kernel to give us a cloexec fd.
|
||||
let newfd = match nix::fcntl::fcntl(raw_fd, FcntlArg::F_DUPFD_CLOEXEC(FIRST_HIGH_FD)) {
|
||||
Ok(newfd) => newfd,
|
||||
Err(err) => {
|
||||
perror("fcntl");
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(unsafe { OwnedFd::from_raw_fd(newfd) })
|
||||
}
|
||||
|
||||
/// Sets CLO_EXEC on a given fd according to the value of \p should_set.
|
||||
|
@ -292,7 +298,7 @@ fn test_pipes() {
|
|||
// Note pipe creation may fail due to fd exhaustion; don't fail in that case.
|
||||
let mut pipes = vec![];
|
||||
for _i in 0..10 {
|
||||
if let Some(pipe) = make_autoclose_pipes() {
|
||||
if let Ok(pipe) = make_autoclose_pipes() {
|
||||
pipes.push(pipe);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ use libc::{EAGAIN, EINTR, ENOENT, ENOTDIR, EPIPE, EWOULDBLOCK, STDOUT_FILENO};
|
|||
use nix::fcntl::OFlag;
|
||||
use nix::sys::stat::Mode;
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::io;
|
||||
use std::os::fd::{AsRawFd, IntoRawFd, OwnedFd, RawFd};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Condvar, Mutex, MutexGuard};
|
||||
|
@ -345,14 +346,14 @@ pub struct IoBufferfill {
|
|||
impl IoBufferfill {
|
||||
/// Create an io_bufferfill_t which, when written from, fills a buffer with the contents.
|
||||
/// \returns nullptr on failure, e.g. too many open fds.
|
||||
pub fn create() -> Option<Arc<IoBufferfill>> {
|
||||
pub fn create() -> io::Result<Arc<IoBufferfill>> {
|
||||
Self::create_opts(0, STDOUT_FILENO)
|
||||
}
|
||||
/// Create an io_bufferfill_t which, when written from, fills a buffer with the contents.
|
||||
/// \returns nullptr on failure, e.g. too many open fds.
|
||||
///
|
||||
/// \param target the fd which this will be dup2'd to - typically stdout.
|
||||
pub fn create_opts(buffer_limit: usize, target: RawFd) -> Option<Arc<IoBufferfill>> {
|
||||
pub fn create_opts(buffer_limit: usize, target: RawFd) -> io::Result<Arc<IoBufferfill>> {
|
||||
assert!(target >= 0, "Invalid target fd");
|
||||
|
||||
// Construct our pipes.
|
||||
|
@ -365,13 +366,13 @@ impl IoBufferfill {
|
|||
Err(e) => {
|
||||
FLOG!(warning, PIPE_ERROR);
|
||||
perror_io("fcntl", &e);
|
||||
return None;
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
// Our fillthread gets the read end of the pipe; out_pipe gets the write end.
|
||||
let buffer = Arc::new(IoBuffer::new(buffer_limit));
|
||||
begin_filling(&buffer, pipes.read);
|
||||
Some(Arc::new(IoBufferfill {
|
||||
Ok(Arc::new(IoBufferfill {
|
||||
target,
|
||||
write_fd: pipes.write,
|
||||
buffer,
|
||||
|
|
Loading…
Reference in a new issue