2 Commits
v0.1.2 ... main

Author SHA1 Message Date
e9b6778b78 Merge pull request 'fix(egg): the launcher drops Carbon's preloader for itself, not just the sidecar' (#17) from fix/launcher-carbon-preload into main
All checks were successful
Release sidecar / release (push) Successful in 5m6s
Reviewed-on: #17
2026-09-26 14:53:33 +00:00
af0e6be9f5 fix(egg): the launcher drops Carbon's preloader for itself, not just the sidecar
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 4m0s
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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-26 04:28:05 -05:00

View File

@@ -16,6 +16,29 @@
# the game's: nothing the bridge gets wrong — an unwritable log, a sidecar that will not start — may # 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`. # 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://<ip>:" 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 RL=/home/container/rust-link
mkdir -p "$RL" 2>/dev/null mkdir -p "$RL" 2>/dev/null
@@ -58,14 +81,14 @@ fi
SIDECAR="$RL/rust-link-sidecar" SIDECAR="$RL/rust-link-sidecar"
if [ ! -x "$SIDECAR" ]; then if [ ! -x "$SIDECAR" ]; then
echo "[rust-link] $SIDECAR is missing - reinstall the server to fetch the bridge. Starting the game without it." echo "[rust-link] $SIDECAR is missing - reinstall the server to fetch the bridge. Starting the game without it."
exec "$@" run_game "$@"
fi fi
# Provision first, so the token can be shown. `--print-config` resolves the configuration exactly as # 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 # 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). # 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 # Tracing is off on that path, so stdout is only the document. LD_PRELOAD is already gone from this
# sidecar in both calls: Carbon's entrypoint puts its Mono preloader in front of the whole startup. # 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 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. # 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"; } 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." echo "[rust-link] $RL is not writable - the game starts without the sidecar."
fi fi
# The game BECOMES this process: the panel console keeps its stdin and stdout, and stop still run_game "$@"
# stops the server, which takes the sidecar down with the container.
exec "$@"