Federico Mariani created CAMEL-26397:
----------------------------------------
Summary: camel-jbang-mcp - on Linux camel_control restart always
fails and dev mode goes undetected, because the JDK hides a command line longer
than 4 KB
Key: CAMEL-26397
URL: https://issues.apache.org/jira/browse/CAMEL-26397
Project: Camel
Issue Type: Bug
Components: camel-jbang, camel-jbang-mcp
Affects Versions: 4.23.0
Reporter: Federico Mariani
Assignee: Federico Mariani
Found on main (4.24.0-SNAPSHOT, commit ffef175654d1); the line numbers below
are from that commit. The same code is in the 4.23.0 release.
h3. Reproduction
On Linux, start an integration with the Camel CLI, then restart it through the
MCP server:
{code}
camel_run {"directory":"/tmp/probe","dev":false,"name":"probe"}
-> {"status":"started","pid":2406,...}
camel_control {"action":"restart","name":"probe"}
-> Cannot restart pid 2406: its command line is not available; stop it and run
it again
{code}
The JDK cause, reproduced in a plain container (Eclipse Temurin 21, Linux
aarch64):
{noformat}
java -cp <3000 chars> Sl -> info.arguments() = [3 args], commandLine length
3049
java -cp <4000 chars> Sl -> info.arguments() = [3 args], commandLine length
4049
java -cp <4100 chars> Sl -> info.arguments() = Optional.empty, commandLine
cut at 4096
camel run t.yaml (jbang) -> /proc/<pid>/cmdline is 15,592 bytes: arguments()
= Optional.empty
{noformat}
On Linux, {{ProcessHandle.Info}} reads only the first page (4096 bytes) of
{{/proc/<pid>/cmdline}}. When the command line is longer, {{arguments()}} is
empty and {{commandLine()}} is cut short. A {{camel run}} started through jbang
always has a longer command line, because its {{-classpath}} alone is about 15
KB. macOS has no such limit, which is why this does not show up on a developer
laptop.
Eval evidence (200 trials on 4.24.0-SNAPSHOT in Linux containers): *12 of 12*
{{camel_control restart}} calls failed (GLM 5.3: 9, DeepSeek V4.1 Flash: 2,
Claude Haiku 4.5: 1), in 9 trials, and no restart ever worked. After the error,
the agent did {{stop}} + {{camel_run}} in 7 cases (2 extra calls each). In 5
cases it left the MCP tools and killed and restarted the app from its shell
with {{kill}}/{{pkill}} + {{camel run}}.
The same lookup also turns off the reload report of the write tools, without
any error. In one trial (GLM 5.3) {{camel_run}} started the app in dev mode
from {{/workspace}} ({{"devMode":true}}, pid 807). Then two {{camel_edit_file}}
calls on its route file answered only the generic "An integration running the
file in dev mode reloads it now; otherwise restart ...", with no {{reload}}
result, although the app did reload (its log shows the reload). The agent added
{{sleep 5}} + {{camel_get_log}} to find out.
h3. Cause
{{IntegrationLauncher}} reads the integration's command line with
{{ProcessHandle.Info.arguments()}}:
* {{restart}}:
{{camel-jbang-core/.../commands/ai/IntegrationLauncher.java:620-635}}. The
arguments are empty, so it throws "command line is not available".
* {{devModeFrom}} ({{:486-494}}). The arguments are null, so no dev-mode
integration is found and {{camel_write_file}}/{{camel_edit_file}} skip the
reload wait ({{commands/ai/AuthoringTools.java:1726-1733}}).
* {{watchesFile}} ({{:466-468}}). It falls back to "watches every file".
* {{runningMessage}} ({{:441-446}}). It falls back to the generic message.
{{camel restart}} ({{commands/process/RestartProcess.java:74-92}}) falls back
to {{Info.commandLine()}}, which on Linux is cut at 4096 characters. So it
would relaunch with a broken classpath.
h3. Fix
* One helper that returns the full argument list of a pid: on Linux, read
{{/proc/<pid>/cmdline}} (NUL-separated, no length limit; the same user can read
it), else {{ProcessHandle.Info.command()}} + {{arguments()}}. Use it in the
four {{IntegrationLauncher}} methods and in {{RestartProcess}}. Do not parse
{{commandLine()}} when it is exactly 4096 characters long, because then it was
cut.
* Optionally, {{camel_run}} keeps the command it launched (next to the
{{ProcessHandle}} it already keeps per directory in
{{IntegrationLauncher.LAUNCHED}}, {{:78}}), and {{restart}} uses it first for
the processes it started.
* Test: an integration whose command line is longer than 4 KB (a long
{{-classpath}}), on Linux: restart works, and {{devModeFrom}} finds it.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)