diff --git a/Cargo.toml b/Cargo.toml index 0abd1f2..bd9eada 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,4 @@ crossterm = "0.27.0" colored = "2.1.0" which = "6.0.3" concat-idents = "1.1.5" +nix = "0.29.0" diff --git a/src/parser.rs b/src/parser.rs index 647cd2a..2561deb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,11 +1,15 @@ use std::{ collections::HashMap, io::ErrorKind, - os::fd::{AsRawFd, FromRawFd}, + os::{ + fd::{AsFd, AsRawFd, FromRawFd}, + unix::process::CommandExt, + }, process::{Child, ChildStdout, Stdio}, sync::{Arc, Mutex}, }; +use nix::unistd::{read, write}; use regex::Regex; use crate::env::EnvManager; @@ -403,15 +407,36 @@ impl Pipeline { // 找到内部命令,优先执行,设置标记 internal = true; - // child_fd - let child_fd = if self.backend { + // 用于同步父子进程的tty setpgrp行为的管道 + let (rfd, wfd) = nix::unistd::pipe().expect("Failed to create pipe"); + + // child_pid + let child_pid = if self.backend { unsafe { libc::fork() } } else { 0 }; // 为子进程或前台运行 - if child_fd == 0 { + if child_pid == 0 { + if self.backend { + drop(wfd); + let mut tmpbf = [0u8; 1]; + loop { + let x = read(rfd.as_raw_fd(), &mut tmpbf) + .expect("Failed to read from pipe"); + if x > 0 { + break; + } else { + std::thread::sleep(std::time::Duration::from_millis(30)); + } + } + drop(rfd); + } else { + drop(rfd); + drop(wfd); + } + let mut old_stdin: Option = None; let mut old_stdout: Option = None; @@ -499,14 +524,18 @@ impl Pipeline { // 当前为后台进程,退出当前进程 std::process::exit(if err.is_none() { 0 } else { 1 }); } - } else if child_fd > 0 { + } else if child_pid > 0 { // 当前进程为父进程 + drop(rfd); unsafe { // 设置前台进程 - libc::tcsetpgrp(libc::STDIN_FILENO, child_fd); + libc::tcsetpgrp(libc::STDIN_FILENO, child_pid); + // 让子进程开始运行 + write(wfd.as_fd(), &[1]).expect("Failed to write to pipe"); + drop(wfd); let mut status = 0; - err = match libc::waitpid(child_fd, &mut status, 0) { + err = match libc::waitpid(child_pid, &mut status, 0) { -1 => Some(ExecuteErrorType::ExecuteFailed), _ => None, }; @@ -522,7 +551,14 @@ impl Pipeline { } // 还原前台进程 - libc::tcsetpgrp(libc::STDIN_FILENO, std::process::id() as i32); + let r = libc::tcsetpgrp(libc::STDIN_FILENO, std::process::id() as i32); + if r == -1 { + let errno = std::io::Error::last_os_error().raw_os_error().unwrap(); + println!( + "[novashell error]: restore tty pgrp: tcsetpgrp failed: {}", + errno + ); + } } } else { err = Some(ExecuteErrorType::ExecuteFailed) @@ -596,7 +632,22 @@ impl Pipeline { child_command.stdout(Stdio::piped()); } } + let (rfd, wfd) = nix::unistd::pipe().expect("Failed to create pipe"); + unsafe { + child_command.pre_exec(move || { + let mut b = [0u8; 1]; + loop { + let x = nix::unistd::read(rfd.as_raw_fd(), &mut b)?; + if x != 0 { + break; + } else { + std::thread::sleep(std::time::Duration::from_millis(30)); + } + } + Ok(()) + }); + } if err.is_none() { match child_command.spawn() { Ok(mut child) => { @@ -611,7 +662,9 @@ impl Pipeline { // 设置前台进程 libc::tcsetpgrp(libc::STDIN_FILENO, child.id() as i32); }; - + // 让子进程继续执行 + write(wfd.as_fd(), &[1u8]).expect("Failed to write to pipe"); + drop(wfd); match child.wait() { Ok(exit_status) => match exit_status.code() { Some(exit_code) => { diff --git a/src/shell/command/mod.rs b/src/shell/command/mod.rs index 424d17b..77b3fe9 100644 --- a/src/shell/command/mod.rs +++ b/src/shell/command/mod.rs @@ -1,5 +1,7 @@ use help::Helper; use std::collections::HashMap; +use std::os::fd::{AsFd, AsRawFd}; +use std::os::unix::process::CommandExt; use std::sync::{Arc, Mutex}; use std::{fs::File, io::Read, print}; @@ -106,18 +108,37 @@ impl BuildInCmd { false }; - let mut err: Option = None; - - match std::process::Command::new(real_path) + let mut child_command = std::process::Command::new(real_path); + child_command .args(args) - .current_dir(EnvManager::current_dir()) - .spawn() - { + .current_dir(EnvManager::current_dir()); + + let (rfd, wfd) = nix::unistd::pipe().expect("Failed to create pipe"); + + unsafe { + child_command.pre_exec(move || { + let mut b = [0u8; 1]; + loop { + let x = nix::unistd::read(rfd.as_raw_fd(), &mut b)?; + if x != 0 { + break; + } else { + std::thread::sleep(std::time::Duration::from_millis(30)); + } + } + Ok(()) + }); + } + + let mut err: Option = None; + match child_command.spawn() { Ok(mut child) => { if run_foreground { unsafe { libc::tcsetpgrp(libc::STDIN_FILENO, child.id() as i32) }; } - + // 让子进程继续执行 + nix::unistd::write(wfd.as_fd(), &[1u8]).expect("Failed to write to pipe"); + drop(wfd); match child.wait() { Ok(exit_status) => match exit_status.code() { Some(exit_code) => { diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 8a8ba28..126342b 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -104,6 +104,7 @@ impl Shell { .push(Rc::new(RefCell::new(command_bytes.clone()))); self.write_commands(&command_bytes); }; + // 命令不为空,执行命令 if !command_bytes.iter().all(|&byte| byte == b' ') { self.exec_commands_in_line(&command_bytes); @@ -177,8 +178,9 @@ impl Shell { fn read_char() -> u8 { let mut buf: [u8; 1] = [0]; loop { - if std::io::stdin().read(&mut buf).is_ok() { - return buf[0]; + match std::io::stdin().read(&mut buf) { + Ok(_) => return buf[0], + Err(e) => println!("read char failed: {}", e), } } }