Remove dependency on Win32_Process

pull/579/head
NextTurn 2018-11-25 00:00:00 +08:00 committed by Next Turn
parent 8beb21568e
commit 846fff5124
7 changed files with 79 additions and 41 deletions

View File

@ -334,7 +334,7 @@ namespace winsw
try try
{ {
Log.Debug("ProcessKill " + _process.Id); Log.Debug("ProcessKill " + _process.Id);
ProcessHelper.StopProcessAndChildren(_process.Id, _descriptor.StopTimeout, _descriptor.StopParentProcessFirst); ProcessHelper.StopProcessAndChildren(_process, _descriptor.StopTimeout, _descriptor.StopParentProcessFirst);
ExtensionManager.FireOnProcessTerminated(_process); ExtensionManager.FireOnProcessTerminated(_process);
} }
catch (InvalidOperationException) catch (InvalidOperationException)

View File

@ -4,5 +4,6 @@
{ {
internal const string Advapi32 = "advapi32.dll"; internal const string Advapi32 = "advapi32.dll";
internal const string Kernel32 = "kernel32.dll"; internal const string Kernel32 = "kernel32.dll";
internal const string NtDll = "ntdll.dll";
} }
} }

View File

@ -24,12 +24,36 @@ namespace winsw.Native
[DllImport(Libraries.Kernel32)] [DllImport(Libraries.Kernel32)]
internal static extern IntPtr GetCurrentProcess(); internal static extern IntPtr GetCurrentProcess();
[DllImport(Libraries.NtDll)]
internal static extern int NtQueryInformationProcess(
IntPtr processHandle,
PROCESSINFOCLASS processInformationClass,
out PROCESS_BASIC_INFORMATION processInformation,
int processInformationLength,
IntPtr returnLength = default);
[DllImport(Libraries.Advapi32, SetLastError = true)] [DllImport(Libraries.Advapi32, SetLastError = true)]
internal static extern bool OpenProcessToken( internal static extern bool OpenProcessToken(
IntPtr processHandle, IntPtr processHandle,
TokenAccessLevels desiredAccess, TokenAccessLevels desiredAccess,
out IntPtr tokenHandle); out IntPtr tokenHandle);
internal enum PROCESSINFOCLASS
{
ProcessBasicInformation = 0,
}
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct PROCESS_BASIC_INFORMATION
{
private readonly IntPtr Reserved1;
private readonly IntPtr PebBaseAddress;
private readonly IntPtr Reserved2_1;
private readonly IntPtr Reserved2_2;
internal readonly IntPtr UniqueProcessId;
internal readonly IntPtr InheritedFromUniqueProcessId;
}
internal struct PROCESS_INFORMATION internal struct PROCESS_INFORMATION
{ {
public IntPtr ProcessHandle; public IntPtr ProcessHandle;

View File

@ -1,9 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Management;
using System.Threading; using System.Threading;
using log4net; using log4net;
using static winsw.Native.ProcessApis;
namespace winsw.Util namespace winsw.Util
{ {
@ -18,30 +19,48 @@ namespace winsw.Util
/// <summary> /// <summary>
/// Gets all children of the specified process. /// Gets all children of the specified process.
/// </summary> /// </summary>
/// <param name="pid">Process PID</param> /// <param name="processId">Process PID</param>
/// <returns>List of child process PIDs</returns> /// <returns>List of child process PIDs</returns>
public static List<int> GetChildPids(int pid) private static unsafe List<Process> GetChildProcesses(int processId)
{ {
var childPids = new List<int>(); var children = new List<Process>();
try foreach (Process process in Process.GetProcesses())
{ {
string query = "SELECT * FROM Win32_Process WHERE ParentProcessID = " + pid; IntPtr handle;
using ManagementObjectSearcher searcher = new ManagementObjectSearcher(query); try
using ManagementObjectCollection results = searcher.Get();
foreach (ManagementBaseObject wmiObject in results)
{ {
var childProcessId = wmiObject["ProcessID"]; handle = process.Handle;
Logger.Info("Found child process: " + childProcessId + " Name: " + wmiObject["Name"]); }
childPids.Add(Convert.ToInt32(childProcessId)); catch (Win32Exception)
{
process.Dispose();
continue;
}
if (NtQueryInformationProcess(
handle,
PROCESSINFOCLASS.ProcessBasicInformation,
out PROCESS_BASIC_INFORMATION information,
sizeof(PROCESS_BASIC_INFORMATION)) != 0)
{
Logger.Warn("Failed to locate children of the process with PID=" + processId + ". Child processes won't be terminated");
process.Dispose();
continue;
}
if ((int)information.InheritedFromUniqueProcessId == processId)
{
Logger.Info("Found child process: " + process.Id + " Name: " + process.ProcessName);
children.Add(process);
}
else
{
process.Dispose();
} }
} }
catch (Exception ex)
{
Logger.Warn("Failed to locate children of the process with PID=" + pid + ". Child processes won't be terminated", ex);
}
return childPids; return children;
} }
/// <summary> /// <summary>
@ -50,22 +69,18 @@ namespace winsw.Util
/// </summary> /// </summary>
/// <param name="pid">PID of the process</param> /// <param name="pid">PID of the process</param>
/// <param name="stopTimeout">Stop timeout</param> /// <param name="stopTimeout">Stop timeout</param>
public static void StopProcess(int pid, TimeSpan stopTimeout) public static void StopProcess(Process process, TimeSpan stopTimeout)
{ {
Logger.Info("Stopping process " + pid); Logger.Info("Stopping process " + process.Id);
Process proc;
try if (process.HasExited)
{ {
proc = Process.GetProcessById(pid); Logger.Info("Process " + process.Id + " is already stopped");
}
catch (ArgumentException ex)
{
Logger.Info("Process " + pid + " is already stopped", ex);
return; return;
} }
// (bool sent, bool exited) // (bool sent, bool exited)
KeyValuePair<bool, bool> result = SignalHelper.SendCtrlCToProcess(proc, stopTimeout); KeyValuePair<bool, bool> result = SignalHelper.SendCtrlCToProcess(process, stopTimeout);
bool exited = result.Value; bool exited = result.Value;
if (!exited) if (!exited)
{ {
@ -74,13 +89,13 @@ namespace winsw.Util
bool sent = result.Key; bool sent = result.Key;
if (sent) if (sent)
{ {
Logger.Warn("Process " + pid + " did not respond to Ctrl+C signal - Killing as fallback"); Logger.Warn("Process " + process.Id + " did not respond to Ctrl+C signal - Killing as fallback");
} }
proc.Kill(); process.Kill();
} }
catch (Exception ex) catch (Exception ex)
{ {
if (!proc.HasExited) if (!process.HasExited)
{ {
throw; throw;
} }
@ -100,23 +115,23 @@ namespace winsw.Util
/// <param name="pid">Process PID</param> /// <param name="pid">Process PID</param>
/// <param name="stopTimeout">Stop timeout (for each process)</param> /// <param name="stopTimeout">Stop timeout (for each process)</param>
/// <param name="stopParentProcessFirst">If enabled, the perent process will be terminated before its children on all levels</param> /// <param name="stopParentProcessFirst">If enabled, the perent process will be terminated before its children on all levels</param>
public static void StopProcessAndChildren(int pid, TimeSpan stopTimeout, bool stopParentProcessFirst) public static void StopProcessAndChildren(Process process, TimeSpan stopTimeout, bool stopParentProcessFirst)
{ {
if (!stopParentProcessFirst) if (!stopParentProcessFirst)
{ {
foreach (var childPid in GetChildPids(pid)) foreach (Process child in GetChildProcesses(process.Id))
{ {
StopProcessAndChildren(childPid, stopTimeout, stopParentProcessFirst); StopProcessAndChildren(child, stopTimeout, stopParentProcessFirst);
} }
} }
StopProcess(pid, stopTimeout); StopProcess(process, stopTimeout);
if (stopParentProcessFirst) if (stopParentProcessFirst)
{ {
foreach (var childPid in GetChildPids(pid)) foreach (Process child in GetChildProcesses(process.Id))
{ {
StopProcessAndChildren(childPid, stopTimeout, stopParentProcessFirst); StopProcessAndChildren(child, stopTimeout, stopParentProcessFirst);
} }
} }
} }

View File

@ -15,7 +15,6 @@
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'"> <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<PackageReference Include="System.Diagnostics.EventLog" Version="4.7.0" /> <PackageReference Include="System.Diagnostics.EventLog" Version="4.7.0" />
<PackageReference Include="System.Management" Version="4.7.0" />
<PackageReference Include="System.Security.AccessControl" Version="4.7.0" /> <PackageReference Include="System.Security.AccessControl" Version="4.7.0" />
</ItemGroup> </ItemGroup>
@ -32,7 +31,6 @@
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1'"> <ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1'">
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" />
<Reference Include="System.Management" />
<Reference Include="System.ServiceProcess" /> <Reference Include="System.ServiceProcess" />
</ItemGroup> </ItemGroup>

View File

@ -276,7 +276,7 @@ namespace winsw.Plugins.RunawayProcessKiller
bldr.Append(proc); bldr.Append(proc);
Logger.Warn(bldr.ToString()); Logger.Warn(bldr.ToString());
ProcessHelper.StopProcessAndChildren(pid, this.StopTimeout, this.StopParentProcessFirst); ProcessHelper.StopProcessAndChildren(proc, this.StopTimeout, this.StopParentProcessFirst);
} }
/// <summary> /// <summary>

View File

@ -110,7 +110,7 @@ $@"<service>
if (!proc.HasExited) if (!proc.HasExited)
{ {
Console.Error.WriteLine("Test: Killing runaway process with ID=" + proc.Id); Console.Error.WriteLine("Test: Killing runaway process with ID=" + proc.Id);
ProcessHelper.StopProcessAndChildren(proc.Id, TimeSpan.FromMilliseconds(100), false); ProcessHelper.StopProcessAndChildren(proc, TimeSpan.FromMilliseconds(100), false);
if (!proc.HasExited) if (!proc.HasExited)
{ {
// The test is failed here anyway, but we add additional diagnostics info // The test is failed here anyway, but we add additional diagnostics info