Foreground process group management, again (#6584)

* Revert "Revert "Try again: in unix like system, set foreground process while running external command (#6273)" (#6542)"

This reverts commit 2bb367f570.

* Make foreground job control hopefully work correctly

These changes are mostly inspired by the glibc manual.

* Fix typo in external command description

* Only restore tty control to shell when no fg procs are left; reuse pgrp

* Rework terminal acquirement code to be like fish

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
This commit is contained in:
unrelentingtech
2022-09-29 21:37:48 +03:00
committed by GitHub
parent 6486364610
commit 4af0a6a3fa
8 changed files with 323 additions and 13 deletions

View File

@ -7,6 +7,7 @@ use nu_protocol::did_you_mean;
use nu_protocol::engine::{EngineState, Stack};
use nu_protocol::{ast::Call, engine::Command, ShellError, Signature, SyntaxShape, Value};
use nu_protocol::{Category, Example, ListStream, PipelineData, RawStream, Span, Spanned};
use nu_system::ForegroundProcess;
use pathdiff::diff_paths;
use std::collections::HashMap;
use std::io::{BufRead, BufReader, Write};
@ -34,7 +35,7 @@ impl Command for External {
Signature::build(self.name())
.switch("redirect-stdout", "redirect-stdout", None)
.switch("redirect-stderr", "redirect-stderr", None)
.required("command", SyntaxShape::Any, "external comamdn to run")
.required("command", SyntaxShape::Any, "external command to run")
.rest("args", SyntaxShape::Any, "arguments for external command")
.category(Category::System)
}
@ -141,7 +142,10 @@ impl ExternalCommand {
let ctrlc = engine_state.ctrlc.clone();
let mut process = self.create_process(&input, false, head)?;
let mut fg_process = ForegroundProcess::new(
self.create_process(&input, false, head)?,
engine_state.pipeline_externals_state.clone(),
);
// mut is used in the windows branch only, suppress warning on other platforms
#[allow(unused_mut)]
let mut child;
@ -156,8 +160,7 @@ impl ExternalCommand {
// fails to be run as a normal executable:
// 1. "shell out" to cmd.exe if the command is a known cmd.exe internal command
// 2. Otherwise, use `which-rs` to look for batch files etc. then run those in cmd.exe
match process.spawn() {
match fg_process.spawn() {
Err(err) => {
// set the default value, maybe we'll override it later
child = Err(err);
@ -174,7 +177,10 @@ impl ExternalCommand {
.any(|&cmd| command_name_upper == cmd);
if looks_like_cmd_internal {
let mut cmd_process = self.create_process(&input, true, head)?;
let mut cmd_process = ForegroundProcess::new(
self.create_process(&input, true, head)?,
engine_state.pipeline_externals_state.clone(),
);
child = cmd_process.spawn();
} else {
#[cfg(feature = "which-support")]
@ -202,8 +208,11 @@ impl ExternalCommand {
item: file_name.to_string_lossy().to_string(),
span: self.name.span,
};
let mut cmd_process = new_command
.create_process(&input, true, head)?;
let mut cmd_process = ForegroundProcess::new(
new_command
.create_process(&input, true, head)?,
engine_state.pipeline_externals_state.clone(),
);
child = cmd_process.spawn();
}
}
@ -221,7 +230,7 @@ impl ExternalCommand {
#[cfg(not(windows))]
{
child = process.spawn()
child = fg_process.spawn()
}
match child {
@ -273,7 +282,7 @@ impl ExternalCommand {
engine_state.config.use_ansi_coloring = false;
// if there is a string or a stream, that is sent to the pipe std
if let Some(mut stdin_write) = child.stdin.take() {
if let Some(mut stdin_write) = child.as_mut().stdin.take() {
std::thread::spawn(move || {
let input = crate::Table::run(
&crate::Table,
@ -314,7 +323,7 @@ impl ExternalCommand {
// and we create a ListStream that can be consumed
if redirect_stderr {
let stderr = child.stderr.take().ok_or_else(|| {
let stderr = child.as_mut().stderr.take().ok_or_else(|| {
ShellError::ExternalCommand(
"Error taking stderr from external".to_string(),
"Redirects need access to stderr of an external command"
@ -353,7 +362,7 @@ impl ExternalCommand {
}
if redirect_stdout {
let stdout = child.stdout.take().ok_or_else(|| {
let stdout = child.as_mut().stdout.take().ok_or_else(|| {
ShellError::ExternalCommand(
"Error taking stdout from external".to_string(),
"Redirects need access to stdout of an external command"
@ -391,7 +400,7 @@ impl ExternalCommand {
}
}
match child.wait() {
match child.as_mut().wait() {
Err(err) => Err(ShellError::ExternalCommand(
"External command exited with error".into(),
err.to_string(),