using log4net;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Management;
using System.Text;
namespace winsw.Util
{
///
/// Provides helper classes for Process Management
///
/// Since WinSW 2.0
public class ProcessHelper
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(ProcessHelper));
///
/// Gets all children of the specified process.
///
/// Process PID
/// List of child process PIDs
public static List GetChildPids(int pid)
{
var childPids = new List();
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;
}
///
/// Stops the process.
/// If the process cannot be stopped within the stop timeout, it gets killed
///
/// PID of the process
/// Stop timeout
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
}
///
/// Terminate process and its children.
/// By default the child processes get terminated first.
///
/// Process PID
/// Stop timeout (for each process)
/// If enabled, the perent process will be terminated before its children on all levels
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
}
}