Commit 88f01500 authored by Nathaniel Barragan's avatar Nathaniel Barragan

Mega commit. Finished most of the error handling work

parent d8026e51
......@@ -13,6 +13,8 @@ use ion_shell::{
parser::Terminator,
types::{self, array},
IonError, PipelineError, Shell, Signal, Value,
unixperms::{UnixPermissions, UnixMode},
error_handling,
};
use itertools::Itertools;
use liner::{Buffer, Context, KeyBindings};
......@@ -313,30 +315,17 @@ impl<'a> InteractiveShell<'a> {
.ok()
.map_or(false, |res| res.is_failure())
{
match err.kind() {
io::ErrorKind::PermissionDenied => {
// Check the permissions on the command
let cmd_permissions = fs::metadata(command[0].as_str()).unwrap().permissions();
// Let's get the unix permissions for this file
let unix_perms = UnixPermissions::from(cmd_permissions.mode() as u16);
if unix_perms.user_mode.execute
|| unix_perms.group_mode.execute
|| unix_perms.other_mode.execute {
eprintln!("{} is executable but not by you.\nCurrent permissions: {}\n\nTry running chmod +x [FILE]",
command[0], unix_perms.to_string());
} else {
eprintln!("{} not marked as executable\nCurrent permissions: {}.\n\nTry running chmod +x [FILE]",
command[0],
unix_perms.to_string());
}
},
_ => eprintln!("ion: Error executing command: {}", err),
}
error_handling::command_exec_error(err, command);
shell.reset_flow();
}
}
Err(err) => {
eprintln!("ion: {}", err);
match err {
IonError::PipelineExecutionError(PipelineError::CommandExecError(ref e, ref command)) => {
error_handling::command_exec_error(e, command);
},
e => eprintln!("ion: Error executing command: {}", e),
}
shell.reset_flow();
}
}
......@@ -393,82 +382,6 @@ impl<'a> InteractiveShell<'a> {
}
}
pub struct UnixMode {
pub write: bool,
pub read: bool,
pub execute: bool,
}
impl UnixMode {
pub fn from_mask(mode: u16, read_mask: u16, write_mask: u16, execute_mask: u16) -> Self {
let read = match mode & read_mask {
0 => false,
_ => true,
};
let write = match mode & write_mask {
0 => false,
_ => true,
};
let execute = match mode &execute_mask {
0 => false,
_ => true,
};
Self {
write, read, execute,
}
}
}
impl ToString for UnixMode {
fn to_string(&self) -> String {
match (self.read, self.write, self.execute) {
(false, false, false) => "---",
(true, false, false) => "r--",
(false, true, false) => "-w-",
(false, false, true) => "--x",
(true, true, false) => "rw-",
(true, false, true) => "r-x",
(false, true, true) => "-wx",
(true, true, true) => "rwx",
}.to_string()
}
}
// A structure which stores Unix Permissions.
pub struct UnixPermissions {
pub user_mode: UnixMode,
pub group_mode: UnixMode,
pub other_mode: UnixMode,
}
impl ToString for UnixPermissions {
fn to_string(&self) -> String {
[self.user_mode.to_string(), self.group_mode.to_string(), self.other_mode.to_string()].join("")
}
}
impl From<u16> for UnixPermissions {
fn from(mode: u16) -> Self {
Self {
user_mode: UnixMode::from_mask(mode, UnixPermissions::S_IRUSR, UnixPermissions::S_IWUSR, UnixPermissions::S_IXUSR),
group_mode: UnixMode::from_mask(mode, UnixPermissions::S_IRGRP, UnixPermissions::S_IWGRP, UnixPermissions::S_IXGRP),
other_mode: UnixMode::from_mask(mode, UnixPermissions::S_IROTH, UnixPermissions::S_IWOTH, UnixPermissions::S_IXOTH),
}
}
}
impl UnixPermissions {
const S_IXUSR: u16 = 64;
const S_IWUSR: u16 = 128;
const S_IRUSR: u16 = 256;
const S_IXGRP: u16 = 8;
const S_IWGRP: u16 = 16;
const S_IRGRP: u16 = 32;
const S_IXOTH: u16 = 1;
const S_IWOTH: u16 = 2;
const S_IROTH: u16 = 4;
}
#[derive(Debug)]
struct WordDivide<I>
where
......
use super::{
types,
unixperms::UnixPermissions
};
use std::io;
/// Handle a CommandExecutionError gracefully with a nice output
///
/// ## A mockup of the various outputs:
///
/// If the command is executable but not by you:
///
/// {COMMAND} is executable, but not by you.
///
/// Current permissions: {PERMISSIONS}
///
/// Try running chmod +x FILE
///
/// If the command is not executable:
///
/// {COMMAND} is not executable
/// Current permissions: {PERMISSIONS}
///
/// Try running chmod +x FILE
///
pub fn command_exec_error(err: &io::Error, cmd: &types::Args) {
match err.kind() {
io::ErrorKind::PermissionDenied => {
let unix_perms = UnixPermissions::from_file(&cmd[0]).unwrap();
if unix_perms.user_mode.execute ||
unix_perms.group_mode.execute ||
unix_perms.other_mode.execute {
eprintln!("{} is executable but not by you.\nCurrent permissions: {}\n\nTry running chmod +x FILE",
cmd[0],
unix_perms.to_string());
} else {
eprintln!("{} is not executable\nCurrent permissions: {}\n\nTry running chmod +x FILE",
cmd[0],
unix_perms.to_string());
}
}
_ => eprintln!("Error executing command \"{}\": {}", cmd[0], err),
}
}
......@@ -88,6 +88,10 @@ pub mod builtins;
pub mod expansion;
mod memory;
mod shell;
/// A unix permission library for error messages
pub mod unixperms;
/// A module to handle the various errors possible.
pub mod error_handling;
pub use nix::sys::signal::Signal;
......
use super::job_control::{BackgroundProcess, ProcessState};
use crate::{
IonError,
PipelineError,
builtins::Status,
expansion::pipelines::Pipeline,
shell::{RefinedJob, Shell},
error_handling,
};
use nix::{
sys::signal::{self, SigHandler, Signal},
......@@ -38,7 +41,10 @@ impl<'a> Shell<'a> {
let code = self
.pipe(pipeline)
.unwrap_or_else(|err| {
eprintln!("{}", err);
match err {
IonError::PipelineExecutionError(PipelineError::CommandExecError(ref e, ref c)) => error_handling::command_exec_error(e, c),
e => eprintln!("test: {}", e),
}
Status::COULD_NOT_EXEC
})
.as_os_code();
......
use std::os::unix::{
fs::PermissionsExt,
};
use std::fs;
/// A structure representing a mode of a file.
#[derive(Debug, PartialEq)]
pub struct UnixMode {
/// A bool representing the write permission
pub write: bool,
/// A bool representing the read permission
pub read: bool,
/// A bool representing the execute permission
pub execute: bool,
}
impl UnixMode {
const S_IXUSR: u16 = 64;
const S_IWUSR: u16 = 128;
const S_IRUSR: u16 = 256;
const S_IXGRP: u16 = 8;
const S_IWGRP: u16 = 16;
const S_IRGRP: u16 = 32;
const S_IXOTH: u16 = 1;
const S_IWOTH: u16 = 2;
const S_IROTH: u16 = 4;
/// Parses a mode from the passed `UnixModeMask`.
///
/// Example:
/// ```
/// # use ion_shell::unixperms::{UnixMode, UnixModeMask};
/// fn main() {
/// let mode = UnixMode::from_mask(0o777, UnixModeMask::User);
/// assert_eq!(UnixMode { write: true, read: true, execute: true }, mode)
/// }
/// ```
pub fn from_mask(mode: u16, mask: UnixModeMask) -> Self {
let read_mask = match mask {
UnixModeMask::User => Self::S_IRUSR,
UnixModeMask::Group => Self::S_IRGRP,
UnixModeMask::Other => Self::S_IROTH,
};
let write_mask = match mask {
UnixModeMask::User => Self::S_IWUSR,
UnixModeMask::Group => Self::S_IWGRP,
UnixModeMask::Other => Self::S_IWOTH,
};
let execute_mask = match mask {
UnixModeMask::User => Self::S_IXUSR,
UnixModeMask::Group => Self::S_IXGRP,
UnixModeMask::Other => Self::S_IXOTH,
};
let read = match mode & read_mask {
0 => false,
_ => true,
};
let write = match mode & write_mask {
0 => false,
_ => true,
};
let execute = match mode & execute_mask {
0 => false,
_ => true,
};
Self {
write, read, execute,
}
}
}
/// Used to select the mask with which to parse the mode
pub enum UnixModeMask {
/// Signal to use the masks associated with a user's permissions
User,
/// Signal to use the masks associated with the group's permissions
Group,
/// Signal to use the masks associated with any other user's permissions
Other
}
impl ToString for UnixMode {
fn to_string(&self) -> String {
match (self.read, self.write, self.execute) {
(false, false, false) => "---",
(true, false, false) => "r--",
(false, true, false) => "-w-",
(false, false, true) => "--x",
(true, true, false) => "rw-",
(true, false, true) => "r-x",
(false, true, true) => "-wx",
(true, true, true) => "rwx",
}.to_string()
}
}
/// A structure which stores Unix file permissions.
#[derive(Debug)]
pub struct UnixPermissions {
/// The user permissions of the file
pub user_mode: UnixMode,
/// The group permissions of the file
pub group_mode: UnixMode,
/// Any other user's permissions of the file
pub other_mode: UnixMode,
}
impl ToString for UnixPermissions {
fn to_string(&self) -> String {
[self.user_mode.to_string(), self.group_mode.to_string(), self.other_mode.to_string()].join("")
}
}
impl From<u16> for UnixPermissions {
fn from(mode: u16) -> Self {
Self {
user_mode: UnixMode::from_mask(mode, UnixModeMask::User),
group_mode: UnixMode::from_mask(mode, UnixModeMask::Group),
other_mode: UnixMode::from_mask(mode, UnixModeMask::Other),
}
}
}
impl UnixPermissions {
/// Create a UnixPermissions from a passed path
pub fn from_file(file: &str) -> Result<UnixPermissions, std::io::Error> {
let file_unix_perms = match fs::metadata(file) {
Err(e) => return Err(e),
Ok(a) => a,
};
Ok(UnixPermissions::from(file_unix_perms.permissions().mode() as u16))
}
}
use self::binary::{builtins, InteractiveShell};
use atty::Stream;
use ion_shell::{BackgroundEvent, BuiltinMap, IonError, PipelineError, Shell, Value};
use ion_shell::{error_handling, BackgroundEvent, BuiltinMap, IonError, PipelineError, Shell, Value};
use liner::KeyBindings;
use nix::{
sys::signal::{self, SaFlags, SigAction, SigHandler, SigSet, Signal},
......@@ -24,6 +24,8 @@ use structopt::StructOpt;
mod binary;
struct KeyBindingsWrapper(KeyBindings);
#[cfg(feature = "advanced_arg_parsing")]
......@@ -243,7 +245,11 @@ fn main() {
let _ = nix::sys::signal::raise(signal);
}
if let Err(why) = err {
eprintln!("ion: {}", why);
match why {
IonError::PipelineExecutionError(PipelineError::CommandExecError(ref e, ref command)) =>
error_handling::command_exec_error(e, command),
e => eprintln!("Error executing script: {}", e),
}
process::exit(1);
}
process::exit(shell.previous_status().as_os_code());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment