mirror of https://github.com/winsw/winsw
123 lines
4.2 KiB
C#
123 lines
4.2 KiB
C#
using log4net;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Management;
|
|
using System.Text;
|
|
|
|
namespace winsw.Util
|
|
{
|
|
/// <summary>
|
|
/// Provides helper classes for Process Management
|
|
/// </summary>
|
|
/// <remarks>Since WinSW 2.0</remarks>
|
|
public class ProcessHelper
|
|
{
|
|
private static readonly ILog Logger = LogManager.GetLogger(typeof(ProcessHelper));
|
|
|
|
/// <summary>
|
|
/// Gets all children of the specified process.
|
|
/// </summary>
|
|
/// <param name="pid">Process PID</param>
|
|
/// <returns>List of child process PIDs</returns>
|
|
public static List<int> GetChildPids(int pid)
|
|
{
|
|
var childPids = new List<int>();
|
|
|
|
try {
|
|
var searcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pid);
|
|
foreach (var mo in searcher.Get())
|
|
{
|
|
var childProcessId = mo["ProcessID"];
|
|
Logger.Info("Found child process: " + childProcessId + " Name: " + mo["Name"]);
|
|
childPids.Add(Convert.ToInt32(childProcessId));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Warn("Failed to locate children of the process with PID=" + pid + ". Child processes won't be terminated", ex);
|
|
}
|
|
|
|
return childPids;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stops the process.
|
|
/// If the process cannot be stopped within the stop timeout, it gets killed
|
|
/// </summary>
|
|
/// <param name="pid">PID of the process</param>
|
|
/// <param name="stopTimeout">Stop timeout</param>
|
|
public static void StopProcess(int pid, TimeSpan stopTimeout)
|
|
{
|
|
Logger.Info("Stopping process " + pid);
|
|
Process proc;
|
|
try
|
|
{
|
|
proc = Process.GetProcessById(pid);
|
|
}
|
|
catch (ArgumentException ex)
|
|
{
|
|
Logger.Info("Process " + pid + " is already stopped", ex);
|
|
return;
|
|
}
|
|
|
|
Logger.Info("Send SIGINT " + pid);
|
|
bool successful = SigIntHelper.SendSIGINTToProcess(proc, stopTimeout);
|
|
if (successful)
|
|
{
|
|
Logger.Info("SIGINT to" + pid + " successful");
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
Logger.Warn("SIGINT to " + pid + " failed - Killing as fallback");
|
|
proc.Kill();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (!proc.HasExited)
|
|
{
|
|
throw;
|
|
}
|
|
|
|
// Process already exited.
|
|
Logger.Warn("Ignoring exception from killing process because it has exited", ex);
|
|
}
|
|
}
|
|
|
|
//TODO: Propagate error if process kill fails? Currently we use the legacy behavior
|
|
}
|
|
|
|
/// <summary>
|
|
/// Terminate process and its children.
|
|
/// By default the child processes get terminated first.
|
|
/// </summary>
|
|
/// <param name="pid">Process PID</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>
|
|
public static void StopProcessAndChildren(int pid, TimeSpan stopTimeout, bool stopParentProcessFirst)
|
|
{
|
|
if (!stopParentProcessFirst)
|
|
{
|
|
foreach (var childPid in GetChildPids(pid))
|
|
{
|
|
StopProcessAndChildren(childPid, stopTimeout, stopParentProcessFirst);
|
|
}
|
|
}
|
|
|
|
StopProcess(pid, stopTimeout);
|
|
|
|
if (stopParentProcessFirst)
|
|
{
|
|
foreach (var childPid in GetChildPids(pid))
|
|
{
|
|
StopProcessAndChildren(childPid, stopTimeout, stopParentProcessFirst);
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Also move StartProcess methods once LogEvent()/WriteEvent() mess gets solved
|
|
}
|
|
}
|