This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 944334c8de73 CAMEL-26931: camel-jbang - camel infra stop --kill no 
longer kills a process that got the pid of a service that is gone (#29940)
944334c8de73 is described below

commit 944334c8de73a0ded8c03a8de6d118c320f93f7e
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Nov 14 19:30:05 2026 +0100

    CAMEL-26931: camel-jbang - camel infra stop --kill no longer kills a 
process that got the pid of a service that is gone (#29940)
    
    A pid file of a service that was killed hard stays behind, and its pid may 
be taken by another
    process, which infra stop --kill then killed. A pid file older than the 
process with its pid is
    left over, as ProcessHelper.isStale finds for the files of an integration 
(CAMEL-26911).
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../core/commands/infra/InfraBaseCommand.java      | 13 +++++++-
 .../jbang/core/commands/infra/InfraStopTest.java   | 39 ++++++++++++++++++++++
 2 files changed, 51 insertions(+), 1 deletion(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraBaseCommand.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraBaseCommand.java
index a15ac0b7b524..a5d4277baa46 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraBaseCommand.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraBaseCommand.java
@@ -47,6 +47,7 @@ import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.common.CamelTableColumns;
 import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
+import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
 import org.apache.camel.dsl.jbang.core.common.TerminalWidthHelper;
 import org.apache.camel.dsl.jbang.core.model.InfraBaseDTO;
 import org.apache.camel.dsl.jbang.run.common.PathUtils;
@@ -158,7 +159,9 @@ public abstract class InfraBaseCommand extends CamelCommand 
{
                     // not a file we recognise, so neither report nor prune it
                     continue;
                 }
-                if (!aliveCheck.test(pidNumber)) {
+                // a pid file older than the process with its pid is left over 
from a service that is gone, and the
+                // pid is now of another process, which infra stop --kill 
would kill (CAMEL-26931)
+                if (!aliveCheck.test(pidNumber) || isReusedPid(pidNumber, 
pidFile)) {
                     pruneStaleFiles(camelDir, sn, pid);
                     continue;
                 }
@@ -175,6 +178,14 @@ public abstract class InfraBaseCommand extends 
CamelCommand {
         return answer;
     }
 
+    /**
+     * Whether the pid file was last written before the process with the pid 
started, as {@link ProcessHelper#isStale}
+     * finds for the files of an integration (CAMEL-26911).
+     */
+    private static boolean isReusedPid(long pid, Path pidFile) {
+        return ProcessHandle.of(pid).map(ph -> ProcessHelper.isStale(ph, 
pidFile)).orElse(false);
+    }
+
     /**
      * Removes the pid and log files left behind by an instance whose process 
is gone, so it stops being reported as
      * running.
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraStopTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraStopTest.java
index 96942372ac4c..a928cf78e41b 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraStopTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraStopTest.java
@@ -16,6 +16,11 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.infra;
 
+import java.nio.file.Files;
+import java.nio.file.attribute.FileTime;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.common.CliException;
 import org.apache.camel.dsl.jbang.core.common.CliExitCode;
@@ -89,6 +94,40 @@ class InfraStopTest extends InfraCommandTestSupport {
         assertThat(pidFile("kafka", 1234)).exists();
     }
 
+    @Test
+    void aPidFileOlderThanTheProcessOfItsPidIsNotTheService() throws Exception 
{
+        // the service was killed hard and its pid is now of another process, 
here this JVM: infra stop --kill killed
+        // that process (CAMEL-26931)
+        long pid = ProcessHandle.current().pid();
+        writePidFile("kafka", pid, "{}");
+        writeLogFile("kafka", pid, "started");
+        Instant started = 
ProcessHandle.current().info().startInstant().orElseThrow();
+        Files.setLastModifiedTime(pidFile("kafka", pid), 
FileTime.from(started.minus(1, ChronoUnit.HOURS)));
+
+        InfraStop command = command("kafka");
+        command.aliveCheck = p -> true;
+
+        assertThatThrownBy(command::doCall)
+                .isInstanceOf(CliException.class)
+                .extracting(e -> ((CliException) 
e).getExitCode()).isEqualTo(CliExitCode.NOT_FOUND);
+        assertThat(printer.getStdout()).doesNotContain("Shutting down");
+        // the files it left behind are removed
+        assertThat(pidFile("kafka", pid)).doesNotExist();
+        assertThat(logFile("kafka", pid)).doesNotExist();
+    }
+
+    @Test
+    void aPidFileWrittenByTheProcessIsTheService() throws Exception {
+        long pid = ProcessHandle.current().pid();
+        writePidFile("kafka", pid, "{}");
+
+        InfraStop command = command("kafka");
+        command.aliveCheck = p -> true;
+
+        assertThat(command.doCall()).isZero();
+        assertThat(printer.getStdout()).isEqualTo("Shutting down external 
services (PID: " + pid + ")");
+    }
+
     @Test
     void serviceWhoseProcessIsGoneIsNotFound() throws Exception {
         writePidFile("kafka", 1234, "{}");

Reply via email to