From 8fc7729400b7ba8b2bad8e5248be61b9a39472eb Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy <mmstickman@gmail.com> Date: Sun, 9 Jul 2017 01:48:48 -0400 Subject: [PATCH] Finally Complete Job Control Work: Fg Works Now! - Job control is now complete for non-redox unix systems. - The fg command now works We need to do a lot of refactoring now to clean this up though. --- README.md | 10 +- src/builtins/job_control.rs | 69 ++---------- src/builtins/mod.rs | 6 +- src/main.rs | 23 +--- src/shell/job_control.rs | 204 +++++++++++++++++++++++++++++------- src/shell/mod.rs | 5 +- src/shell/pipe.rs | 32 ++---- src/shell/signals.rs | 40 +++++++ 8 files changed, 237 insertions(+), 152 deletions(-) create mode 100644 src/shell/signals.rs diff --git a/README.md b/README.md index 4eedf34a..cf43e54a 100644 --- a/README.md +++ b/README.md @@ -32,13 +32,13 @@ Below is an overview of features that Ion has either already implemented, or aim - [x] Process Expansions - [x] String-based Command Substitution (**$()**) - [x] Array-based Command Substitution (**@()**) - - [ ] Arithmetic Expansions + - [x] Arithmetic Expansions - [x] Flow Control - [x] For Loops - [ ] Foreach Loops - [x] While Loops - [x] If Conditionals - - [ ] Match Statements + - [x] Match Statements - [x] Functions - [x] Optionally-typed Function Parameters - [x] Script Execution @@ -72,7 +72,7 @@ Below is an overview of features that Ion has either already implemented, or aim - [x] Signal Handling - [x] **&&** and **||** Conditionals - [x] Redirecting Stdout / Stderr -- [ ] Redirecting Stdout & Stderr +- [x] Redirecting Stdout & Stderr - [x] Piping Builtins - [x] Background Jobs - [ ] Piping Functions @@ -116,8 +116,8 @@ Unlike Bash, job arguments are their specified job IDs. This area is still a work in progress. When a foreground task is stopped with the **Ctrl+Z** signal, that process will be added to the background process list as a stopped job. When a supplied command ends with the **&** operator, this will specify to run the task the background as a running job. To resume a stopped job, executing the `bg <job_id>` -command will send a `SIGCONT` to the specified job ID, hence resuming the job. The `fg` command doesn't work at the -moment though (coming soon). +command will send a `SIGCONT` to the specified job ID, hence resuming the job. The `fg` command will similarly do the +same, but also set that task as the foreground process. #### Exiting the Shell diff --git a/src/builtins/job_control.rs b/src/builtins/job_control.rs index b53b10f0..35ee8d6d 100644 --- a/src/builtins/job_control.rs +++ b/src/builtins/job_control.rs @@ -1,35 +1,7 @@ use shell::Shell; -use shell::job_control::{JobControl, ProcessState}; +use shell::job_control::{JobControl, ProcessState, resume}; use shell::status::*; use std::io::{stderr, Write}; -#[cfg(all(unix, not(target_os = "redox")))] use libc::pid_t; -#[cfg(not(target_os = "redox"))] use nix::sys::signal::{self, Signal}; -#[cfg(not(target_os = "redox"))] use nix::unistd; - -#[cfg(all(unix, not(target_os = "redox")))] -/// When given a process ID, that process's group will be assigned as the foreground process group. -pub fn set_foreground(pid: u32) { - let _ = unistd::tcsetpgrp(0, pid as i32); - let _ = unistd::tcsetpgrp(1, pid as i32); - let _ = unistd::tcsetpgrp(2, pid as i32); -} - -#[cfg(target_os = "redox")] -pub fn set_foreground(pid: u32) { - // TODO -} - -#[cfg(all(unix, not(target_os = "redox")))] -/// Suspends a given process by it's process ID. -pub fn suspend(pid: u32) { - let _ = signal::kill(-(pid as pid_t), Some(Signal::SIGSTOP)); -} - -#[cfg(all(unix, not(target_os = "redox")))] -/// Resumes a given process by it's process ID. -fn resume(pid: u32) { - let _ = signal::kill(-(pid as pid_t), Some(Signal::SIGCONT)); -} pub fn disown(shell: &mut Shell, args: &[&str]) -> i32 { let stderr = stderr(); @@ -94,19 +66,6 @@ pub fn disown(shell: &mut Shell, args: &[&str]) -> i32 { SUCCESS } - -#[cfg(target_os = "redox")] -pub fn suspend(pid: u32) { - use syscall; - let _ = syscall::kill(pid as usize, syscall::SIGSTOP); -} - -#[cfg(target_os = "redox")] -fn resume(pid: u32) { - use syscall; - let _ = syscall::kill(pid as usize, syscall::SIGCONT); -} - /// Display a list of all jobs running in the background. pub fn jobs(shell: &mut Shell) { let stderr = stderr(); @@ -133,24 +92,16 @@ pub fn fg(shell: &mut Shell, args: &[&str]) -> i32 { continue } - match job.state { - ProcessState::Running => { - set_foreground(njob); - // TODO: This doesn't work - status = shell.watch_foreground(njob) - }, - ProcessState::Stopped => { - resume(job.pid); - set_foreground(njob); - // TODO: This doesn't work - status = shell.watch_foreground(njob); - }, + // Bring the process into the foreground and wait for it to finish. + status = match job.state { + ProcessState::Running => shell.set_bg_task_in_foreground(job.pid, false), + ProcessState::Stopped => shell.set_bg_task_in_foreground(job.pid, true), ProcessState::Empty => { let stderr = stderr(); let _ = writeln!(stderr.lock(), "ion: fg: job {} does not exist", njob); - status = FAILURE; + FAILURE } - } + }; } else { let stderr = stderr(); let _ = writeln!(stderr.lock(), "ion: fg: {} is not a valid job number", arg); @@ -171,11 +122,7 @@ pub fn bg(shell: &mut Shell, args: &[&str]) -> i32 { let _ = writeln!(stderr, "ion: bg: job {} is already running", njob); error = true; }, - ProcessState::Stopped => { - resume(job.pid); - job.state = ProcessState::Running; - let _ = writeln!(stderr, "[{}] {} Running", njob, job.pid); - }, + ProcessState::Stopped => resume(job.pid), ProcessState::Empty => { let _ = writeln!(stderr, "ion: bg: job {} does not exist", njob); error = true; diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index 75076252..32ae0830 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -22,7 +22,7 @@ use std::error::Error; use parser::QuoteTerminator; use shell::job_control::{JobControl, ProcessState}; -use shell::{Shell, FlowLogic, ShellHistory}; +use shell::{self, Shell, FlowLogic, ShellHistory}; use shell::status::*; #[cfg(target_os = "redox")] @@ -261,8 +261,8 @@ impl Builtin { commands.insert("suspend", Builtin { name: "suspend", help: "Suspends the shell with a SIGTSTOP signal", - main: Box::new(|args: &[&str], shell: &mut Shell| -> i32 { - job_control::suspend(0); + main: Box::new(|_: &[&str], _: &mut Shell| -> i32 { + shell::job_control::suspend(0); SUCCESS }) }); diff --git a/src/main.rs b/src/main.rs index 6b604eff..bc6c8c0f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -83,7 +83,7 @@ fn main() { // Block the SIGTSTP signal -- prevents the shell from being stopped // when the foreground group is changed during command execution. - block_signals(); + shell::signals::unix::block(); // Create a stream that will select over SIGINT, SIGTERM, and SIGHUP signals. let signals = Signal::new(unix_signal::SIGINT, &handle).flatten_stream() @@ -103,24 +103,3 @@ fn main() { let (_, signals_rx) = mpsc::channel(); inner_main(signals_rx); } - -#[cfg(all(unix, not(target_os = "redox")))] -fn block_signals() { - unsafe { - use libc::*; - use std::mem; - use std::ptr; - let mut sigset = mem::uninitialized::<sigset_t>(); - sigemptyset(&mut sigset as *mut sigset_t); - sigaddset(&mut sigset as *mut sigset_t, SIGTSTP); - sigaddset(&mut sigset as *mut sigset_t, SIGTTOU); - sigaddset(&mut sigset as *mut sigset_t, SIGTTIN); - sigaddset(&mut sigset as *mut sigset_t, SIGCHLD); - sigprocmask(SIG_BLOCK, &sigset as *const sigset_t, ptr::null_mut() as *mut sigset_t); - } -} - -#[cfg(target_os = "redox")] -fn block_signals() { - // TODO -} diff --git a/src/shell/job_control.rs b/src/shell/job_control.rs index 6ea85775..7313ecc6 100644 --- a/src/shell/job_control.rs +++ b/src/shell/job_control.rs @@ -1,15 +1,117 @@ #[cfg(all(unix, not(target_os = "redox")))] use libc::{self, pid_t, c_int}; -#[cfg(all(unix, not(target_os = "redox")))] use nix::sys::signal::{self, Signal as NixSignal}; +#[cfg(all(unix, not(target_os = "redox")))] use nix::sys::signal::{self, Signal}; +#[cfg(all(unix, not(target_os = "redox")))] use super::signals::unix as signals; +#[cfg(all(unix, not(target_os = "redox")))] use nix::unistd; +#[cfg(target_os = "redox")] use super::signals::unix as signals; use std::fmt; use std::thread::{sleep, spawn}; use std::time::Duration; use std::process; use std::sync::{Arc, Mutex}; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use super::status::*; use super::Shell; +use super::pipe::crossplat::get_pid; + +#[cfg(all(unix, not(target_os = "redox")))] +/// When given a process ID, that process's group will be assigned as the foreground process group. +pub fn set_foreground_as(pid: u32) { + signals::block(); + let _ = unistd::tcsetpgrp(0, pid as i32); + signals::unblock(); +} + +#[cfg(all(unix, not(target_os = "redox")))] +/// Suspends a given process by it's process ID. +pub fn suspend(pid: u32) { + let _ = signal::kill(-(pid as pid_t), Some(Signal::SIGSTOP)); +} + +#[cfg(all(unix, not(target_os = "redox")))] +/// Resumes a given process by it's process ID. +pub fn resume(pid: u32) { + let _ = signal::kill(-(pid as pid_t), Some(Signal::SIGCONT)); +} + +#[cfg(target_os = "redox")] +pub fn suspend(pid: u32) { + use syscall; + let _ = syscall::kill(pid as usize, syscall::SIGSTOP); +} + +#[cfg(target_os = "redox")] +pub fn resume(pid: u32) { + use syscall; + let _ = syscall::kill(pid as usize, syscall::SIGCONT); +} + +#[cfg(target_os = "redox")] +pub fn set_foreground_as(pid: u32) { + // TODO +} + +pub enum BackgroundResult { + Errored, + Status(u8) +} + +pub struct ForegroundSignals { + grab: AtomicUsize, // TODO: Use AtomicU32 when stable + status: AtomicUsize, // TODO: Use AtomicU8 when stable + reply: AtomicBool, + errored: AtomicBool // TODO: Combine with reply when U8 is stable +} + +impl ForegroundSignals { + pub fn new() -> ForegroundSignals { + ForegroundSignals { + grab: AtomicUsize::new(0), + status: AtomicUsize::new(0), + reply: AtomicBool::new(false), + errored: AtomicBool::new(false) + } + } + + pub fn signal_to_grab(&self, pid: u32) { + self.grab.store(pid as usize, Ordering::Relaxed); + } + + pub fn reply_with(&self, status: i8) { + self.grab.store(0, Ordering::Relaxed); + self.reply.store(true, Ordering::Relaxed); + self.status.store(status as usize, Ordering::Relaxed); + } + + pub fn errored(&self) { + self.errored.store(true, Ordering::Relaxed); + } + + pub fn was_processed(&self) -> Option<BackgroundResult> { + if self.reply.load(Ordering::Relaxed) { + self.reply.store(false, Ordering::Relaxed); + if self.errored.load(Ordering::Relaxed) { + self.errored.store(false, Ordering::Relaxed); + Some(BackgroundResult::Errored) + } else { + Some(BackgroundResult::Status(self.status.load(Ordering::Relaxed) as u8)) + } + } else { + None + } + } + + pub fn was_grabbed(&self, pid: u32) -> bool { + self.grab.load(Ordering::Relaxed) == pid as usize + } +} pub trait JobControl { + /// Waits for background jobs to finish before returning. fn wait_for_background(&mut self); + /// Takes a background tasks's PID and whether or not it needs to be continued; resumes the task + /// and sets it as the foreground process. Once the task exits or stops, the exit status will + /// be returned, and ownership of the TTY given back to the shell. + fn set_bg_task_in_foreground(&self, pid: u32, cont: bool) -> i32; fn handle_signal(&self, signal: i32); fn foreground_send(&self, signal: i32); fn background_send(&self, signal: i32); @@ -36,34 +138,55 @@ impl fmt::Display for ProcessState { } #[cfg(target_os = "redox")] -pub fn watch_background_pid(processes: Arc<Mutex<Vec<BackgroundProcess>>>, pid: u32, njob: usize) { +pub fn watch_background ( + fg: Arc<ForegroundSignals>, + processes: Arc<Mutex<Vec<BackgroundProcess>>>, + pid: u32, + njob: usize +) { // TODO: Implement this using syscall::call::waitpid } #[cfg(all(unix, not(target_os = "redox")))] -pub fn watch_background_pid ( +pub fn watch_background ( + fg: Arc<ForegroundSignals>, processes: Arc<Mutex<Vec<BackgroundProcess>>>, pid: u32, - njob: usize) -{ + njob: usize +) { use nix::sys::wait::*; + let mut fg_was_grabbed = false; loop { - match waitpid(-(pid as pid_t), Some(WUNTRACED)) { + if !fg_was_grabbed { + if fg.was_grabbed(pid) { fg_was_grabbed = true; } + } + match waitpid(-(pid as pid_t), Some(WUNTRACED | WCONTINUED | WNOHANG)) { Ok(WaitStatus::Exited(_, status)) => { - eprintln!("ion: ([{}] {}) exited with {}", njob, pid, status); + if !fg_was_grabbed { + eprintln!("ion: ([{}] {}) exited with {}", njob, pid, status); + } let mut processes = processes.lock().unwrap(); let process = &mut processes.iter_mut().nth(njob).unwrap(); process.state = ProcessState::Empty; + if fg_was_grabbed { fg.reply_with(status); } break }, Ok(WaitStatus::Stopped(pid, _)) => { - eprintln!("ion: ([{}] {}) Stopped", njob, pid); + if !fg_was_grabbed { + eprintln!("ion: ([{}] {}) Stopped", njob, pid); + } let mut processes = processes.lock().unwrap(); let process = &mut processes.iter_mut().nth(njob).unwrap(); + if fg_was_grabbed { + fg.reply_with(TERMINATED as i8); + fg_was_grabbed = false; + } process.state = ProcessState::Stopped; }, Ok(WaitStatus::Continued(pid)) => { - eprintln!("ion: ([{}] {}) Running", njob, pid); + if !fg_was_grabbed { + eprintln!("ion: ([{}] {}) Running", njob, pid); + } let mut processes = processes.lock().unwrap(); let process = &mut processes.iter_mut().nth(njob).unwrap(); process.state = ProcessState::Running; @@ -74,9 +197,11 @@ pub fn watch_background_pid ( let mut processes = processes.lock().unwrap(); let process = &mut processes.iter_mut().nth(njob).unwrap(); process.state = ProcessState::Empty; + if fg_was_grabbed { fg.errored(); } break } } + sleep(Duration::from_millis(100)); } } @@ -86,30 +211,20 @@ pub fn add_to_background ( state: ProcessState ) -> usize { let mut processes = processes.lock().unwrap(); - match (*processes).iter().position(|x| { - if let ProcessState::Empty = x.state { true } else { false } - }) { + match (*processes).iter().position(|x| x.state == ProcessState::Empty) { Some(id) => { - (*processes)[id] = BackgroundProcess { - pid: pid, - ignore_sighup: false, - state: state - }; + (*processes)[id] = BackgroundProcess { pid: pid, ignore_sighup: false, state: state }; id }, None => { let njobs = (*processes).len(); - (*processes).push(BackgroundProcess { - pid: pid, - ignore_sighup: false, - state: state - }); + (*processes).push(BackgroundProcess { pid: pid, ignore_sighup: false, state: state }); njobs } } } -#[derive(Clone)] +#[derive(Clone, Debug)] /// A background process is a process that is attached to, but not directly managed /// by the shell. The shell will only retain information about the process, such /// as the process ID, state that the process is in, and the command that the @@ -123,6 +238,28 @@ pub struct BackgroundProcess { } impl<'a> JobControl for Shell<'a> { + fn set_bg_task_in_foreground(&self, pid: u32, cont: bool) -> i32 { + // Resume the background task, if needed. + if cont { resume(pid); } + // Pass the TTY to the background job + set_foreground_as(pid); + // Signal the background thread that is waiting on this process to stop waiting. + self.foreground_signals.signal_to_grab(pid); + let status = loop { + // When the background thread that is monitoring the task receives an exit/stop signal, + // the status of that process will be communicated back. To avoid consuming CPU cycles, + // we wait 25 ms between polls. + match self.foreground_signals.was_processed() { + Some(BackgroundResult::Status(stat)) => break stat as i32, + Some(BackgroundResult::Errored) => break TERMINATED, + None => sleep(Duration::from_millis(25)) + } + }; + // Have the shell reclaim the TTY + set_foreground_as(get_pid()); + status + } + #[cfg(all(unix, not(target_os = "redox")))] /// Waits until all running background tasks have completed, and listens for signals in the /// event that a signal is sent to kill the running tasks. @@ -151,13 +288,11 @@ impl<'a> JobControl for Shell<'a> { #[cfg(all(unix, not(target_os = "redox")))] fn watch_foreground(&mut self, pid: u32) -> i32 { - use nix::sys::wait::{waitpid, WaitStatus, WUNTRACED, WNOHANG}; + use nix::sys::wait::{waitpid, WaitStatus, WUNTRACED}; use nix::sys::signal::Signal; loop { - match waitpid(-(pid as pid_t), Some(WUNTRACED | WNOHANG)) { - Ok(WaitStatus::Exited(_, status)) => { - break status as i32 - }, + match waitpid(-(pid as pid_t), Some(WUNTRACED)) { + Ok(WaitStatus::Exited(_, status)) => break status as i32, Ok(WaitStatus::Signaled(_, signal, _)) => { eprintln!("ion: process ended by signal"); if signal == Signal::SIGTERM { @@ -180,7 +315,6 @@ impl<'a> JobControl for Shell<'a> { break FAILURE } } - sleep(Duration::from_millis(1)); } } @@ -194,7 +328,7 @@ impl<'a> JobControl for Shell<'a> { loop { let mut status_raw = 0; - match syscall::waitpid(pid as usize, &mut status_raw, WNOHANG) { + match syscall::waitpid(pid as usize, &mut status_raw, 0) { Ok(0) => (), Ok(_pid) => { let status = ExitStatus::from_raw(status_raw as i32); @@ -214,7 +348,6 @@ impl<'a> JobControl for Shell<'a> { break 100 // TODO what should we return here? } } - sleep(Duration::from_millis(1)); } } @@ -222,7 +355,7 @@ impl<'a> JobControl for Shell<'a> { /// Send a kill signal to all running foreground tasks. fn foreground_send(&self, signal: i32) { for process in self.foreground.iter() { - let _ = signal::kill(-(*process as pid_t), NixSignal::from_c_int(signal as c_int).ok()); + let _ = signal::kill(-(*process as pid_t), Signal::from_c_int(signal as c_int).ok()); } } @@ -237,13 +370,13 @@ impl<'a> JobControl for Shell<'a> { if signal == libc::SIGHUP { for process in self.background.lock().unwrap().iter() { if !process.ignore_sighup { - let _ = signal::kill(-(process.pid as pid_t), NixSignal::from_c_int(signal as c_int).ok()); + let _ = signal::kill(-(process.pid as pid_t), Signal::from_c_int(signal as c_int).ok()); } } } else { for process in self.background.lock().unwrap().iter() { if let ProcessState::Running = process.state { - let _ = signal::kill(-(process.pid as pid_t), NixSignal::from_c_int(signal as c_int).ok()); + let _ = signal::kill(-(process.pid as pid_t), Signal::from_c_int(signal as c_int).ok()); } } } @@ -256,10 +389,11 @@ impl<'a> JobControl for Shell<'a> { fn send_to_background(&mut self, pid: u32, state: ProcessState) { let processes = self.background.clone(); + let fg_signals = self.foreground_signals.clone(); let _ = spawn(move || { let njob = add_to_background(processes.clone(), pid, state); eprintln!("ion: bg [{}] {}", njob, pid); - watch_background_pid(processes, pid, njob); + watch_background(fg_signals, processes, pid, njob); }); } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index ccb49228..8cdff2aa 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -8,6 +8,7 @@ mod history; pub mod job_control; mod job; mod pipe; +pub mod signals; pub mod status; pub mod variables; @@ -36,7 +37,7 @@ use smallstring::SmallString; use self::completer::{MultiCompleter, IonFileCompleter}; use self::directory_stack::DirectoryStack; use self::flow_control::{FlowControl, Function, FunctionArgument, Statement, Type}; -use self::job_control::{JobControl, BackgroundProcess}; +use self::job_control::{JobControl, BackgroundProcess, ForegroundSignals}; use self::variables::Variables; use self::status::*; use self::pipe::PipelineExecution; @@ -64,6 +65,7 @@ pub struct Shell<'a> { foreground: Vec<u32>, pub background: Arc<Mutex<Vec<BackgroundProcess>>>, pub received_sigtstp: bool, + pub foreground_signals: Arc<ForegroundSignals> } impl<'a> Shell<'a> { @@ -85,6 +87,7 @@ impl<'a> Shell<'a> { foreground: Vec::new(), background: Arc::new(Mutex::new(Vec::new())), received_sigtstp: false, + foreground_signals: Arc::new(ForegroundSignals::new()) } } diff --git a/src/shell/pipe.rs b/src/shell/pipe.rs index 7dee66a6..2923adf2 100644 --- a/src/shell/pipe.rs +++ b/src/shell/pipe.rs @@ -13,12 +13,15 @@ use super::{JobKind, Shell}; use super::status::*; use parser::peg::{Pipeline, RedirectFrom}; +#[cfg(all(unix, not(target_os = "redox")))] use super::signals::unix as signals; +#[cfg(target_os = "redox")] use super::signals::redox as signals; + use self::crossplat::*; /// The `crossplat` module contains components that are meant to be abstracted across /// different platforms #[cfg(not(target_os = "redox"))] -mod crossplat { +pub mod crossplat { use libc; use nix::{fcntl, unistd}; use parser::peg::{RedirectFrom}; @@ -44,21 +47,6 @@ mod crossplat { } } - pub fn unblock_signals() { - unsafe { - use libc::*; - use std::mem; - use std::ptr; - let mut sigset = mem::uninitialized::<sigset_t>(); - sigemptyset(&mut sigset as *mut sigset_t); - sigaddset(&mut sigset as *mut sigset_t, SIGTSTP); - sigaddset(&mut sigset as *mut sigset_t, SIGTTOU); - sigaddset(&mut sigset as *mut sigset_t, SIGTTIN); - sigaddset(&mut sigset as *mut sigset_t, SIGCHLD); - sigprocmask(SIG_UNBLOCK, &sigset as *const sigset_t, ptr::null_mut() as *mut sigset_t); - } - } - /// When given a process ID, that process will be assigned to a new process group. pub fn create_process_group() { let _ = unistd::setpgid(0, 0); @@ -67,8 +55,6 @@ mod crossplat { /// When given a process ID, that process's group will be assigned as the foreground process group. pub fn set_foreground(pid: u32) { let _ = unistd::tcsetpgrp(0, pid as i32); - let _ = unistd::tcsetpgrp(1, pid as i32); - let _ = unistd::tcsetpgrp(2, pid as i32); } pub fn get_pid() -> u32 { @@ -130,10 +116,6 @@ mod crossplat { } } - pub fn unblock_signals() { - // TODO - } - pub fn create_process_group() { // TODO } @@ -296,7 +278,7 @@ fn fork_pipe(shell: &mut Shell, commands: Vec<(Command, JobKind)>) -> i32 { SUCCESS }, Ok(Fork::Child) => { - unblock_signals(); + signals::unblock(); create_process_group(); exit(pipe(shell, commands, false)); }, @@ -345,7 +327,7 @@ fn pipe ( macro_rules! spawn_proc { ($cmd:expr) => {{ let child = $cmd.before_exec(move || { - unblock_signals(); + signals::unblock(); create_process_group(); Ok(()) }).spawn(); @@ -419,7 +401,7 @@ fn terminate_fg(shell: &mut Shell) { fn execute_command(shell: &mut Shell, command: &mut Command, foreground: bool) -> i32 { match command.before_exec(move || { - unblock_signals(); + signals::unblock(); create_process_group(); Ok(()) }).spawn() { diff --git a/src/shell/signals.rs b/src/shell/signals.rs new file mode 100644 index 00000000..1b32fa46 --- /dev/null +++ b/src/shell/signals.rs @@ -0,0 +1,40 @@ +#[cfg(all(unix, not(target_os = "redox")))] +pub mod unix { + pub fn block() { + unsafe { + use libc::*; + use std::mem; + use std::ptr; + let mut sigset = mem::uninitialized::<sigset_t>(); + sigemptyset(&mut sigset as *mut sigset_t); + sigaddset(&mut sigset as *mut sigset_t, SIGTSTP); + sigaddset(&mut sigset as *mut sigset_t, SIGTTOU); + sigaddset(&mut sigset as *mut sigset_t, SIGTTIN); + sigaddset(&mut sigset as *mut sigset_t, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigset as *const sigset_t, ptr::null_mut() as *mut sigset_t); + } + } + + pub fn unblock() { + unsafe { + use libc::*; + use std::mem; + use std::ptr; + let mut sigset = mem::uninitialized::<sigset_t>(); + sigemptyset(&mut sigset as *mut sigset_t); + sigaddset(&mut sigset as *mut sigset_t, SIGTSTP); + sigaddset(&mut sigset as *mut sigset_t, SIGTTOU); + sigaddset(&mut sigset as *mut sigset_t, SIGTTIN); + sigaddset(&mut sigset as *mut sigset_t, SIGCHLD); + sigprocmask(SIG_UNBLOCK, &sigset as *const sigset_t, ptr::null_mut() as *mut sigset_t); + } + } +} + +// TODO +#[cfg(target_os = "redox")] +pub mod redox { + pub fn block() { } + + pub fn unblock() { } +} -- GitLab