33const path = require ( 'path' ) ;
44
55const WINDOWS_PROCESS_QUERY_TIMEOUT_MS = 2000 ;
6+ const COMMAND_LINE_QUERY_UNAVAILABLE_KEY = '__command_line_query_unavailable__' ;
7+ const COMMAND_LINE_FALLBACK_LOGGED_KEY = '__command_line_fallback_logged__' ;
68
79function normalizeWindowsPathForMatch ( value ) {
810 return String ( value || '' )
@@ -15,56 +17,38 @@ function isGenericWindowsPythonImage(imageName) {
1517 return normalized === 'python.exe' || normalized === 'pythonw.exe' || normalized === 'py.exe' ;
1618}
1719
18- function queryWindowsProcessCommandLine ( { pid, shellName, spawnSync, timeoutMs } ) {
19- const query = `$p = Get-CimInstance Win32_Process -Filter "ProcessId = ${ pid } "; if ($null -ne $p) { $p.CommandLine }` ;
20- const args = [ '-NoProfile' , '-NonInteractive' , '-Command' , query ] ;
21- const options = {
22- stdio : [ 'ignore' , 'pipe' , 'ignore' ] ,
23- encoding : 'utf8' ,
24- windowsHide : true ,
25- timeout : timeoutMs ,
26- } ;
27- if ( shellName === 'powershell' ) {
28- return spawnSync ( 'powershell' , args , options ) ;
29- }
30- if ( shellName === 'pwsh' ) {
31- return spawnSync ( 'pwsh' , args , options ) ;
32- }
33- throw new Error ( `Unsupported shell for process command line query: ${ shellName } ` ) ;
34- }
35-
36- function parseWindowsProcessCommandLine ( result ) {
37- if ( ! result || ! result . stdout ) {
38- return null ;
39- }
40- return (
41- result . stdout
42- . split ( / \r ? \n / )
43- . map ( ( item ) => item . trim ( ) )
44- . find ( ( item ) => item . length > 0 ) || null
45- ) ;
46- }
47-
4820function getWindowsProcessCommandLine ( { pid, commandLineCache, spawnSync, log, timeoutMs } ) {
4921 const numericPid = Number . parseInt ( `${ pid } ` , 10 ) ;
5022 if ( ! Number . isInteger ( numericPid ) ) {
51- return null ;
23+ return { commandLine : null , commandLineQueryUnavailable : false } ;
24+ }
25+
26+ if ( commandLineCache && commandLineCache . get ( COMMAND_LINE_QUERY_UNAVAILABLE_KEY ) === true ) {
27+ return { commandLine : null , commandLineQueryUnavailable : true } ;
5228 }
5329
5430 if ( commandLineCache && commandLineCache . has ( numericPid ) ) {
55- return commandLineCache . get ( numericPid ) ;
31+ return {
32+ commandLine : commandLineCache . get ( numericPid ) ,
33+ commandLineQueryUnavailable : false ,
34+ } ;
5635 }
5736
37+ const query = `$p = Get-CimInstance Win32_Process -Filter "ProcessId = ${ numericPid } "; if ($null -ne $p) { $p.CommandLine }` ;
38+ const args = [ '-NoProfile' , '-NonInteractive' , '-Command' , query ] ;
39+ const options = {
40+ stdio : [ 'ignore' , 'pipe' , 'ignore' ] ,
41+ encoding : 'utf8' ,
42+ windowsHide : true ,
43+ timeout : timeoutMs ,
44+ } ;
45+
5846 const queryAttempts = [ 'powershell' , 'pwsh' ] ;
47+ let hasAvailableShell = false ;
5948 for ( const shellName of queryAttempts ) {
6049 let result = null ;
6150 try {
62- result = queryWindowsProcessCommandLine ( {
63- pid : numericPid ,
64- shellName,
65- spawnSync,
66- timeoutMs,
67- } ) ;
51+ result = spawnSync ( shellName , args , options ) ;
6852 } catch ( error ) {
6953 if ( error instanceof Error && error . message ) {
7054 log (
@@ -77,41 +61,48 @@ function getWindowsProcessCommandLine({ pid, commandLineCache, spawnSync, log, t
7761 if ( result . error && result . error . code === 'ENOENT' ) {
7862 continue ;
7963 }
64+ hasAvailableShell = true ;
8065 if ( result . error && result . error . code === 'ETIMEDOUT' ) {
8166 log (
8267 `Timed out (${ timeoutMs } ms) querying process command line by ${ shellName } for pid=${ numericPid } .` ,
8368 ) ;
8469 continue ;
8570 }
71+ if ( result . error ) {
72+ if ( result . error . message ) {
73+ log (
74+ `Failed to query process command line by ${ shellName } for pid=${ numericPid } : ${ result . error . message } ` ,
75+ ) ;
76+ }
77+ continue ;
78+ }
8679
8780 if ( result . status === 0 ) {
88- const commandLine = parseWindowsProcessCommandLine ( result ) ;
81+ const commandLine =
82+ result . stdout
83+ . split ( / \r ? \n / )
84+ . map ( ( item ) => item . trim ( ) )
85+ . find ( ( item ) => item . length > 0 ) || null ;
8986 if ( commandLineCache ) {
9087 commandLineCache . set ( numericPid , commandLine ) ;
88+ commandLineCache . set ( COMMAND_LINE_QUERY_UNAVAILABLE_KEY , false ) ;
9189 }
92- return commandLine ;
90+ return { commandLine, commandLineQueryUnavailable : false } ;
9391 }
9492 }
9593
94+ if ( ! hasAvailableShell ) {
95+ if ( commandLineCache ) {
96+ commandLineCache . set ( COMMAND_LINE_QUERY_UNAVAILABLE_KEY , true ) ;
97+ }
98+ return { commandLine : null , commandLineQueryUnavailable : true } ;
99+ }
100+
96101 if ( commandLineCache ) {
97102 commandLineCache . set ( numericPid , null ) ;
103+ commandLineCache . set ( COMMAND_LINE_QUERY_UNAVAILABLE_KEY , false ) ;
98104 }
99- return null ;
100- }
101-
102- function getFallbackWindowsBackendImageName ( fallbackCmdRaw ) {
103- const fallbackCmd = String ( fallbackCmdRaw || 'python.exe' )
104- . trim ( )
105- . split ( / \s + / , 1 ) [ 0 ] ;
106- return path . basename ( fallbackCmd || 'python.exe' ) . toLowerCase ( ) ;
107- }
108-
109- function getExpectedWindowsBackendImageName ( backendConfig , fallbackCmdRaw ) {
110- const safeBackendConfig =
111- backendConfig && typeof backendConfig === 'object' ? backendConfig : { } ;
112- return path
113- . basename ( safeBackendConfig . cmd || getFallbackWindowsBackendImageName ( fallbackCmdRaw ) )
114- . toLowerCase ( ) ;
105+ return { commandLine : null , commandLineQueryUnavailable : false } ;
115106}
116107
117108function buildBackendCommandLineMarkers ( backendConfig ) {
@@ -145,7 +136,14 @@ function shouldKillUnmanagedBackendProcess({
145136 log,
146137 fallbackCmdRaw,
147138} ) {
148- const expectedImageName = getExpectedWindowsBackendImageName ( backendConfig , fallbackCmdRaw ) ;
139+ const safeBackendConfig =
140+ backendConfig && typeof backendConfig === 'object' ? backendConfig : { } ;
141+ const fallbackCmd = String ( fallbackCmdRaw || 'python.exe' )
142+ . trim ( )
143+ . split ( / \s + / , 1 ) [ 0 ] ;
144+ const expectedImageName = path
145+ . basename ( safeBackendConfig . cmd || fallbackCmd || 'python.exe' )
146+ . toLowerCase ( ) ;
149147 const actualImageName = processInfo . imageName . toLowerCase ( ) ;
150148 if ( actualImageName !== expectedImageName ) {
151149 log (
@@ -164,14 +162,24 @@ function shouldKillUnmanagedBackendProcess({
164162 return false ;
165163 }
166164
167- const commandLine = getWindowsProcessCommandLine ( {
165+ const { commandLine, commandLineQueryUnavailable } = getWindowsProcessCommandLine ( {
168166 pid,
169167 commandLineCache,
170168 spawnSync,
171169 log,
172170 timeoutMs : WINDOWS_PROCESS_QUERY_TIMEOUT_MS ,
173171 } ) ;
174172 if ( ! commandLine ) {
173+ if ( commandLineQueryUnavailable ) {
174+ if ( commandLineCache && ! commandLineCache . get ( COMMAND_LINE_FALLBACK_LOGGED_KEY ) ) {
175+ commandLineCache . set ( COMMAND_LINE_FALLBACK_LOGGED_KEY , true ) ;
176+ log (
177+ 'Neither powershell nor pwsh is available. ' +
178+ 'Falling back to image-name-only matching for generic Python backend cleanup.' ,
179+ ) ;
180+ }
181+ return true ;
182+ }
175183 log ( `Skip unmanaged cleanup for pid=${ pid } : unable to resolve process command line.` ) ;
176184 return false ;
177185 }
0 commit comments