From e204a0c5bc237684f9df9b85d474f16396307fea Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Wed, 9 Nov 2016 23:32:18 -0500 Subject: [PATCH 1/2] Use an exit-event to stop openvpn instead of killing it Currently openvpn is stopped by killing the process causing potential truncation of log messages, client not sending exit-notify to the server etc. Using an exit-event provides a way to cleanly stop the process. Signed-off-by: Selva Nair --- Service.cs | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/Service.cs b/Service.cs index a70d4ab..36b61df 100644 --- a/Service.cs +++ b/Service.cs @@ -6,9 +6,22 @@ using System.IO; using System.Diagnostics; using System.ServiceProcess; +using System.Runtime.InteropServices; namespace OpenVpn { + class SysWin32 + { + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "CreateEventW", SetLastError = true)] + public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool CloseHandle(IntPtr hObject); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool SetEvent(IntPtr hEvent); + } + class OpenVpnService : System.ServiceProcess.ServiceBase { public static string DefaultServiceName = "OpenVpnService"; @@ -35,10 +48,17 @@ protected override void OnStop() RequestAdditionalTime(3000); foreach (var child in Subprocesses) { - child.StopProcess(); + child.SignalProcess(); + } + // Kill all processes -- wait for 2500 msec at most + DateTime tEnd = DateTime.Now.AddMilliseconds(2500.0); + foreach (var child in Subprocesses) + { + int timeout = (int) (tEnd - DateTime.Now).TotalMilliseconds; + child.StopProcess(timeout > 0 ? timeout : 0); } } - + private RegistryKey GetRegistrySubkey(RegistryView rView) { try @@ -229,6 +249,7 @@ class OpenVpnChild { System.Timers.Timer restartTimer; OpenVpnServiceConfiguration config; string configFile; + string exitEvent; public OpenVpnChild(OpenVpnServiceConfiguration config, string configFile) { this.config = config; @@ -236,6 +257,7 @@ public OpenVpnChild(OpenVpnServiceConfiguration config, string configFile) { /* Because we will be using the filenames in our closures, * so make sure we are working on a copy */ this.configFile = String.Copy(configFile); + this.exitEvent = Path.GetFileName(configFile) + "_" + Process.GetCurrentProcess().Id.ToString(); var justFilename = System.IO.Path.GetFileName(configFile); var logFilename = config.logDir + "\\" + justFilename.Substring(0, justFilename.Length - config.configExt.Length) + ".log"; @@ -254,7 +276,9 @@ public OpenVpnChild(OpenVpnServiceConfiguration config, string configFile) { /// SET UP PROCESS START INFO string[] procArgs = { "--config", - "\"" + configFile + "\"" + "\"" + configFile + "\"", + "--service ", + "\"" + exitEvent + "\"" + " 0" }; this.startInfo = new System.Diagnostics.ProcessStartInfo() { @@ -283,13 +307,41 @@ output streams **/ flushTimer.Start(); } - public void StopProcess() { + // set exit event so that openvpn will terminate + public void SignalProcess() { if (restartTimer != null) { restartTimer.Stop(); } try { if (!process.HasExited) + { + var h = SysWin32.CreateEvent(IntPtr.Zero, true, false, exitEvent); + if (h != IntPtr.Zero) + { + process.Exited -= Watchdog; // Don't restart the process after exit + SysWin32.SetEvent(h); + SysWin32.CloseHandle(h); + } + else + { + var e = Marshal.GetLastWin32Error(); + config.eventLog.WriteEntry ("Error creating exit event named '" + exitEvent + + "' (error code = " + e + ")", EventLogEntryType.Error); + } + } + } + catch (InvalidOperationException) { } + } + + // terminate process after a timeout + public void StopProcess(int timeout) { + if (restartTimer != null) { + restartTimer.Stop(); + } + try + { + if (!process.WaitForExit(timeout)) { process.Exited -= Watchdog; // Don't restart the process after kill process.Kill(); From 9e2292aa1508cdc32d924218cb597f88cd070a96 Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Fri, 11 Nov 2016 14:29:30 -0500 Subject: [PATCH 2/2] Set logfile to autoflush Fix delay in log file updates and missing log messages on exit. Also simplifies the code as the timer used to periodically trigger flushing is no more needed. Signed-off-by: Selva Nair --- Service.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/Service.cs b/Service.cs index 36b61df..f25dff6 100644 --- a/Service.cs +++ b/Service.cs @@ -272,6 +272,7 @@ public OpenVpnChild(OpenVpnServiceConfiguration config, string configFile) { config.logAppend ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read), new UTF8Encoding(false)); + logFile.AutoFlush = true; /// SET UP PROCESS START INFO string[] procArgs = { @@ -294,17 +295,6 @@ public OpenVpnChild(OpenVpnServiceConfiguration config, string configFile) { UseShellExecute = false, /* create_new_console is not exposed -- but we probably don't need it?*/ }; - - /// SET UP FLUSH TIMER - /** .NET has a very annoying habit of taking a very long time to flush - output streams **/ - var flushTimer = new System.Timers.Timer(60000); - flushTimer.AutoReset = true; - flushTimer.Elapsed += (object source, System.Timers.ElapsedEventArgs e) => - { - logFile.Flush(); - }; - flushTimer.Start(); } // set exit event so that openvpn will terminate