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; 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(); SERVICE_FAILURE_ACTIONS sfa = new SERVICE_FAILURE_ACTIONS();
sfa.dwResetPeriod = failureResetPeriod.Seconds; sfa.dwResetPeriod = failureResetPeriod.Seconds;
@ -55,11 +55,11 @@ namespace Advapi32
int len = Marshal.SizeOf(typeof(SC_ACTION)); int len = Marshal.SizeOf(typeof(SC_ACTION));
sfa.cActions = actions.Length; sfa.cActions = actions.Count;
sfa.lpsaActions = Marshal.AllocHGlobal(len * actions.Length); sfa.lpsaActions = Marshal.AllocHGlobal(len * actions.Count);
try 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); 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]"); // WriteEvent("WaitForProcessToExit [start]");
while (!process.WaitForExit(descriptor.SleepTime)) while (!process.WaitForExit(descriptor.SleepTime.Milliseconds))
{ {
SignalShutdownPending(); SignalShutdownPending();
// WriteEvent("WaitForProcessToExit [repeat]"); // WriteEvent("WaitForProcessToExit [repeat]");
@ -354,7 +354,7 @@ namespace winsw
{ {
IntPtr handle = this.ServiceHandle; IntPtr handle = this.ServiceHandle;
wrapperServiceStatus.checkPoint++; wrapperServiceStatus.checkPoint++;
wrapperServiceStatus.waitHint = descriptor.WaitHint; wrapperServiceStatus.waitHint = descriptor.WaitHint.Milliseconds;
// WriteEvent("SignalShutdownPending " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint); // WriteEvent("SignalShutdownPending " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint);
wrapperServiceStatus.currentState = (int)State.SERVICE_STOP_PENDING; wrapperServiceStatus.currentState = (int)State.SERVICE_STOP_PENDING;
SetServiceStatus(handle, ref wrapperServiceStatus); SetServiceStatus(handle, ref wrapperServiceStatus);
@ -500,6 +500,18 @@ namespace winsw
// so using a classic method to set the description. Ugly. // so using a classic method to set the description. Ugly.
Registry.LocalMachine.OpenSubKey("System").OpenSubKey("CurrentControlSet").OpenSubKey("Services") Registry.LocalMachine.OpenSubKey("System").OpenSubKey("CurrentControlSet").OpenSubKey("Services")
.OpenSubKey(d.Id, true).SetValue("Description", d.Description); .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") if (args[0] == "uninstall")
{ {
@ -551,18 +563,6 @@ namespace winsw
else else
Console.WriteLine("Stopped"); 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") if (args[0] == "test")
{ {
WrapperService wsvc = new WrapperService(); WrapperService wsvc = new WrapperService();

View File

@ -13,6 +13,8 @@ using WMI;
using System.Xml; using System.Xml;
using System.Threading; using System.Threading;
using Microsoft.Win32; using Microsoft.Win32;
using Advapi32;
namespace winsw namespace winsw
{ {
/// <summary> /// <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> /// <summary>
/// Path to the executable. /// Path to the executable.
/// </summary> /// </summary>
@ -356,29 +393,29 @@ namespace winsw
/// <summary> /// <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 /// 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) /// with either an incremented checkPoint value or a change in currentState. (see http://msdn.microsoft.com/en-us/library/ms685996.aspx)
/// </summary> /// </summary>
public int WaitHint public TimeSpan WaitHint
{ {
get get
{ {
return SingleIntElement(dom, "waithint", 15000); return SingleTimeSpanElement(dom, "waithint", TimeSpan.FromSeconds(15));
} }
} }
/// <summary> /// <summary>
/// The time, in milliseconds (default 1 sec), before the service should make its next call to the SetServiceStatus function /// The time before the service should make its next call to the SetServiceStatus function
/// with an incremented checkPoint value. /// 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. /// 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> /// </summary>
public int SleepTime public TimeSpan SleepTime
{ {
get get
{ {
return SingleIntElement(dom, "sleeptime", 15000); return SingleTimeSpanElement(dom, "sleeptime", TimeSpan.FromSeconds(1));
} }
} }
@ -429,5 +466,44 @@ namespace winsw
return r; 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);
}
}
} }
} }