Browse Source

修复执行前台命令时未设置前台进程的错误 (#51)

* 修复前台命令未设置前台进程的问题

* 修复exec命令未设置前台进程的问题

* 完善pipeline的执行逻辑
MemoryShore 5 months ago
parent
commit
cb835e03e4
2 changed files with 78 additions and 21 deletions
  1. 45 11
      src/parser.rs
  2. 33 10
      src/shell/command/mod.rs

+ 45 - 11
src/parser.rs

@@ -479,9 +479,8 @@ impl Pipeline {
 
                         // 如果之前没有出错,执行命令
                         if err.is_none() {
-                            match f(&cmd.args) {
-                                Ok(_) => err = None,
-                                Err(err_type) => err = Some(err_type),
+                            if let Err(err_type) = f(&cmd.args) {
+                                err = Some(err_type);
                             }
                         }
 
@@ -495,16 +494,38 @@ impl Pipeline {
                                 libc::dup2(old_stdout, libc::STDOUT_FILENO);
                             }
                         }
-                    } else if child_fd < 0 {
-                        err = Some(ExecuteErrorType::ExecuteFailed)
-                    }
 
-                    // 后台命令且当前进程为父进程
-                    if self.backend && !child_fd == 0 {
-                        err = match unsafe { libc::waitpid(child_fd, std::ptr::null_mut(), 0) } {
-                            -1 => Some(ExecuteErrorType::ExecuteFailed),
-                            _ => None,
+                        if self.backend {
+                            // 当前为后台进程,退出当前进程
+                            std::process::exit(if err.is_none() { 0 } else { 1 });
+                        }
+                    } else if child_fd > 0 {
+                        // 当前进程为父进程
+                        unsafe {
+                            // 设置前台进程
+                            libc::tcsetpgrp(libc::STDIN_FILENO, child_fd);
+
+                            let mut status = 0;
+                            err = match libc::waitpid(child_fd, &mut status, 0) {
+                                -1 => Some(ExecuteErrorType::ExecuteFailed),
+                                _ => None,
+                            };
+
+                            if status != 0 {
+                                if libc::WIFEXITED(status) {
+                                    if libc::WEXITSTATUS(status) != 0 {
+                                        err = Some(ExecuteErrorType::ExitWithCode(status));
+                                    }
+                                } else if libc::WIFSIGNALED(status) {
+                                    err = Some(ExecuteErrorType::ProcessTerminated);
+                                }
+                            }
+
+                            // 还原前台进程
+                            libc::tcsetpgrp(libc::STDIN_FILENO, std::process::id() as i32);
                         }
+                    } else {
+                        err = Some(ExecuteErrorType::ExecuteFailed)
                     }
                 }
             };
@@ -586,6 +607,11 @@ impl Pipeline {
 
                                     // println!("exec command: {child_command:#?}");
 
+                                    unsafe {
+                                        // 设置前台进程
+                                        libc::tcsetpgrp(libc::STDIN_FILENO, child.id() as i32);
+                                    };
+
                                     match child.wait() {
                                         Ok(exit_status) => match exit_status.code() {
                                             Some(exit_code) => {
@@ -600,6 +626,14 @@ impl Pipeline {
                                         Err(_) => err = Some(ExecuteErrorType::ExecuteFailed),
                                     };
 
+                                    // 还原前台进程
+                                    unsafe {
+                                        libc::tcsetpgrp(
+                                            libc::STDIN_FILENO,
+                                            std::process::id() as i32,
+                                        );
+                                    }
+
                                     children.push(child);
                                 }
 

+ 33 - 10
src/shell/command/mod.rs

@@ -93,6 +93,19 @@ impl BuildInCmd {
                 which::which(name).map_err(|_| ExecuteErrorType::CommandNotFound)
             }?;
 
+            let pgrp = unsafe { libc::tcgetpgrp(libc::STDIN_FILENO) };
+
+            // 如果当前终端的前台进程等于当前进程,则设置前台进程
+            let run_foreground = if pgrp >= 0 {
+                if pgrp as u32 == std::process::id() {
+                    true
+                } else {
+                    false
+                }
+            } else {
+                false
+            };
+
             let mut err: Option<ExecuteErrorType> = None;
 
             match std::process::Command::new(real_path)
@@ -100,17 +113,27 @@ impl BuildInCmd {
                 .current_dir(EnvManager::current_dir())
                 .spawn()
             {
-                Ok(mut child) => match child.wait() {
-                    Ok(exit_status) => match exit_status.code() {
-                        Some(exit_code) => {
-                            if exit_code != 0 {
-                                err = Some(ExecuteErrorType::ExitWithCode(exit_code));
+                Ok(mut child) => {
+                    if run_foreground {
+                        unsafe { libc::tcsetpgrp(libc::STDIN_FILENO, child.id() as i32) };
+                    }
+
+                    match child.wait() {
+                        Ok(exit_status) => match exit_status.code() {
+                            Some(exit_code) => {
+                                if exit_code != 0 {
+                                    err = Some(ExecuteErrorType::ExitWithCode(exit_code));
+                                }
                             }
-                        }
-                        None => err = Some(ExecuteErrorType::ProcessTerminated),
-                    },
-                    Err(_) => err = Some(ExecuteErrorType::ExecuteFailed),
-                },
+                            None => err = Some(ExecuteErrorType::ProcessTerminated),
+                        },
+                        Err(_) => err = Some(ExecuteErrorType::ExecuteFailed),
+                    }
+
+                    if run_foreground {
+                        unsafe { libc::tcsetpgrp(libc::STDIN_FILENO, std::process::id() as i32) };
+                    }
+                }
                 Err(_) => todo!(),
             };
             return if let Some(err) = err {