mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

ludovicp
27.52.2010 98e131bb4dde9eb6e2befce74256f5caa2eb093b
Fix for issue #4554 - Potential problems using Windows Service during startup.
The fix consists on waiting in the service code to the start-ds.bat call to finish (the re-entrant one). This avoids having a race condition problem locking the file (as can happen today).
There is a new environment variable that can be configured (OPENDS_WINDOWS_SERVICE_STARTDS_WAIT). The default value is 300000 milliseconds (5 minutes). This is the maximum time in milliseconds the service code will wait for the server to start before checking directly its status.
Also update setup.bat and uninstall.bat to return an error code when called in CLI mode.
9 files modified
242 ■■■■ changed files
opends/lib/launcher_administrator.exe patch | view | raw | blame | history
opends/lib/opends_service.exe patch | view | raw | blame | history
opends/lib/winlauncher.exe patch | view | raw | blame | history
opends/resource/bin/start-ds.bat 2 ●●● patch | view | raw | blame | history
opends/resource/setup.bat 3 ●●●● patch | view | raw | blame | history
opends/resource/uninstall.bat 3 ●●●● patch | view | raw | blame | history
opends/src/build-tools/windows/common.c 121 ●●●●● patch | view | raw | blame | history
opends/src/build-tools/windows/common.h 8 ●●●● patch | view | raw | blame | history
opends/src/build-tools/windows/service.c 105 ●●●● patch | view | raw | blame | history
opends/lib/launcher_administrator.exe
Binary files differ
opends/lib/opends_service.exe
Binary files differ
opends/lib/winlauncher.exe
Binary files differ
opends/resource/bin/start-ds.bat
@@ -42,7 +42,7 @@
set LOG="%INSTANCE_ROOT%\logs\native-windows.out"
set SCRIPT=start-ds.bat
echo %SCRIPT%: invoked > %LOG%
echo %SCRIPT%: invoked >> %LOG%
set SCRIPT_NAME=start-ds
opends/resource/setup.bat
@@ -23,7 +23,7 @@
rem CDDL HEADER END
rem
rem
rem      Copyright 2006-2008 Sun Microsystems, Inc.
rem      Copyright 2006-2010 Sun Microsystems, Inc.
setlocal
@@ -73,6 +73,7 @@
rem return part
if %errorlevel% == 50 goto version
if NOT %errorlevel% == 0 exit /B %errorlevel%
goto end
:version
opends/resource/uninstall.bat
@@ -23,7 +23,7 @@
rem CDDL HEADER END
rem
rem
rem      Copyright 2006-2008 Sun Microsystems, Inc.
rem      Copyright 2006-2010 Sun Microsystems, Inc.
setlocal
for %%i in (%~sf0) do set DIR_HOME=%%~dPsi.
@@ -59,6 +59,7 @@
rem return part
if %errorlevel% == 50 goto version
if NOT %errorlevel% == 0 exit /B %errorlevel%
goto end
:version
opends/src/build-tools/windows/common.c
@@ -22,10 +22,11 @@
* CDDL HEADER END
*
*
*      Copyright 2008 Sun Microsystems, Inc.
*      Copyright 2008-2010 Sun Microsystems, Inc.
*/
#include "common.h"
#include "service.h"
#include <errno.h>
#include <fcntl.h>
#include <io.h>
@@ -56,7 +57,8 @@
  HANDLE hStdout;                 /* stdout */
  HANDLE hStderr;                 /* stderr */
  debug("Attempting to create child process '%s' background=%d.", command,
  debug("createChildProcess: Attempting to create child process '%s' background=%d.",
      command,
      background);
  // reset process info first
@@ -91,29 +93,95 @@
  if (createOk)
  {
    debug("Successfully created child process '%s'.", command);
    debug("createChildProcess: Successfully created child process '%s'.", command);
  }
  else
  {
    debugError("Failed to create child process '%s'.  Last error = %d.",
    debugError(
        "createChildProcess: Failed to create child process '%s'.  Last error = %d.",
        command, GetLastError());
  }
  
  return createOk;
} // createChildProcess
BOOL createBatchFileChildProcess(char* batchFile, BOOL background,
PROCESS_INFORMATION* procInfo)
{
  BOOL createOk;
  STARTUPINFO startInfo; // info to pass to the new process
  DWORD processFlag; // background process flag
  HANDLE hStdin;                  /* stdin */
  HANDLE hStdout;                 /* stdout */
  HANDLE hStderr;                 /* stderr */
  char command[COMMAND_SIZE]; // full command line
  if (strlen(batchFile) + 3 >= COMMAND_SIZE)
  {
    debug("createBatchFileChildProcess: the batch file path is too long.");
    return FALSE;
  }
  sprintf(command, "/c %s", batchFile);
  debug("createBatchFileChildProcess: Attempting to create child process '%s' background=%d.",
      command,
      background);
  // reset process info first
  ZeroMemory(procInfo, sizeof(PROCESS_INFORMATION));
  // initialize handles to pass to the child process
  ZeroMemory(&startInfo, sizeof(STARTUPINFO));
  startInfo.cb = sizeof(STARTUPINFO);
  startInfo.dwFlags |= STARTF_USESTDHANDLES;  // use handles above
  hStdin= GetStdHandle(STD_INPUT_HANDLE);
  SetHandleInformation (hStdin,  HANDLE_FLAG_INHERIT, FALSE);
  hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
  SetHandleInformation (hStdout, HANDLE_FLAG_INHERIT, FALSE);
  hStderr = GetStdHandle(STD_ERROR_HANDLE);
  SetHandleInformation (hStderr, HANDLE_FLAG_INHERIT, FALSE);
  // Create the child process
  processFlag = background == TRUE ? DETACHED_PROCESS : 0;
  createOk = CreateProcess(
    "cmd.exe",          // application name
    command,       // command line
    NULL,          // process security attributes
    NULL,          // primary thread security attributes
    TRUE,          // handles are inherited
    processFlag,   // creation flags
    NULL,          // use parent's environment
    NULL,          // use parent's current directory
    &startInfo,    // STARTUPINFO pointer
    procInfo       // receives PROCESS_INFORMATION
  );
  if (createOk)
  {
    debug("createBatchFileChildProcess: Successfully created child process '%s'.", command);
  }
  else
  {
    debugError("createBatchFileChildProcess: Failed to create child process '%s'.  Last error = %d.",
        command, GetLastError());
  }
  return createOk;
} // createChildProcess
// ----------------------------------------------------
// Function used to launch a process for the given command
// If the process could be created it returns the pid of
// the created process and -1 otherwise.
// ----------------------------------------------------
int spawn(const char* command, BOOL background)
int spawn(char* command, BOOL background)
{
  DWORD childPid = -1; // child's pid
  PROCESS_INFORMATION procInfo; // info on the new process
  BOOL createOk;
  createOk = createChildProcess((char*)command, background, &procInfo);
  createOk = createChildProcess(command, background, &procInfo);
  if(createOk)
  {
@@ -132,7 +200,46 @@
  }
} // spawn
// ----------------------------------------------------
// Function used to wait for a process.
// The passed waitTime parameter is maximum the time in milliseconds to wait.
// Returns TRUE if the process ended and updates the exitCode
// parameter with the return value of the process.
// Returns FALSE if the process did not end with the provided
// timeout and the error code returned by WaitForSingleObject
// in the provided exitCode value.
// ----------------------------------------------------
BOOL waitForProcess(PROCESS_INFORMATION* procInfo, DWORD waitTime,
 DWORD* exitCode)
{
  BOOL returnValue = TRUE;
  DWORD waitForSingleCode;
  debug("waitForProcess: wait time is: %d", waitTime);
  waitForSingleCode = WaitForSingleObject (procInfo->hProcess, waitTime);
  if (waitForSingleCode == WAIT_OBJECT_0)
  {
    debug("waitForProcess: was successful");
    GetExitCodeProcess(procInfo->hProcess, exitCode);
    debug("waitForProcess exitCode: %d", *exitCode);
  }
  else
  {
    returnValue = FALSE;
    switch (waitForSingleCode)
    {
      case WAIT_FAILED:
        debug("waitForProcess: Wait for process failed: %d", GetLastError());
        break;
      case WAIT_TIMEOUT:
        debug("waitForProcess: Process timed out.");
        break;
      default:
        debug("waitForProcess: WaitForSingleObject returned %d", waitForSingleCode);
    }
    *exitCode = waitForSingleCode;
  }
  return returnValue;
}
// ---------------------------------------------------
// Debug utility.
// ---------------------------------------------------
opends/src/build-tools/windows/common.h
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
*      Copyright 2008 Sun Microsystems, Inc.
*      Copyright 2008-2010 Sun Microsystems, Inc.
*/
// Just some functions and constants to be used by winlauncher.c
@@ -33,9 +33,13 @@
#include <Windows.h>
int spawn(const char* command, BOOL background);
int spawn(char* command, BOOL background);
BOOL createChildProcess(char* command, BOOL background,
PROCESS_INFORMATION* procInfo);
BOOL createBatchFileChildProcess(char* batchFile, BOOL background,
PROCESS_INFORMATION* procInfo);
void debug(const char *msg, ...);
void debugError(const char *msg, ...);
void updateDebugFlag(char* argv[], int argc);
BOOL waitForProcess(PROCESS_INFORMATION* procInfo, DWORD waitTime,
  DWORD* exitCode);
opends/src/build-tools/windows/service.c
@@ -518,43 +518,102 @@
  // init out params
  char* relativePath = "\\bat\\start-ds.bat";
  char command[COMMAND_SIZE];
  PROCESS_INFORMATION procInfo; // info on the new process
  BOOL createOk;
  BOOL waitOk;
  debug("doStartApplication called.");
  debug("doStartApplication called");
  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);
    // launch the command
    debug("doStartApplication attempting to spawn '%s'", command);
    if (spawn(command, FALSE) != -1)
    if (createOk)
    {
      // Try to see if server is really running
      int nTries = 100;
      char * nTriesEnv = getenv("OPENDS_WINDOWS_SERVICE_START_NTRIES");
      BOOL running = FALSE;
      if (nTriesEnv != NULL)
      // At this point start-ds.bat has been launched.  Try to wait till it
      // returns.  At the end of start-ds.bat we can be sure that the server (at
      // least) tried to start: the script checks that the file created during
      // startup is deleted (file logs\server.starting).
      const DWORD STARTDS_WAIT_DEFAULT_VALUE = 300000;
      DWORD wait = STARTDS_WAIT_DEFAULT_VALUE;
      char * nWaitForStartDS = getenv("OPENDS_WINDOWS_SERVICE_STARTDS_WAIT");
      DWORD startDSExit;
      if (nWaitForStartDS != NULL)
      {
        nTries = (int)strtol(nTriesEnv, (char **)NULL, 10);
        if (nTries <= 0)
        debug("doStartApplication: OPENDS_WINDOWS_SERVICE_STARTDS_WAIT env var set to %s",
            nWaitForStartDS);
        wait = (int)strtol(nWaitForStartDS, (char **)NULL, 10);
        if (wait <= 0)
        {
          nTries = 100;
          wait = STARTDS_WAIT_DEFAULT_VALUE;
        }
      }
      else
      {
        debug("OPENDS_WINDOWS_SERVICE_START_NTRIES is not set.  Using default 100 tries.");
        debug("doStartApplication: OPENDS_WINDOWS_SERVICE_STARTDS_WAIT is not set. Using default %d milliseconds.",
            STARTDS_WAIT_DEFAULT_VALUE);
      }
      waitOk = waitForProcess(&procInfo, wait, &startDSExit);
      if (waitOk)
      {
        debug("doStartApplication: waited properly for process end.");
        debug("doStartApplication: exit code of script: %d", startDSExit);
        if (startDSExit != 0)
        {
          createOk = FALSE;
        }
      }
    }
    else
    {
      debug("The batch file process could not be created property");
    }
    if (createOk && waitOk)
    {
      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)
      {
        returnValue = SERVICE_RETURN_OK;
        debug("doStartApplication: server running.");
      }
      else
      {
        returnValue = SERVICE_RETURN_ERROR;
        debug("doStartApplication: server not running.");
      }
    }
    else if (createOk)
    {
      // Try to see if server is really running
      const DWORD DEFAULT_TRIES = 100;
      int nTries = DEFAULT_TRIES;
      char * nTriesEnv = getenv("OPENDS_WINDOWS_SERVICE_START_NTRIES");
      BOOL running = FALSE;
      if (nTriesEnv != NULL)
      {
        debug("OPENDS_WINDOWS_SERVICE_START_NTRIES env var set to %s", nTriesEnv);
        nTries = (int)strtol(nTriesEnv, (char **)NULL, 10);
        if (nTries <= 0)
        {
          nTries = DEFAULT_TRIES;
        }
      }
      else
      {
        debug("OPENDS_WINDOWS_SERVICE_START_NTRIES is not set.  Using default %d tries.", nTries);
      } 
      debug(
          "doStartApplication: the spawn of the process worked.  Command: '%s'",
          "doStartApplication: the spawn of the batch command worked.  Command: '%s'",
          command);
      // Wait to be able to launch the java process in order it to get the lock
      // on the file.
      debug("Sleeping for 5 seconds to allow the process to get the lock.");
      Sleep(5000);
      while ((nTries > 0) && !running)
      {
        nTries--;
@@ -584,7 +643,7 @@
    {
      returnValue = SERVICE_RETURN_ERROR;
      debug("doStartApplication: spawn failed.  Sent command: '%s'", command);
      debug("doStartApplication: batch command spawn failed.  Sent command: '%s'", command);
    }
  }
  else
@@ -595,6 +654,8 @@
  return returnValue;
}  // doStartApplication
// ----------------------------------------------------
// Start the application using stop-ds.bat
// The functions returns SERVICE_RETURN_OK if we could stop the server