Skip to content

Fix stdin inherited by child processes when no input provided#1512

Open
JohnMcPMS wants to merge 3 commits intoPowerShell:mainfrom
JohnMcPMS:fix/stdin-null-when-no-input
Open

Fix stdin inherited by child processes when no input provided#1512
JohnMcPMS wants to merge 3 commits intoPowerShell:mainfrom
JohnMcPMS:fix/stdin-null-when-no-input

Conversation

@JohnMcPMS
Copy link
Copy Markdown
Collaborator

When invoke_command is called with input = None, the child process previously inherited the parent's stdin handle. In CI environments where the parent has a redirected pipe for stdin, this caused PowerShell child processes to block indefinitely on $Input (reading from the inherited pipe that never closes).

The following was able to repro the problem minimally (choose any PS adapted resource):

& { while ($true) { Start-Sleep 5 } } | dsc resource export -r Microsoft.Windows.Settings/WindowsSettings

The fix sets stdin to Stdio::null() when no input is provided, ensuring the child process sees an immediate EOF rather than inheriting whatever stdin handle the parent holds.

Root cause: commit 1af2c8b changed the PowerShell adapter from "config": "full" to "config": "single", which routes export through a code path that calls invoke_command with no input. Previously, the full-config path always provided input so stdin was always piped.

Also adds:

  • Integration test that detects this regression in all environments (terminal and CI) without leaving hanging threads
  • A -RustTestFilter parameter to build.ps1 / Test-RustProject to allow running a specific Rust test by name via the build script

When invoke_command is called with input = None, the child process
previously inherited the parent's stdin handle. In CI environments
where the parent has a redirected pipe for stdin, this caused PowerShell
child processes to block indefinitely on `$Input` (reading from the
inherited pipe that never closes).

The fix sets stdin to Stdio::null() when no input is provided, ensuring
the child process sees an immediate EOF rather than inheriting whatever
stdin handle the parent holds.

Root cause: commit 1af2c8b changed the PowerShell adapter from
"config": "full" to "config": "single", which routes export through a
code path that calls invoke_command with no input. Previously, the
full-config path always provided input so stdin was always piped.

Also adds:
- Integration test that detects this regression in all environments
  (terminal and CI) without leaving hanging threads
- A -RustTestFilter parameter to build.ps1 / Test-RustProject to
  allow running a specific Rust test by name via the build script

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 4, 2026 22:46
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Fixes a hang where invoke_command(input=None) caused child processes to inherit the parent’s stdin (often a never-closing pipe in CI), and adds tooling + coverage to prevent regressions.

Changes:

  • Set child process stdin to Stdio::null() when invoke_command is called with input = None (ensures immediate EOF).
  • Add an integration test that detects stdin-inheritance regressions without leaving hanging threads.
  • Add -RustTestFilter support to build.ps1 / Test-RustProject to run a specific Rust test by name.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
lib/dsc-lib/src/dscresources/command_resource.rs Forces stdin to null when no input is provided to avoid inherited-stdin hangs.
lib/dsc-lib/tests/integration/main.rs Registers the new integration test module.
lib/dsc-lib/tests/integration/command_resource.rs Adds regression test ensuring stdin is EOF when input=None.
helpers.build.psm1 Adds TestFilter parameter and forwards it to cargo test.
build.ps1 Adds -RustTestFilter and wires it into Test-RustProject.

Comment thread lib/dsc-lib/tests/integration/command_resource.rs
Comment thread lib/dsc-lib/src/dscresources/command_resource.rs
Comment thread lib/dsc-lib/tests/integration/command_resource.rs
Copy link
Copy Markdown
Member

@SteveL-MSFT SteveL-MSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making them pester tests means you can also revert the build changes

Comment thread lib/dsc-lib/tests/integration/command_resource.rs
Comment thread lib/dsc-lib/src/dscresources/command_resource.rs
Copy link
Copy Markdown
Member

@SteveL-MSFT SteveL-MSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix!

@SteveL-MSFT SteveL-MSFT added this to the 3.2-Consider milestone May 5, 2026
@SteveL-MSFT
Copy link
Copy Markdown
Member

I wouldn't think your change affects the WindowsUpdate resource tests, so it might be that the image used for CI isn't sufficient for the WindowsUpdate resource tests. I'll investigate this separately and will update this PR if a fix is needed there.

@JohnMcPMS
Copy link
Copy Markdown
Collaborator Author

It actually makes me think that this fix is flawed. Those tests are relying on the behavior pre-fix; passing an input into dsc.exe ends up passing that along into the child process.

If that behavior is supported, this change breaks it. In fact, winget explicitly uses the input stream and not the parameter. I can change our use to always provide input, but trying that I'm getting different behavior between this fix and the input swap. Was there some other fix that isn't in 3.2 that would produce better errors on an attempt to export without an Export method on the PS class?

@SteveL-MSFT
Copy link
Copy Markdown
Member

SteveL-MSFT commented May 6, 2026

It actually makes me think that this fix is flawed. Those tests are relying on the behavior pre-fix; passing an input into dsc.exe ends up passing that along into the child process.

If that behavior is supported, this change breaks it. In fact, winget explicitly uses the input stream and not the parameter. I can change our use to always provide input, but trying that I'm getting different behavior between this fix and the input swap. Was there some other fix that isn't in 3.2 that would produce better errors on an attempt to export without an Export method on the PS class?

Perhaps what needs to happen is that if the resource indicates it receives input via stdin, we need the current behavior, but otherwise it means via parameter and stdin can be null. The resource manifest has "input":"stdin" per operation.

Copy link
Copy Markdown
Member

@SteveL-MSFT SteveL-MSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to update so it looks at the resource manifest and if the resource doesn't read input from stdin then set it to null.

@SteveL-MSFT
Copy link
Copy Markdown
Member

@JohnMcPMS I figured out the problem after doing some debugging. I had forgotten a change we made awhile back on how stdin is sent to dsc. Basically, we had an issue in CI where stdio is always redirected, so dsc couldn't correct identify if the console was a tty. I had made a change in dsc to use the posix convention that to specify reading from stdin, you use - as the --file name. So the problem is the WindowsUpdate resource tests were inadvertently relying on the inherited stdin behavior when it should have specified explicitly for dsc to read from stdin instead. I've made the changes to the tests and pushed a commit to your branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants