Skip to content

Commit

Permalink
Basic DEBUG trap impl
Browse files Browse the repository at this point in the history
  • Loading branch information
reubeno committed May 17, 2024
1 parent e2b996a commit 6eb6a75
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 22 deletions.
6 changes: 6 additions & 0 deletions cli/tests/cases/builtins/help.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ cases:
ignore_stderr: true
stdin: |
help
- name: "Topic-specific help"
ignore_stdout: true
ignore_stderr: true
stdin: |
help echo
29 changes: 29 additions & 0 deletions cli/tests/cases/builtins/trap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: "Builtins: trap"
cases:
- name: "trap registration"
stdin: |
trap "echo 1" SIGINT
trap "echo 2" SIGINT
trap
trap "echo 3" int
trap
trap "echo 4" 2
trap
- name: "trap EXIT"
known_failure: true
stdin: |
trap "echo [exit]" EXIT
trap
- name: "trap DEBUG"
stdin: |
trap 'echo [command: ${BASH_COMMAND}]' DEBUG
trap
- name: "trap ERR"
stdin: |
trap "echo [err]" ERR
trap
31 changes: 19 additions & 12 deletions shell/src/builtins/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{io::Write, str::FromStr};

use crate::{
builtin::{BuiltinCommand, BuiltinExitCode},
error,
error, traps,
};

#[derive(Parser)]
Expand Down Expand Up @@ -77,20 +77,18 @@ impl TrapCommand {

fn display_handlers_for(
context: &crate::context::CommandExecutionContext<'_>,
signal_type: nix::sys::signal::Signal,
signal_type: traps::TrapSignal,
) -> Result<(), error::Error> {
if let Some(handlers) = context.shell.traps.handlers.get(&signal_type) {
for handler in handlers {
writeln!(context.stdout(), "trap -- '{handler}' {signal_type}")?;
}
if let Some(handler) = context.shell.traps.handlers.get(&signal_type) {
writeln!(context.stdout(), "trap -- '{handler}' {signal_type}")?;
}
Ok(())
}

#[allow(clippy::unnecessary_wraps)]
fn remove_all_handlers(
context: &mut crate::context::CommandExecutionContext<'_>,
signal: nix::sys::signal::Signal,
signal: traps::TrapSignal,
) -> Result<(), error::Error> {
context.shell.traps.remove_handlers(signal);
Ok(())
Expand All @@ -99,7 +97,7 @@ impl TrapCommand {
#[allow(clippy::unnecessary_wraps)]
fn register_handler(
context: &mut crate::context::CommandExecutionContext<'_>,
signals: Vec<nix::sys::signal::Signal>,
signals: Vec<traps::TrapSignal>,
handler: &str,
) -> Result<(), error::Error> {
for signal in signals {
Expand All @@ -113,21 +111,30 @@ impl TrapCommand {
}
}

fn parse_signal(signal: &str) -> Result<nix::sys::signal::Signal, error::Error> {
fn parse_signal(signal: &str) -> Result<traps::TrapSignal, error::Error> {
if signal.chars().all(|c| c.is_ascii_digit()) {
let digits = signal
.parse::<i32>()
.map_err(|_| error::Error::InvalidSignal)?;

nix::sys::signal::Signal::try_from(digits).map_err(|_| error::Error::InvalidSignal)
Ok(traps::TrapSignal::Signal(
nix::sys::signal::Signal::try_from(digits).map_err(|_| error::Error::InvalidSignal)?,
))
} else {
let mut signal_to_parse = signal.to_ascii_uppercase();

if !signal_to_parse.starts_with("SIG") {
signal_to_parse.insert_str(0, "SIG");
}

nix::sys::signal::Signal::from_str(signal_to_parse.as_str())
.map_err(|_| error::Error::InvalidSignal)
match signal_to_parse {
s if s == "SIGDEBUG" => Ok(traps::TrapSignal::Debug),
s if s == "SIGERR" => Ok(traps::TrapSignal::Err),
s if s == "SIGEXIT" => Ok(traps::TrapSignal::Exit),
_ => Ok(traps::TrapSignal::Signal(
nix::sys::signal::Signal::from_str(signal_to_parse.as_str())
.map_err(|_| error::Error::InvalidSignal)?,
)),
}
}
}
39 changes: 38 additions & 1 deletion shell/src/interp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::shell::Shell;
use crate::variables::{
ArrayLiteral, ShellValue, ShellValueLiteral, ShellValueUnsetType, ShellVariable,
};
use crate::{builtin, context, error, expansion, extendedtests, jobs, openfiles};
use crate::{builtin, context, error, expansion, extendedtests, jobs, openfiles, traps};

#[derive(Debug, Default)]
pub struct ExecutionResult {
Expand Down Expand Up @@ -895,6 +895,43 @@ impl ExecuteInPipeline for ast::SimpleCommand {
.trace_command(args.iter().map(|arg| arg.to_string()).join(" "))?;
}

// TODO: This is adding more complexity here; should be factored out into an appropriate
// helper.
if context.shell.traps.handler_depth == 0 {
let debug_trap_handler = context
.shell
.traps
.handlers
.get(&traps::TrapSignal::Debug)
.cloned();
if let Some(debug_trap_handler) = debug_trap_handler {
let params = ExecutionParameters {
open_files: open_files.clone(),
};

let full_cmd = args.iter().map(|arg| arg.to_string()).join(" ");

// TODO: This shouldn't *just* be set in a trap situation.
context.shell.env.update_or_add(
"BASH_COMMAND",
ShellValueLiteral::Scalar(full_cmd),
|_| Ok(()),
EnvironmentLookup::Anywhere,
EnvironmentScope::Global,
)?;

context.shell.traps.handler_depth += 1;

// TODO: Discard result?
let _ = context
.shell
.run_string(debug_trap_handler.as_str(), &params)
.await?;

context.shell.traps.handler_depth -= 1;
}
}

let cmd_context = context::CommandExecutionContext {
shell: context.shell,
command_name: cmd_name,
Expand Down
34 changes: 25 additions & 9 deletions shell/src/traps.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
use std::collections::HashMap;
use std::{collections::HashMap, fmt::Display};

#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub enum TrapSignal {
Signal(nix::sys::signal::Signal),
Debug,
Err,
Exit,
}

impl Display for TrapSignal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TrapSignal::Signal(s) => s.fmt(f),
TrapSignal::Debug => write!(f, "DEBUG"),
TrapSignal::Err => write!(f, "ERR"),
TrapSignal::Exit => write!(f, "EXIT"),
}
}
}

#[derive(Clone, Default)]
pub struct TrapHandlerConfig {
pub handlers: HashMap<nix::sys::signal::Signal, Vec<String>>,
pub handlers: HashMap<TrapSignal, String>,
pub handler_depth: i32,
}

impl TrapHandlerConfig {
pub fn register_handler(&mut self, signal_type: nix::sys::signal::Signal, command: String) {
if let Some(handlers) = self.handlers.get_mut(&signal_type) {
handlers.push(command);
} else {
self.handlers.insert(signal_type, vec![command]);
}
pub fn register_handler(&mut self, signal_type: TrapSignal, command: String) {
let _ = self.handlers.insert(signal_type, command);
}

pub fn remove_handlers(&mut self, signal_type: nix::sys::signal::Signal) {
pub fn remove_handlers(&mut self, signal_type: TrapSignal) {
self.handlers.remove(&signal_type);
}
}

0 comments on commit 6eb6a75

Please sign in to comment.