Browse Source

Add option to send a SIGINT to the process being wrapped

Ed Frey 12 years ago
parent
commit
1ed045b817
4 changed files with 93 additions and 2 deletions
  1. 19 1
      Main.cs
  2. 10 0
      ServiceDescriptor.cs
  3. 62 0
      SigIntHelper.cs
  4. 2 1
      winsw.csproj

+ 19 - 1
Main.cs

@@ -340,7 +340,25 @@ namespace winsw
             try
             {
                 var proc = Process.GetProcessById(pid);
-                proc.Kill();
+                if (descriptor.SendSIGINT)
+                {
+                    WriteEvent("Send SIGINT " + process.Id);
+                    bool successful = SigIntHelper.SendSIGINTToProcess(proc);
+                    if (successful)
+                    {
+                        WriteEvent("SIGINT to" + process.Id + " successful");
+                    }
+                    else
+                    {
+                        WriteEvent("SIGINT to " + process.Id + " failed - Killing as fallback");
+                        proc.Kill();
+                    }
+                }
+                else
+                {
+                    WriteEvent("ProcessKill " + process.Id);
+                    proc.Kill();
+                }
             }
             catch (ArgumentException)
             {

+ 10 - 0
ServiceDescriptor.cs

@@ -561,5 +561,15 @@ namespace winsw
 			return !string.IsNullOrEmpty(serviceAccountDomain) && !string.IsNullOrEmpty(serviceAccountName);
 		}
 
+         /// <summary>
+         /// True if the service can interact with the desktop.
+         /// </summary>
+         public bool SendSIGINT
+         {
+             get
+             {
+                 return dom.SelectSingleNode("//sendsigint") != null;
+             }
+         }
     }
 }

+ 62 - 0
SigIntHelper.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace winsw
+{
+    public static class SigIntHelper
+    {
+        private const string KERNEL32 = "kernel32.dll";
+
+        [DllImport(KERNEL32, SetLastError = true)]
+        private static extern bool AttachConsole(uint dwProcessId);
+
+        [DllImport(KERNEL32, SetLastError = true, ExactSpelling = true)]
+        private static extern bool FreeConsole();
+
+        [DllImport(KERNEL32)]
+        private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add);
+        // Delegate type to be used as the Handler Routine for SCCH
+        private delegate Boolean ConsoleCtrlDelegate(CtrlTypes CtrlType);
+
+        // Enumerated type for the control messages sent to the handler routine
+        private enum CtrlTypes : uint
+        {
+            CTRL_C_EVENT = 0,
+            CTRL_BREAK_EVENT,
+            CTRL_CLOSE_EVENT,
+            CTRL_LOGOFF_EVENT = 5,
+            CTRL_SHUTDOWN_EVENT
+        }
+
+        [DllImport(KERNEL32)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
+
+        /// <summary>
+        /// Uses the native funciton "AttachConsole" to attach the thread to the executing process to try to trigger a CTRL_C event (SIGINT).  If the application
+        /// doesn't honor the event and shut down gracefully, the. wait period will time out after 15 seconds.
+        /// </summary>
+        /// <param name="process">The process to attach to and send the SIGINT</param>
+        /// <returns>True if the process shut down successfully to the SIGINT, false if it did not.</returns>
+        public static bool SendSIGINTToProcess(Process process)
+        {
+            if (AttachConsole((uint)process.Id))
+            {
+                //Disable Ctrl-C handling for our program
+                SetConsoleCtrlHandler(null, true);
+                GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
+
+                process.WaitForExit(15000);
+
+                return process.HasExited;
+            }
+            else
+            {
+                return false;
+            }
+        }
+    }
+}

+ 2 - 1
winsw.csproj

@@ -3,7 +3,7 @@
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>9.0.21022</ProductVersion>
+    <ProductVersion>9.0.30729</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
     <ProjectGuid>{0DE77F55-ADE5-43C1-999A-0BC81153B039}</ProjectGuid>
     <OutputType>Exe</OutputType>
@@ -56,6 +56,7 @@
     <Compile Include="PeriodicRollingCalendar.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="ServiceDescriptor.cs" />
+    <Compile Include="SigIntHelper.cs" />
     <Compile Include="Wmi.cs" />
     <Compile Include="WmiSchema.cs" />
   </ItemGroup>