From 3cac1eb488f4d634b307912c98d2e596d3098cb6 Mon Sep 17 00:00:00 2001 From: Xavier L'Heureux <xavier.lheureux@icloud.com> Date: Mon, 10 Jun 2019 09:12:51 -0400 Subject: [PATCH] Start implementing errors for pipes --- src/lib/shell/pipe_exec/fork.rs | 15 +++--- src/lib/shell/pipe_exec/mod.rs | 90 +++++++++++++++------------------ 2 files changed, 49 insertions(+), 56 deletions(-) diff --git a/src/lib/shell/pipe_exec/fork.rs b/src/lib/shell/pipe_exec/fork.rs index 43cb23d9..d6f44d8d 100644 --- a/src/lib/shell/pipe_exec/fork.rs +++ b/src/lib/shell/pipe_exec/fork.rs @@ -5,7 +5,6 @@ use super::{ job_control::{BackgroundProcess, ProcessState}, }; use crate::parser::pipelines::Pipeline; -use std::process::exit; impl<'a> Shell<'a> { /// Ensures that the forked child is given a unique process ID. @@ -31,7 +30,14 @@ impl<'a> Shell<'a> { Self::create_process_group(0); // After execution of it's commands, exit with the last command's status. - sys::fork_exit(self.pipe(pipeline).as_os_code()); + sys::fork_exit( + self.pipe(pipeline) + .unwrap_or_else(|err| { + eprintln!("{}", err); + Status::COULD_NOT_EXEC + }) + .as_os_code(), + ); } Ok(pid) => { if state != ProcessState::Empty { @@ -40,10 +46,7 @@ impl<'a> Shell<'a> { } Status::SUCCESS } - Err(why) => { - eprintln!("ion: background fork failed: {}", why); - exit(1); - } + Err(why) => Status::error(format!("ion: background fork failed: {}", why)), } } } diff --git a/src/lib/shell/pipe_exec/mod.rs b/src/lib/shell/pipe_exec/mod.rs index fe9e31ee..3383f40d 100644 --- a/src/lib/shell/pipe_exec/mod.rs +++ b/src/lib/shell/pipe_exec/mod.rs @@ -41,9 +41,9 @@ use std::{ #[derive(Debug, Error)] pub enum InputError { - #[error(display = "ion: failed to redirect '{}' to stdin: {}", file, why)] + #[error(display = "failed to redirect '{}' to stdin: {}", file, why)] File { file: String, why: io::Error }, - #[error(display = "ion: failed to redirect herestring '{}' to stdin: {}", string, why)] + #[error(display = "failed to redirect herestring '{}' to stdin: {}", string, why)] HereString { string: String, why: io::Error }, } @@ -54,17 +54,25 @@ pub struct OutputError { why: io::Error, } -#[derive(Debug)] +#[derive(Debug, Error)] pub enum RedirectError { - Input(InputError), - Output(OutputError), + #[error(display = "{}", cause)] + Input { cause: InputError }, + #[error(display = "{}", cause)] + Output { cause: OutputError }, +} + +#[derive(Debug, Error)] +pub enum PipeError { + #[error(display = "ion: {}", cause)] + RedirectError { cause: RedirectError }, } impl fmt::Display for OutputError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "ion: failed to redirect {} to file '{}': {}", + "failed to redirect {} to file '{}': {}", match self.redirect { RedirectFrom::Both => "both stdout and stderr", RedirectFrom::Stdout => "stdout", @@ -81,30 +89,16 @@ impl std::error::Error for OutputError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.why) } } -impl fmt::Display for RedirectError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - RedirectError::Output(why) => write!(f, "ion: failed to redirect {}", why), - RedirectError::Input(why) => write!(f, "ion: failed to redirect {}", why), - } - } -} - -impl std::error::Error for RedirectError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - Some(match self { - RedirectError::Output(why) => why, - RedirectError::Input(why) => why, - }) - } -} - impl From<OutputError> for RedirectError { - fn from(error: OutputError) -> Self { RedirectError::Output(error) } + fn from(cause: OutputError) -> Self { RedirectError::Output { cause } } } impl From<InputError> for RedirectError { - fn from(error: InputError) -> Self { RedirectError::Input(error) } + fn from(cause: InputError) -> Self { RedirectError::Input { cause } } +} + +impl From<RedirectError> for PipeError { + fn from(cause: RedirectError) -> Self { PipeError::RedirectError { cause } } } /// Create an OS pipe and write the contents of a byte slice to one end @@ -442,7 +436,10 @@ impl<'b> Shell<'b> { PipeType::Background => self.fork_pipe(pipeline, command, ProcessState::Running), // Execute each command in the pipeline, giving each command the foreground. PipeType::Normal => { - let exit_status = self.pipe(pipeline); + let exit_status = self.pipe(pipeline).unwrap_or_else(|err| { + eprintln!("{}", err); + Status::COULD_NOT_EXEC + }); // Set the shell as the foreground process again to regain the TTY. if !self.opts.is_background_shell { let _ = sys::tcsetpgrp(0, process::id()); @@ -455,14 +452,8 @@ impl<'b> Shell<'b> { /// Executes a piped job `job1 | job2 | job3` /// /// This function will panic if called with an empty slice - fn pipe(&mut self, pipeline: Pipeline<'b>) -> Status { - let mut commands = match prepare(self, pipeline) { - Ok(c) => c.into_iter().peekable(), - Err(why) => { - eprintln!("{}", why); - return Status::COULD_NOT_EXEC; - } - }; + fn pipe(&mut self, pipeline: Pipeline<'b>) -> Result<Status, PipeError> { + let mut commands = prepare(self, pipeline)?.into_iter().peekable(); if let Some((mut parent, mut kind)) = commands.next() { if kind == RedirectFrom::None && !parent.needs_forking() { @@ -471,7 +462,7 @@ impl<'b> Shell<'b> { let _ = io::stdout().flush(); let _ = io::stderr().flush(); - status + Ok(status) } else { let (mut pgid, mut last_pid, mut current_pid) = (0, 0, 0); @@ -510,18 +501,17 @@ impl<'b> Shell<'b> { RedirectFrom::None => (), RedirectFrom::Stderr => parent.stderr(writer), RedirectFrom::Stdout => parent.stdout(writer), - RedirectFrom::Both => match writer.try_clone() { - Err(e) => { - eprintln!( - "ion: failed to redirect stdout and stderr: {}", - e - ); - } - Ok(duped) => { - parent.stderr(writer); - parent.stdout(duped); - } - }, + RedirectFrom::Both => { + let duped = writer.try_clone().map_err(|why| { + RedirectError::from(OutputError { + redirect: kind, + file: "pipe".to_string(), + why, + }) + })?; + parent.stderr(writer); + parent.stdout(duped); + } } } } @@ -554,10 +544,10 @@ impl<'b> Shell<'b> { let _ = io::stdout().flush(); let _ = io::stderr().flush(); } - status + Ok(status) } } else { - Status::SUCCESS + Ok(Status::SUCCESS) } } } -- GitLab