From 4a478ad081e5e34da3376b5d9bc88168c28cbbe1 Mon Sep 17 00:00:00 2001 From: DavHau Date: Sun, 19 Jan 2025 17:52:47 +0700 Subject: [PATCH] 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 --- pkgs/by-name/br/breakpointHook/attach.sh | 69 ++++++++++++++----- .../br/breakpointHook/breakpoint-hook.sh | 4 +- pkgs/by-name/br/breakpointHook/package.nix | 15 ++-- 3 files changed, 58 insertions(+), 30 deletions(-) diff --git a/pkgs/by-name/br/breakpointHook/attach.sh b/pkgs/by-name/br/breakpointHook/attach.sh index 06ccdc3eee06..0605adafdb62 100755 --- a/pkgs/by-name/br/breakpointHook/attach.sh +++ b/pkgs/by-name/br/breakpointHook/attach.sh @@ -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//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//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 +" diff --git a/pkgs/by-name/br/breakpointHook/breakpoint-hook.sh b/pkgs/by-name/br/breakpointHook/breakpoint-hook.sh index 6c79f58604cd..8c96fcfc6832 100644 --- a/pkgs/by-name/br/breakpointHook/breakpoint-hook.sh +++ b/pkgs/by-name/br/breakpointHook/breakpoint-hook.sh @@ -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)" diff --git a/pkgs/by-name/br/breakpointHook/package.nix b/pkgs/by-name/br/breakpointHook/package.nix index a94ef881a430..88725ffd4282 100644 --- a/pkgs/by-name/br/breakpointHook/package.nix +++ b/pkgs/by-name/br/breakpointHook/package.nix @@ -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} "$@"