Skip to content

Instantly share code, notes, and snippets.

@jcttrll
Last active June 12, 2023 13:39
Show Gist options
  • Save jcttrll/2c30ed9243f8a05de604a5efbd1238db to your computer and use it in GitHub Desktop.
Save jcttrll/2c30ed9243f8a05de604a5efbd1238db to your computer and use it in GitHub Desktop.
Attempt to use PID namespace to control process with timeout, while ensuring child cleanup
#!/bin/bash
waitPortOpen() {
local host=$1
local port=$2
local timeoutSeconds=$3
local intervalSeconds=$4
local connectTimeoutSeconds=$5
if [[ -z "$intervalSeconds" ]]; then
intervalSeconds=0.5
fi
if [[ -z "$connectTimeoutSeconds" ]]; then
connectTimeoutSeconds=1.0
fi
local semaphoreDir
semaphoreDir=$(mktemp -d)
if [[ -z "$semaphoreDir" ]]; then
return 1
fi
local timeoutPid
# Note: If bash -c is run with a command that doesn't require Bash (e.g., sleep 1), it apparently execs
# that command, defeating our purpose of having bash = PID 1, sleep = PID 2; we add the >/dev/null redirect
# for this reason.
read -r timeoutPid < <(
unshare -pUf --mount-proc \
bash -c "sleep $timeoutSeconds >/dev/null" 2>/dev/null &
pgrep --parent $!
)
(exec 2>/dev/null; nsenter -t "$timeoutPid" --preserve-credentials -U -m -p bash <<-EOF
while true; do
# nmap commands are allowed to overlap when connectTimeoutSeconds is greater than intervalSeconds. For
# example, with connectTimeoutSeconds=1.0 and intervalSeconds=0.25, we would expect up to four nmap
# processes running concurrently.
nmap -n -Pn -p "$port" \
--min-rtt-timeout "$connectTimeoutSeconds"s \
--max-rtt-timeout "$connectTimeoutSeconds"s \
--max-retries 0 \
-oG /dev/stdout '$host' 2>/dev/null |
grep -Fq 'Ports: $port/open' && touch "$semaphoreDir/up" && kill -9 2 &
sleep "$intervalSeconds"
done
EOF
)
if [[ -e "$semaphoreDir/up" ]]; then
rm -rf "$semaphoreDir"
return 0
fi
rm -rf "$semaphoreDir"
return 1
}
waitPortOpen "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment