breakpointHook: several improvements

1. Implement pgrep in bash to get rid of procps dependency
2. dump env vars one more time to make sure /build/env-vars includes everything at the point of failure
3. Load env vars via `--init-file /build/env-vars` when executing the interacitve bash session to include bash native variables
4. Use buildPackages for tools used in attach script, to allow usage in cross compiling screnarios
5. use util-linuxMinimal instead of util-linux to get rid of systemd dependency
6. add flags to nsenter: --ipc --uts --pid
This commit is contained in:
DavHau 2025-01-19 17:52:47 +07:00
parent 52a2bfcee4
commit 4a478ad081
3 changed files with 58 additions and 30 deletions

View File

@ -2,16 +2,36 @@
set -eu -o pipefail
id="$1"
pids="$(pgrep -f "sleep $id" || :)"
if [ -z "$pids" ]; then
echo "Error: No process found for 'sleep $id'. The build must still be running in order to attach. Also make sure it's not on a remote builder." >&2
exit 1
elif [ "$(echo "$pids" | wc -l)" -ne 1 ]; then
echo "Error: Multiple processes found matching 'sleep $id'" >&2
exit 1
fi
pid="$(echo "$pids" | head -n1)"
# A shell implementation for pgrep as we don't want to depend on procps
pgrep(){
PATTERN="$1"
for pid_dir in /proc/[0-9]*; do
pid="${pid_dir##*/}"
# Attempt to open /proc/<PID>/cmdline for reading on a new file descriptor
# If we can't read it (no permission or doesn't exist), skip
exec {fd}< "$pid_dir/cmdline" 2>/dev/null || continue
cmdline=""
# Read each null-delimited token from /proc/<PID>/cmdline
# and join them with a space for easier pattern matching
while IFS= read -r -d $'\0' arg <&$fd; do
if [[ -z "$cmdline" ]]; then
cmdline="$arg"
else
cmdline="$cmdline $arg"
fi
done
# Close the file descriptor
exec {fd}>&-
# If cmdline is non-empty and matches the pattern, print the PID
if [[ -n "$cmdline" && "$cmdline" =~ $PATTERN ]]; then
echo "$pid"
fi
done
}
# helper to extract variables from the build env
getVar(){
@ -25,6 +45,17 @@ getVar(){
| cut -d "=" -f 2
}
id="$1"
pids="$(pgrep "sleep $id" || :)"
if [ -z "$pids" ]; then
echo "Error: No process found for 'sleep $id'. The build must still be running in order to attach. Also make sure it's not on a remote builder." >&2
exit 1s
elif [ "$(echo "$pids" | wc -l)" -ne 1 ]; then
echo "Error: Multiple processes found matching 'sleep $id'" >&2
exit 1
fi
pid="$(echo "$pids" | head -n1)"
# bash is needed to load the env vars, as we do not know the syntax of the debug shell.
# bashInteractive is used instead of bash, as we depend on it anyways, due to it being
# the default debug shell
@ -35,12 +66,16 @@ debugShell="$(getVar debugShell)"
pwd="$(readlink /proc/$pid/cwd)"
# enter the namespace of the failed build
exec nsenter --mount --net --target "$pid" "$bashInteractive" -c "
# bash needs to be executed with --init-file /build/env-vars to include the bash native
# variables like ones declared via `declare -a`.
# If another shell is chosen via `debugShell`, it will only have simple env vars avaialable.
exec nsenter --mount --ipc --uts --pid --net --target "$pid" "$bashInteractive" -c "
set -eu -o pipefail
while IFS= read -r -d \$'\0' line; do
export \"\$line\"
done <&3
exec 3>&-
source /build/env-vars
cd \"$pwd\"
exec \"$debugShell\"
" 3< /proc/$pid/environ
if [ -n \"$debugShell\" ]; then
exec \"$debugShell\"
else
exec \"$bashInteractive\" --init-file /build/env-vars
fi
"

View File

@ -6,9 +6,7 @@ breakpointHook() {
# provide the user with an interactive shell for better experience
export bashInteractive="@bashInteractive@"
if [ -z "$debugShell" ]; then
export debugShell="@bashInteractive@"
fi
dumpVars
local id
id="$(shuf -i 999999-9999999 -n1)"

View File

@ -1,24 +1,19 @@
{
lib,
stdenv,
buildPackages,
bash,
bashInteractive,
coreutils,
makeSetupHook,
procps,
util-linux,
writeShellScriptBin,
}:
let
attach = writeShellScriptBin "attach" ''
attach = buildPackages.writeShellScriptBin "attach" ''
export PATH="${
lib.makeBinPath [
bash
coreutils
procps # needed for pgrep
util-linux # needed for nsenter
buildPackages.bash
buildPackages.coreutils
buildPackages.util-linuxMinimal # needed for nsenter
]
}"
exec bash ${./attach.sh} "$@"