Skip to content

Instantly share code, notes, and snippets.

@silverweed
Last active July 6, 2023 07:53
Show Gist options
  • Save silverweed/36d539ac68b13c6a50d5f449d64d4754 to your computer and use it in GitHub Desktop.
Save silverweed/36d539ac68b13c6a50d5f449d64d4754 to your computer and use it in GitHub Desktop.
Checks if LAN hosts are up in parallel
#!/bin/bash
# parallel port check via nc. Takes ~1 s per scanned service, but
# should almost not depend on the number of hosts.
# by silverweed
# FIXME: echo-ing lines from check_host may result in more lines
#+ bloated together.
# file containing the IPs to scan
HOSTLIST="$HOME/.hostlist.txt"
NC="/bin/nc"
# map of default services -> ports to scan
declare -A TO_CHECK
TO_CHECK[ssh]=22
TO_CHECK[http]=80
# preliminary checks
[[ -r $HOSTLIST ]] || {
echo "$HOSTLIST not readable: exiting." >&2
exit 1
}
[[ -x $NC ]] || {
echo "$NC not executable: exiting." >&2
exit 2
}
# given a host IP, checks all services in TO_CHECK and returns an associative
# array IS_UP with the results (0 = up, 1 = not up)
check_host() {
[[ -n $1 ]] || {
echo "Warning: invalid argument for check_host(): $1" >&2
return 1
}
ping -c 1 -w 1 $1 &> /dev/null || {
[[ $1 =~ ^([0-9]{1,3}\.){3}([0-9]{1,3})$ ]] || {
echo -en "\033[1;31m"
printf "%-16s " "$1:"
echo -e "down\033[0;0m"
}
return
}
declare -A IS_UP
for SERVICE in ${!TO_CHECK[@]}; do
$NC -w 1 $1 ${TO_CHECK[$SERVICE]} &> /dev/null
IS_UP[$SERVICE]=$?
done
echo -en "\033[1;32m"
printf "%-16s " "$1:"
for SERVICE in ${!IS_UP[@]}; do
if [[ ${IS_UP[$SERVICE]} == 0 ]]; then
echo -en "\033[1;32m"
else
echo -en "\033[1;31m"
fi
echo -en "$SERVICE \033[0;0m"
done
echo ""
}
add_service() {
SRV=$(awk -v FS=':' '{ print $1 }' <(echo $1))
PORT=$(awk -v FS=':' '{ print $2 }' <(echo $1))
if [[ $PORT ]]; then
TO_CHECK["${SRV:-?}"]=$PORT
else
# try to resolve port number from service name
TO_CHECK["$SRV"]=$(awk "/^\s*[^#]/ && /$SRV/ { print \$2; exit }" /etc/services | cut -f1 -d'/')
if [[ ${TO_CHECK["$SRV"]} ]]; then
echo "[INFO] Scanning service $SRV on port ${TO_CHECK[$SRV]}." >&2
else
echo -n "[ERR] Failed to resolve port for service $SRV. "
echo "Please specify it with '-s $SRV:<port>'." >&2
fi
fi
}
SORT='sort -g'
while [[ $# -gt 0 ]]; do
case $1 in
-s)
unset TO_CHECK
declare -A TO_CHECK
shift
while [[ $# -gt 0 && ! $1 =~ -.* ]]; do
add_service "$1"
shift
done
;;
-n)
DONTRESOLVE=true
SORT="sort -g -k4 -t'.'"
shift
;;
*)
echo "Usage: $0 [-n] [-s <service1[:port]> [service2[:port] ...] ]" >&2
exit 0
;;
esac
done
declare -a PIDS
i=0
echo "Checking $(wc -l $HOSTLIST | awk '{print $1}') hosts..." >&2
STARTTIME=$(date +%s)
for HOST in $(< $HOSTLIST); do
if [[ $DONTRESOLVE != true ]]; then
RESOLVE=$(grep $HOST /etc/hosts | awk '{print $2}')
[[ -n $RESOLVE ]] && HOST=$RESOLVE
fi
# parallel scan
check_host $HOST & PIDS[$i]=$!
let i++
done | eval "$SORT"
for ((j = 0; j < i; j++)); do
wait ${PIDS[$j]}
done
echo "Elapsed time: $(($(date +%s) - STARTTIME)) s" >&2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment