From 161fa81eb1a6a9f0d647966ee38acb36d6ad00dd 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.

---
 opendj-sdk/opends/src/server/org/opends/server/tools/StartWindowsService.java     |    4 +
 opendj-sdk/opends/src/server/org/opends/server/tools/ConfigureWindowsService.java |   13 ++--
 opendj-sdk/opends/lib/opendj_service.exe                                          |    0 
 opendj-sdk/opends/src/build-tools/windows/service.h                               |    2 
 opendj-sdk/opends/src/build-tools/windows/service.c                               |  115 ++++++++++++++++++++++++++++----------
 opendj-sdk/opends/lib/winlauncher.exe                                             |    0 
 opendj-sdk/opends/src/server/org/opends/server/tools/StopWindowsService.java      |   21 +++---
 opendj-sdk/opends/lib/launcher_administrator.exe                                  |    0 
 8 files changed, 108 insertions(+), 47 deletions(-)

diff --git a/opendj-sdk/opends/lib/launcher_administrator.exe b/opendj-sdk/opends/lib/launcher_administrator.exe
index 9440509..303839e 100644
--- a/opendj-sdk/opends/lib/launcher_administrator.exe
+++ b/opendj-sdk/opends/lib/launcher_administrator.exe
Binary files differ
diff --git a/opendj-sdk/opends/lib/opendj_service.exe b/opendj-sdk/opends/lib/opendj_service.exe
index 8ba800b..4647066 100644
--- a/opendj-sdk/opends/lib/opendj_service.exe
+++ b/opendj-sdk/opends/lib/opendj_service.exe
Binary files differ
diff --git a/opendj-sdk/opends/lib/winlauncher.exe b/opendj-sdk/opends/lib/winlauncher.exe
index 7e85f39..730a960 100755
--- a/opendj-sdk/opends/lib/winlauncher.exe
+++ b/opendj-sdk/opends/lib/winlauncher.exe
Binary files differ
diff --git a/opendj-sdk/opends/src/build-tools/windows/service.c b/opendj-sdk/opends/src/build-tools/windows/service.c
index fad6a83..abf0e51 100644
--- a/opendj-sdk/opends/src/build-tools/windows/service.c
+++ b/opendj-sdk/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();
   }
diff --git a/opendj-sdk/opends/src/build-tools/windows/service.h b/opendj-sdk/opends/src/build-tools/windows/service.h
index 32cd2c2..fc13019 100644
--- a/opendj-sdk/opends/src/build-tools/windows/service.h
+++ b/opendj-sdk/opends/src/build-tools/windows/service.h
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2008 Sun Microsystems, Inc.
+ *      Portions Copyright 2013 ForgeRock AS.
  */
 
 #include "common.h"
@@ -106,4 +107,5 @@
    SERVICE_STATUS_HANDLE *serviceStatusHandle
    );
 void serviceHandler(DWORD controlCode);
+BOOL getServiceStatus(char *serviceName, LPDWORD returnState);
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/ConfigureWindowsService.java b/opendj-sdk/opends/src/server/org/opends/server/tools/ConfigureWindowsService.java
index fd0c801..38545fc 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/ConfigureWindowsService.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/ConfigureWindowsService.java
@@ -23,7 +23,7 @@
  *
  *
  *      Copyright 2008-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011 ForgeRock AS
+ *      Portions Copyright 2011-2013 ForgeRock AS
  */
 
 package org.opends.server.tools;
@@ -686,6 +686,7 @@
       returnValue = SERVICE_CLEANUP_ERROR;
       msg = ERR_WINDOWS_SERVICE_CLEANUP_ERROR.get(serviceName);
       err.println(msg);
+      err.println("Exception:" + t.toString());
     }
     return returnValue;
   }
@@ -763,10 +764,10 @@
         break;
       case 2:
         returnValue = SERVICE_STATE_ERROR;
-        if (out != null)
+        if (err != null)
         {
           msg = ERR_WINDOWS_SERVICE_STATE_ERROR.get();
-          out.println(msg);
+          err.println(msg);
         }
         break;
       default:
@@ -784,7 +785,8 @@
       if (err != null)
       {
         msg = ERR_WINDOWS_SERVICE_STATE_ERROR.get();
-        err.println(wrapText(msg, MAX_LINE_WIDTH));
+        err.println(msg);
+        err.println(wrapText(t.toString(), MAX_LINE_WIDTH));
       }
     }
     return returnValue;
@@ -806,8 +808,7 @@
        * Do a best effort to avoid having a relative representation (for
        * instance to avoid having ../../../).
        */
-      File canonical = f.getCanonicalFile();
-      f = canonical;
+      f = f.getCanonicalFile();
     }
     catch (IOException ioe)
     {
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/StartWindowsService.java b/opendj-sdk/opends/src/server/org/opends/server/tools/StartWindowsService.java
index b432134..b784ac0 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/StartWindowsService.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/StartWindowsService.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2008-2009 Sun Microsystems, Inc.
+ *      Portions Copyright 2013 ForgeRock AS.
  */
 
 package org.opends.server.tools;
@@ -151,7 +152,8 @@
       {
 
         Message message = ERR_WINDOWS_SERVICE_START_ERROR.get();
-        out.println(message);
+        err.println(message);
+        err.println("Exception:" + t.toString());
         returnValue = SERVICE_START_ERROR;
       }
     }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/StopWindowsService.java b/opendj-sdk/opends/src/server/org/opends/server/tools/StopWindowsService.java
index f6b4c4b..594e544 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/StopWindowsService.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/StopWindowsService.java
@@ -23,7 +23,7 @@
  *
  *
  *      Copyright 2008-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2012 ForgeRock AS
+ *      Portions Copyright 2012-2013 ForgeRock AS
  */
 
 package org.opends.server.tools;
@@ -55,10 +55,6 @@
     */
   public static final int SERVICE_NOT_FOUND = 1;
   /**
-    * The service was already stopped.
-    */
-  public static final int SERVICE_ALREADY_STOPPED = 2;
-  /**
     * The service could not be stopped.
     */
   public static final int SERVICE_STOP_ERROR = 3;
@@ -79,9 +75,8 @@
    * Invokes the net stop on the service corresponding to this server, it writes
    * information and error messages in the provided streams.
    * @return <CODE>SERVICE_STOP_SUCCESSFUL</CODE>,
-   * <CODE>SERVICE_NOT_FOUND</CODE>, <CODE>SERVICE_ALREADY_STOPPED</CODE> or
-   * <CODE>SERVICE_STOP_ERROR</CODE> depending on whether the service could be
-   * stopped or not.
+   * <CODE>SERVICE_NOT_FOUND</CODE> or <CODE>SERVICE_STOP_ERROR</CODE>
+   * depending on whether the service could be stopped or not.
    * @param  outStream  The stream to write standard output messages.
    * @param  errStream  The stream to write error messages.
    */
@@ -143,7 +138,12 @@
       /* Check if is a running service */
       try
       {
-        if (Runtime.getRuntime().exec(cmd).waitFor() == 0)
+        int resultCode = Runtime.getRuntime().exec(cmd).waitFor();
+        if (resultCode == 0)
+        {
+          returnValue = SERVICE_STOP_SUCCESSFUL;
+        }
+        else if (resultCode == 2)
         {
           returnValue = SERVICE_STOP_SUCCESSFUL;
         }
@@ -156,7 +156,8 @@
       {
 
         Message message = ERR_WINDOWS_SERVICE_STOP_ERROR.get();
-        out.println(message);
+        err.println(message);
+        err.println("Exception:" + t.toString());
         returnValue = SERVICE_STOP_ERROR;
       }
     }

--
Gitblit v1.10.0