diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..2676ab14f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# WiX sources must stay LF so the WiX toolset parses them consistently across runners. +*.wxs text eol=lf +*.wxi text eol=lf diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 041c6b6ebf..db6499f4b9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,19 +35,12 @@ jobs: - { os: 'windows-latest', java: '26' } fail-fast: false steps: - - name: Install wine+rpm for distribution + - name: Install rpm for distribution if: runner.os == 'Linux' shell: bash run: | - sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list - sudo dpkg --add-architecture i386 - sudo mkdir -pm755 /etc/apt/keyrings && sudo wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key - sudo wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/$(lsb_release -c -s)/winehq-$(lsb_release -c -s).sources sudo apt-get update - sudo apt install --install-recommends winehq-stable || sudo apt install --install-recommends winehq-staging - wine --version - version="9.4.0"; sudo wget "https://dl.winehq.org/wine/wine-mono/$version/wine-mono-$version-x86.msi" -O /tmp/wine-mono.msi - wine msiexec /i /tmp/wine-mono.msi + sudo apt-get install -y rpm - uses: actions/checkout@v6 with: fetch-depth: 0 @@ -86,6 +79,19 @@ jobs: run: | echo "MAVEN_PROFILE_FLAG=-P precommit" >> $GITHUB_OUTPUT + - name: Setup WiX (.NET tool) for MSI + if: runner.os == 'Windows' + shell: bash + run: | + # The MSI builds on Windows only (WiX cannot author MSIs on Linux/macOS). WiX 5 ships as a + # net6.0 tool; allow it to run on the newer .NET runtime present on the runner. + echo "DOTNET_ROLL_FORWARD=Major" >> "$GITHUB_ENV" + export DOTNET_ROLL_FORWARD=Major + dotnet tool install --global wix --version 5.0.2 || dotnet tool update --global wix --version 5.0.2 + echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH" + export PATH="$HOME/.dotnet/tools:$PATH" + wix --version + wix extension add -g WixToolset.UI.wixext/5.0.2 || true - name: Build with Maven timeout-minutes: 180 env: @@ -506,3 +512,169 @@ jobs: *.jmeter.out if-no-files-found: warn retention-days: 90 + + test-msi: + needs: build-maven + runs-on: 'windows-latest' + steps: + - name: Download artifacts + uses: actions/download-artifact@v8 + with: + name: windows-latest-11 + - name: Install must fail without a JRE + shell: pwsh + run: | + # Simulate a machine with no Java. The launch condition is evaluated by the Windows + # Installer service, which sees the MACHINE environment - so hide Java there too and + # restart msiserver to make it pick the change up; restore everything afterwards. + Stop-Service msiserver -Force -ErrorAction SilentlyContinue + $oldJH = [Environment]::GetEnvironmentVariable('JAVA_HOME','Machine') + $oldPM = [Environment]::GetEnvironmentVariable('Path','Machine') + [Environment]::SetEnvironmentVariable('JAVA_HOME',$null,'Machine') + [Environment]::SetEnvironmentVariable('Path',((($oldPM -split ';') | Where-Object { $_ -notmatch 'java|jdk|jre|zulu|javapath' }) -join ';'),'Machine') + $env:JAVA_HOME = "" + $env:PATH = (($env:PATH -split ';') | Where-Object { $_ -notmatch 'java|jdk|jre|zulu|javapath' }) -join ';' + try { + $msi = (Get-ChildItem -Recurse -Filter *.msi -Path opendj-packages/opendj-msi | Select-Object -First 1).FullName + if (-not $msi) { throw "MSI not found in the windows-latest-11 artifact" } + $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i `"$msi`" /quiet /qn /norestart /l*v install-nojre.log" + if ($p.ExitCode -eq 0) { + Get-Content install-nojre.log -Tail 60 + throw "installer succeeded without a JRE, but the Java launch condition must fail it" + } + Write-Host "Installer refused to install without a JRE as expected (exit $($p.ExitCode))" + Select-String -Path install-nojre.log -Pattern "requires Java" | Select-Object -First 1 + } finally { + [Environment]::SetEnvironmentVariable('JAVA_HOME',$oldJH,'Machine') + [Environment]::SetEnvironmentVariable('Path',$oldPM,'Machine') + Stop-Service msiserver -Force -ErrorAction SilentlyContinue + } + - name: Set up Java + uses: actions/setup-java@v5 + with: + java-version: '25' + distribution: 'zulu' + - name: Install MSI (silent) + shell: pwsh + run: | + $msi = (Get-ChildItem -Recurse -Filter *.msi -Path opendj-packages/opendj-msi | Select-Object -First 1).FullName + if (-not $msi) { throw "MSI not found in the windows-latest-11 artifact" } + Write-Host "MSI: $msi" + # No OPENDJ property: exercise the x64 default C:\Program Files\OpenDJ (a path with + # spaces, which the server scripts must handle). + $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i `"$msi`" /quiet /qn /norestart /l*v install.log" + if ($p.ExitCode -ne 0) { Get-Content install.log -Tail 80; throw "msiexec /i failed: $($p.ExitCode)" } + $root = "C:\Program Files\OpenDJ" + if (-not (Test-Path "$root\setup.bat")) { Get-Content install.log -Tail 80; throw "OpenDJ not installed into the x64 default $root" } + Write-Host "Installed to $root" + "OPENDJ_ROOT=$root" | Out-File -FilePath $env:GITHUB_ENV -Append + - name: Setup and start/stop the Windows service + shell: pwsh + run: | + $root = $env:OPENDJ_ROOT + $env:OPENDJ_JAVA_ARGS = "-server -Xmx512m" + & "$root\setup.bat" -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com --addBaseEntry --cli --acceptLicense --no-prompt --doNotStart + if ($LASTEXITCODE -ne 0) { throw "setup.bat failed: $LASTEXITCODE" } + # The service is already registered by the MSI (WiX ServiceInstall); just start it. + net start "OpenDJ Server" + if ($LASTEXITCODE -ne 0) { throw "net start failed: $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 } } + & "$root\bat\ldapsearch.bat" --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1 + if ($LASTEXITCODE -ne 0) { throw "ldapsearch failed: $LASTEXITCODE" } + net stop "OpenDJ Server" + if ($LASTEXITCODE -ne 0) { throw "net stop failed: $LASTEXITCODE" } + - name: Uninstall MSI + shell: pwsh + run: | + $msi = (Get-ChildItem -Recurse -Filter *.msi -Path opendj-packages/opendj-msi | Select-Object -First 1).FullName + $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/x `"$msi`" /quiet /qn /norestart /l*v uninstall.log" + if ($p.ExitCode -ne 0) { Get-Content uninstall.log -Tail 80; throw "msiexec /x failed: $($p.ExitCode)" } + Write-Host "Uninstalled OK" + + # Upgrade path: released 5.1.1 x86 MSI (wine-built, WiX3) -> this build's x64 MSI. + # Verifies the new installer detects the legacy Program Files (x86) install, keeps the + # instance data in place, replaces the windows-service.bat service with the MSI-managed + # one, and the upgraded server starts with the old data. + test-msi-upgrade: + needs: build-maven + runs-on: 'windows-latest' + steps: + - name: Download artifacts + uses: actions/download-artifact@v8 + with: + name: windows-latest-11 + - name: Set up Java + uses: actions/setup-java@v5 + with: + java-version: '25' + distribution: 'zulu' + - name: Install released 5.1.1 MSI and configure an instance + shell: pwsh + run: | + Invoke-WebRequest -Uri "https://github.com/OpenIdentityPlatform/OpenDJ/releases/download/5.1.1/opendj-5.1.1.msi" -OutFile opendj-5.1.1.msi + # The released 5.1.1 scripts cannot run from a directory with spaces (unquoted + # java.io.tmpdir), so put the old install into C:\opendj. + $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i opendj-5.1.1.msi /quiet /qn /norestart OPENDJ=C:\opendj /l*v install-old.log" + if ($p.ExitCode -ne 0) { Get-Content install-old.log -Tail 80; throw "msiexec /i (5.1.1) failed: $($p.ExitCode)" } + $root = "C:\opendj" + if (-not (Test-Path "$root\setup.bat")) { Get-Content install-old.log -Tail 80; throw "5.1.1 install root not found at $root" } + $env:OPENDJ_JAVA_ARGS = "-server -Xmx512m" + & "$root\setup.bat" -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com --addBaseEntry --cli --acceptLicense --no-prompt --doNotStart + if ($LASTEXITCODE -ne 0) { throw "setup.bat (5.1.1) failed: $LASTEXITCODE" } + # Register the LEGACY service the pre-MSI way and prove it works, then stop it. + & "$root\bat\windows-service.bat" --enableService + if ($LASTEXITCODE -ne 0) { throw "windows-service --enableService failed: $LASTEXITCODE" } + net start "OpenDJ Server" + if ($LASTEXITCODE -ne 0) { throw "net start (5.1.1) failed: $LASTEXITCODE" } + net stop "OpenDJ Server" + if ($LASTEXITCODE -ne 0) { throw "net stop (5.1.1) failed: $LASTEXITCODE" } + - name: Upgrade with the newly built MSI (same directory passed explicitly) + shell: pwsh + run: | + $msi = (Get-ChildItem -Recurse -Filter *.msi -Path opendj-packages/opendj-msi | Select-Object -First 1).FullName + if (-not $msi) { throw "MSI not found in the windows-latest-11 artifact" } + $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i `"$msi`" /quiet /qn /norestart OPENDJ=C:\opendj /l*v upgrade.log" + if ($p.ExitCode -ne 0) { Get-Content upgrade.log -Tail 120; throw "msiexec /i (upgrade) failed: $($p.ExitCode)" } + $root = "C:\opendj" + # New package files landed in the old directory, not the x64 default + if (-not (Test-Path "$root\setup.bat")) { throw "upgrade did not keep the old install dir" } + if (Test-Path "C:\Program Files\OpenDJ") { throw "upgrade unexpectedly installed into the x64 default dir" } + # Instance data survived + if (-not (Test-Path "$root\config\config.ldif")) { throw "instance data (config\config.ldif) lost by the upgrade" } + # Legacy service replaced by the MSI-managed one: display name maps to key 'OpenDJ' + $key = (sc.exe getkeyname "OpenDJ Server" | Select-String -Pattern "Name = (.+)").Matches[0].Groups[1].Value.Trim() + if ($key -ne "OpenDJ") { sc.exe query; throw "expected MSI-managed service key 'OpenDJ', got '$key'" } + sc.exe qc OpenDJ + "OPENDJ_ROOT=$root" | Out-File -FilePath $env:GITHUB_ENV -Append + - name: Run upgrade.bat and start the upgraded server + shell: pwsh + run: | + $root = $env:OPENDJ_ROOT + $env:OPENDJ_JAVA_ARGS = "-server -Xmx512m" + & "$root\upgrade.bat" --no-prompt --acceptLicense --force + if ($LASTEXITCODE -ne 0) { throw "upgrade.bat failed: $LASTEXITCODE" } + net start "OpenDJ Server" + if ($LASTEXITCODE -ne 0) { throw "net start (upgraded) failed: $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 } } + # The pre-upgrade data must still be served + & "$root\bat\ldapsearch.bat" --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1 + if ($LASTEXITCODE -ne 0) { throw "ldapsearch after upgrade failed: $LASTEXITCODE" } + net stop "OpenDJ Server" + if ($LASTEXITCODE -ne 0) { throw "net stop (upgraded) failed: $LASTEXITCODE" } + - name: Auto-detect the legacy default directory on a fresh install + shell: pwsh + run: | + $msi = (Get-ChildItem -Recurse -Filter *.msi -Path opendj-packages/opendj-msi | Select-Object -First 1).FullName + # Clean up the previous scenario first. + $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/x `"$msi`" /quiet /qn /norestart /l*v uninstall1.log" + if ($p.ExitCode -ne 0) { Get-Content uninstall1.log -Tail 80; throw "msiexec /x failed: $($p.ExitCode)" } + if (Get-Service OpenDJ -ErrorAction SilentlyContinue) { throw "service not removed by uninstall" } + # An existing legacy default directory must be picked up when OPENDJ is not given. + New-Item -ItemType Directory -Force "C:\Program Files (x86)\OpenDJ" | Out-Null + $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i `"$msi`" /quiet /qn /norestart /l*v install-autodetect.log" + if ($p.ExitCode -ne 0) { Get-Content install-autodetect.log -Tail 80; throw "msiexec /i (autodetect) failed: $($p.ExitCode)" } + if (-not (Test-Path "C:\Program Files (x86)\OpenDJ\setup.bat")) { Get-Content install-autodetect.log -Tail 80; throw "installer did not auto-detect the legacy default dir" } + if (Test-Path "C:\Program Files\OpenDJ") { throw "installer used the x64 default dir despite an existing legacy dir" } + Write-Host "Legacy default directory auto-detected OK" + $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/x `"$msi`" /quiet /qn /norestart /l*v uninstall2.log" + if ($p.ExitCode -ne 0) { Get-Content uninstall2.log -Tail 80; throw "msiexec /x (cleanup) failed: $($p.ExitCode)" } diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8b56d45faa..723c8606be 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -14,19 +14,12 @@ jobs: env: GITHUB_CONTEXT: ${{ toJSON(github) }} run: echo "$GITHUB_CONTEXT" - - name: Install wine+rpm for distribution + - name: Install rpm for distribution if: runner.os == 'Linux' shell: bash run: | - sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list - sudo dpkg --add-architecture i386 - sudo mkdir -pm755 /etc/apt/keyrings && sudo wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key - sudo wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/$(lsb_release -c -s)/winehq-$(lsb_release -c -s).sources sudo apt-get update - sudo apt install --install-recommends winehq-stable || sudo apt install --install-recommends winehq-staging - wine --version - version="9.4.0"; sudo wget "https://dl.winehq.org/wine/wine-mono/$version/wine-mono-$version-x86.msi" -O /tmp/wine-mono.msi - wine msiexec /i /tmp/wine-mono.msi + sudo apt-get install -y rpm - uses: actions/checkout@v6 with: fetch-depth: 0 @@ -90,11 +83,22 @@ jobs: with: name: OpenDJ RPM Package path: opendj-packages/opendj-rpm/opendj-rpm-standard/target/rpm/opendj/RPMS/noarch/*.rpm + # The MSI can only be built on Windows; reuse the one already built by the triggering + # Build run (windows-latest-11 artifact) instead of rebuilding it here. + - name: Download Windows build artifact (contains the MSI) + continue-on-error: true + uses: actions/download-artifact@v8 + with: + name: windows-latest-11 + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + path: windows-build - name: Upload artifacts OpenDJ MSI Package + continue-on-error: true uses: actions/upload-artifact@v7 with: name: OpenDJ MSI Package - path: opendj-packages/opendj-msi/opendj-msi-standard/target/*.msi + path: windows-build/opendj-packages/opendj-msi/opendj-msi-standard/target/*.msi - name: Upload artifacts OpenDJ Docker Packages uses: actions/upload-artifact@v7 with: @@ -169,3 +173,4 @@ jobs: git commit -a -m "upload ${{github.event.repository.name}} docs after deploy ${{ github.sha }}" git push --force https://github.com/OpenIdentityPlatform/doc.openidentityplatform.org.git fi + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1309c07ba7..0ca4f0c670 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,18 +19,11 @@ jobs: env: GITHUB_CONTEXT: ${{ toJSON(github) }} run: echo "$GITHUB_CONTEXT" - - name: Install wine+rpm for distribution + - name: Install rpm for distribution shell: bash run: | - sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list - sudo dpkg --add-architecture i386 - sudo mkdir -pm755 /etc/apt/keyrings && sudo wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key - sudo wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/$(lsb_release -c -s)/winehq-$(lsb_release -c -s).sources sudo apt-get update - sudo apt install --install-recommends winehq-stable || sudo apt install --install-recommends winehq-staging - wine --version - version="9.4.0"; sudo wget "https://dl.winehq.org/wine/wine-mono/$version/wine-mono-$version-x86.msi" -O /tmp/wine-mono.msi - wine msiexec /i /tmp/wine-mono.msi + sudo apt-get install -y rpm - uses: actions/checkout@v6 with: fetch-depth: 0 @@ -70,6 +63,15 @@ jobs: MAVEN_OPTS: -Dhttps.protocols=TLSv1.2 -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.requestSentEnabled=true -Dmaven.wagon.http.retryHandler.count=10 if: ${{ env.MAVEN_USERNAME!='' && env.MAVEN_PASSWORD!='' }} run: mvn --batch-mode -Darguments="-Dgpg.passphrase=${{ secrets.GPG_PASSPHRASE }}" -DsignTag=true -DtagNameFormat="${{ github.event.inputs.releaseVersion }}" -DreleaseVersion=${{ github.event.inputs.releaseVersion }} -DdevelopmentVersion=${{ github.event.inputs.developmentVersion }} release:prepare release:perform --file pom.xml + # Hand the just-released server zip to the release-msi job (the MSI can only be + # built on Windows), so it does not have to rebuild opendj-server-legacy. + - name: Upload the server zip for the MSI job + continue-on-error: true + uses: actions/upload-artifact@v7 + with: + name: release-server-zip + retention-days: 1 + path: target/checkout/opendj-server-legacy/target/package/*.zip - name: Release on GitHub uses: softprops/action-gh-release@v2 with: @@ -84,7 +86,6 @@ jobs: target/checkout/opendj-ldap-toolkit/target/*.zip target/checkout/opendj-packages/opendj-deb/opendj-deb-standard/target/*.deb target/checkout/opendj-packages/opendj-rpm/opendj-rpm-standard/target/rpm/opendj/RPMS/noarch/*.rpm - target/checkout/opendj-packages/opendj-msi/opendj-msi-standard/target/*.msi target/checkout/opendj-packages/opendj-docker/target/Dockerfile.zip target/checkout/opendj-packages/opendj-openshift-template/*.yaml target/checkout/opendj-doc-generated-ref/target/*.zip @@ -130,6 +131,65 @@ jobs: git tag -f ${TAG_NAME} git push --quiet --force origin ${TAG_NAME} + # The MSI can only be built on Windows. Reuses the server zip built by release-maven + # (installed into the local repo), so only the opendj-msi-standard module is built here. + # continue-on-error: an MSI failure must not break the release. + release-msi: + name: Windows MSI release + runs-on: 'windows-latest' + continue-on-error: true + needs: + - release-maven + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.inputs.releaseVersion }} + submodules: recursive + - name: Set up Java + uses: actions/setup-java@v5 + with: + java-version: '11' + distribution: 'temurin' + - name: Cache Maven packages + uses: actions/cache@v5 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-m2-repository-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2-repository + - name: Setup WiX (.NET tool) + shell: bash + run: | + echo "DOTNET_ROLL_FORWARD=Major" >> "$GITHUB_ENV" + export DOTNET_ROLL_FORWARD=Major + dotnet tool install --global wix --version 5.0.2 || dotnet tool update --global wix --version 5.0.2 + echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH" + export PATH="$HOME/.dotnet/tools:$PATH" + wix --version + wix extension add -g WixToolset.UI.wixext/5.0.2 || true + - name: Download the server zip built by release-maven + uses: actions/download-artifact@v8 + with: + name: release-server-zip + path: server-zip + - name: Install the server zip into the local Maven repository + shell: bash + run: | + ZIP=$(ls server-zip/*.zip | head -1) + echo "Installing $ZIP as opendj-server-legacy:${{ github.event.inputs.releaseVersion }}:zip" + mvn --batch-mode install:install-file -Dfile="$ZIP" \ + -DgroupId=org.openidentityplatform.opendj -DartifactId=opendj-server-legacy \ + -Dversion=${{ github.event.inputs.releaseVersion }} -Dpackaging=zip + - name: Build the MSI (packaging only, no rebuild) + env: + MAVEN_OPTS: -Dhttps.protocols=TLSv1.2 -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.requestSentEnabled=true -Dmaven.wagon.http.retryHandler.count=10 + run: mvn --batch-mode --errors -DskipTests package -pl :opendj-msi-standard --file pom.xml + - name: Attach the MSI to the GitHub release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.event.inputs.releaseVersion }} + fail_on_unmatched_files: true + files: opendj-packages/opendj-msi/opendj-msi-standard/target/*.msi + release-docker: name: Docker release runs-on: 'ubuntu-latest' diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/ADNotificationRequestControl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/ADNotificationRequestControl.java index c2c6ea3669..631f5e1e05 100644 --- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/ADNotificationRequestControl.java +++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/ADNotificationRequestControl.java @@ -12,6 +12,7 @@ * information: "Portions Copyright [year] [name of copyright owner]". * * Copyright 2013-2016 ForgeRock AS. + * Portions Copyright 2026 3A Systems, LLC */ package org.forgerock.opendj.ldap.controls; @@ -39,12 +40,12 @@ * SearchResultEntry entry = reader.readEntry(); // Entry that changed * * Boolean isDeleted = entry.parseAttribute("isDeleted").asBoolean(); - * if (isDeleted != null && isDeleted) { + * if (isDeleted != null && isDeleted) { * // Handle entry deletion * } * String whenCreated = entry.parseAttribute("whenCreated").asString(); * String whenChanged = entry.parseAttribute("whenChanged").asString(); - * if (whenCreated != null && whenChanged != null) { + * if (whenCreated != null && whenChanged != null) { * if (whenCreated.equals(whenChanged)) { * //Handle entry addition * } else { diff --git a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-install.adoc b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-install.adoc index 92be08f08a..5770bfc24d 100644 --- a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-install.adoc +++ b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-install.adoc @@ -40,6 +40,8 @@ This chapter covers installation of OpenDJ server software and includes the foll * xref:#install-rpm["To Install From the RPM Package"] +* xref:#install-msi["To Install With the Windows Installer (MSI)"] + * xref:#install-properties-file["To Install OpenDJ Directory Server With a Properties File"] * xref:#pdb-to-je["To Move Data from a PDB Backend to a JE Backend"] @@ -635,6 +637,49 @@ opendj 0:off 1:off 2:on 3:on 4:on 5:on 6:off ==== +[#install-msi] +.To Install With the Windows Installer (MSI) +==== +On Windows you can install OpenDJ directory server from the `.msi` package. The installer copies the server files to disk and registers the `OpenDJ Server` Windows service, but it does not configure or start a server (run `setup` first) and it does not install a Java runtime. + +. Make sure a supported Java runtime is available, as described in xref:#before-you-install["To Prepare For Installation"]. ++ +The installer refuses to install when it cannot detect a Java installation: install a JRE (for example link:https://adoptium.net[Eclipse Temurin, window=\_blank]), set `JAVA_HOME` to your Java installation, or make sure the `java` executable is on the `PATH`, before running it. If your default Java environment is not the one OpenDJ should use, additionally set `OPENDJ_JAVA_HOME` to the correct Java installation (or `OPENDJ_JAVA_BIN` to the absolute path of the `java` command) before you run `setup` or start the server. + +. Install the package, either with the GUI or silently: ++ +The package is not code-signed, so Windows SmartScreen or User Account Control may warn about an unrecognized publisher; choose to run the installer anyway. ++ +* GUI: double-click `opendj-{opendj-version}.msi` and follow the wizard. ++ +* Silent: run the following command (optionally set the installation directory with the `OPENDJ` property): ++ + +[source, console, subs="attributes"] +---- +C:\> msiexec /i opendj-{opendj-version}.msi /quiet OPENDJ="C:\opendj" +---- ++ +By default the x64 package installs under `C:\Program Files\OpenDJ`. + +. Configure OpenDJ directory server by running the `setup` command, described in xref:../reference/admin-tools-ref.adoc#setup-1[setup(1)] in the __Reference__, from the installation directory. Use `setup.bat` for the GUI wizard or `setup.bat --cli` for the command-line: ++ + +[source, console] +---- +C:\path\to\opendj> setup.bat --cli +---- + +. Start the OpenDJ Windows service. The installer already registered it as `OpenDJ Server`; after configuring with `setup`, start it (it is not started automatically during installation): ++ + +[source, console] +---- +C:\> net start "OpenDJ Server" +---- + +==== + [#install-properties-file] .To Install OpenDJ Directory Server With a Properties File ==== diff --git a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-uninstall.adoc b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-uninstall.adoc index 19cc3a3875..3adc45d381 100644 --- a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-uninstall.adoc +++ b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-uninstall.adoc @@ -12,7 +12,7 @@ information: "Portions copyright [year] [name of copyright owner]". Copyright 2017 ForgeRock AS. - Portions Copyright 2024 3A Systems LLC. + Portions Copyright 2024-2026 3A Systems LLC. //// :figure-caption!: @@ -33,6 +33,8 @@ This chapter includes the following procedures: * xref:#uninstall-rpm["To Uninstall the RPM Package"] +* xref:#uninstall-msi["To Uninstall the Windows MSI Package"] + [#uninstall-gui] .To Remove OpenDJ With the GUI Uninstaller @@ -157,3 +159,20 @@ Removing the package does not remove your data or configuration. You must remove ==== +[#uninstall-msi] +.To Uninstall the Windows MSI Package +==== +Remove OpenDJ directory server installed from the `.msi` package like any other Windows program. The uninstaller stops and removes the `OpenDJ Server` Windows service that the package registered. + +* Uninstall the package, either through __Settings > Apps__ (or __Control Panel > Programs and Features__) by selecting OpenDJ and choosing Uninstall, or from the command-line: ++ + +[source, console, subs="attributes"] +---- +C:\> msiexec /x opendj-{opendj-version}.msi /quiet +---- ++ +Uninstalling removes the files installed by the package. Your configured instance data under the installation directory (for example `config`, `db`, and `logs`) is not removed; delete the installation directory manually to remove all files. + +==== + diff --git a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-upgrade.adoc b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-upgrade.adoc index cf0268dec0..d91b763711 100644 --- a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-upgrade.adoc +++ b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-upgrade.adoc @@ -45,6 +45,8 @@ This chapter includes the following procedures and examples: * xref:#upgrade-zip-example["Upgrading to OpenDJ {opendj-version-short}"] +* xref:#upgrade-msi["To Upgrade the Windows MSI Installation"] + * xref:#upgrade-repl["To Upgrade Replicated Servers"] * xref:#new-repl-mixed-topology["To Add a New Replica to an Existing Topology"] @@ -243,6 +245,43 @@ $ ---- ==== +[#upgrade-msi] +.To Upgrade the Windows MSI Installation +==== +Before starting this procedure, follow the steps in xref:#before-you-upgrade["Before You Upgrade"]. Installing the newer `.msi` performs a major upgrade that replaces the installed program files, so make a full file-system backup of the current installation first. + +. Stop the current OpenDJ server (if it runs as a Windows service, `net stop "OpenDJ Server"`). + +. Back up the file-system directory where OpenDJ is installed. + +. Install the newer package (GUI or silent). The installer detects the existing installation — the location recorded in the registry by a previous x64 package, or the default directory of the older 32-bit package (`C:\Program Files (x86)\OpenDJ`) — and installs into the same directory, so your configured instance data (`config`, `db`, `logs`) is kept and only the program files are replaced. If the older server was installed in a custom directory the installer did not detect, select that directory in the GUI or pass it explicitly: ++ + +[source, console, subs="attributes"] +---- +C:\> msiexec /i opendj-{opendj-version}.msi /quiet OPENDJ="C:\path\to\opendj" +---- ++ +The installer replaces a service registered by the older `windows-service.bat` command with the MSI-managed `OpenDJ Server` service automatically; no manual `--disableService`/`--enableService` is needed. + +. Run the `upgrade` command, described in xref:../reference/admin-tools-ref.adoc#upgrade-1[upgrade(1)] in the __Reference__, to bring the configuration and application data up to date with the new binary and script files: ++ + +[source, console] +---- +C:\path\to\opendj> upgrade.bat --no-prompt --acceptLicense +---- + +. Start the upgraded OpenDJ server: ++ + +[source, console] +---- +C:\> net start "OpenDJ Server" +---- + +==== + [#upgrade-repl] .To Upgrade Replicated Servers ==== diff --git a/opendj-packages/opendj-msi/opendj-msi-standard/pom.xml b/opendj-packages/opendj-msi/opendj-msi-standard/pom.xml index 9b4bf07b7e..f7505785c1 100644 --- a/opendj-packages/opendj-msi/opendj-msi-standard/pom.xml +++ b/opendj-packages/opendj-msi/opendj-msi-standard/pom.xml @@ -13,7 +13,7 @@ information: "Portions Copyright [year] [name of copyright owner]". Copyright 2015-2016 ForgeRock AS. - Portions Copyright 2018 Open Identity Platform Community + Portions Copyright 2018-2026 3A Systems, LLC --> 4.0.0 @@ -29,265 +29,130 @@ OpenDJ MSI Standard Package - This module generates an OpenDJ MSI package. + This module generates an OpenDJ MSI package using the WiX Toolset v5 .NET tool + (cross-platform; no wine). The `wix` tool must be on the PATH (CI installs it via + `dotnet tool install --global wix`). ${basedir}/resources/msi ${project.build.directory}/${product.name.lowercase} + + ${project.build.directory}/msi-staging + ${project.build.directory}/msi-staging-lib + ${package.dir}/lib/opendj_service.exe + ${project.build.directory}/${product.name.lowercase}-${project.version}.msi - - - + + ${project.groupId}.${project.artifactId} + org.codehaus.mojo build-helper-maven-plugin - - - - org.openidentityplatform.commons - maven-external-dependency-plugin - false - - - ${project.build.directory}/dependencies/ - - false - true - false - - - openidentityplatform.org - wixtoolset - 3.11.1 - zip - - https://github.com/wixtoolset/wix3/releases/download/wix3111rtm/wix311-binaries.zip - - false - - - openidentityplatform.org - winetricks - LAST - sh - - https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks - - false - - - - - - clean-external-dependencies - clean - - clean-external - - - - resolve-install-external-dependencies - process-resources - - resolve-external - install-external - - - - deploy-external-dependencies - deploy - - deploy-external - - - - - - org.apache.maven.plugins - maven-dependency-plugin - unpack-wix + attach-msi-and-bundle package - unpack + attach-artifact - - - openidentityplatform.org - wixtoolset - 3.11.1 - zip - - - - ${project.build.directory}/wix - - - - - unpack-winetricks - package - - copy - - - - - openidentityplatform.org - winetricks - LAST - sh - - - - ${project.build.directory}/winetricks - + + ${msi.file}msi + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + org.apache.maven.plugins maven-antrun-plugin - build-msi-package-prepare + stage-msi-payload package run - - - + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ------------------- ${exec.heat} ${param.heat} ------------------- - - - - - - - - - - - - - - - - ------------------- ${exec.candle} ${param.candle} ------------------- - - - - - - - - - - - - - ------------------- ${exec.light} ${param.light} ------------------- - - - - - - - - - - - - - - - - - + + + + org.codehaus.mojo + exec-maven-plugin + + + wix-build-msi + package + + exec + + + wix + ${project.build.directory} + + build + ${msi.resources}/package.wxs + -archx64 + -extWixToolset.UI.wixext + -bindpath${msi.resources} + -dname=${product.name} + -dmajor=${parsedVersion.majorVersion} + -dminor=${parsedVersion.minorVersion} + -dpoint=${parsedVersion.incrementalVersion} + -dstagingRoot=${staging.root} + -dstagingLib=${staging.lib} + -dserviceExe=${service.exe} + -o${msi.file} + + + + + - \ No newline at end of file + diff --git a/opendj-packages/opendj-msi/opendj-msi-standard/resources/msi/package.wxs b/opendj-packages/opendj-msi/opendj-msi-standard/resources/msi/package.wxs index 23f4dd4471..cfb0d85b79 100644 --- a/opendj-packages/opendj-msi/opendj-msi-standard/resources/msi/package.wxs +++ b/opendj-packages/opendj-msi/opendj-msi-standard/resources/msi/package.wxs @@ -13,47 +13,157 @@ ! information: "Portions Copyright [year] [name of copyright owner]". ! ! Copyright 2013-2016 ForgeRock AS. - ! Portion Copyright 2018 Open Identity Platform Community + ! Portions Copyright 2018-2026 3A Systems, LLC ! --> - - - - - - - + + + + + + + + - - - + + + + + + + + + + + + + + + + - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + - - - - - - - NOT Installed - 1 - - + + + diff --git a/opendj-packages/opendj-msi/pom.xml b/opendj-packages/opendj-msi/pom.xml index f652541ac6..3a697e73f4 100644 --- a/opendj-packages/opendj-msi/pom.xml +++ b/opendj-packages/opendj-msi/pom.xml @@ -32,63 +32,10 @@ This module contains configuration and generic plugin call to build OpenDJ MSI packages. - - - /usr/bin/wine - - unix - /usr/bin/wine - - - opendj-msi-standard - - - /usr/bin/wine${project.build.directory}/wix/heat.exe - /usr/bin/wine${project.build.directory}/wix/candle.exe - /usr/bin/wine${project.build.directory}/wix/light.exe - - - - /usr/local/bin/wine - - unix - /usr/local/bin/wine - - - opendj-msi-standard - - - /usr/local/bin/wine${project.build.directory}/wix/heat.exe - /usr/local/bin/wine${project.build.directory}/wix/candle.exe - /usr/local/bin/wine${project.build.directory}/wix/light.exe - - - - /opt/local/bin/wine - - unix - /opt/local/bin/wine - - - opendj-msi-standard - - - /opt/local/bin/wine${project.build.directory}/wix/heat.exe - /opt/local/bin/wine${project.build.directory}/wix/candle.exe - /opt/local/bin/wine${project.build.directory}/wix/light.exe - - - - windows - windows - - opendj-msi-standard - - - ${project.build.directory}\wix\heat.exe - ${project.build.directory}\wix\candle.exe - ${project.build.directory}\wix\light.exe - - - + + + opendj-msi-standard + diff --git a/opendj-packages/pom.xml b/opendj-packages/pom.xml index 739e77779f..83d43d7ec0 100644 --- a/opendj-packages/pom.xml +++ b/opendj-packages/pom.xml @@ -55,7 +55,6 @@ opendj-deb opendj-rpm opendj-svr4 - opendj-msi opendj-docker @@ -69,7 +68,6 @@ opendj-svr4 - opendj-msi opendj-docker @@ -79,6 +77,8 @@ windows + opendj-msi opendj-docker diff --git a/opendj-server-legacy/lib/launcher_administrator.exe b/opendj-server-legacy/lib/launcher_administrator.exe index 50b885811d..bfb8ae4fdf 100644 Binary files a/opendj-server-legacy/lib/launcher_administrator.exe and b/opendj-server-legacy/lib/launcher_administrator.exe differ diff --git a/opendj-server-legacy/lib/opendj_service.exe b/opendj-server-legacy/lib/opendj_service.exe index 0b458d8f72..4e20ac7941 100644 Binary files a/opendj-server-legacy/lib/opendj_service.exe and b/opendj-server-legacy/lib/opendj_service.exe differ diff --git a/opendj-server-legacy/lib/winlauncher.exe b/opendj-server-legacy/lib/winlauncher.exe index 04f210cb64..d1e3eaef75 100644 Binary files a/opendj-server-legacy/lib/winlauncher.exe and b/opendj-server-legacy/lib/winlauncher.exe differ diff --git a/opendj-server-legacy/resource/bin/_script-util.bat b/opendj-server-legacy/resource/bin/_script-util.bat index f6bacbb9d1..bea162b7cd 100644 --- a/opendj-server-legacy/resource/bin/_script-util.bat +++ b/opendj-server-legacy/resource/bin/_script-util.bat @@ -13,7 +13,7 @@ rem information: "Portions Copyright [year] [name of copyright owner]". rem rem Copyright 2008-2010 Sun Microsystems, Inc. rem Portions Copyright 2011-2016 ForgeRock AS. -rem Portions Copyright 2020-2025 3A Systems, LLC. +rem Portions Copyright 2020-2026 3A Systems, LLC. set SET_JAVA_HOME_AND_ARGS_DONE=false set SET_ENVIRONMENT_VARS_DONE=false @@ -56,10 +56,10 @@ rem get the absolute paths before building the classpath rem it also helps comparing the two paths FOR /F "delims=" %%i IN ("%INSTALL_ROOT%") DO set INSTALL_ROOT=%%~dpnxi FOR /F "delims=" %%i IN ("%INSTANCE_ROOT%") DO set INSTANCE_ROOT=%%~dpnxi -call "%INSTALL_ROOT%\lib\setcp.bat" %INSTALL_ROOT%\lib\bootstrap-client.jar +call "%INSTALL_ROOT%\lib\setcp.bat" "%INSTALL_ROOT%\lib\bootstrap-client.jar" set CLASSPATH=%INSTANCE_ROOT%\classes;%CLASSPATH% if "%INSTALL_ROOT%" == "%INSTANCE_ROOT%" goto setClassPathDone -FOR %%x in ("%INSTANCE_ROOT%\lib\*.jar") DO call "%INSTANCE_ROOT%\lib\setcp.bat" %%x +FOR %%x in ("%INSTANCE_ROOT%\lib\*.jar") DO call "%INSTANCE_ROOT%\lib\setcp.bat" "%%x" :setClassPathDone set SET_CLASSPATH_DONE=true goto scriptBegin @@ -71,7 +71,7 @@ rem get the absolute paths before building the classpath rem it also helps comparing the two paths FOR /F "delims=" %%i IN ("%INSTALL_ROOT%") DO set INSTALL_ROOT=%%~dpnxi FOR /F "delims=" %%i IN ("%INSTANCE_ROOT%") DO set INSTANCE_ROOT=%%~dpnxi -call "%INSTALL_ROOT%\lib\setcp.bat" %INSTALL_ROOT%\lib\* +call "%INSTALL_ROOT%\lib\setcp.bat" "%INSTALL_ROOT%\lib\*" set CLASSPATH=%INSTANCE_ROOT%\classes;%CLASSPATH% set SET_CLASSPATH_DONE=true goto scriptBegin @@ -100,7 +100,8 @@ rem if not "%OPENDJ_JAVA_ARGS%" == "" goto checkEnvJavaHome set SCRIPT_JAVA_ARGS_PROPERTY=%SCRIPT_NAME%.java-args call:readProperty %SCRIPT_JAVA_ARGS_PROPERTY% set OPENDJ_JAVA_ARGS=%OPENDJ_JAVA_ARGS% %PROPERTY_VALUE% -if not "%OPENDJ_JAVA_ARGS%" == "" goto checkEnvJavaHome +rem "if defined" comparisons: the value may contain quotes (java.io.tmpdir), which break "%VAR%" == "" checks. +if defined OPENDJ_JAVA_ARGS goto checkEnvJavaHome call:readProperty default.java-args set OPENDJ_JAVA_ARGS=%OPENDJ_JAVA_ARGS% %PROPERTY_VALUE if "%OPENDJ_JAVA_BIN%" == "" goto checkEnvJavaHome @@ -186,12 +187,12 @@ goto scriptBegin if %SET_TEMP_DIR_DONE% == "true" goto end set OPENDJ_TMP_DIR=%INSTANCE_ROOT%\tmp if not exist "%OPENDJ_TMP_DIR%" mkdir "%OPENDJ_TMP_DIR%" -set OPENDJ_JAVA_ARGS=%OPENDJ_JAVA_ARGS% -Djava.io.tmpdir=%OPENDJ_TMP_DIR% +set OPENDJ_JAVA_ARGS=%OPENDJ_JAVA_ARGS% -Djava.io.tmpdir="%OPENDJ_TMP_DIR%" set SET_TEMP_DIR_DONE=true goto scriptBegin :testJava -if "%OPENDJ_JAVA_ARGS%" == "" goto checkLegacyArgs +if not defined OPENDJ_JAVA_ARGS goto checkLegacyArgs :continueTestJava "%OPENDJ_JAVA_BIN%" %OPENDJ_JAVA_ARGS% org.opends.server.tools.CheckJVMVersion > NUL 2>&1 set RESULT_CODE=%errorlevel% @@ -200,12 +201,12 @@ if not %RESULT_CODE% == 0 goto noValidJavaHome goto end :checkLegacyArgs -if "%OPENDS_JAVA_ARGS%" == "" goto continueTestJava +if not defined OPENDS_JAVA_ARGS goto continueTestJava set OPENDJ_JAVA_ARGS=%OPENDS_JAVA_ARGS% goto continueTestJava :noValidJavaHome -if NOT "%OPENDJ_JAVA_ARGS%" == "" goto noValidHomeWithArgs +if defined OPENDJ_JAVA_ARGS goto noValidHomeWithArgs echo ERROR: The detected Java version could not be used. The detected echo Java binary is: echo %OPENDJ_JAVA_BIN% diff --git a/opendj-server-legacy/resource/bin/setcp.bat b/opendj-server-legacy/resource/bin/setcp.bat index a47d4e6ac5..eca179c082 100644 --- a/opendj-server-legacy/resource/bin/setcp.bat +++ b/opendj-server-legacy/resource/bin/setcp.bat @@ -12,14 +12,17 @@ rem Header, with the fields enclosed by brackets [] replaced by your own identif rem information: "Portions Copyright [year] [name of copyright owner]". rem rem Copyright 2006-2008 Sun Microsystems, Inc. +rem Portions Copyright 2026 3A Systems, LLC -set CLASSPATHCOMPONENT=%1 -if ""%1""=="""" goto gotAllArgs +rem Use %~1 and quoted comparisons so paths containing spaces and parentheses +rem (for example C:\Program Files (x86)\OpenDJ) do not break the parser. +set CLASSPATHCOMPONENT=%~1 +if "%~1"=="" goto gotAllArgs shift :argCheck -if ""%1""=="""" goto gotAllArgs -set CLASSPATHCOMPONENT=%CLASSPATHCOMPONENT% %1 +if "%~1"=="" goto gotAllArgs +set CLASSPATHCOMPONENT=%CLASSPATHCOMPONENT% %~1 shift goto argCheck diff --git a/opendj-server-legacy/resource/bin/start-ds.bat b/opendj-server-legacy/resource/bin/start-ds.bat index 7f7be60d89..f591502ba2 100644 --- a/opendj-server-legacy/resource/bin/start-ds.bat +++ b/opendj-server-legacy/resource/bin/start-ds.bat @@ -14,7 +14,7 @@ rem information: "Portions Copyright [year] [name of copyright owner]". rem rem Copyright 2006-2010 Sun Microsystems, Inc. rem Portions Copyright 2011-2014 ForgeRock AS. -rem Portions Copyright 2025 3A Systems LLC. +rem Portions Copyright 2025-2026 3A Systems LLC. setlocal set DIR_HOME=%~dp0.. @@ -61,10 +61,12 @@ echo %SCRIPT%: PATH=%PATH% >> %LOG% rem cleanup the tmp directory set CUR_DIR=%CD% set OPENDJ_TMP_DIR=%INSTANCE_ROOT%\tmp -dir /b /s /a %OPENDJ_TMP_DIR% | findstr .>nul && ( - cd /d %OPENDJ_TMP_DIR% +rem The paths must be quoted: an unquoted parenthesis (e.g. from +rem "C:\Program Files (x86)") terminates the ( ) block at parse time. +dir /b /s /a "%OPENDJ_TMP_DIR%" | findstr .>nul && ( + cd /d "%OPENDJ_TMP_DIR%" for /F "delims=" %%i in ('dir /b') do (rmdir "%%i" /s/q>NUL 2>&1 || del "%%i" /s/q>NUL 2>&1) - cd /d %CUR_DIR% + cd /d "%CUR_DIR%" ) "%OPENDJ_JAVA_BIN%" -client %SCRIPT_NAME_ARG% org.opends.server.core.DirectoryServer --configFile "%INSTANCE_ROOT%\config\config.ldif" --checkStartability %* diff --git a/opendj-server-legacy/src/build-tools/windows/service.c b/opendj-server-legacy/src/build-tools/windows/service.c index 1fd7e373d0..29fbb87381 100644 --- a/opendj-server-legacy/src/build-tools/windows/service.c +++ b/opendj-server-legacy/src/build-tools/windows/service.c @@ -826,6 +826,118 @@ ServiceReturnCode createServiceBinPath(char* serviceBinPath) return returnValue; } // createServiceBinPath +// ---------------------------------------------------- +// Reads the next command line token starting at *p into out (at most +// outSize - 1 characters). A token is either a run of non-blank +// characters or a double-quoted string; quotes are treated as plain +// delimiters without escape processing, so a trailing backslash-quote +// sequence (as written by msiexec) leaves trailing backslashes on the +// token, which normalizeInstanceDir strips. Returns the position right +// after the token. +// ---------------------------------------------------- + +static const char* nextCmdToken(const char* p, char* out, int outSize) +{ + int i = 0; + while ((*p == ' ') || (*p == '\t')) + { + p++; + } + if (*p == '"') + { + p++; + while ((*p != '\0') && (*p != '"')) + { + if (i < (outSize - 1)) + { + out[i++] = *p; + } + p++; + } + if (*p == '"') + { + p++; + } + } + else + { + while ((*p != '\0') && (*p != ' ') && (*p != '\t')) + { + if (i < (outSize - 1)) + { + out[i++] = *p; + } + p++; + } + } + out[i] = '\0'; + return p; +} // nextCmdToken + +// ---------------------------------------------------- +// Strips trailing backslashes and "\." path segments from an instance +// dir so that "C:\opendj", "C:\opendj\" and "C:\opendj\." all compare +// equal. +// ---------------------------------------------------- + +static void normalizeInstanceDir(char* dir) +{ + size_t len = strlen(dir); + BOOL changed = TRUE; + while (changed && (len > 0)) + { + changed = FALSE; + if (dir[len - 1] == '\\') + { + dir[--len] = '\0'; + changed = TRUE; + } + else if ((len > 1) && (dir[len - 1] == '.') && (dir[len - 2] == '\\')) + { + dir[--len] = '\0'; + changed = TRUE; + } + } +} // normalizeInstanceDir + +// ---------------------------------------------------- +// Tells whether two service command lines refer to the same server +// instance. The strings cannot be compared verbatim because every +// writer quotes differently: this executable and the java tools write +// '"\lib\opendj_service.exe" start ""' while the MSI +// ServiceInstall writes the executable unquoted (when the path has no +// spaces) and the instance dir with a trailing backslash. Instead the +// executable path, the subcommand and the normalized instance dir are +// compared token by token, case-insensitively. +// ---------------------------------------------------- + +static BOOL serviceCmdsMatch(const char* cmd1, const char* cmd2) +{ + char exe1[COMMAND_SIZE]; + char exe2[COMMAND_SIZE]; + char sub1[COMMAND_SIZE]; + char sub2[COMMAND_SIZE]; + char dir1[COMMAND_SIZE]; + char dir2[COMMAND_SIZE]; + const char* p1 = cmd1; + const char* p2 = cmd2; + + p1 = nextCmdToken(p1, exe1, COMMAND_SIZE); + p1 = nextCmdToken(p1, sub1, COMMAND_SIZE); + nextCmdToken(p1, dir1, COMMAND_SIZE); + + p2 = nextCmdToken(p2, exe2, COMMAND_SIZE); + p2 = nextCmdToken(p2, sub2, COMMAND_SIZE); + nextCmdToken(p2, dir2, COMMAND_SIZE); + + normalizeInstanceDir(dir1); + normalizeInstanceDir(dir2); + + return (_stricmp(exe1, exe2) == 0) + && (_stricmp(sub1, sub2) == 0) + && (_stricmp(dir1, dir2) == 0); +} // serviceCmdsMatch + // ---------------------------------------------------- // Returns the service name that maps the command used to start the // product. All commands are supposed to be unique because they have @@ -866,7 +978,7 @@ ServiceReturnCode getServiceName(char* cmdToRun, char* serviceName) ServiceDescriptor curService = serviceList[i]; if (curService.cmdToRun != NULL) { - if (_stricmp(cmdToRun, curService.cmdToRun) == 0) + if (serviceCmdsMatch(cmdToRun, curService.cmdToRun)) { if (strlen(curService.serviceName) < MAX_SERVICE_NAME) {