diff --git a/Advapi32.cs b/Advapi32.cs index 7ea99fe..f3b569d 100755 --- a/Advapi32.cs +++ b/Advapi32.cs @@ -46,7 +46,7 @@ namespace Advapi32 Handle = service; } - public void ChangeConfig(TimeSpan failureResetPeriod, SC_ACTION[] actions) + public void ChangeConfig(TimeSpan failureResetPeriod, List 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); } diff --git a/Main.cs b/Main.cs index f095088..572082d 100644 --- a/Main.cs +++ b/Main.cs @@ -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(); diff --git a/ServiceDescriptor.cs b/ServiceDescriptor.cs index 9ee0c7c..57c522e 100755 --- a/ServiceDescriptor.cs +++ b/ServiceDescriptor.cs @@ -13,6 +13,8 @@ using WMI; using System.Xml; using System.Threading; using Microsoft.Win32; +using Advapi32; + namespace winsw { /// @@ -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 SUFFIX = new Dictionary { + { "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 } + }; + /// /// Path to the executable. /// @@ -356,29 +393,29 @@ namespace winsw /// - /// 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) /// - public int WaitHint + public TimeSpan WaitHint { get { - return SingleIntElement(dom, "waithint", 15000); + return SingleTimeSpanElement(dom, "waithint", TimeSpan.FromSeconds(15)); } } /// - /// 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. /// - 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 FailureActions + { + get + { + List r = new List(); + 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); + } + } + } }