Use nix & Results

This commit is contained in:
PolyMeilex 2024-02-10 04:49:07 +01:00 committed by ridiculousfish
parent 971d774e67
commit b9ba9e57e8
5 changed files with 49 additions and 43 deletions

View file

@ -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);
}

View file

@ -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();

View file

@ -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);
};

View file

@ -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);
}
}

View file

@ -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,