Browse Source

Integrated failure action.

Also improved the timespan support.
Defaulting to ms for backward compatibility.
Kohsuke Kawaguchi 12 years ago
parent
commit
0eaa554ed3
3 changed files with 101 additions and 25 deletions
  1. 4 4
      Advapi32.cs
  2. 14 14
      Main.cs
  3. 83 7
      ServiceDescriptor.cs

+ 4 - 4
Advapi32.cs

@@ -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);
                 }

+ 14 - 14
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();

+ 83 - 7
ServiceDescriptor.cs

@@ -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);
+            }
+        }
+
     }
 }