2016-04-05 00:51:36 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using System.IO;
|
|
|
|
using System.Runtime.InteropServices;
|
2016-12-13 22:15:35 +00:00
|
|
|
using System.Threading.Tasks;
|
2016-04-05 00:51:36 +00:00
|
|
|
|
|
|
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
|
|
|
{
|
|
|
|
internal static class ProcessExtensions
|
|
|
|
{
|
2016-04-21 20:41:22 +00:00
|
|
|
#if NET451
|
|
|
|
private static readonly bool _isWindows = true;
|
|
|
|
#else
|
2016-04-05 00:51:36 +00:00
|
|
|
private static readonly bool _isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
2016-04-21 20:41:22 +00:00
|
|
|
#endif
|
2016-04-05 00:51:36 +00: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 22:15:35 +00: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-05 00:51:36 +00:00
|
|
|
}
|
|
|
|
}
|