Files
nixpkgs/pkgs/by-name/br/breakpointHook/attach.sh
Dark Steveneq 646b892680
Some checks failed
Periodic Merges (6h) / master → staging-nixos (push) Failing after 12m50s
Periodic Merges (6h) / master → staging-next (push) Failing after 12m54s
Periodic Merges (24h) / merge-base(master,staging) → haskell-updates (push) Failing after 11m54s
Periodic Merges (6h) / staging-next → staging (push) Failing after 12m13s
Periodic Merges (24h) / staging-next-25.05 → staging-25.05 (push) Failing after 13m24s
Periodic Merges (24h) / release-25.05 → staging-next-25.05 (push) Failing after 14m28s
push sheeet
2025-10-09 14:15:47 +02:00

86 lines
2.6 KiB
Bash
Executable File

#! /usr/bin/env bash
set -eu -o pipefail
# 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(){
while IFS= read -r -d $'\0' line; do
case "$line" in
*"$1="* )
echo "$line"
;;
esac
done < /proc/$pid/environ \
| 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)"
# get the build top level directory inside the sandbox (eg. /build)
buildDir=$(getVar NIX_BUILD_TOP)
# 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
bashInteractive="$(getVar bashInteractive)"
# the debug shell will be started as interactive shell after loading the env vars
debugShell="$(getVar debugShell)"
# to drop the user into the working directory at the point of failure
pwd="$(readlink /proc/$pid/cwd)"
# enter the namespace of the failed build
# 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
source \"$buildDir/env-vars\"
cd \"$pwd\"
if [ -n \"$debugShell\" ]; then
exec \"$debugShell\"
else
exec \"$bashInteractive\" --init-file \"$buildDir/env-vars\"
fi
"