mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
fix win_shell/win_command deadlock on large interleaved stdout/stderr (#5384)
fixes #5229
This commit is contained in:
parent
1ecb63f0e0
commit
8e97539e20
2 changed files with 79 additions and 8 deletions
|
@ -42,8 +42,11 @@ If($removes -and -not $(Test-Path $removes)) {
|
||||||
$util_def = @'
|
$util_def = @'
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ansible.Command
|
namespace Ansible.Command
|
||||||
{
|
{
|
||||||
|
@ -68,6 +71,32 @@ namespace Ansible.Command
|
||||||
|
|
||||||
return cmdlineParts;
|
return cmdlineParts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void GetProcessOutput(StreamReader stdoutStream, StreamReader stderrStream, out string stdout, out string stderr)
|
||||||
|
{
|
||||||
|
var sowait = new EventWaitHandle(false, EventResetMode.ManualReset);
|
||||||
|
var sewait = new EventWaitHandle(false, EventResetMode.ManualReset);
|
||||||
|
|
||||||
|
string so = null, se = null;
|
||||||
|
|
||||||
|
ThreadPool.QueueUserWorkItem((s)=>
|
||||||
|
{
|
||||||
|
so = stdoutStream.ReadToEnd();
|
||||||
|
sowait.Set();
|
||||||
|
});
|
||||||
|
|
||||||
|
ThreadPool.QueueUserWorkItem((s) =>
|
||||||
|
{
|
||||||
|
se = stderrStream.ReadToEnd();
|
||||||
|
sewait.Set();
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach(var wh in new WaitHandle[] { sowait, sewait })
|
||||||
|
wh.WaitOne();
|
||||||
|
|
||||||
|
stdout = so;
|
||||||
|
stderr = se;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'@
|
'@
|
||||||
|
@ -112,11 +141,12 @@ Catch [System.ComponentModel.Win32Exception] {
|
||||||
Exit-Json @{failed=$true;changed=$false;cmd=$raw_command_line;rc=$excep.Exception.NativeErrorCode;msg=$excep.Exception.Message}
|
Exit-Json @{failed=$true;changed=$false;cmd=$raw_command_line;rc=$excep.Exception.NativeErrorCode;msg=$excep.Exception.Message}
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO: resolve potential deadlock here if stderr fills buffer (~4k) before stdout is closed,
|
$stdout = $stderr = [string] $null
|
||||||
# perhaps some async stream pumping with Process Output/ErrorDataReceived events...
|
|
||||||
|
|
||||||
$result.stdout = $proc.StandardOutput.ReadToEnd()
|
[Ansible.Command.NativeUtil]::GetProcessOutput($proc.StandardOutput, $proc.StandardError, [ref] $stdout, [ref] $stderr) | Out-Null
|
||||||
$result.stderr = $proc.StandardError.ReadToEnd()
|
|
||||||
|
$result.stdout = $stdout
|
||||||
|
$result.stderr = $stderr
|
||||||
|
|
||||||
$proc.WaitForExit() | Out-Null
|
$proc.WaitForExit() | Out-Null
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,44 @@
|
||||||
Set-StrictMode -Version 2
|
Set-StrictMode -Version 2
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$helper_def = @"
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Ansible.Shell
|
||||||
|
{
|
||||||
|
public class ProcessUtil
|
||||||
|
{
|
||||||
|
public static void GetProcessOutput(StreamReader stdoutStream, StreamReader stderrStream, out string stdout, out string stderr)
|
||||||
|
{
|
||||||
|
var sowait = new EventWaitHandle(false, EventResetMode.ManualReset);
|
||||||
|
var sewait = new EventWaitHandle(false, EventResetMode.ManualReset);
|
||||||
|
|
||||||
|
string so = null, se = null;
|
||||||
|
|
||||||
|
ThreadPool.QueueUserWorkItem((s)=>
|
||||||
|
{
|
||||||
|
so = stdoutStream.ReadToEnd();
|
||||||
|
sowait.Set();
|
||||||
|
});
|
||||||
|
|
||||||
|
ThreadPool.QueueUserWorkItem((s) =>
|
||||||
|
{
|
||||||
|
se = stderrStream.ReadToEnd();
|
||||||
|
sewait.Set();
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach(var wh in new WaitHandle[] { sowait, sewait })
|
||||||
|
wh.WaitOne();
|
||||||
|
|
||||||
|
stdout = so;
|
||||||
|
stderr = se;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"@
|
||||||
|
|
||||||
$parsed_args = Parse-Args $args $false
|
$parsed_args = Parse-Args $args $false
|
||||||
|
|
||||||
$raw_command_line = $(Get-AnsibleParam $parsed_args "_raw_params" -failifempty $true).Trim()
|
$raw_command_line = $(Get-AnsibleParam $parsed_args "_raw_params" -failifempty $true).Trim()
|
||||||
|
@ -40,6 +78,8 @@ If($removes -and -not $(Test-Path $removes)) {
|
||||||
Exit-Json @{cmd=$raw_command_line; msg="skipped, since $removes does not exist"; changed=$false; skipped=$true; rc=0}
|
Exit-Json @{cmd=$raw_command_line; msg="skipped, since $removes does not exist"; changed=$false; skipped=$true; rc=0}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Add-Type -TypeDefinition $helper_def
|
||||||
|
|
||||||
$exec_args = $null
|
$exec_args = $null
|
||||||
|
|
||||||
If(-not $executable -or $executable -eq "powershell") {
|
If(-not $executable -or $executable -eq "powershell") {
|
||||||
|
@ -80,11 +120,12 @@ Catch [System.ComponentModel.Win32Exception] {
|
||||||
Exit-Json @{failed=$true;changed=$false;cmd=$raw_command_line;rc=$excep.Exception.NativeErrorCode;msg=$excep.Exception.Message}
|
Exit-Json @{failed=$true;changed=$false;cmd=$raw_command_line;rc=$excep.Exception.NativeErrorCode;msg=$excep.Exception.Message}
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO: resolve potential deadlock here if stderr fills buffer (~4k) before stdout is closed,
|
$stdout = $stderr = [string] $null
|
||||||
# perhaps some async stream pumping with Process Output/ErrorDataReceived events...
|
|
||||||
|
|
||||||
$result.stdout = $proc.StandardOutput.ReadToEnd()
|
[Ansible.Shell.ProcessUtil]::GetProcessOutput($proc.StandardOutput, $proc.StandardError, [ref] $stdout, [ref] $stderr) | Out-Null
|
||||||
$result.stderr = $proc.StandardError.ReadToEnd()
|
|
||||||
|
$result.stdout = $stdout
|
||||||
|
$result.stderr = $stderr
|
||||||
|
|
||||||
# TODO: decode CLIXML stderr output (and other streams?)
|
# TODO: decode CLIXML stderr output (and other streams?)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue