[Openvpn-devel,4/5] Fix signal handling on Windows

Message ID 20230101215109.1521549-5-selva.nair@gmail.com
State Superseded
Headers show
Series Improve signals and avoid losing high priority ones | expand

Commit Message

Selva Nair Jan. 1, 2023, 9:51 p.m. UTC
From: Selva Nair <selva.nair@gmail.com>

- In win32_signal_get() re-order the check so that Windows
  signals are picked up even if signal_received is non-zero

- When management is not active, management_sleep() becomes sleep()
  but it is not interruptible by signals on Windows. Fix this by
  periodically checking for signal.

Fixes Trac #311 #639 (windows specific part)

Github: Fixes OpenVPN/openvpn#205 (windows specific part)
Note: if stuck  in address resolution, press ctrl-C and wait for getaddrinfo()
to timeout.

Signed-off-by: Selva Nair <selva.nair@gmail.com>
---
 src/openvpn/manage.c |  4 ++
 src/openvpn/win32.c  | 98 +++++++++++++++++++++++++++++---------------
 2 files changed, 68 insertions(+), 34 deletions(-)

Patch

diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c
index 5465b7e9..ac37e557 100644
--- a/src/openvpn/manage.c
+++ b/src/openvpn/manage.c
@@ -4091,10 +4091,14 @@  man_persist_client_stats(struct management *man, struct context *c)
 void
 management_sleep(const int n)
 {
+#ifdef WIN32
+    win32_sleep(n);
+#else
     if (n > 0)
     {
         sleep(n);
     }
+#endif /* ifdef WIN32 */
 }
 
 #endif /* ENABLE_MANAGEMENT */
diff --git a/src/openvpn/win32.c b/src/openvpn/win32.c
index c3520bca..e16d5461 100644
--- a/src/openvpn/win32.c
+++ b/src/openvpn/win32.c
@@ -642,50 +642,44 @@  int
 win32_signal_get(struct win32_signal *ws)
 {
     int ret = 0;
-    if (siginfo_static.signal_received)
-    {
-        ret = siginfo_static.signal_received;
-    }
-    else
+
+    if (ws->mode == WSO_MODE_SERVICE)
     {
-        if (ws->mode == WSO_MODE_SERVICE)
+        if (win32_service_interrupt(ws))
         {
-            if (win32_service_interrupt(ws))
-            {
-                ret = SIGTERM;
-            }
+            ret = SIGTERM;
         }
-        else if (ws->mode == WSO_MODE_CONSOLE)
+    }
+    else if (ws->mode == WSO_MODE_CONSOLE)
+    {
+        switch (win32_keyboard_get(ws))
         {
-            switch (win32_keyboard_get(ws))
-            {
-                case 0x3B: /* F1 -> USR1 */
-                    ret = SIGUSR1;
-                    break;
+            case 0x3B: /* F1 -> USR1 */
+                ret = SIGUSR1;
+                break;
 
-                case 0x3C: /* F2 -> USR2 */
-                    ret = SIGUSR2;
-                    break;
+            case 0x3C: /* F2 -> USR2 */
+                ret = SIGUSR2;
+                break;
 
-                case 0x3D: /* F3 -> HUP */
-                    ret = SIGHUP;
-                    break;
+            case 0x3D: /* F3 -> HUP */
+                ret = SIGHUP;
+                break;
 
-                case 0x3E: /* F4 -> TERM */
-                    ret = SIGTERM;
-                    break;
+            case 0x3E: /* F4 -> TERM */
+                ret = SIGTERM;
+                break;
 
-                case 0x03: /* CTRL-C -> TERM */
-                    ret = SIGTERM;
-                    break;
-            }
-        }
-        if (ret)
-        {
-            throw_signal(ret); /* this will update signinfo_static.signal received */
+            case 0x03: /* CTRL-C -> TERM */
+                ret = SIGTERM;
+                break;
         }
     }
-    return ret;
+    if (ret)
+    {
+        throw_signal(ret); /* this will update signinfo_static.signal received */
+    }
+    return (siginfo_static.signal_received);
 }
 
 void
@@ -1603,4 +1597,40 @@  set_openssl_env_vars()
     }
 }
 
+void
+win32_sleep(const int n)
+{
+    if (n < 0)
+    {
+        return;
+    }
+
+    /* Sleep() is not interruptible. Use a WAIT_OBJECT to catch signal */
+    if (HANDLE_DEFINED(win32_signal.in.read))
+    {
+        time_t expire = 0;
+        update_time();
+        expire = now + n;
+        while (expire >= now)
+        {
+            DWORD status = WaitForSingleObject(win32_signal.in.read, (expire - now)*1000);
+            if (win32_signal_get(&win32_signal) || status == WAIT_TIMEOUT)
+            {
+                return;
+            }
+
+            update_time();
+
+            if (status == WAIT_FAILED && expire > now)
+            {
+                Sleep((expire-now)*1000);
+                return;
+            }
+        }
+    }
+    else
+    {
+        Sleep(n*1000);
+    }
+}
 #endif /* ifdef _WIN32 */