diff --git a/lib/ansible/modules/windows/win_command.ps1 b/lib/ansible/modules/windows/win_command.ps1 index 5e934abe0f0..2a03b6a03d5 100644 --- a/lib/ansible/modules/windows/win_command.ps1 +++ b/lib/ansible/modules/windows/win_command.ps1 @@ -42,8 +42,11 @@ If($removes -and -not $(Test-Path $removes)) { $util_def = @' using System; using System.ComponentModel; +using System.Diagnostics; +using System.IO; using System.Linq; using System.Runtime.InteropServices; +using System.Threading; namespace Ansible.Command { @@ -68,6 +71,32 @@ namespace Ansible.Command 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} } -# TODO: resolve potential deadlock here if stderr fills buffer (~4k) before stdout is closed, -# perhaps some async stream pumping with Process Output/ErrorDataReceived events... +$stdout = $stderr = [string] $null -$result.stdout = $proc.StandardOutput.ReadToEnd() -$result.stderr = $proc.StandardError.ReadToEnd() +[Ansible.Command.NativeUtil]::GetProcessOutput($proc.StandardOutput, $proc.StandardError, [ref] $stdout, [ref] $stderr) | Out-Null + +$result.stdout = $stdout +$result.stderr = $stderr $proc.WaitForExit() | Out-Null diff --git a/lib/ansible/modules/windows/win_shell.ps1 b/lib/ansible/modules/windows/win_shell.ps1 index 750ca121ad4..850f2b9561a 100644 --- a/lib/ansible/modules/windows/win_shell.ps1 +++ b/lib/ansible/modules/windows/win_shell.ps1 @@ -22,6 +22,44 @@ Set-StrictMode -Version 2 $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 $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} } +Add-Type -TypeDefinition $helper_def + $exec_args = $null 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} } -# TODO: resolve potential deadlock here if stderr fills buffer (~4k) before stdout is closed, -# perhaps some async stream pumping with Process Output/ErrorDataReceived events... +$stdout = $stderr = [string] $null -$result.stdout = $proc.StandardOutput.ReadToEnd() -$result.stderr = $proc.StandardError.ReadToEnd() +[Ansible.Shell.ProcessUtil]::GetProcessOutput($proc.StandardOutput, $proc.StandardError, [ref] $stdout, [ref] $stderr) | Out-Null + +$result.stdout = $stdout +$result.stderr = $stderr # TODO: decode CLIXML stderr output (and other streams?)