From 98e131bb4dde9eb6e2befce74256f5caa2eb093b Mon Sep 17 00:00:00 2001
From: ludovicp <ludovicp@localhost>
Date: Thu, 27 May 2010 09:52:42 +0000
Subject: [PATCH] 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.
---
opends/lib/opends_service.exe | 0
opends/lib/winlauncher.exe | 0
opends/src/build-tools/windows/common.h | 8 +
opends/lib/launcher_administrator.exe | 0
opends/src/build-tools/windows/common.c | 121 ++++++++++++++++++++++-
opends/src/build-tools/windows/service.c | 105 ++++++++++++++++----
opends/resource/uninstall.bat | 3
opends/resource/bin/start-ds.bat | 2
opends/resource/setup.bat | 3
9 files changed, 208 insertions(+), 34 deletions(-)
diff --git a/opends/lib/launcher_administrator.exe b/opends/lib/launcher_administrator.exe
index 7937e88..313119a 100644
--- a/opends/lib/launcher_administrator.exe
+++ b/opends/lib/launcher_administrator.exe
Binary files differ
diff --git a/opends/lib/opends_service.exe b/opends/lib/opends_service.exe
index 2ecde9f..5cf5dfe 100755
--- a/opends/lib/opends_service.exe
+++ b/opends/lib/opends_service.exe
Binary files differ
diff --git a/opends/lib/winlauncher.exe b/opends/lib/winlauncher.exe
index c82fb22..2b64d5b 100755
--- a/opends/lib/winlauncher.exe
+++ b/opends/lib/winlauncher.exe
Binary files differ
diff --git a/opends/resource/bin/start-ds.bat b/opends/resource/bin/start-ds.bat
index 61ab3af..776fe7f 100644
--- a/opends/resource/bin/start-ds.bat
+++ b/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
diff --git a/opends/resource/setup.bat b/opends/resource/setup.bat
index 834517a..2b0bc14 100644
--- a/opends/resource/setup.bat
+++ b/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
diff --git a/opends/resource/uninstall.bat b/opends/resource/uninstall.bat
index ae19628..5ae2088 100644
--- a/opends/resource/uninstall.bat
+++ b/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
diff --git a/opends/src/build-tools/windows/common.c b/opends/src/build-tools/windows/common.c
index 3974748..db1d407 100644
--- a/opends/src/build-tools/windows/common.c
+++ b/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.
// ---------------------------------------------------
diff --git a/opends/src/build-tools/windows/common.h b/opends/src/build-tools/windows/common.h
index fc596e2..7d1b9fb 100644
--- a/opends/src/build-tools/windows/common.h
+++ b/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);
\ No newline at end of file
diff --git a/opends/src/build-tools/windows/service.c b/opends/src/build-tools/windows/service.c
index 5d33fa5..7102901 100644
--- a/opends/src/build-tools/windows/service.c
+++ b/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
--
Gitblit v1.10.0