From bce0666db7ec0fd758c781cd3db6ea493f78d408 Mon Sep 17 00:00:00 2001
From: Ludovic Poitou <ludovic.poitou@forgerock.com>
Date: Mon, 08 Apr 2013 17:26:50 +0000
Subject: [PATCH] Fix for OPENDJ-617. Resolve race condition in Windows Services when stopping the server.

---
 opends/src/build-tools/windows/service.c |  115 ++++++++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 85 insertions(+), 30 deletions(-)

diff --git a/opends/src/build-tools/windows/service.c b/opends/src/build-tools/windows/service.c
index fad6a83..abf0e51 100644
--- a/opends/src/build-tools/windows/service.c
+++ b/opends/src/build-tools/windows/service.c
@@ -23,7 +23,7 @@
 *
 *
 *      Copyright 2008-2010 Sun Microsystems, Inc.
-*      Portions Copyright 2011 ForgeRock AS
+*      Portions Copyright 2011-2013 ForgeRock AS
 */
 
 #include "service.h"
@@ -527,7 +527,7 @@
 
   if (strlen(relativePath)+strlen(_instanceDir)+1 < COMMAND_SIZE)
   {
-	sprintf(command, "\"%s%s\" --windowsNetStart", _instanceDir, relativePath);
+  sprintf(command, "\"%s%s\" --windowsNetStart", _instanceDir, relativePath);
     debug("doStartApplication: Launching batch file: %s", command);
     createOk = createBatchFileChildProcess(command, FALSE, &procInfo);
 
@@ -572,14 +572,14 @@
       debug("The batch file process could not be created property");
     }
 
-	if (createOk && waitOk)
+  if (createOk && waitOk)
     {
-	  BOOL running;
+    BOOL running;
       // Just check once if the server is running or not: since the wait
       // wait was successful, if the server is getting the lock, it already
       // got it.
-	  isServerRunning(&running, TRUE);
-	  if (running)
+    isServerRunning(&running, TRUE);
+    if (running)
       {
         returnValue = SERVICE_RETURN_OK;
         debug("doStartApplication: server running.");
@@ -589,7 +589,7 @@
         returnValue = SERVICE_RETURN_ERROR;
         debug("doStartApplication: server not running.");
       }
-	}
+  }
     else if (createOk)
     {
       // Try to see if server is really running
@@ -609,7 +609,7 @@
       else
       {
         debug("OPENDJ_WINDOWS_SERVICE_START_NTRIES is not set.  Using default %d tries.", nTries);
-      }	
+      }
 
       debug(
           "doStartApplication: the spawn of the batch command worked.  Command: '%s'",
@@ -658,7 +658,7 @@
 
 
 // ----------------------------------------------------
-// Start the application using stop-ds.bat
+// Stop the application using stop-ds.bat
 // The functions returns SERVICE_RETURN_OK if we could stop the server
 // and SERVICE_RETURN_ERROR otherwise.
 // ----------------------------------------------------
@@ -751,7 +751,7 @@
   {
     // failed to get the path of the executable file
     returnValue = SERVICE_RETURN_ERROR;
-	debug("Could not get the path of the executable file.");
+  debug("Could not get the path of the executable file.");
   }
   else
   {
@@ -956,6 +956,53 @@
 }  // updateServiceStatus
 
 // ----------------------------------------------------
+// Query the current status of the service serviceName into returnedStatus
+// return true if it was successful, false otherwise
+// ----------------------------------------------------
+BOOL getServiceStatus(char *serviceName, LPDWORD returnedState)
+{
+  SC_HANDLE hScm;
+  SC_HANDLE hService;
+  BOOL ret = FALSE;
+
+  if (openScm(SC_MANAGER_ALL_ACCESS, &hScm) != SERVICE_RETURN_OK)
+  {
+    debugError("getServiceStatus: openScm did not work. Last error = %d",
+    GetLastError());
+    return FALSE;
+  }
+
+
+  hService = OpenService(hScm, serviceName, SERVICE_QUERY_STATUS);
+  if(hService == NULL)
+  {
+    debugError("getServiceStatus: openService did not work. Last error = %d",
+      GetLastError());
+  }
+  else
+  {
+    SERVICE_STATUS ss;
+    memset(&ss, 0, sizeof(ss));
+    if (QueryServiceStatus(hService, &ss))
+    {
+      *returnedState = ss.dwCurrentState;
+    ret = TRUE;
+  }
+  else
+  {
+    debugError("getServiceStatus: Failed to query the service status. Last error = %d",
+                  GetLastError());
+    }
+    // close the service handle
+    CloseServiceHandle(hService);
+  }
+  // close the service control manager handle
+  CloseServiceHandle(hScm);
+
+  return ret;
+}
+
+// ----------------------------------------------------
 // This function is the "main" of the service. It has been registered
 // to the SCM by the main right after the service has been started through
 // NET START command.
@@ -1090,7 +1137,7 @@
   if (code == SERVICE_RETURN_OK)
   {
     BOOL updatedRunningStatus = FALSE;
-	DWORD returnValue;
+  DWORD returnValue;
     int refreshPeriodSeconds = 10;
     char *refreshPeriodEnv = getenv("OPENDJ_WINDOWS_SERVICE_REFRESH_PERIOD");
     if (refreshPeriodEnv != NULL)
@@ -1108,7 +1155,7 @@
       {
         returnValue = WaitForSingleObject (_terminationEvent, INFINITE);
         break;
-      }  
+      }
       else
       {
         running = FALSE;
@@ -1123,7 +1170,7 @@
           if (returnValue == WAIT_OBJECT_0)
           {
             debug("The application has exited.");
-            break;           
+            break;
           }
         }
         code = isServerRunning(&running, FALSE);
@@ -1134,16 +1181,16 @@
         else if (running)
         {
           _serviceCurStatus = SERVICE_RUNNING;
-		  if (!updatedRunningStatus)
-		  {
-		    WORD argCount = 1;
+      if (!updatedRunningStatus)
+      {
+        WORD argCount = 1;
             const char *argc[] = {_instanceDir};
             updateServiceStatus (
                _serviceCurStatus,
                NO_ERROR,
                0,
                checkPoint ++,
-		       TIMEOUT_NONE,
+           TIMEOUT_NONE,
                _serviceStatusHandle
              );
              reportLogEvent(
@@ -1151,27 +1198,35 @@
                WIN_EVENT_ID_SERVER_STARTED,
                argCount, argc
              );
-			 updatedRunningStatus = TRUE;
-		  }
+       updatedRunningStatus = TRUE;
+      }
         }
         else
         {
-	      WORD argCount = 1;
-          const char *argc[] = {_instanceDir};    
-          _serviceCurStatus = SERVICE_STOPPED;
-          debug("checking in serviceMain serviceHandler: service stopped.");
+      // Check current Status
+      DWORD state;
+      BOOL success = getServiceStatus(serviceName, &state);
+          if (!(success &&
+               ((state == SERVICE_STOPPED) ||
+                (state == SERVICE_STOP_PENDING))))
+          {
+          WORD argCount = 1;
+            const char *argc[] = {_instanceDir};
+            _serviceCurStatus = SERVICE_STOPPED;
+            debug("checking in serviceMain serviceHandler: service stopped with error.");
 
-          updateServiceStatus (
+            updateServiceStatus (
               _serviceCurStatus,
               ERROR_SERVICE_SPECIFIC_ERROR,
               -1,
               CHECKPOINT_NO_ONGOING_OPERATION,
               TIMEOUT_NONE,
               _serviceStatusHandle);
-          reportLogEvent(
+            reportLogEvent(
               EVENTLOG_ERROR_TYPE,
               WIN_EVENT_ID_SERVER_STOPPED_OUTSIDE_SCM,
               argCount, argc);
+           }
           break;
         }
       }
@@ -1272,7 +1327,7 @@
       {
         WORD argCount = 1;
         const char *argc[] = {_instanceDir};
-		debug("serviceHandler: The server could not be stopped.");
+    debug("serviceHandler: The server could not be stopped.");
         // We could not stop the server
         reportLogEvent(
         EVENTLOG_ERROR_TYPE,
@@ -1374,7 +1429,7 @@
 
   if (myService == NULL)
   {
-	  debugError("getBinaryPathName: Failed to open the service '%s'.", serviceName);
+    debugError("getBinaryPathName: Failed to open the service '%s'.", serviceName);
   }
   else
   {
@@ -1423,8 +1478,8 @@
     }
     if (!CloseServiceHandle(myService))
     {
-	  debug("getBinaryPathName: error closing handle of service. Code [%d]",
-	        GetLastError());
+    debug("getBinaryPathName: error closing handle of service. Code [%d]",
+          GetLastError());
     }
   }
 
@@ -2237,7 +2292,7 @@
       WIN_EVENT_ID_SERVER_START_FAILED,
       argCount, argc
       );
-	  debugError("startService: For instance dir '%s', %s", argc[0], argc[1]);
+    debugError("startService: For instance dir '%s', %s", argc[0], argc[1]);
     }
     deregisterEventLog();
   }

--
Gitblit v1.10.0