From af0e6be9f5f11b6a6483cdcc9294bba3c911adb9 Mon Sep 17 00:00:00 2001 From: wtclaude Date: Sat, 26 Sep 2026 04:28:05 -0500 Subject: [PATCH] fix(egg): the launcher drops Carbon's preloader for itself, not just the sidecar Carbon's entrypoint puts LD_PRELOAD=libdoorstop.so in front of the whole startup string, and with Carbon's DOORSTOP_* environment that preloader breaks the launcher shell's own command substitution: the sidecar's --print-config output went straight to the console and the capture came back empty. The first Carbon server built from the egg printed its config JSON, token included, unframed, then "server id 'main', sidecar URL http://192.168.0.12: (listening on )" and never the once-only banner. `env -u LD_PRELOAD` on the sidecar alone was not enough: the shell doing the capturing had the preload too. The launcher now re-execs itself once without LD_PRELOAD, keeps it aside, and gives it back to the game in run_game, the only process that needs it. Reproduced and fixed under Carbon's real environment (environment.sh + libdoorstop.so): the released launcher dumps the JSON; this one prints the banner and "server id 'egg-carbon', sidecar URL http://192.168.0.12:21019", and the game still gets LD_PRELOAD and DOORSTOP_ENABLED. The four no-preload cases in the game image are unchanged. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY --- egg/with-sidecar.sh | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/egg/with-sidecar.sh b/egg/with-sidecar.sh index 940294b..67d8199 100755 --- a/egg/with-sidecar.sh +++ b/egg/with-sidecar.sh @@ -16,6 +16,29 @@ # the game's: nothing the bridge gets wrong — an unwritable log, a sidecar that will not start — may # keep the server from booting. Each step reports its own failure and the script goes on to `exec`. +# Carbon's entrypoint puts LD_PRELOAD=libdoorstop.so in front of the WHOLE startup string, and with +# Carbon's DOORSTOP_* environment that preloader breaks this shell's own `$(...)`: the child's output +# goes straight to the console and the capture comes back empty. The phase 18 walk saw exactly that +# on the first Carbon server built from the egg — the config JSON, token included, dumped raw to the +# console, and an empty "server id 'main', sidecar URL http://:" line. Dropping LD_PRELOAD for +# the sidecar alone is not enough; the shell doing the capturing has it too. So the script runs +# once more without it, keeps it aside, and gives it back to the game — the one process that needs +# it — in `run_game` below. +if [ -n "${LD_PRELOAD-}" ] && [ -z "${RUNICGATEWAY_LAUNCHER_CLEAN-}" ]; then + exec env -u LD_PRELOAD RUNICGATEWAY_GAME_PRELOAD="$LD_PRELOAD" RUNICGATEWAY_LAUNCHER_CLEAN=1 \ + sh "$0" "$@" +fi + +# The game BECOMES this process: the panel console keeps its stdin and stdout, and stop still +# stops the server, which takes the sidecar down with the container. +run_game() { + if [ -n "${RUNICGATEWAY_GAME_PRELOAD-}" ]; then + export LD_PRELOAD="$RUNICGATEWAY_GAME_PRELOAD" + fi + unset RUNICGATEWAY_GAME_PRELOAD RUNICGATEWAY_LAUNCHER_CLEAN + exec "$@" +} + RL=/home/container/rust-link mkdir -p "$RL" 2>/dev/null @@ -58,14 +81,14 @@ fi SIDECAR="$RL/rust-link-sidecar" if [ ! -x "$SIDECAR" ]; then echo "[rust-link] $SIDECAR is missing - reinstall the server to fetch the bridge. Starting the game without it." - exec "$@" + run_game "$@" fi # Provision first, so the token can be shown. `--print-config` resolves the configuration exactly as # a start does, writing sidecar.toml with a generated token when there is none; the JSON it prints # says whether it generated one on THIS call, which is what makes "print once" true (D152). -# Tracing is off on that path, so stdout is only the document. LD_PRELOAD is dropped for the -# sidecar in both calls: Carbon's entrypoint puts its Mono preloader in front of the whole startup. +# Tracing is off on that path, so stdout is only the document. LD_PRELOAD is already gone from this +# shell (see the top); `env -u` stays so a preload set some other way cannot reach the sidecar. if CFG="$(env -u LD_PRELOAD "$SIDECAR" --print-config 2>&1)"; then # The Nth `"key": "value"` line of the pretty-printed JSON. `bind` appears twice — game, then web. field() { printf '%s\n' "$CFG" | sed -n "s/^ *\"$1\": \"\([^\"]*\)\",\{0,1\}\$/\1/p" | sed -n "${2:-1}p"; } @@ -98,6 +121,4 @@ else echo "[rust-link] $RL is not writable - the game starts without the sidecar." fi -# The game BECOMES this process: the panel console keeps its stdin and stdout, and stop still -# stops the server, which takes the sidecar down with the container. -exec "$@" +run_game "$@" -- 2.49.1