1
1
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2
2
// See LICENSE in the project root for license information.
3
3
4
- import * as child_process from 'child_process' ;
5
4
import * as os from 'os' ;
5
+ import * as child_process from 'child_process' ;
6
6
import * as path from 'path' ;
7
7
import { EnvironmentMap } from './EnvironmentMap' ;
8
8
@@ -11,6 +11,8 @@ import { PosixModeBits } from './PosixModeBits';
11
11
import { Text } from './Text' ;
12
12
import { InternalError } from './InternalError' ;
13
13
14
+ const OS_PLATFORM : NodeJS . Platform = os . platform ( ) ;
15
+
14
16
/**
15
17
* Typings for one of the streams inside IExecutableSpawnSyncOptions.stdio.
16
18
* @public
@@ -221,29 +223,33 @@ export interface IProcessInfo {
221
223
}
222
224
223
225
export async function parseProcessListOutputAsync (
224
- stream : NodeJS . ReadableStream
226
+ stream : NodeJS . ReadableStream ,
227
+ platform : NodeJS . Platform = OS_PLATFORM
225
228
) : Promise < Map < number , IProcessInfo > > {
226
229
const processInfoById : Map < number , IProcessInfo > = new Map < number , IProcessInfo > ( ) ;
227
230
let seenHeaders : boolean = false ;
228
231
for await ( const line of Text . readLinesFromIterableAsync ( stream , { ignoreEmptyLines : true } ) ) {
229
232
if ( ! seenHeaders ) {
230
233
seenHeaders = true ;
231
234
} else {
232
- parseProcessInfoEntry ( line , processInfoById ) ;
235
+ parseProcessInfoEntry ( line , processInfoById , platform ) ;
233
236
}
234
237
}
235
238
return processInfoById ;
236
239
}
237
240
238
- // eslint-disable-next-line @rushstack/no-new-null
239
- export function parseProcessListOutput ( output : Iterable < string | null > ) : Map < number , IProcessInfo > {
241
+ export function parseProcessListOutput (
242
+ // eslint-disable-next-line @rushstack/no-new-null
243
+ output : Iterable < string | null > ,
244
+ platform : NodeJS . Platform = OS_PLATFORM
245
+ ) : Map < number , IProcessInfo > {
240
246
const processInfoById : Map < number , IProcessInfo > = new Map < number , IProcessInfo > ( ) ;
241
247
let seenHeaders : boolean = false ;
242
248
for ( const line of Text . readLinesFromIterable ( output , { ignoreEmptyLines : true } ) ) {
243
249
if ( ! seenHeaders ) {
244
250
seenHeaders = true ;
245
251
} else {
246
- parseProcessInfoEntry ( line , processInfoById ) ;
252
+ parseProcessInfoEntry ( line , processInfoById , platform ) ;
247
253
}
248
254
}
249
255
return processInfoById ;
@@ -253,18 +259,28 @@ export function parseProcessListOutput(output: Iterable<string | null>): Map<num
253
259
// Name ParentProcessId ProcessId
254
260
// process name 1234 5678
255
261
// unix format:
256
- // COMMAND PPID PID
257
- // process name 51234 56784
262
+ // PPID PID COMMAND
263
+ // 51234 56784 process name
258
264
const NAME_GROUP : 'name' = 'name' ;
259
265
const PROCESS_ID_GROUP : 'pid' = 'pid' ;
260
266
const PARENT_PROCESS_ID_GROUP : 'ppid' = 'ppid' ;
261
267
// eslint-disable-next-line @rushstack/security/no-unsafe-regexp
262
- const PROCESS_LIST_ENTRY_REGEX : RegExp = new RegExp (
268
+ const PROCESS_LIST_ENTRY_REGEX_WIN32 : RegExp = new RegExp (
263
269
`^(?<${ NAME_GROUP } >.+?)\\s+(?<${ PARENT_PROCESS_ID_GROUP } >\\d+)\\s+(?<${ PROCESS_ID_GROUP } >\\d+)\\s*$`
264
270
) ;
271
+ // eslint-disable-next-line @rushstack/security/no-unsafe-regexp
272
+ const PROCESS_LIST_ENTRY_REGEX_UNIX : RegExp = new RegExp (
273
+ `^\\s*(?<${ PARENT_PROCESS_ID_GROUP } >\\d+)\\s+(?<${ PROCESS_ID_GROUP } >\\d+)\\s+(?<${ NAME_GROUP } >.+?)\\s*$`
274
+ ) ;
265
275
266
- function parseProcessInfoEntry ( line : string , existingProcessInfoById : Map < number , IProcessInfo > ) : void {
267
- const match : RegExpMatchArray | null = line . match ( PROCESS_LIST_ENTRY_REGEX ) ;
276
+ function parseProcessInfoEntry (
277
+ line : string ,
278
+ existingProcessInfoById : Map < number , IProcessInfo > ,
279
+ platform : NodeJS . Platform
280
+ ) : void {
281
+ const processListEntryRegex : RegExp =
282
+ platform === 'win32' ? PROCESS_LIST_ENTRY_REGEX_WIN32 : PROCESS_LIST_ENTRY_REGEX_UNIX ;
283
+ const match : RegExpMatchArray | null = line . match ( processListEntryRegex ) ;
268
284
if ( ! match ?. groups ) {
269
285
throw new InternalError ( `Invalid process list entry: ${ line } ` ) ;
270
286
}
@@ -326,8 +342,6 @@ function convertToProcessInfoByNameMap(
326
342
return processInfoByNameMap ;
327
343
}
328
344
329
- const OS_PLATFORM : NodeJS . Platform = os . platform ( ) ;
330
-
331
345
function getProcessListProcessOptions ( ) : ICommandLineOptions {
332
346
let command : string ;
333
347
let args : string [ ] ;
@@ -338,10 +352,12 @@ function getProcessListProcessOptions(): ICommandLineOptions {
338
352
} else {
339
353
command = 'ps' ;
340
354
// -A: Select all processes
355
+ // -w: Wide format
341
356
// -o: User-defined format
342
- // Order of declared properties impacts the order of the output, so match
343
- // the order of wmic.exe output
344
- args = [ '-Ao' , 'comm,ppid,pid' ] ;
357
+ // Order of declared properties impacts the order of the output. We will
358
+ // need to request the "comm" property last in order to ensure that the
359
+ // process names are not truncated on certain platforms
360
+ args = [ '-Awo' , 'ppid,pid,comm' ] ;
345
361
}
346
362
return { path : command , args } ;
347
363
}
0 commit comments