2016-04-04 17:51:36 -07:00
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.IO ;
using System.Runtime.InteropServices ;
2016-12-13 14:15:35 -08:00
using System.Threading.Tasks ;
2016-04-04 17:51:36 -07:00
namespace Microsoft.DotNet.Tools.Test.Utilities
{
internal static class ProcessExtensions
{
2016-04-21 13:41:22 -07:00
#if NET451
private static readonly bool _isWindows = true ;
#else
2016-04-04 17:51:36 -07:00
private static readonly bool _isWindows = RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ;
2016-04-21 13:41:22 -07:00
#endif
2016-04-04 17:51:36 -07:00
private static readonly TimeSpan _defaultTimeout = TimeSpan . FromSeconds ( 30 ) ;
public static void KillTree ( this Process process )
{
process . KillTree ( _defaultTimeout ) ;
}
public static void KillTree ( this Process process , TimeSpan timeout )
{
string stdout ;
if ( _isWindows )
{
RunProcessAndWaitForExit (
"taskkill" ,
$"/T /F /PID {process.Id}" ,
timeout ,
out stdout ) ;
}
else
{
var children = new HashSet < int > ( ) ;
GetAllChildIdsUnix ( process . Id , children , timeout ) ;
foreach ( var childId in children )
{
KillProcessUnix ( childId , timeout ) ;
}
KillProcessUnix ( process . Id , timeout ) ;
}
}
private static void GetAllChildIdsUnix ( int parentId , ISet < int > children , TimeSpan timeout )
{
string stdout ;
var exitCode = RunProcessAndWaitForExit (
"pgrep" ,
$"-P {parentId}" ,
timeout ,
out stdout ) ;
if ( exitCode = = 0 & & ! string . IsNullOrEmpty ( stdout ) )
{
using ( var reader = new StringReader ( stdout ) )
{
while ( true )
{
var text = reader . ReadLine ( ) ;
if ( text = = null )
{
return ;
}
int id ;
if ( int . TryParse ( text , out id ) )
{
children . Add ( id ) ;
// Recursively get the children
GetAllChildIdsUnix ( id , children , timeout ) ;
}
}
}
}
}
private static void KillProcessUnix ( int processId , TimeSpan timeout )
{
string stdout ;
RunProcessAndWaitForExit (
"kill" ,
$"-TERM {processId}" ,
timeout ,
out stdout ) ;
}
private static int RunProcessAndWaitForExit ( string fileName , string arguments , TimeSpan timeout , out string stdout )
{
var startInfo = new ProcessStartInfo
{
FileName = fileName ,
Arguments = arguments ,
RedirectStandardOutput = true ,
UseShellExecute = false
} ;
var process = Process . Start ( startInfo ) ;
stdout = null ;
if ( process . WaitForExit ( ( int ) timeout . TotalMilliseconds ) )
{
stdout = process . StandardOutput . ReadToEnd ( ) ;
}
else
{
process . Kill ( ) ;
}
return process . ExitCode ;
}
2016-12-13 14:15:35 -08:00
public static Task StartAndWaitForExitAsync ( this Process subject )
{
var taskCompletionSource = new TaskCompletionSource < object > ( ) ;
subject . EnableRaisingEvents = true ;
subject . Exited + = ( s , a ) = >
{
taskCompletionSource . SetResult ( null ) ;
subject . Dispose ( ) ;
} ;
subject . Start ( ) ;
return taskCompletionSource . Task ;
}
2016-04-04 17:51:36 -07:00
}
}