mirror of https://github.com/winsw/winsw
Merge pull request #133 from oleg-nenashev/processTerminationOnStartup
[Issue #125] Runaway process termination on startuppull/143/head
commit
62b495f088
|
@ -269,6 +269,7 @@ namespace winsw
|
||||||
WriteEvent("Starting " + _descriptor.Executable + ' ' + startarguments);
|
WriteEvent("Starting " + _descriptor.Executable + ' ' + startarguments);
|
||||||
|
|
||||||
StartProcess(_process, startarguments, _descriptor.Executable);
|
StartProcess(_process, startarguments, _descriptor.Executable);
|
||||||
|
ExtensionManager.FireOnProcessStarted(_process);
|
||||||
|
|
||||||
// send stdout and stderr to its respective output file.
|
// send stdout and stderr to its respective output file.
|
||||||
HandleLogfiles();
|
HandleLogfiles();
|
||||||
|
@ -320,7 +321,8 @@ namespace winsw
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
WriteEvent("ProcessKill " + _process.Id);
|
WriteEvent("ProcessKill " + _process.Id);
|
||||||
StopProcessAndChildren(_process.Id);
|
ProcessHelper.StopProcessAndChildren(_process.Id, _descriptor.StopTimeout, _descriptor.StopParentProcessFirst);
|
||||||
|
ExtensionManager.FireOnProcessTerminated(_process);
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException)
|
catch (InvalidOperationException)
|
||||||
{
|
{
|
||||||
|
@ -368,75 +370,6 @@ namespace winsw
|
||||||
WriteEvent("Finished " + _descriptor.Id);
|
WriteEvent("Finished " + _descriptor.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StopProcessAndChildren(int pid)
|
|
||||||
{
|
|
||||||
var childPids = GetChildPids(pid);
|
|
||||||
|
|
||||||
if (_descriptor.StopParentProcessFirst)
|
|
||||||
{
|
|
||||||
StopProcess(pid);
|
|
||||||
foreach (var childPid in childPids)
|
|
||||||
{
|
|
||||||
StopProcessAndChildren(childPid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var childPid in childPids)
|
|
||||||
{
|
|
||||||
StopProcessAndChildren(childPid);
|
|
||||||
}
|
|
||||||
StopProcess(pid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<int> GetChildPids(int pid)
|
|
||||||
{
|
|
||||||
var searcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pid);
|
|
||||||
var childPids = new List<int>();
|
|
||||||
foreach (var mo in searcher.Get())
|
|
||||||
{
|
|
||||||
var childProcessId = mo["ProcessID"];
|
|
||||||
WriteEvent("Found child process: " + childProcessId + " Name: " + mo["Name"]);
|
|
||||||
childPids.Add(Convert.ToInt32(childProcessId));
|
|
||||||
}
|
|
||||||
return childPids;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StopProcess(int pid)
|
|
||||||
{
|
|
||||||
WriteEvent("Stopping process " + pid);
|
|
||||||
Process proc;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
proc = Process.GetProcessById(pid);
|
|
||||||
}
|
|
||||||
catch (ArgumentException)
|
|
||||||
{
|
|
||||||
WriteEvent("Process " + pid + " is already stopped");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteEvent("Send SIGINT " + pid);
|
|
||||||
bool successful = SigIntHelper.SendSIGINTToProcess(proc, _descriptor.StopTimeout);
|
|
||||||
if (successful)
|
|
||||||
{
|
|
||||||
WriteEvent("SIGINT to" + pid + " successful");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
WriteEvent("SIGINT to " + pid + " failed - Killing as fallback", Level.Warn);
|
|
||||||
proc.Kill();
|
|
||||||
}
|
|
||||||
catch (ArgumentException)
|
|
||||||
{
|
|
||||||
// Process already exited.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WaitForProcessToExit(Process processoWait)
|
private void WaitForProcessToExit(Process processoWait)
|
||||||
{
|
{
|
||||||
SignalShutdownPending();
|
SignalShutdownPending();
|
||||||
|
@ -516,8 +449,13 @@ namespace winsw
|
||||||
ps.RedirectStandardError = true;
|
ps.RedirectStandardError = true;
|
||||||
|
|
||||||
foreach (string key in _envs.Keys)
|
foreach (string key in _envs.Keys)
|
||||||
|
{
|
||||||
Environment.SetEnvironmentVariable(key, _envs[key]);
|
Environment.SetEnvironmentVariable(key, _envs[key]);
|
||||||
// ps.EnvironmentVariables[key] = envs[key]; // bugged (lower cases all variable names due to StringDictionary being used, see http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=326163)
|
// ps.EnvironmentVariables[key] = envs[key]; // bugged (lower cases all variable names due to StringDictionary being used, see http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=326163)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make it generic via extension points. The issue mentioned above should be ideally worked around somehow
|
||||||
|
ps.EnvironmentVariables[WinSWSystem.ENVVAR_NAME_SERVICE_ID.ToLower()] = _descriptor.Id;
|
||||||
|
|
||||||
processToStart.Start();
|
processToStart.Start();
|
||||||
WriteEvent("Started " + processToStart.Id);
|
WriteEvent("Started " + processToStart.Id);
|
||||||
|
|
|
@ -109,6 +109,10 @@
|
||||||
</BootstrapperPackage>
|
</BootstrapperPackage>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Plugins\RunawayProcessKiller\RunawayProcessKiller.csproj">
|
||||||
|
<Project>{57284b7a-82a4-407a-b706-ebea6bf8ea13}</Project>
|
||||||
|
<Name>RunawayProcessKiller</Name>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\..\Plugins\SharedDirectoryMapper\SharedDirectoryMapper.csproj">
|
<ProjectReference Include="..\..\Plugins\SharedDirectoryMapper\SharedDirectoryMapper.csproj">
|
||||||
<Project>{ca5c71db-c5a8-4c27-bf83-8e6daed9d6b5}</Project>
|
<Project>{ca5c71db-c5a8-4c27-bf83-8e6daed9d6b5}</Project>
|
||||||
<Name>SharedDirectoryMapper</Name>
|
<Name>SharedDirectoryMapper</Name>
|
||||||
|
@ -141,6 +145,7 @@
|
||||||
<MergeAsm Include="$(OutputPath)$(TargetFileName)" />
|
<MergeAsm Include="$(OutputPath)$(TargetFileName)" />
|
||||||
<MergeAsm Include="$(OutputPath)WinSWCore.dll" />
|
<MergeAsm Include="$(OutputPath)WinSWCore.dll" />
|
||||||
<MergeAsm Include="$(OutputPath)SharedDirectoryMapper.dll" />
|
<MergeAsm Include="$(OutputPath)SharedDirectoryMapper.dll" />
|
||||||
|
<MergeAsm Include="$(OutputPath)RunawayProcessKiller.dll" />
|
||||||
<MergeAsm Include="$(OutputPath)log4net.dll" />
|
<MergeAsm Include="$(OutputPath)log4net.dll" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|
|
@ -23,5 +23,15 @@ namespace winsw.Extensions
|
||||||
{
|
{
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void OnProcessStarted(System.Diagnostics.Process process)
|
||||||
|
{
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void OnProcessTerminated(System.Diagnostics.Process process)
|
||||||
|
{
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@ namespace winsw.Extensions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// All implementations should provide the default empty constructor.
|
/// All implementations should provide the default empty constructor.
|
||||||
/// The initialization will be performed by Init methods
|
/// The initialization will be performed by Init methods.
|
||||||
|
/// Binary comparibility of the class is not guaranteed in WinSW 2.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public interface IWinSWExtension
|
public interface IWinSWExtension
|
||||||
{
|
{
|
||||||
|
@ -37,6 +38,22 @@ namespace winsw.Extensions
|
||||||
/// <exception cref="ExtensionException">Any error during execution</exception>
|
/// <exception cref="ExtensionException">Any error during execution</exception>
|
||||||
void OnStart(IEventWriter logger);
|
void OnStart(IEventWriter logger);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler, which is being invoked once the child process is started.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="process">Process</param>
|
||||||
|
/// <param name="logger">Logger</param>
|
||||||
|
/// <exception cref="ExtensionException">Any error during execution</exception>
|
||||||
|
void OnProcessStarted(System.Diagnostics.Process process);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler, which is being invoked once the child process is terminated.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="process">Process</param>
|
||||||
|
/// <param name="logger">Logger</param>
|
||||||
|
/// <exception cref="ExtensionException">Any error during execution</exception>
|
||||||
|
void OnProcessTerminated(System.Diagnostics.Process process);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stop handler. Called during stop of the service
|
/// Stop handler. Called during stop of the service
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -4,6 +4,7 @@ using System.Xml;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using winsw.Util;
|
using winsw.Util;
|
||||||
|
using log4net;
|
||||||
|
|
||||||
namespace winsw.Extensions
|
namespace winsw.Extensions
|
||||||
{
|
{
|
||||||
|
@ -12,6 +13,8 @@ namespace winsw.Extensions
|
||||||
public Dictionary<string, IWinSWExtension> Extensions { private set; get; }
|
public Dictionary<string, IWinSWExtension> Extensions { private set; get; }
|
||||||
public ServiceDescriptor ServiceDescriptor { private set; get; }
|
public ServiceDescriptor ServiceDescriptor { private set; get; }
|
||||||
|
|
||||||
|
private static readonly ILog Log = LogManager.GetLogger(typeof(WinSWExtensionManager));
|
||||||
|
|
||||||
public WinSWExtensionManager(ServiceDescriptor serviceDescriptor)
|
public WinSWExtensionManager(ServiceDescriptor serviceDescriptor)
|
||||||
{
|
{
|
||||||
ServiceDescriptor = serviceDescriptor;
|
ServiceDescriptor = serviceDescriptor;
|
||||||
|
@ -42,6 +45,44 @@ namespace winsw.Extensions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler, which is being invoked once the child process is started.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="process">Process</param>
|
||||||
|
public void FireOnProcessStarted(System.Diagnostics.Process process)
|
||||||
|
{
|
||||||
|
foreach (var ext in Extensions)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ext.Value.OnProcessStarted(process);
|
||||||
|
}
|
||||||
|
catch (ExtensionException ex)
|
||||||
|
{
|
||||||
|
Log.Error("onProcessStarted() handler failed for " + ext.Value.DisplayName, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler, which is being invoked once the child process is terminated.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="process">Process</param>
|
||||||
|
public void FireOnProcessTerminated(System.Diagnostics.Process process)
|
||||||
|
{
|
||||||
|
foreach (var ext in Extensions)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ext.Value.OnProcessTerminated(process);
|
||||||
|
}
|
||||||
|
catch (ExtensionException ex)
|
||||||
|
{
|
||||||
|
Log.Error("onProcessTerminated() handler failed for " + ext.Value.DisplayName, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: Implement loading of external extensions. Current version supports internal hack
|
//TODO: Implement loading of external extensions. Current version supports internal hack
|
||||||
#region Extension load management
|
#region Extension load management
|
||||||
|
|
||||||
|
@ -79,6 +120,7 @@ namespace winsw.Extensions
|
||||||
{
|
{
|
||||||
IWinSWExtension extension = CreateExtensionInstance(descriptor.Id, descriptor.ClassName);
|
IWinSWExtension extension = CreateExtensionInstance(descriptor.Id, descriptor.ClassName);
|
||||||
extension.Descriptor = descriptor;
|
extension.Descriptor = descriptor;
|
||||||
|
//TODO: Handle exceptions
|
||||||
extension.Configure(ServiceDescriptor, configNode, logger);
|
extension.Configure(ServiceDescriptor, configNode, logger);
|
||||||
Extensions.Add(id, extension);
|
Extensions.Add(id, extension);
|
||||||
logger.LogEvent("Extension loaded: "+id, EventLogEntryType.Information);
|
logger.LogEvent("Extension loaded: "+id, EventLogEntryType.Information);
|
||||||
|
|
|
@ -72,7 +72,11 @@ namespace winsw
|
||||||
Environment.SetEnvironmentVariable("BASE", d.FullName);
|
Environment.SetEnvironmentVariable("BASE", d.FullName);
|
||||||
// ditto for ID
|
// ditto for ID
|
||||||
Environment.SetEnvironmentVariable("SERVICE_ID", Id);
|
Environment.SetEnvironmentVariable("SERVICE_ID", Id);
|
||||||
Environment.SetEnvironmentVariable("WINSW_EXECUTABLE", ExecutablePath);
|
|
||||||
|
// New name
|
||||||
|
Environment.SetEnvironmentVariable(WinSWSystem.ENVVAR_NAME_EXECUTABLE_PATH, ExecutablePath);
|
||||||
|
// Also inject system environment variables
|
||||||
|
Environment.SetEnvironmentVariable(WinSWSystem.ENVVAR_NAME_SERVICE_ID, Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
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 searcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pid);
|
||||||
|
var childPids = new List<int>();
|
||||||
|
foreach (var mo in searcher.Get())
|
||||||
|
{
|
||||||
|
var childProcessId = mo["ProcessID"];
|
||||||
|
Logger.Info("Found child process: " + childProcessId + " Name: " + mo["Name"]);
|
||||||
|
childPids.Add(Convert.ToInt32(childProcessId));
|
||||||
|
}
|
||||||
|
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 (ArgumentException)
|
||||||
|
{
|
||||||
|
// Process already exited.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//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)
|
||||||
|
{
|
||||||
|
var childPids = GetChildPids(pid);
|
||||||
|
|
||||||
|
if (stopParentProcessFirst)
|
||||||
|
{
|
||||||
|
StopProcess(pid, stopTimeout);
|
||||||
|
foreach (var childPid in childPids)
|
||||||
|
{
|
||||||
|
StopProcessAndChildren(childPid, stopTimeout, stopParentProcessFirst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var childPid in childPids)
|
||||||
|
{
|
||||||
|
StopProcessAndChildren(childPid, stopTimeout, stopParentProcessFirst);
|
||||||
|
}
|
||||||
|
StopProcess(pid, stopTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Also move StartProcess methods once LogEvent()/WriteEvent() mess gets solved
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace winsw.Util
|
||||||
|
{
|
||||||
|
public static class SigIntHelper
|
||||||
|
{
|
||||||
|
private const string KERNEL32 = "kernel32.dll";
|
||||||
|
|
||||||
|
[DllImport(KERNEL32, SetLastError = true)]
|
||||||
|
private static extern bool AttachConsole(uint dwProcessId);
|
||||||
|
|
||||||
|
[DllImport(KERNEL32, SetLastError = true, ExactSpelling = true)]
|
||||||
|
private static extern bool FreeConsole();
|
||||||
|
|
||||||
|
[DllImport(KERNEL32)]
|
||||||
|
private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add);
|
||||||
|
// Delegate type to be used as the Handler Routine for SCCH
|
||||||
|
private delegate Boolean ConsoleCtrlDelegate(CtrlTypes CtrlType);
|
||||||
|
|
||||||
|
// Enumerated type for the control messages sent to the handler routine
|
||||||
|
private enum CtrlTypes : uint
|
||||||
|
{
|
||||||
|
CTRL_C_EVENT = 0,
|
||||||
|
CTRL_BREAK_EVENT,
|
||||||
|
CTRL_CLOSE_EVENT,
|
||||||
|
CTRL_LOGOFF_EVENT = 5,
|
||||||
|
CTRL_SHUTDOWN_EVENT
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport(KERNEL32)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uses the native funciton "AttachConsole" to attach the thread to the executing process to try to trigger a CTRL_C event (SIGINT). If the application
|
||||||
|
/// doesn't honor the event and shut down gracefully, the. wait period will time out after 15 seconds.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="process">The process to attach to and send the SIGINT</param>
|
||||||
|
/// <returns>True if the process shut down successfully to the SIGINT, false if it did not.</returns>
|
||||||
|
public static bool SendSIGINTToProcess(Process process, TimeSpan shutdownTimeout)
|
||||||
|
{
|
||||||
|
if (AttachConsole((uint)process.Id))
|
||||||
|
{
|
||||||
|
//Disable Ctrl-C handling for our program
|
||||||
|
SetConsoleCtrlHandler(null, true);
|
||||||
|
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
|
||||||
|
|
||||||
|
process.WaitForExit((int)shutdownTimeout.TotalMilliseconds);
|
||||||
|
|
||||||
|
return process.HasExited;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ namespace winsw.Util
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="node">Parent node</param>
|
/// <param name="node">Parent node</param>
|
||||||
/// <param name="tagName">Element name</param>
|
/// <param name="tagName">Element name</param>
|
||||||
/// <param name="optional">If otional, don't throw an exception if the elemen is missing</param>
|
/// <param name="optional">If optional, don't throw an exception if the elemen is missing</param>
|
||||||
/// <returns>String value or null</returns>
|
/// <returns>String value or null</returns>
|
||||||
/// <exception cref="InvalidDataException">The required element is missing</exception>
|
/// <exception cref="InvalidDataException">The required element is missing</exception>
|
||||||
public static string SingleElement(XmlNode node, string tagName, Boolean optional)
|
public static string SingleElement(XmlNode node, string tagName, Boolean optional)
|
||||||
|
|
|
@ -62,8 +62,11 @@
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="ServiceDescriptor.cs" />
|
<Compile Include="ServiceDescriptor.cs" />
|
||||||
<Compile Include="Util\IEventWriter.cs" />
|
<Compile Include="Util\IEventWriter.cs" />
|
||||||
|
<Compile Include="Util\ProcessHelper.cs" />
|
||||||
|
<Compile Include="Util\SigIntHelper.cs" />
|
||||||
<Compile Include="Util\XmlHelper.cs" />
|
<Compile Include="Util\XmlHelper.cs" />
|
||||||
<Compile Include="WinSWException.cs" />
|
<Compile Include="WinSWException.cs" />
|
||||||
|
<Compile Include="WinSWSystem.cs" />
|
||||||
<Compile Include="Wmi.cs" />
|
<Compile Include="Wmi.cs" />
|
||||||
<Compile Include="WmiSchema.cs" />
|
<Compile Include="WmiSchema.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace winsw
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class, which contains generic information about WinSW runtime.
|
||||||
|
/// This information can be used by the service and extensions.
|
||||||
|
/// </summary>
|
||||||
|
public class WinSWSystem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prefix for all environment variables being injected for WinSW
|
||||||
|
/// </summary>
|
||||||
|
public static readonly string SYSTEM_EVNVVAR_PREFIX = "WINSW_";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Variable, which points to the service ID.
|
||||||
|
/// It may be used to determine runaway processes.
|
||||||
|
/// </summary>
|
||||||
|
public static string ENVVAR_NAME_SERVICE_ID { get { return SYSTEM_EVNVVAR_PREFIX + "SERVICE_ID"; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Variable, which specifies path to the executable being launched by WinSW.
|
||||||
|
/// </summary>
|
||||||
|
public static string ENVVAR_NAME_EXECUTABLE_PATH { get { return SYSTEM_EVNVVAR_PREFIX + "EXECUTABLE"; } }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("RunawayProcessKiller")]
|
||||||
|
[assembly: AssemblyDescription("Kills runaway process on startup")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("RunawayProcessKiller")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2015")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("d962c792-b900-4e60-8ae6-6c8d05b23a61")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{57284B7A-82A4-407A-B706-EBEA6BF8EA13}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>winsw.Plugins.RunawayProcessKiller</RootNamespace>
|
||||||
|
<AssemblyName>RunawayProcessKiller</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
|
||||||
|
<RestorePackages>true</RestorePackages>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="log4net">
|
||||||
|
<HintPath>..\..\packages\log4net.2.0.5\lib\net20-full\log4net.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="RunawayProcessKillerExtension.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Core\WinSWCore\WinSWCore.csproj">
|
||||||
|
<Project>{9d0c63e2-b6ff-4a85-bd36-b3e5d7f27d06}</Project>
|
||||||
|
<Name>WinSWCore</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
|
||||||
|
</Target>
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
|
@ -0,0 +1,155 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using winsw.Extensions;
|
||||||
|
using winsw.Util;
|
||||||
|
using log4net;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
|
||||||
|
namespace winsw.Plugins.RunawayProcessKiller
|
||||||
|
{
|
||||||
|
public class RunawayProcessKillerExtension : AbstractWinSWExtension
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Absolute path to the PID file, which stores ID of the previously launched process.
|
||||||
|
/// </summary>
|
||||||
|
public String Pidfile { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the process termination timeout in milliseconds.
|
||||||
|
/// This timeout will be applied multiple times for each child process.
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan StopTimeout { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, the parent process will be terminated first if the runaway process gets terminated.
|
||||||
|
/// </summary>
|
||||||
|
public bool StopParentProcessFirst { get; private set; }
|
||||||
|
|
||||||
|
public override String DisplayName { get { return "Runaway Process Killer"; } }
|
||||||
|
|
||||||
|
private String ServiceId { get; set; }
|
||||||
|
|
||||||
|
private static readonly ILog Logger = LogManager.GetLogger(typeof(RunawayProcessKillerExtension));
|
||||||
|
|
||||||
|
public RunawayProcessKillerExtension()
|
||||||
|
{
|
||||||
|
// Default initializer
|
||||||
|
}
|
||||||
|
|
||||||
|
public RunawayProcessKillerExtension(String pidfile)
|
||||||
|
{
|
||||||
|
this.Pidfile = pidfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Configure(ServiceDescriptor descriptor, XmlNode node, IEventWriter logger)
|
||||||
|
{
|
||||||
|
// We expect the upper logic to process any errors
|
||||||
|
// TODO: a better parser API for types would be useful
|
||||||
|
Pidfile = XmlHelper.SingleElement(node, "pidfile", false);
|
||||||
|
StopTimeout = TimeSpan.FromMilliseconds(Int32.Parse(XmlHelper.SingleElement(node, "stopTimeout", false)));
|
||||||
|
StopParentProcessFirst = Boolean.Parse(XmlHelper.SingleElement(node, "stopParentFirst", false));
|
||||||
|
ServiceId = descriptor.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method checks if the PID file is stored on the disk and then terminates runaway processes if they exist.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">Unused logger</param>
|
||||||
|
public override void OnStart(IEventWriter logger)
|
||||||
|
{
|
||||||
|
// Read PID file from the disk
|
||||||
|
int pid;
|
||||||
|
if (System.IO.File.Exists(Pidfile)) {
|
||||||
|
string pidstring;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pidstring = System.IO.File.ReadAllText(Pidfile);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error("Cannot read PID file from " + Pidfile, ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pid = Int32.Parse(pidstring);
|
||||||
|
}
|
||||||
|
catch (FormatException e)
|
||||||
|
{
|
||||||
|
Logger.Error("Invalid PID file number in '" + Pidfile + "'. The runaway process won't be checked", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Warn("The requested PID file '" + Pidfile + "' does not exist. The runaway process won't be checked");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now check the process
|
||||||
|
Process proc;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
proc = Process.GetProcessById(pid);
|
||||||
|
}
|
||||||
|
catch (ArgumentException ex)
|
||||||
|
{
|
||||||
|
Logger.Debug("No runaway process with PID=" + pid + ". The process has been already stopped.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the process references the service
|
||||||
|
String affiliatedServiceId;
|
||||||
|
// TODO: This method is not ideal since it works only for vars explicitly mentioned in the start info
|
||||||
|
// No Windows 10- compatible solution for EnvVars retrieval, see https://blog.gapotchenko.com/eazfuscator.net/reading-environment-variables
|
||||||
|
StringDictionary previousProcessEnvVars = proc.StartInfo.EnvironmentVariables;
|
||||||
|
String expectedEnvVarName = WinSWSystem.ENVVAR_NAME_SERVICE_ID.ToLower();
|
||||||
|
if (previousProcessEnvVars.ContainsKey(expectedEnvVarName))
|
||||||
|
{
|
||||||
|
affiliatedServiceId = previousProcessEnvVars[expectedEnvVarName];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Warn("The process " + pid + " has no " + expectedEnvVarName + " environment variable defined. "
|
||||||
|
+ "The process has not been started by this service, hence it won't be terminated.");
|
||||||
|
if (Logger.IsDebugEnabled) {
|
||||||
|
foreach (string key in previousProcessEnvVars.Keys) {
|
||||||
|
Logger.Debug("Env var of " + pid + ": " + key + "=" + previousProcessEnvVars[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the service ID value
|
||||||
|
if (!ServiceId.Equals(affiliatedServiceId))
|
||||||
|
{
|
||||||
|
Logger.Warn("The process " + pid + " has been started by Windows service with ID='" + affiliatedServiceId + "'. "
|
||||||
|
+ "It is another service (current service id is '" + ServiceId + "'), hence the process won't be terminated.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill the runaway process
|
||||||
|
Logger.Warn("Stopping the runaway process (pid=" + pid + ") and its children.");
|
||||||
|
ProcessHelper.StopProcessAndChildren(pid, this.StopTimeout, this.StopParentProcessFirst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Records the started process PID for the future use in OnStart() after the restart.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="process"></param>
|
||||||
|
public override void OnProcessStarted(System.Diagnostics.Process process)
|
||||||
|
{
|
||||||
|
Logger.Info("Recording PID of the started process:" + process.Id + ". PID file destination is " + Pidfile);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
System.IO.File.WriteAllText(Pidfile, process.Id.ToString());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error("Cannot update the PID file " + Pidfile, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="log4net" version="2.0.3" targetFramework="net20" />
|
||||||
|
</packages>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<service>
|
||||||
|
<id>SERVICE_NAME</id>
|
||||||
|
<name>Jenkins Slave</name>
|
||||||
|
<description>This service runs a slave for Jenkins continuous integration system.</description>
|
||||||
|
<executable>C:\Program Files\Java\jre7\bin\java.exe</executable>
|
||||||
|
<arguments>-Xrs -jar "%BASE%\slave.jar" -jnlpUrl ...</arguments>
|
||||||
|
<logmode>rotate</logmode>
|
||||||
|
|
||||||
|
<extensions>
|
||||||
|
<!-- This is a sample configuration for the RunawayProcessKiller extension. -->
|
||||||
|
<extension enabled="true" className="winsw.Plugins.RunawayProcessKiller.RunawayProcessKillerExtension" id="killOnStartup">
|
||||||
|
<!-- Absolute path to the PID file, which stores ID of the previously launched process. -->
|
||||||
|
<pidfile>%BASE%\pid.txt</pidfile>
|
||||||
|
<!-- Defines the process termination timeout in milliseconds. This timeout will be applied multiple times for each child process. -->
|
||||||
|
<stopTimeout>5000</stopTimeout>
|
||||||
|
<!-- If true, the parent process will be terminated first if the runaway process gets terminated. -->
|
||||||
|
<stopParentFirst>false</stopParentFirst>
|
||||||
|
</extension>
|
||||||
|
</extensions>
|
||||||
|
</service>
|
|
@ -0,0 +1,63 @@
|
||||||
|
using winsw;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using winsw.Extensions;
|
||||||
|
using winsw.Plugins.SharedDirectoryMapper;
|
||||||
|
using winswTests.util;
|
||||||
|
using winsw.Plugins.RunawayProcessKiller;
|
||||||
|
|
||||||
|
namespace winswTests.extensions
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
class RunawayProcessKillerExtensionTest
|
||||||
|
{
|
||||||
|
ServiceDescriptor _testServiceDescriptor;
|
||||||
|
readonly TestLogger _logger = new TestLogger();
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp()
|
||||||
|
{
|
||||||
|
string testExtension = typeof (RunawayProcessKillerExtension).ToString();
|
||||||
|
string seedXml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
|
||||||
|
+ "<service> "
|
||||||
|
+ " <id>SERVICE_NAME</id> "
|
||||||
|
+ " <name>Jenkins Slave</name> "
|
||||||
|
+ " <description>This service runs a slave for Jenkins continuous integration system.</description> "
|
||||||
|
+ " <executable>C:\\Program Files\\Java\\jre7\\bin\\java.exe</executable> "
|
||||||
|
+ " <arguments>-Xrs -jar \\\"%BASE%\\slave.jar\\\" -jnlpUrl ...</arguments> "
|
||||||
|
+ " <logmode>rotate</logmode> "
|
||||||
|
+ " <extensions> "
|
||||||
|
+ " <extension enabled=\"true\" className=\"" + testExtension + "\" id=\"mapNetworDirs\"> "
|
||||||
|
+ " <pidfile>foo/bar/pid.txt</pidfile>"
|
||||||
|
+ " <stopTimeout>5000</stopTimeout> "
|
||||||
|
+ " <stopParentFirst>true</stopParentFirst>"
|
||||||
|
+ " </extension> "
|
||||||
|
+ " </extensions> "
|
||||||
|
+ "</service>";
|
||||||
|
_testServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void LoadExtensions()
|
||||||
|
{
|
||||||
|
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
|
||||||
|
manager.LoadExtensions(_logger);
|
||||||
|
Assert.AreEqual(1, manager.Extensions.Count, "One extension should be loaded");
|
||||||
|
|
||||||
|
// Check the file is correct
|
||||||
|
var extension = manager.Extensions[typeof(RunawayProcessKillerExtension).ToString()] as RunawayProcessKillerExtension;
|
||||||
|
Assert.IsNotNull(extension, "RunawayProcessKillerExtension should be loaded");
|
||||||
|
Assert.AreEqual("foo/bar/pid.txt", extension.Pidfile, "Loaded PID file path is not equal to the expected one");
|
||||||
|
Assert.AreEqual(5000, extension.StopTimeout.TotalMilliseconds, "Loaded Stop Timeout is not equal to the expected one");
|
||||||
|
Assert.AreEqual(true, extension.StopParentProcessFirst, "Loaded StopParentFirst is not equal to the expected one");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void StartStopExtension()
|
||||||
|
{
|
||||||
|
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
|
||||||
|
manager.LoadExtensions(_logger);
|
||||||
|
manager.OnStart(_logger);
|
||||||
|
manager.OnStop(_logger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,6 +52,7 @@
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Extensions\RunawayProcessKillerTest.cs" />
|
||||||
<Compile Include="Extensions\WinSWExtensionManagerTest.cs" />
|
<Compile Include="Extensions\WinSWExtensionManagerTest.cs" />
|
||||||
<Compile Include="MainTest.cs" />
|
<Compile Include="MainTest.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
@ -68,6 +69,10 @@
|
||||||
<Project>{9d0c63e2-b6ff-4a85-bd36-b3e5d7f27d06}</Project>
|
<Project>{9d0c63e2-b6ff-4a85-bd36-b3e5d7f27d06}</Project>
|
||||||
<Name>WinSWCore</Name>
|
<Name>WinSWCore</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\Plugins\RunawayProcessKiller\RunawayProcessKiller.csproj">
|
||||||
|
<Project>{57284b7a-82a4-407a-b706-ebea6bf8ea13}</Project>
|
||||||
|
<Name>RunawayProcessKiller</Name>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\..\Plugins\SharedDirectoryMapper\SharedDirectoryMapper.csproj">
|
<ProjectReference Include="..\..\Plugins\SharedDirectoryMapper\SharedDirectoryMapper.csproj">
|
||||||
<Project>{ca5c71db-c5a8-4c27-bf83-8e6daed9d6b5}</Project>
|
<Project>{ca5c71db-c5a8-4c27-bf83-8e6daed9d6b5}</Project>
|
||||||
<Name>SharedDirectoryMapper</Name>
|
<Name>SharedDirectoryMapper</Name>
|
||||||
|
|
|
@ -2,5 +2,6 @@
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository path="..\Core\ServiceWrapper\packages.config" />
|
<repository path="..\Core\ServiceWrapper\packages.config" />
|
||||||
<repository path="..\Core\WinSWCore\packages.config" />
|
<repository path="..\Core\WinSWCore\packages.config" />
|
||||||
|
<repository path="..\Plugins\RunawayProcessKiller\packages.config" />
|
||||||
<repository path="..\Test\winswTests\packages.config" />
|
<repository path="..\Test\winswTests\packages.config" />
|
||||||
</repositories>
|
</repositories>
|
|
@ -29,6 +29,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".build", ".build", "{D88064
|
||||||
.build\MSBuild.Community.Tasks.targets = .build\MSBuild.Community.Tasks.targets
|
.build\MSBuild.Community.Tasks.targets = .build\MSBuild.Community.Tasks.targets
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RunawayProcessKiller", "Plugins\RunawayProcessKiller\RunawayProcessKiller.csproj", "{57284B7A-82A4-407A-B706-EBEA6BF8EA13}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -87,6 +89,16 @@ Global
|
||||||
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Release|Win32.ActiveCfg = Release|Any CPU
|
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||||
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Release|Win32.Build.0 = Release|Any CPU
|
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Release|Win32.Build.0 = Release|Any CPU
|
||||||
|
{57284B7A-82A4-407A-B706-EBEA6BF8EA13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{57284B7A-82A4-407A-B706-EBEA6BF8EA13}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{57284B7A-82A4-407A-B706-EBEA6BF8EA13}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{57284B7A-82A4-407A-B706-EBEA6BF8EA13}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{57284B7A-82A4-407A-B706-EBEA6BF8EA13}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||||
|
{57284B7A-82A4-407A-B706-EBEA6BF8EA13}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{57284B7A-82A4-407A-B706-EBEA6BF8EA13}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{57284B7A-82A4-407A-B706-EBEA6BF8EA13}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{57284B7A-82A4-407A-B706-EBEA6BF8EA13}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{57284B7A-82A4-407A-B706-EBEA6BF8EA13}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -96,5 +108,6 @@ Global
|
||||||
{93843402-842B-44B4-B303-AEE829BE0B43} = {077C2CEC-B687-4B53-86E9-C1A1BF5554E5}
|
{93843402-842B-44B4-B303-AEE829BE0B43} = {077C2CEC-B687-4B53-86E9-C1A1BF5554E5}
|
||||||
{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5} = {BC4AD891-E87E-4F30-867C-FD8084A29E5D}
|
{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5} = {BC4AD891-E87E-4F30-867C-FD8084A29E5D}
|
||||||
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06} = {5297623A-1A95-4F89-9AAE-DA634081EC86}
|
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06} = {5297623A-1A95-4F89-9AAE-DA634081EC86}
|
||||||
|
{57284B7A-82A4-407A-B706-EBEA6BF8EA13} = {BC4AD891-E87E-4F30-867C-FD8084A29E5D}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
Loading…
Reference in New Issue