From 9e5b39c0791ac8955a9461a7760dccb28046dfac Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy <mmstickman@gmail.com> Date: Thu, 26 Oct 2017 14:03:31 -0400 Subject: [PATCH] Remove Dependency On nix Crate Closes #547 This will remove all dependency upon the nix crate, as we will be using `libc` directly for the `waitpid` functionality on *nix systems. This should also make it easier to integrate with Redox. --- Cargo.lock | 37 ++--- Cargo.toml | 76 ++++------ src/lib.rs | 6 +- src/main.rs | 6 +- src/shell/pipe_exec/fork.rs | 1 + src/sys/redox.rs | 25 +--- src/sys/unix.rs | 289 ------------------------------------ src/sys/unix/job_control.rs | 153 +++++++++++++++++++ src/sys/unix/mod.rs | 121 +++++++++++++++ src/sys/unix/signals.rs | 34 +++++ 10 files changed, 360 insertions(+), 388 deletions(-) delete mode 100644 src/sys/unix.rs create mode 100644 src/sys/unix/job_control.rs create mode 100644 src/sys/unix/mod.rs create mode 100644 src/sys/unix/signals.rs diff --git a/Cargo.lock b/Cargo.lock index 6f6311ec..48504811 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,13 +6,13 @@ dependencies = [ "app_dirs 1.1.1 (git+https://github.com/redox-os/app-dirs-rs.git)", "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "calculate 0.3.0 (git+https://github.com/redox-os/calc.git)", + "errno 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", "libloading 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "liner 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "permutate 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -47,11 +47,6 @@ dependencies = [ "xdg 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "bitflags" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "bitflags" version = "0.9.1" @@ -75,11 +70,6 @@ dependencies = [ "decimal 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "cfg-if" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "decimal" version = "2.0.0" @@ -93,6 +83,16 @@ dependencies = [ "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "errno" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fnv" version = "1.0.5" @@ -155,17 +155,6 @@ dependencies = [ "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "nix" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "ole32-sys" version = "0.2.0" @@ -331,13 +320,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" "checksum app_dirs 1.1.1 (git+https://github.com/redox-os/app-dirs-rs.git)" = "<none>" -"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5cde24d1b2e2216a726368b2363a273739c91f4e3eb4e0dd12d672d396ad989" "checksum bytecount 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "4bbeb7c30341fce29f6078b4bdf876ea4779600866e98f5b2d203a534f195050" "checksum calculate 0.3.0 (git+https://github.com/redox-os/calc.git)" = "<none>" -"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" "checksum decimal 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8e3bfe070d7876527c8f901afc27b4190a715b6e52c8961f72fb35430960e80" +"checksum errno 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b2c858c42ac0b88532f48fca88b0ed947cad4f1f64d904bcd6c9f138f7b95d70" "checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344" "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" @@ -347,7 +335,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum libloading 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3f92926a9a4ba7aeeb01f5fba3f0d577147243b6e7fa8261c219cd1d6fbe3b1c" "checksum liner 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aff4455f523eb06630ad00ea139701ba51a5425d0a8163935e7bb1841c81e6e8" "checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4" -"checksum nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47e49f6982987135c5e9620ab317623e723bd06738fd85377e8d55f57c8b6487" "checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" "checksum ord_subset 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bdb8b8c275824bc609c8294f20a55d37d808a1b071cec596da746ce4df0405e8" "checksum permutate 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53b7d5b19a715ffab38693a9dd44b067fdfa2b18eef65bd93562dfe507022fae" diff --git a/Cargo.toml b/Cargo.toml index 85c63b5a..87c359c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,76 +1,54 @@ [package] -name = "ion-shell" -description = "The Ion Shell" -repository = "https://github.com/redox-os/ion" -version = "1.0.5" -license-file = "LICENSE" -readme = "README.md" authors = [ - "Michael Aaron Murphy <mmstickman@gmail.com>", - "Hunter Goldstein <hunter.d.goldstein@gmail.com>", - "Skyler Berg <skylertheberg@gmail.com>", - "Jeremy Soller <jackpot51@gmail.com>", - "Michael Gattozzi <mgattozzi@gmail.com>", - "Łukasz Niemier <lukasz@niemier.pl>", + "Michael Aaron Murphy <mmstickman@gmail.com>", + "Hunter Goldstein <hunter.d.goldstein@gmail.com>", + "Skyler Berg <skylertheberg@gmail.com>", + "Jeremy Soller <jackpot51@gmail.com>", + "Michael Gattozzi <mgattozzi@gmail.com>", + "Łukasz Niemier <lukasz@niemier.pl>", ] build = "build.rs" +description = "The Ion Shell" +license-file = "LICENSE" +name = "ion-shell" +readme = "README.md" +repository = "https://github.com/redox-os/ion" +version = "1.0.5" [[bin]] name = "ion" path = "src/main.rs" -## Shared Dependencies +[build-dependencies] +ansi_term = "0.9" +version_check = "0.1.3" [dependencies] -# Provides XDG app directory support -app_dirs = {git = "https://github.com/redox-os/app-dirs-rs.git"} -# Provides methods for bitwise flag operations bitflags = "0.9.1" -# Provides inline arithmetic expression and `calc` functionality -calculate = {git = "https://github.com/redox-os/calc.git"} -# A faster hashing algorithm for the hash maps in the shell. fnv = "1.0" -# Performs globbing on words that are detected to be potentially globbable. glob = "0.2" -# Provides a macro for lazily-evalulated statics lazy_static = "0.2" -# Provides the line editor / prompt for the shell liner = "0.4" -# Provides permutations of strings in brace expansions permutate = "0.3" -# Enables strings to be stored inline on the stack, when possible. +regex = "0.2" smallstring = "0.1" -# Same as the above, but for vectors. smallvec = "0.4" -# Provides grapheme-based string iterators. unicode-segmentation = "1.2" -# Rusts regex crate -regex = "0.2" -## Redox Dependencies +[dependencies.app_dirs] +git = "https://github.com/redox-os/app-dirs-rs.git" -[target.'cfg(target_os = "redox")'.dependencies] -# Provides access to Redox syscalls for signals/job control. -redox_syscall = "0.1" +[dependencies.calculate] +git = "https://github.com/redox-os/calc.git" -## *nix Dependencies (Linux, Mac OS, BSDs) +[profile.release] +panic = "abort" -[target.'cfg(all(unix, not(target_os = "redox")))'.dependencies] -# Required to access some *nix-specific syscalls for signals/job control. +[target."cfg(all(unix, not(target_os = \"redox\")))".dependencies] +errno = "0.2.3" libc = "0.2" -# A higher level abstraction of libc-acquired syscalls for signals/job control. -nix = "0.8" -# Obtains user directories -users = "0.5.1" -# Enables loading plugins libloading = "0.4" +users = "0.5.1" -[build-dependencies] -ansi_term = "0.9" -version_check = "0.1.3" - -[profile.release] -# debug = true -# rustflags = [ "-C", "target-cpu=native"] -# lto = true -panic = "abort" +[target."cfg(target_os = \"redox\")".dependencies] +redox_syscall = "0.1" diff --git a/src/lib.rs b/src/lib.rs index b758479a..6f277b14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,8 @@ extern crate app_dirs; #[macro_use] extern crate bitflags; extern crate calc; +#[cfg(all(unix, not(target_os = "redox")))] +extern crate errno; extern crate fnv; extern crate glob; #[macro_use] @@ -16,8 +18,6 @@ extern crate libc; #[cfg(all(unix, not(target_os = "redox")))] extern crate libloading; extern crate liner; -#[cfg(all(unix, not(target_os = "redox")))] -extern crate nix; extern crate regex; extern crate smallstring; extern crate smallvec; @@ -32,7 +32,7 @@ extern crate users as users_unix; mod sys; #[cfg(unix)] -#[path = "sys/unix.rs"] +#[path = "sys/unix/mod.rs"] mod sys; #[macro_use] diff --git a/src/main.rs b/src/main.rs index 36928eb3..cd28846c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,8 @@ extern crate app_dirs; #[macro_use] extern crate bitflags; extern crate calc; +#[cfg(all(unix, not(target_os = "redox")))] +extern crate errno; extern crate fnv; extern crate glob; #[macro_use] @@ -20,8 +22,6 @@ extern crate libc; #[cfg(all(unix, not(target_os = "redox")))] extern crate libloading; extern crate liner; -#[cfg(all(unix, not(target_os = "redox")))] -extern crate nix; extern crate regex; extern crate smallstring; extern crate smallvec; @@ -36,7 +36,7 @@ extern crate users as users_unix; mod sys; #[cfg(unix)] -#[path = "sys/unix.rs"] +#[path = "sys/unix/mod.rs"] mod sys; #[macro_use] diff --git a/src/shell/pipe_exec/fork.rs b/src/shell/pipe_exec/fork.rs index 476b0d23..5ac83622 100644 --- a/src/shell/pipe_exec/fork.rs +++ b/src/shell/pipe_exec/fork.rs @@ -25,6 +25,7 @@ pub(crate) fn fork_pipe( let _ = sys::reset_signal(sys::SIGTERM); // This ensures that the child fork has a unique PGID. create_process_group(0); + sys::close_stdin(); // After execution of it's commands, exit with the last command's status. exit(pipe(shell, commands, false)); } diff --git a/src/sys/redox.rs b/src/sys/redox.rs index a314db42..0c812180 100644 --- a/src/sys/redox.rs +++ b/src/sys/redox.rs @@ -83,6 +83,10 @@ pub(crate) fn dup2(old: RawFd, new: RawFd) -> io::Result<RawFd> { pub(crate) fn close(fd: RawFd) -> io::Result<()> { cvt(syscall::close(fd)).and(Ok(())) } +pub(crate) fn close_stdin() { + syscall::close(STDIN_FILENO); +} + pub(crate) fn isatty(fd: RawFd) -> bool { if let Ok(tfd) = syscall::dup(fd, b"termios") { let _ = syscall::close(tfd); @@ -99,29 +103,12 @@ fn cvt(result: Result<usize, syscall::Error>) -> io::Result<usize> { // TODO pub mod signals { - pub(crate) fn block() - // fn block() - // fn block() - // fn block() - // fn block() - // fn block() // fn block() // fn block() // fn block() // fn block() // - // fn block() - { - } + pub(crate) fn block() { } /// Unblocks the SIGTSTP/SIGTTOU/SIGTTIN/SIGCHLD signals so children processes can be /// controlled /// by the shell. - pub(crate) fn unblock() - // fn unblock() - // fn unblock() - // fn unblock() - // fn unblock() - // fn unblock() - // fn unblock() // fn unblock() // fn unblock() // fn unblock() // fn - // unblock() - { - } + pub(crate) fn unblock() { } } pub mod job_control { diff --git a/src/sys/unix.rs b/src/sys/unix.rs deleted file mode 100644 index d287f4bc..00000000 --- a/src/sys/unix.rs +++ /dev/null @@ -1,289 +0,0 @@ -extern crate libc; - -use libc::{c_int, pid_t, sighandler_t}; -use std::io; -use std::os::unix::io::RawFd; - -pub(crate) const PATH_SEPARATOR: &str = ":"; - -pub(crate) const O_CLOEXEC: usize = libc::O_CLOEXEC as usize; -pub(crate) const SIGHUP: i32 = libc::SIGHUP; -pub(crate) const SIGINT: i32 = libc::SIGINT; -pub(crate) const SIGTERM: i32 = libc::SIGTERM; -pub(crate) const SIGCONT: i32 = libc::SIGCONT; -pub(crate) const SIGSTOP: i32 = libc::SIGSTOP; -pub(crate) const SIGTSTP: i32 = libc::SIGTSTP; - -pub(crate) const STDOUT_FILENO: i32 = libc::STDOUT_FILENO; -pub(crate) const STDERR_FILENO: i32 = libc::STDERR_FILENO; -pub(crate) const STDIN_FILENO: i32 = libc::STDIN_FILENO; - -pub(crate) fn is_root() -> bool { unsafe { libc::geteuid() == 0 } } - -pub unsafe fn fork() -> io::Result<u32> { cvt(libc::fork()).map(|pid| pid as u32) } - -pub(crate) fn getpid() -> io::Result<u32> { cvt(unsafe { libc::getpid() }).map(|pid| pid as u32) } - -pub(crate) fn kill(pid: u32, signal: i32) -> io::Result<()> { - cvt(unsafe { libc::kill(pid as pid_t, signal as c_int) }).and(Ok(())) -} - -pub(crate) fn killpg(pgid: u32, signal: i32) -> io::Result<()> { - cvt(unsafe { libc::kill(-(pgid as pid_t), signal as c_int) }).and(Ok(())) -} - -pub(crate) fn pipe2(flags: usize) -> io::Result<(RawFd, RawFd)> { - let mut fds = [0; 2]; - - #[cfg(not(target_os = "macos"))] - cvt(unsafe { libc::pipe2(fds.as_mut_ptr(), flags as c_int) })?; - - #[cfg(target_os = "macos")] - cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?; - - Ok((fds[0], fds[1])) -} - -pub(crate) fn setpgid(pid: u32, pgid: u32) -> io::Result<()> { - cvt(unsafe { libc::setpgid(pid as pid_t, pgid as pid_t) }).and(Ok(())) -} - -#[allow(dead_code)] -pub(crate) fn signal(signal: i32, handler: extern "C" fn(i32)) -> io::Result<()> { - if unsafe { libc::signal(signal as c_int, handler as sighandler_t) } == libc::SIG_ERR { - Err(io::Error::last_os_error()) - } else { - Ok(()) - } -} - -pub(crate) fn reset_signal(signal: i32) -> io::Result<()> { - if unsafe { libc::signal(signal as c_int, libc::SIG_DFL) } == libc::SIG_ERR { - Err(io::Error::last_os_error()) - } else { - Ok(()) - } -} - -pub(crate) fn tcsetpgrp(fd: RawFd, pgrp: u32) -> io::Result<()> { - cvt(unsafe { libc::tcsetpgrp(fd as c_int, pgrp as pid_t) }).and(Ok(())) -} - -pub(crate) fn dup(fd: RawFd) -> io::Result<RawFd> { cvt(unsafe { libc::dup(fd) }) } - -pub(crate) fn dup2(old: RawFd, new: RawFd) -> io::Result<RawFd> { - cvt(unsafe { libc::dup2(old, new) }) -} - -pub(crate) fn close(fd: RawFd) -> io::Result<()> { cvt(unsafe { libc::close(fd) }).and(Ok(())) } - -pub(crate) fn isatty(fd: RawFd) -> bool { unsafe { libc::isatty(fd) == 1 } } - -// Support functions for converting libc return values to io errors { -trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) - } - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> { - if t.is_minus_one() { - Err(io::Error::last_os_error()) - } else { - Ok(t) - } -} -// } End of support functions - -pub mod signals { - /// Blocks the SIGTSTP/SIGTTOU/SIGTTIN/SIGCHLD signals so that the shell never receives - /// them. - pub(crate) 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); - } - } - - /// Unblocks the SIGTSTP/SIGTTOU/SIGTTIN/SIGCHLD signals so children processes can be - /// controlled - /// by the shell. - pub(crate) 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); - } - } -} - -pub mod job_control { - use shell::job_control::*; - - use libc::{self, pid_t}; - use shell::Shell; - use shell::foreground::ForegroundSignals; - use shell::status::{FAILURE, TERMINATED}; - use std::sync::{Arc, Mutex}; - use std::thread::sleep; - use std::time::Duration; - - use nix::sys::wait::{waitpid, WaitStatus, WNOHANG, WUNTRACED}; - #[cfg(not(target_os = "macos"))] - use nix::sys::wait::WCONTINUED; - - use nix::{Errno, Error}; - use nix::sys::signal::Signal; - - pub(crate) fn watch_background( - fg: Arc<ForegroundSignals>, - processes: Arc<Mutex<Vec<BackgroundProcess>>>, - pid: u32, - njob: usize, - ) { - let mut fg_was_grabbed = false; - loop { - if !fg_was_grabbed { - if fg.was_grabbed(pid) { - fg_was_grabbed = true; - } - } - - #[cfg(not(target_os = "macos"))] - let opts = Some(WUNTRACED | WCONTINUED | WNOHANG); - #[cfg(target_os = "macos")] - let opts = Some(WUNTRACED | WNOHANG); - - match waitpid(-(pid as pid_t), opts) { - Ok(WaitStatus::Exited(_, 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, _)) => { - 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)) => { - 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; - } - Ok(_) => (), - Err(why) => { - eprintln!("ion: ([{}] {}) errored: {}", njob, pid, why); - 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)); - } - } - - pub(crate) fn watch_foreground<'a, F, D>( - shell: &mut Shell<'a>, - _pid: u32, - last_pid: u32, - get_command: F, - mut drop_command: D, - ) -> i32 - where F: FnOnce() -> String, - D: FnMut(i32) - { - let mut exit_status = 0; - loop { - match waitpid(-1, Some(WUNTRACED)) { - Ok(WaitStatus::Exited(pid, status)) => if pid == (last_pid as i32) { - break status as i32; - } else { - drop_command(pid); - exit_status = status; - }, - Ok(WaitStatus::Signaled(_, signal, _)) => { - eprintln!("ion: process ended by signal"); - if signal == Signal::SIGTERM { - shell.handle_signal(libc::SIGTERM); - shell.exit(TERMINATED); - } else if signal == Signal::SIGHUP { - shell.handle_signal(libc::SIGHUP); - shell.exit(TERMINATED); - } else if signal == Signal::SIGINT { - shell.foreground_send(libc::SIGINT as i32); - shell.break_flow = true; - } - break TERMINATED; - } - Ok(WaitStatus::Stopped(pid, _)) => { - shell.send_to_background(pid as u32, ProcessState::Stopped, get_command()); - shell.break_flow = true; - break TERMINATED; - } - Ok(_) => (), - // ECHILD signifies that all children have exited - Err(Error::Sys(Errno::ECHILD)) => break exit_status as i32, - Err(why) => { - eprintln!("ion: process doesn't exist: {}", why); - break FAILURE; - } - } - } - } -} - -pub mod variables { - use users_unix::get_user_by_name; - use users_unix::os::unix::UserExt; - - pub(crate) fn get_user_home(username: &str) -> Option<String> { - match get_user_by_name(username) { - Some(user) => Some(user.home_dir().to_string_lossy().into_owned()), - None => None, - } - } -} diff --git a/src/sys/unix/job_control.rs b/src/sys/unix/job_control.rs new file mode 100644 index 00000000..922ebc65 --- /dev/null +++ b/src/sys/unix/job_control.rs @@ -0,0 +1,153 @@ +use errno::errno; +use libc::*; +use shell::Shell; +use shell::foreground::ForegroundSignals; +use shell::job_control::*; +use shell::status::{FAILURE, TERMINATED}; +use std::sync::{Arc, Mutex}; +use std::thread::sleep; +use std::time::Duration; + +pub(crate) fn watch_background( + fg: Arc<ForegroundSignals>, + processes: Arc<Mutex<Vec<BackgroundProcess>>>, + pid: u32, + njob: usize, +) { + let mut fg_was_grabbed = false; + loop { + if !fg_was_grabbed { + if fg.was_grabbed(pid) { + fg_was_grabbed = true; + } + } + + let opts = WUNTRACED | WCONTINUED | WNOHANG; + let mut status = 0; + + unsafe { + let pid = waitpid(-(pid as pid_t), &mut status, opts); + match pid { + -1 => { + eprintln!("ion: ([{}] {}) errored: {}", njob, pid, errno()); + 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; + } + 0 => (), + _ if WIFEXITED(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(WEXITSTATUS(status) as i8); + } + break; + } + _ if WIFSTOPPED(status) => { + 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; + } + _ if WIFCONTINUED(status) => { + 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; + } + _ => (), + } + } + sleep(Duration::from_millis(100)); + } +} + +const FIRST: u8 = 1; +const LAST: u8 = 2; + +pub(crate) fn watch_foreground<'a, F, D>( + shell: &mut Shell<'a>, + first_pid: u32, + last_pid: u32, + get_command: F, + mut drop_command: D, +) -> i32 + where F: FnOnce() -> String, + D: FnMut(i32) +{ + let mut exit_status = 0; + let mut found = 0; + loop { + unsafe { + let mut status = 0; + let pid = waitpid(-1, &mut status, WUNTRACED); + match pid { + -1 => { + let error = errno(); + match error.0 { + ECHILD => break exit_status, + _ => { + eprintln!("ion: {}", error); + break FAILURE; + } + } + } + 0 => (), + _ if WIFEXITED(status) => { + let status = WEXITSTATUS(status) as i32; + if pid == (last_pid as i32) { + found |= LAST; + } + + if pid == (first_pid as i32) { + found |= FIRST; + } + + if found == FIRST + LAST { + break status; + } else { + drop_command(pid); + exit_status = status; + } + } + _ if WIFSIGNALED(status) => { + eprintln!("ion: process ended by signal"); + let signal = WTERMSIG(status); + match signal { + SIGINT => { + shell.foreground_send(signal as i32); + shell.break_flow = true; + } + _ => { + shell.handle_signal(signal); + shell.exit(TERMINATED); + } + } + break TERMINATED; + } + _ if WIFSTOPPED(status) => { + shell.send_to_background(pid as u32, ProcessState::Stopped, get_command()); + shell.break_flow = true; + break TERMINATED; + } + _ => (), + } + } + } +} diff --git a/src/sys/unix/mod.rs b/src/sys/unix/mod.rs new file mode 100644 index 00000000..898717ff --- /dev/null +++ b/src/sys/unix/mod.rs @@ -0,0 +1,121 @@ +extern crate libc; + +pub mod job_control; +pub mod signals; + +use libc::{c_int, pid_t, sighandler_t}; +use std::io; +use std::os::unix::io::RawFd; + +pub(crate) const PATH_SEPARATOR: &str = ":"; + +pub(crate) const O_CLOEXEC: usize = libc::O_CLOEXEC as usize; +pub(crate) const SIGHUP: i32 = libc::SIGHUP; +pub(crate) const SIGINT: i32 = libc::SIGINT; +pub(crate) const SIGTERM: i32 = libc::SIGTERM; +pub(crate) const SIGCONT: i32 = libc::SIGCONT; +pub(crate) const SIGSTOP: i32 = libc::SIGSTOP; +pub(crate) const SIGTSTP: i32 = libc::SIGTSTP; + +pub(crate) const STDOUT_FILENO: i32 = libc::STDOUT_FILENO; +pub(crate) const STDERR_FILENO: i32 = libc::STDERR_FILENO; +pub(crate) const STDIN_FILENO: i32 = libc::STDIN_FILENO; + +pub(crate) fn is_root() -> bool { unsafe { libc::geteuid() == 0 } } + +pub unsafe fn fork() -> io::Result<u32> { cvt(libc::fork()).map(|pid| pid as u32) } + +pub(crate) fn getpid() -> io::Result<u32> { cvt(unsafe { libc::getpid() }).map(|pid| pid as u32) } + +pub(crate) fn kill(pid: u32, signal: i32) -> io::Result<()> { + cvt(unsafe { libc::kill(pid as pid_t, signal as c_int) }).and(Ok(())) +} + +pub(crate) fn killpg(pgid: u32, signal: i32) -> io::Result<()> { + cvt(unsafe { libc::kill(-(pgid as pid_t), signal as c_int) }).and(Ok(())) +} + +pub(crate) fn pipe2(flags: usize) -> io::Result<(RawFd, RawFd)> { + let mut fds = [0; 2]; + + #[cfg(not(target_os = "macos"))] + cvt(unsafe { libc::pipe2(fds.as_mut_ptr(), flags as c_int) })?; + + #[cfg(target_os = "macos")] + cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?; + + Ok((fds[0], fds[1])) +} + +pub(crate) fn setpgid(pid: u32, pgid: u32) -> io::Result<()> { + cvt(unsafe { libc::setpgid(pid as pid_t, pgid as pid_t) }).and(Ok(())) +} + +#[allow(dead_code)] +pub(crate) fn signal(signal: i32, handler: extern "C" fn(i32)) -> io::Result<()> { + if unsafe { libc::signal(signal as c_int, handler as sighandler_t) } == libc::SIG_ERR { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } +} + +pub(crate) fn reset_signal(signal: i32) -> io::Result<()> { + if unsafe { libc::signal(signal as c_int, libc::SIG_DFL) } == libc::SIG_ERR { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } +} + +pub(crate) fn tcsetpgrp(fd: RawFd, pgrp: u32) -> io::Result<()> { + cvt(unsafe { libc::tcsetpgrp(fd as c_int, pgrp as pid_t) }).and(Ok(())) +} + +pub(crate) fn dup(fd: RawFd) -> io::Result<RawFd> { cvt(unsafe { libc::dup(fd) }) } + +pub(crate) fn dup2(old: RawFd, new: RawFd) -> io::Result<RawFd> { + cvt(unsafe { libc::dup2(old, new) }) +} + +pub(crate) fn close(fd: RawFd) -> io::Result<()> { cvt(unsafe { libc::close(fd) }).and(Ok(())) } + +pub(crate) fn close_stdin() { + unsafe { libc::close(STDIN_FILENO); } +} + +pub(crate) fn isatty(fd: RawFd) -> bool { unsafe { libc::isatty(fd) == 1 } } + +trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) + } + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> { + if t.is_minus_one() { + Err(io::Error::last_os_error()) + } else { + Ok(t) + } +} + +pub mod variables { + use users_unix::get_user_by_name; + use users_unix::os::unix::UserExt; + + pub(crate) fn get_user_home(username: &str) -> Option<String> { + match get_user_by_name(username) { + Some(user) => Some(user.home_dir().to_string_lossy().into_owned()), + None => None, + } + } +} diff --git a/src/sys/unix/signals.rs b/src/sys/unix/signals.rs new file mode 100644 index 00000000..659384f0 --- /dev/null +++ b/src/sys/unix/signals.rs @@ -0,0 +1,34 @@ +use libc::*; +use std::mem; +use std::ptr; + +/// Blocks the SIGTSTP/SIGTTOU/SIGTTIN/SIGCHLD signals so that the shell never receives +/// them. +pub(crate) fn block() // fn block() // fn block() +{ + unsafe { + 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); + } +} + +/// Unblocks the SIGTSTP/SIGTTOU/SIGTTIN/SIGCHLD signals so children processes can be +/// controlled +/// by the shell. +pub(crate) fn unblock() // fn unblock() // fn unblock() +{ + unsafe { + 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); + } +} -- GitLab