From 74cb4f5a84c9c86dab01c0af9992c69ba8f7cc23 Mon Sep 17 00:00:00 2001
From: Copilot <198982749+Copilot@users.noreply.github.com>
Date: Fri, 03 Apr 2026 10:14:58 +0000
Subject: [PATCH] Fix infinite loop in `doStopApplication()` on Windows service stop (#610)
---
opendj-server-legacy/src/build-tools/windows/service.c | 90 ++++++++++++++++++++++++++++++++-------------
opendj-server-legacy/lib/launcher_administrator.exe | 0
.github/workflows/build.yml | 2 +
opendj-server-legacy/src/build-tools/windows/service.h | 2 +
opendj-server-legacy/lib/opendj_service.exe | 0
opendj-server-legacy/lib/winlauncher.exe | 0
6 files changed, 68 insertions(+), 26 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 6a72759..c90d4df 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -246,9 +246,11 @@
opendj-server-legacy\target\package\opendj\bat\stop-ds.bat
opendj-server-legacy\target\package\opendj\bat\windows-service.bat --enableService
net start "OpenDJ Server"
+ if ($LASTEXITCODE -ne 0) { throw "net start 'OpenDJ Server' failed with exit code $LASTEXITCODE" }
for ($i=0; $i -lt 12; $i++) { try { $c = New-Object System.Net.Sockets.TcpClient('localhost', 1636); $c.Close(); break } catch { Start-Sleep -Seconds 5 } }
opendj-server-legacy\target\package\opendj\bat\ldapsearch.bat --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example2,dc=com" --searchScope sub "(uid=user.*)" dn | find /c '"dn:"' | findstr "10000"
net stop "OpenDJ Server"
+ if ($LASTEXITCODE -ne 0) { throw "net stop 'OpenDJ Server' failed with exit code $LASTEXITCODE" }
opendj-server-legacy\target\package\opendj\bat\windows-service.bat --disableService
- name: Upload Windows exe artifacts
diff --git a/opendj-server-legacy/lib/launcher_administrator.exe b/opendj-server-legacy/lib/launcher_administrator.exe
index bf71337..50b8858 100644
--- a/opendj-server-legacy/lib/launcher_administrator.exe
+++ b/opendj-server-legacy/lib/launcher_administrator.exe
Binary files differ
diff --git a/opendj-server-legacy/lib/opendj_service.exe b/opendj-server-legacy/lib/opendj_service.exe
index e4f456c..0b458d8 100644
--- a/opendj-server-legacy/lib/opendj_service.exe
+++ b/opendj-server-legacy/lib/opendj_service.exe
Binary files differ
diff --git a/opendj-server-legacy/lib/winlauncher.exe b/opendj-server-legacy/lib/winlauncher.exe
old mode 100755
new mode 100644
index f971e1b..04f210c
--- a/opendj-server-legacy/lib/winlauncher.exe
+++ b/opendj-server-legacy/lib/winlauncher.exe
Binary files differ
diff --git a/opendj-server-legacy/src/build-tools/windows/service.c b/opendj-server-legacy/src/build-tools/windows/service.c
index 42082bc..1fd7e37 100644
--- a/opendj-server-legacy/src/build-tools/windows/service.c
+++ b/opendj-server-legacy/src/build-tools/windows/service.c
@@ -503,7 +503,7 @@
// The functions returns SERVICE_RETURN_OK if we could start the server
// and SERVICE_RETURN_ERROR otherwise.
// ----------------------------------------------------
-ServiceReturnCode doStartApplication()
+ServiceReturnCode doStartApplication(SERVICE_STATUS_HANDLE *serviceStatusHandle, DWORD *checkPoint)
{
ServiceReturnCode returnValue;
// init out params
@@ -546,6 +546,11 @@
debug("doStartApplication: OPENDJ_WINDOWS_SERVICE_STARTDS_WAIT is not set. Using default %d milliseconds.",
STARTDS_WAIT_DEFAULT_VALUE);
}
+ if (serviceStatusHandle != NULL && checkPoint != NULL)
+ {
+ updateServiceStatus(SERVICE_START_PENDING, NO_ERROR, 0,
+ (*checkPoint)++, wait + 30000, serviceStatusHandle);
+ }
waitOk = waitForProcess(&procInfo, wait, &startDSExit);
if (waitOk)
{
@@ -590,6 +595,11 @@
while ((nTries > 0) && !running)
{
nTries--;
+ if (serviceStatusHandle != NULL && checkPoint != NULL)
+ {
+ updateServiceStatus(SERVICE_START_PENDING, NO_ERROR, 0,
+ (*checkPoint)++, 10000, serviceStatusHandle);
+ }
if (isServerRunning(&running, TRUE) != SERVICE_RETURN_OK)
{
break;
@@ -640,6 +650,11 @@
while ((nTries > 0) && !running)
{
nTries--;
+ if (serviceStatusHandle != NULL && checkPoint != NULL)
+ {
+ updateServiceStatus(SERVICE_START_PENDING, NO_ERROR, 0,
+ (*checkPoint)++, 10000, serviceStatusHandle);
+ }
if (isServerRunning(&running, TRUE) != SERVICE_RETURN_OK)
{
break;
@@ -700,7 +715,7 @@
if (spawn(command, FALSE) != -1)
{
// Try to see if server is really stopped
- int nTries = 10;
+ int nTries = 30;
BOOL running = TRUE;
debug("doStopApplication: the spawn of the process worked.");
@@ -710,6 +725,7 @@
Sleep(3000);
while ((nTries > 0) && running)
{
+ nTries--;
if (isServerRunning(&running, TRUE) != SERVICE_RETURN_OK)
{
break;
@@ -1118,7 +1134,7 @@
{
WORD argCount = 1;
const char *argc[] = {_instanceDir};
- code = doStartApplication();
+ code = doStartApplication(_serviceStatusHandle, &checkPoint);
switch (code)
{
@@ -1225,31 +1241,53 @@
}
else
{
- // Check current Status
- DWORD state;
- BOOL success = getServiceStatus(serviceName, &state);
- if (!(success &&
- ((state == SERVICE_STOPPED) ||
- (state == SERVICE_STOP_PENDING))))
+ // Server appears not running - retry a few times before concluding
+ // it has actually stopped (the lock file check can be transient,
+ // e.g. during JVM GC pressure or heavy I/O after a large ldapsearch).
+ // 3 retries × 2 seconds gives up to 6 extra seconds of tolerance.
+ int retryCount = 3;
+ BOOL confirmedStopped = TRUE;
+ while (retryCount > 0)
{
- WORD argCount = 1;
- const char *argc[] = {_instanceDir};
- _serviceCurStatus = SERVICE_STOPPED;
- debug("checking in serviceMain serviceHandler: service stopped with error.");
+ retryCount--;
+ Sleep(2000); // wait 2 seconds between retries before re-checking
+ code = isServerRunning(&running, TRUE);
+ if (code == SERVICE_RETURN_OK && running)
+ {
+ confirmedStopped = FALSE;
+ break;
+ }
+ }
- updateServiceStatus (
- _serviceCurStatus,
- ERROR_SERVICE_SPECIFIC_ERROR,
- -1,
- CHECKPOINT_NO_ONGOING_OPERATION,
- TIMEOUT_NONE,
- _serviceStatusHandle);
- reportLogEvent(
- EVENTLOG_ERROR_TYPE,
- WIN_EVENT_ID_SERVER_STOPPED_OUTSIDE_SCM,
- argCount, argc);
- }
- break;
+ if (confirmedStopped)
+ {
+ // 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 (
+ _serviceCurStatus,
+ ERROR_SERVICE_SPECIFIC_ERROR,
+ -1,
+ CHECKPOINT_NO_ONGOING_OPERATION,
+ TIMEOUT_NONE,
+ _serviceStatusHandle);
+ reportLogEvent(
+ EVENTLOG_ERROR_TYPE,
+ WIN_EVENT_ID_SERVER_STOPPED_OUTSIDE_SCM,
+ argCount, argc);
+ }
+ break;
+ }
+ // else: server is actually still running, continue monitoring
}
}
}
diff --git a/opendj-server-legacy/src/build-tools/windows/service.h b/opendj-server-legacy/src/build-tools/windows/service.h
index 6d6b933..d75deba 100644
--- a/opendj-server-legacy/src/build-tools/windows/service.h
+++ b/opendj-server-legacy/src/build-tools/windows/service.h
@@ -13,6 +13,7 @@
*
* Copyright 2008 Sun Microsystems, Inc.
* Portions Copyright 2013 ForgeRock AS.
+ * Portions Copyright 2026 3A Systems, LLC.
*/
#include "common.h"
@@ -95,6 +96,7 @@
DWORD waitHint,
SERVICE_STATUS_HANDLE *serviceStatusHandle
);
+ServiceReturnCode doStartApplication(SERVICE_STATUS_HANDLE *serviceStatusHandle, DWORD *checkPoint);
void serviceHandler(DWORD controlCode);
BOOL getServiceStatus(char *serviceName, LPDWORD returnState);
--
Gitblit v1.10.0