Skip to content

Commit d66b2a5

Browse files
authored
Fix Parent property on processes with complex name (PowerShell#17545)
1 parent 6e00e8a commit d66b2a5

File tree

2 files changed

+57
-17
lines changed

2 files changed

+57
-17
lines changed

src/System.Management.Automation/CoreCLR/CorePsPlatform.cs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -883,20 +883,40 @@ public static int GetProcFSParentPid(int pid)
883883
{
884884
const int invalidPid = -1;
885885

886-
// read /proc/<pid>/stat
887-
// 4th column will contain the ppid, 92 in the example below
888-
// ex: 93 (bash) S 92 93 2 4294967295 ...
889-
var path = $"/proc/{pid}/stat";
886+
// read /proc/<pid>/status
887+
// Row beginning with PPid: \d is the parent process id.
888+
// This used to check /proc/<pid>/stat but that file was meant
889+
// to be a space delimited line but it contains a value which
890+
// could contain spaces itself. Using the status file is a lot
891+
// simpler because each line contains a record with a simple
892+
// label.
893+
// https://github.com/PowerShell/PowerShell/issues/17541#issuecomment-1159911577
894+
var path = $"/proc/{pid}/status";
890895
try
891896
{
892-
var stat = System.IO.File.ReadAllText(path);
893-
var parts = stat.Split(' ', 5);
894-
if (parts.Length < 5)
897+
using FileStream fs = File.OpenRead(path);
898+
using StreamReader sr = new(fs);
899+
string line;
900+
while ((line = sr.ReadLine()) != null)
895901
{
896-
return invalidPid;
902+
if (!line.StartsWith("PPid:\t", StringComparison.OrdinalIgnoreCase))
903+
{
904+
continue;
905+
}
906+
907+
string[] lineSplit = line.Split('\t', 2, StringSplitOptions.RemoveEmptyEntries);
908+
if (lineSplit.Length != 2)
909+
{
910+
continue;
911+
}
912+
913+
if (int.TryParse(lineSplit[1].Trim(), out var ppid))
914+
{
915+
return ppid;
916+
}
897917
}
898918

899-
return int.Parse(parts[3]);
919+
return invalidPid;
900920
}
901921
catch (Exception)
902922
{

test/powershell/Modules/Microsoft.PowerShell.Management/Get-Process.Tests.ps1

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Describe "Get-Process for admin" -Tags @('CI', 'RequireAdminOnWindows') {
2222
$pwshVersion.FileBuildPart | Should -BeExactly $PSVersionTable.PSVersion.Patch
2323
$gitCommitId = $PSVersionTable.GitCommitId
2424
if ($gitCommitId.StartsWith("v")) { $gitCommitId = $gitCommitId.Substring(1) }
25-
$productVersion = $pwshVersion.ProductVersion.Replace(" Commits: ","-").Replace(" SHA: ","-g")
25+
$productVersion = $pwshVersion.ProductVersion.Replace(" Commits: ", "-").Replace(" SHA: ", "-g")
2626
$productVersion | Should -Match $gitCommitId
2727
} else {
2828
$pwshVersion.FileVersion | Should -BeNullOrEmpty
@@ -46,7 +46,7 @@ Describe "Get-Process" -Tags "CI" {
4646
$idleProcessPid = 0
4747
}
4848
It "Should return a type of Object[] for Get-Process cmdlet" -Pending:$IsMacOS {
49-
,$ps | Should -BeOfType System.Object[]
49+
, $ps | Should -BeOfType System.Object[]
5050
}
5151

5252
It "Should have not empty Name flags set for Get-Process object" -Pending:$IsMacOS {
@@ -78,7 +78,7 @@ Describe "Get-Process" -Tags "CI" {
7878
(Get-Process -Id $PID).Id | Should -BeExactly $PID
7979
}
8080

81-
It "Should fail to run Get-Process with -IncludeUserName without admin" -Skip:(!$IsWindows) {
81+
It "Should fail to run Get-Process with -IncludeUserName without admin" -Skip:(!$IsWindows) {
8282
{ Get-Process -IncludeUserName } | Should -Throw -ErrorId "IncludeUserNameRequiresElevation,Microsoft.PowerShell.Commands.GetProcessCommand"
8383
}
8484

@@ -95,7 +95,7 @@ Describe "Get-Process" -Tags "CI" {
9595
{ Get-Process -FileVersionInfo -ErrorAction Stop } | Should -Throw -ErrorId "CouldNotEnumerateFileVer,Microsoft.PowerShell.Commands.GetProcessCommand"
9696
}
9797

98-
It "Should return CommandLine property" -Skip:($IsMacOS) {
98+
It "Should return CommandLine property" -Skip:($IsMacOS) {
9999
$command = "(Get-Process -Id `$pid).CommandLine"
100100
$result = & "$PSHOME/pwsh" -NoProfile -NonInteractive -Command $command
101101
$result | Should -BeLike "*$command*"
@@ -105,19 +105,18 @@ Describe "Get-Process" -Tags "CI" {
105105
Describe "Get-Process Formatting" -Tags "Feature" {
106106
BeforeAll {
107107
$skip = $false
108-
if ($IsWindows)
109-
{
108+
if ($IsWindows) {
110109
# on Windows skip this test until issue #11016 is resolved
111110
$skip = $true
112111
}
113112
}
114113

115114
It "Should not have Handle in table format header" -Skip:$skip {
116-
$types = "System.Diagnostics.Process","System.Diagnostics.Process#IncludeUserName"
115+
$types = "System.Diagnostics.Process", "System.Diagnostics.Process#IncludeUserName"
117116

118117
foreach ($type in $types) {
119118
$formatData = Get-FormatData -TypeName $type -PowerShellVersion $PSVersionTable.PSVersion
120-
$tableControls = $formatData.FormatViewDefinition | Where-Object {$_.Control -is "System.Management.Automation.TableControl"}
119+
$tableControls = $formatData.FormatViewDefinition | Where-Object { $_.Control -is "System.Management.Automation.TableControl" }
121120
foreach ($tableControl in $tableControls) {
122121
$tableControl.Control.Headers.Label -match "Handle*" | Should -BeNullOrEmpty
123122
# verify that rows without headers isn't the handlecount (as PowerShell will create a header that matches the property name)
@@ -138,5 +137,26 @@ Describe "Process Parent property" -Tags "CI" {
138137
$powershellexe = (Get-Process -Id $PID).mainmodule.filename
139138
& $powershellexe -noprofile -command '(Get-Process -Id $PID).Parent.Id' | Should -Be $PID
140139
}
140+
141+
It "Can find parent with spaces and parenthesis in the name on non-Windows" -Skip:($IsWindows) {
142+
# Bug. See https://github.com/PowerShell/PowerShell/issues/12908
143+
$commandName = 't ( e ( s ) t )'
144+
145+
$script = @'
146+
#!/bin/sh
147+
148+
while true; do sleep 1; done
149+
'@
150+
151+
# Can't use testdrive: as unelevated user doesn't have perms in test in CI
152+
Set-Content -Path /tmp/$commandName -Value $script
153+
chmod +x (Resolve-Path -Path /tmp/$commandName -Relative)
154+
try {
155+
$p = Start-Process -FilePath /tmp/$commandName -PassThru
156+
$p.Parent.Id | Should -Be $pid
157+
} finally {
158+
$p | Stop-Process
159+
}
160+
}
141161
}
142162

0 commit comments

Comments
 (0)