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 } }