Integrated failure action.

Also improved the timespan support.
Defaulting to ms for backward compatibility.
pull/18/head
Kohsuke Kawaguchi 2013-04-20 18:08:35 -07:00
parent becb2d249a
commit 0eaa554ed3
3 changed files with 101 additions and 25 deletions

View File

@ -46,7 +46,7 @@ namespace Advapi32
Handle = service;
}
public void ChangeConfig(TimeSpan failureResetPeriod, SC_ACTION[] actions)
public void ChangeConfig(TimeSpan failureResetPeriod, List<SC_ACTION> actions)
{
SERVICE_FAILURE_ACTIONS sfa = new SERVICE_FAILURE_ACTIONS();
sfa.dwResetPeriod = failureResetPeriod.Seconds;
@ -55,11 +55,11 @@ namespace Advapi32
int len = Marshal.SizeOf(typeof(SC_ACTION));
sfa.cActions = actions.Length;
sfa.lpsaActions = Marshal.AllocHGlobal(len * actions.Length);
sfa.cActions = actions.Count;
sfa.lpsaActions = Marshal.AllocHGlobal(len * actions.Count);
try
{
for (int i = 0; i < actions.Length; i++)
for (int i = 0; i < actions.Count; i++)
{
Marshal.StructureToPtr(actions[i], new IntPtr(sfa.lpsaActions.ToInt64() + i * len), false);
}

28
Main.cs
View File

@ -336,7 +336,7 @@ namespace winsw
{
// WriteEvent("WaitForProcessToExit [start]");
while (!process.WaitForExit(descriptor.SleepTime))
while (!process.WaitForExit(descriptor.SleepTime.Milliseconds))
{
SignalShutdownPending();
// WriteEvent("WaitForProcessToExit [repeat]");
@ -354,7 +354,7 @@ namespace winsw
{
IntPtr handle = this.ServiceHandle;
wrapperServiceStatus.checkPoint++;
wrapperServiceStatus.waitHint = descriptor.WaitHint;
wrapperServiceStatus.waitHint = descriptor.WaitHint.Milliseconds;
// WriteEvent("SignalShutdownPending " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint);
wrapperServiceStatus.currentState = (int)State.SERVICE_STOP_PENDING;
SetServiceStatus(handle, ref wrapperServiceStatus);
@ -500,6 +500,18 @@ namespace winsw
// so using a classic method to set the description. Ugly.
Registry.LocalMachine.OpenSubKey("System").OpenSubKey("CurrentControlSet").OpenSubKey("Services")
.OpenSubKey(d.Id, true).SetValue("Description", d.Description);
var actions = d.FailureActions;
if (actions.Count > 0)
{// set the failure actions
using (Advapi32.ServiceManager scm = new Advapi32.ServiceManager())
{
using (Advapi32.Service sc = scm.Open(d.Id))
{
sc.ChangeConfig(d.ResetFailureAfter, actions);
}
}
}
}
if (args[0] == "uninstall")
{
@ -551,18 +563,6 @@ namespace winsw
else
Console.WriteLine("Stopped");
}
if (args[0] == "autorestart")
{// debug only. to be removed.
using (Advapi32.ServiceManager scm = new Advapi32.ServiceManager())
{
using (Advapi32.Service sc = scm.Open(d.Id))
{
SC_ACTION[] lpsaActions = new SC_ACTION[1];
lpsaActions[0] = new SC_ACTION(SC_ACTION_TYPE.SC_ACTION_RESTART, 1000);
sc.ChangeConfig(TimeSpan.FromHours(48),lpsaActions);
}
}
}
if (args[0] == "test")
{
WrapperService wsvc = new WrapperService();

View File

@ -13,6 +13,8 @@ using WMI;
using System.Xml;
using System.Threading;
using Microsoft.Win32;
using Advapi32;
namespace winsw
{
/// <summary>
@ -96,6 +98,41 @@ namespace winsw
}
}
private TimeSpan SingleTimeSpanElement(XmlNode parent, string tagName, TimeSpan defaultValue)
{
var e = parent.SelectSingleNode(tagName);
if (e == null)
{
return defaultValue;
}
else
{
string v = e.InnerText;
foreach (var s in SUFFIX) {
if (v.EndsWith(s.Key))
{
return TimeSpan.FromMilliseconds(int.Parse(v.Substring(0,v.Length-s.Key.Length).Trim())*s.Value);
}
}
return TimeSpan.FromMilliseconds(int.Parse(v));
}
}
private static readonly Dictionary<string,long> SUFFIX = new Dictionary<string,long> {
{ "ms", 1 },
{ "sec", 1000L },
{ "secs", 1000L },
{ "min", 1000L*60L },
{ "mins", 1000L*60L },
{ "hr", 1000L*60L*60L },
{ "hrs", 1000L*60L*60L },
{ "hour", 1000L*60L*60L },
{ "hours", 1000L*60L*60L },
{ "day", 1000L*60L*60L*24L },
{ "days", 1000L*60L*60L*24L }
};
/// <summary>
/// Path to the executable.
/// </summary>
@ -356,29 +393,29 @@ namespace winsw
/// <summary>
/// The estimated time required for a pending stop operation, in milliseconds (default 15 secs).
/// The estimated time required for a pending stop operation (default 15 secs).
/// Before the specified amount of time has elapsed, the service should make its next call to the SetServiceStatus function
/// with either an incremented checkPoint value or a change in currentState. (see http://msdn.microsoft.com/en-us/library/ms685996.aspx)
/// </summary>
public int WaitHint
public TimeSpan WaitHint
{
get
{
return SingleIntElement(dom, "waithint", 15000);
return SingleTimeSpanElement(dom, "waithint", TimeSpan.FromSeconds(15));
}
}
/// <summary>
/// The time, in milliseconds (default 1 sec), before the service should make its next call to the SetServiceStatus function
/// with an incremented checkPoint value.
/// The time before the service should make its next call to the SetServiceStatus function
/// with an incremented checkPoint value (default 1 sec).
/// Do not wait longer than the wait hint. A good interval is one-tenth of the wait hint but not less than 1 second and not more than 10 seconds.
/// </summary>
public int SleepTime
public TimeSpan SleepTime
{
get
{
return SingleIntElement(dom, "sleeptime", 15000);
return SingleTimeSpanElement(dom, "sleeptime", TimeSpan.FromSeconds(1));
}
}
@ -429,5 +466,44 @@ namespace winsw
return r;
}
}
public List<SC_ACTION> FailureActions
{
get
{
List<SC_ACTION> r = new List<SC_ACTION>();
foreach (XmlNode n in dom.SelectNodes("//onfailure"))
{
SC_ACTION_TYPE type;
string action = n.Attributes["action"].Value;
switch (action)
{
case "restart":
type = SC_ACTION_TYPE.SC_ACTION_RESTART;
break;
case "none":
type = SC_ACTION_TYPE.SC_ACTION_NONE;
break;
case "reboot":
type = SC_ACTION_TYPE.SC_ACTION_REBOOT;
break;
default:
throw new Exception("Invalid failure action: " + action);
}
XmlAttribute delay = n.Attributes["delay"];
r.Add(new SC_ACTION(type, delay!=null ? uint.Parse(delay.Value) : 0));
}
return r;
}
}
public TimeSpan ResetFailureAfter
{
get
{
return SingleTimeSpanElement(dom, "resetfailure", TimeSpan.Zero);
}
}
}
}