Skip to content

Commit

Permalink
Merge pull request #53 from oleg-nenashev/PR_47_refactoring
Browse files Browse the repository at this point in the history
PR #47 - merged version
  • Loading branch information
oleg-nenashev committed Nov 21, 2014
2 parents c4336a4 + a2f4c7f commit b93e08e
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 71 deletions.
3 changes: 3 additions & 0 deletions .gitignore
@@ -1,3 +1,6 @@
bin
obj
*.suo
/UpgradeLog.htm
/winsw.csproj.DotSettings.user
/winsw.csproj.user
32 changes: 32 additions & 0 deletions LogAppenders.cs
Expand Up @@ -163,6 +163,22 @@ public class TimeBasedRollingLogAppender : AbstractFileLogAppender
private string pattern;
private int period;

public string Pattern
{
get
{
return pattern;
}
}

public int Period
{
get
{
return period;
}
}

public TimeBasedRollingLogAppender(string logDirectory, string baseName, string pattern, int period)
: base(logDirectory, baseName)
{
Expand Down Expand Up @@ -246,6 +262,22 @@ public class SizeBasedRollingLogAppender : AbstractFileLogAppender
private int sizeThreshold;
private int filesToKeep;

public int SizeTheshold
{
get
{
return sizeThreshold;
}
}

public int FilesToKeep
{
get
{
return filesToKeep;
}
}

public SizeBasedRollingLogAppender(string logDirectory, string baseName, int sizeThreshold, int filesToKeep)
: base(logDirectory, baseName)
{
Expand Down
46 changes: 43 additions & 3 deletions Main.cs
Expand Up @@ -318,16 +318,56 @@ private void StopIt()
}

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())
{
StopProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
var childProcessId = mo["ProcessID"];
WriteEvent("Found child process: " + childProcessId + " Name: " + mo["Name"]);
childPids.Add(Convert.ToInt32(childProcessId));
}
return childPids;
}

var proc = Process.GetProcessById(pid);
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);
bool successful = SigIntHelper.SendSIGINTToProcess(proc, descriptor.StopTimeout);
if (successful)
{
WriteEvent("SIGINT to" + pid + " successful");
Expand Down
6 changes: 6 additions & 0 deletions README.markdown
Expand Up @@ -292,3 +292,9 @@ Possible values are `idle`, `belownormal`, `normal`, `abovenormal`, `high`, `rea
<priority>idle</priority>

Specifying a priority higher than normal has unintended consequences. See <a href="http://msdn.microsoft.com/en-us/library/system.diagnostics.processpriorityclass(v=vs.110).aspx">MSDN discussion</a> for details. This feature is intended primarily to launch a process in a lower priority so as not to interfere with the computer's interactive usage.

###stopparentprocessfirst
Optionally specify the order of service shutdown. If true, the parent process is shutdown first. This is useful when the main process is a console, which can respond to Ctrol+C command and will gracefully shutdown child processes
```
<stopparentprocessfirst>true</stopparentprocessfirst>
```
20 changes: 17 additions & 3 deletions ServiceDescriptor.cs
Expand Up @@ -28,13 +28,13 @@ public class ServiceDescriptor
///
/// This string is "c:\abc\def\ghi" when the configuration XML is "c:\abc\def\ghi.xml"
/// </summary>
public readonly string BasePath;
public string BasePath { get; set; }
/// <summary>
/// The file name portion of the configuration file.
///
/// In the above example, this would be "ghi".
/// </summary>
public readonly string BaseName;
public string BaseName { get; set; }

public virtual string ExecutablePath
{
Expand Down Expand Up @@ -580,7 +580,21 @@ public TimeSpan StopTimeout
{
get
{
return SingleTimeSpanElement(dom, "stoptimeout", TimeSpan.FromSeconds(15));
return SingleTimeSpanElement(dom.FirstChild, "stoptimeout", TimeSpan.FromSeconds(15));
}
}

public bool StopParentProcessFirst
{
get
{
var value = SingleElement("stopparentprocessfirst", true);
bool result;
if (bool.TryParse(value, out result))
{
return result;
}
return false;
}
}

Expand Down
8 changes: 3 additions & 5 deletions SigIntHelper.cs
Expand Up @@ -51,12 +51,10 @@ public static bool SendSIGINTToProcess(Process process, TimeSpan shutdownTimeout

process.WaitForExit((int)shutdownTimeout.TotalMilliseconds);

return process.HasExited;
}
else
{
return false;
return process.HasExited;
}

return false;
}
}
}
201 changes: 141 additions & 60 deletions Tests/winswTests/ServiceDescriptorTests.cs
@@ -1,63 +1,65 @@
using NUnit.Framework;
using NUnit.Framework;
using winsw;
using System.Diagnostics;
using System.Xml;

namespace winswTests
{
[TestFixture]
public class ServiceDescriptorTests
{

private ServiceDescriptor extendedServiceDescriptor;

private const string ExpectedWorkingDirectory = @"Z:\Path\SubPath";
private const string Username = "User";
private const string Password = "Password";
private const string Domain = "Domain";

[SetUp]
public void SetUp()
{
const string SeedXml = "<service>"
+ "<id>service.exe</id>"
+ "<name>Service</name>"
+ "<description>The service.</description>"
+ "<executable>node.exe</executable>"
+ "<arguments>My Arguments</arguments>"
+ "<logmode>rotate</logmode>"
+ "<serviceaccount>"
+ "<domain>" + Domain + "</domain>"
+ "<user>" + Username + "</user>"
+ "<password>" + Password + "</password>"
+ "</serviceaccount>"
+ "<workingdirectory>"
+ ExpectedWorkingDirectory
+ "</workingdirectory>"
+ @"<logpath>C:\logs</logpath>"
using System.Xml;

namespace winswTests
{
using System;

[TestFixture]
public class ServiceDescriptorTests
{

private ServiceDescriptor extendedServiceDescriptor;

private const string ExpectedWorkingDirectory = @"Z:\Path\SubPath";
private const string Username = "User";
private const string Password = "Password";
private const string Domain = "Domain";

[SetUp]
public void SetUp()
{
const string SeedXml = "<service>"
+ "<id>service.exe</id>"
+ "<name>Service</name>"
+ "<description>The service.</description>"
+ "<executable>node.exe</executable>"
+ "<arguments>My Arguments</arguments>"
+ "<logmode>rotate</logmode>"
+ "<serviceaccount>"
+ "<domain>" + Domain + "</domain>"
+ "<user>" + Username + "</user>"
+ "<password>" + Password + "</password>"
+ "</serviceaccount>"
+ "<workingdirectory>"
+ ExpectedWorkingDirectory
+ "</workingdirectory>"
+ @"<logpath>C:\logs</logpath>"
+ "</service>";
extendedServiceDescriptor = ServiceDescriptor.FromXML(SeedXml);
}

[Test]
public void VerifyWorkingDirectory()
{
System.Diagnostics.Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + extendedServiceDescriptor.WorkingDirectory);
Assert.That(extendedServiceDescriptor.WorkingDirectory, Is.EqualTo(ExpectedWorkingDirectory));
}

[Test]
public void VerifyUsername()
{
System.Diagnostics.Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + extendedServiceDescriptor.WorkingDirectory);
Assert.That(extendedServiceDescriptor.ServiceAccountUser, Is.EqualTo(Domain + "\\" + Username));
}

[Test]
public void VerifyPassword()
{
System.Diagnostics.Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + extendedServiceDescriptor.WorkingDirectory);
Assert.That(extendedServiceDescriptor.ServiceAccountPassword, Is.EqualTo(Password));
extendedServiceDescriptor = ServiceDescriptor.FromXML(SeedXml);
}

[Test]
public void VerifyWorkingDirectory()
{
System.Diagnostics.Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + extendedServiceDescriptor.WorkingDirectory);
Assert.That(extendedServiceDescriptor.WorkingDirectory, Is.EqualTo(ExpectedWorkingDirectory));
}

[Test]
public void VerifyUsername()
{
System.Diagnostics.Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + extendedServiceDescriptor.WorkingDirectory);
Assert.That(extendedServiceDescriptor.ServiceAccountUser, Is.EqualTo(Domain + "\\" + Username));
}

[Test]
public void VerifyPassword()
{
System.Diagnostics.Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + extendedServiceDescriptor.WorkingDirectory);
Assert.That(extendedServiceDescriptor.ServiceAccountPassword, Is.EqualTo(Password));
}

[Test]
Expand All @@ -71,6 +73,85 @@ public void Priority()

sd = ServiceDescriptor.FromXML("<service><id>test</id></service>");
Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Normal));
}
}
}
}

[Test]
public void StopParentProcessFirstIsFalseByDefault()
{
Assert.False(extendedServiceDescriptor.StopParentProcessFirst);
}

[Test]
public void CanParseStopParentProcessFirst()
{
const string SeedXml = "<service>"
+ "<stopparentprocessfirst>true</stopparentprocessfirst>"
+ "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(SeedXml);

Assert.True(serviceDescriptor.StopParentProcessFirst);
}

[Test]
public void CanParseStopTimeout()
{
const string SeedXml = "<service>"
+ "<stoptimeout>60sec</stoptimeout>"
+ "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(SeedXml);

Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromSeconds(60)));
}

[Test]
public void CanParseStopTimeoutFromMinutes()
{
const string SeedXml = "<service>"
+ "<stoptimeout>10min</stoptimeout>"
+ "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(SeedXml);

Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromMinutes(10)));
}

[Test]
public void LogModeRollBySize()
{
const string SeedXml = "<service>"
+ "<logpath>c:\\</logpath>"
+ "<log mode=\"roll-by-size\">"
+ "<sizeThreshold>112</sizeThreshold>"
+ "<keepFiles>113</keepFiles>"
+ "</log>"
+ "</service>";

var serviceDescriptor = ServiceDescriptor.FromXML(SeedXml);
serviceDescriptor.BaseName = "service";

var logHandler = serviceDescriptor.LogHandler as SizeBasedRollingLogAppender;
Assert.NotNull(logHandler);
Assert.That(logHandler.SizeTheshold, Is.EqualTo(112 * 1024));
Assert.That(logHandler.FilesToKeep, Is.EqualTo(113));
}

[Test]
public void LogModeRollByTime()
{
const string SeedXml = "<service>"
+ "<logpath>c:\\</logpath>"
+ "<log mode=\"roll-by-time\">"
+ "<period>7</period>"
+ "<pattern>log pattern</pattern>"
+ "</log>"
+ "</service>";

var serviceDescriptor = ServiceDescriptor.FromXML(SeedXml);
serviceDescriptor.BaseName = "service";

var logHandler = serviceDescriptor.LogHandler as TimeBasedRollingLogAppender;
Assert.NotNull(logHandler);
Assert.That(logHandler.Period, Is.EqualTo(7));
Assert.That(logHandler.Pattern, Is.EqualTo("log pattern"));
}
}
}

0 comments on commit b93e08e

Please sign in to comment.