#!/bin/sh #check.sh : functionnal check functions for various server #Copyright (C) 2016 Weber Yann # #This program is free software; you can redistribute it and/or modify #it under the terms of the GNU General Public License as published by #the Free Software Foundation; either version 3 of the License, or #any later version. # #This program is distributed in the hope that it will be useful, #but WITHOUT ANY WARRANTY; without even the implied warranty of #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #GNU General Public License for more details. # #You should have received a copy of the GNU General Public License #along with this program. If not, see . if [ -z "$color" ] then color=1 fi if [ -z "$verbose" ] then verbose=1 fi if [ -z "$exit_on_fail" ] then exit_on_fail=1 fi alias echo="/bin/echo -e" col_reset() { if [ "$color" -gt 0 ] then tput sgr0 fi } col_set() { if [ -z "$1" ] then return fi if [ "$color" -gt 0 ] then tput setaf $1 fi } log() { echo "$(col_set $3)[$(printf "%7s" "$1")]$(col_reset) $2" } datetime() { date -Iseconds } logdate() { echo "$(date -Iseconds) $(col_set $3)[$(printf "%7s" "$1")]$(col_reset) $2" } fail() { if [ "$verbose" -lt 2 ] then echo "" #for the dot printed with -n fi test_msg="$1" test_status=1 } err() { msg="$(log ERR "$1" 1)" test_msg="$1" test_status=-1 } success() { msg="$(log OK "$1" 2)" test_msg="$1" test_status=0 } # # Tests & testcase functions # tc_name="" tc_infos="" tc_fail=0 tc_run=0 total_fail=0 total_run=0 test_msg="" test_status=0 _test_setup() { test_status=0 test_msg="" } TC_INIT() { tc_name=$1 tc_infos=$2 tc_fail=0 tc_run=0 msg="Running testcase '$tc_name'" if [ -n "$tc_infos" ] then msg="${msg} ($tc_infos)" fi if [ "$verbose" -gt 0 ] then logdate TESTCASE "$msg" 4 fi } TC_END() { if [ "$tc_fail" -gt 0 ] then if [ "$verbose" -gt 1 ] then logdate TESTCASE "-------------$tc_name END------------" 1 fi logdate FAIL "Testcase '$tc_name' tests: ${tc_run} fails: $(col_set 1)${tc_fail}$(col_reset)" 1 if [ "$exit_on_fail" -ne 0 ] then CHECK_REPORT fi elif [ "$verbose" -eq 1 ] then echo "" #for the dot printed with -n logdate TESTCASE "Testcase '$tc_name' tests:${tc_run} fails: $tc_fail" 2 elif [ "$verbose" -gt 1 ] then logdate TESTCASE "-------------$tc_name END------------" 4 fi tc_name="" tc_infos="" tc_fail=0 tc_run=0 } TC_RUN() { _test_setup cmd=$1 shift args="" case $# in 1)$cmd "$1";; 2)$cmd "$1" "$2";; 3)$cmd "$1" "$2" "$3";; 4)$cmd "$1" "$2" "$3" "$4";; *)fail "To many arguments for $cmd" esac #$cmd $args tc_run=$(expr $tc_run + 1) total_run=$(expr $total_run + 1) if [ "$test_status" -ne 0 ] then err_type="FAIL" if [ "$test_status" -lt 0 ] then err_type="ERR" fi tc_fail=$(expr $tc_fail + 1) total_fail=$(expr $total_fail + 1) logdate $err_type "$tc_name: $test_msg" 1 >&2 elif [ "$verbose" -gt 1 ] then logdate OK "$tc_name: $test_msg" 2 else echo -n '.' fi } CHECK_START() { logdate STATUS "Starting tests" 3 } CHECK_REPORT() { if [ "$verbose" -eq 0 -a "$tc_fail" -eq "0" ] then echo "" # for dot printed with -n fi if [ "$verbose" -gt 0 ] then logdate STATUS "All tests done" 3 fi if [ "$total_fail" -gt 0 ] then logdate FAIL "Summary : tests: ${total_run} fails: $(col_set 1)${total_fail}$(col_reset)" 1 exit 1 else logdate OK "Summary : tests:${total_run} fails: $total_fail" 2 exit 0 fi } # # HTTP/HTTPS tests # _check_http_status() { # $1 url # $2 status # $* curl options url=$1 shift 1 expt=$1 if [ -z "$1" ] then expt=200 fi shift 1 status=$(curl -s -o /dev/null -w "%{http_code}" $* $url) if [ "$status" -ne "$expt" ] then fail "Check http status $expt for $url : $status returned" elif [ "$verbose" -gt 0 ] then success "Check http status $status for $url" fi } check_http_status() { # $1 url # $2 status _check_http_status $1 $2 } check_http_200() { _check_http_status $1 200 } check_https_cert() { # Check that SSL Cert is valid # $1 URL status=$(curl -s -o /dev/null -w "%{http_code}" https://$1) rep=$? if [ "$rep" -eq 0 ] then success "https://$1 cert verified" return fi status=$(curl -k -s -o /dev/null -q "%{http_code}" https://$1) rep=$? if [ "$rep" -eq 0 ] then fail "https://$1 cert invalid" else err "Unable to curl https://$1" fi } check_html_title() { url="$1" expt="$2" tmpxsl=$(mktemp -t XSL.XXXXXXXXXXX) echo ' ' > $tmpxsl tmphtml=$(mktemp -t html.XXXXXXXXX) curl --silent $url > $tmphtml title=$(xsltproc --html --novalid $tmpxsl $tmphtml 2>/dev/null) if [ "$?" -ne "0" ] then title=$(xsltproc --novalid $tmpxsl $tmphtml 2>/dev/null) fi if [ "$title" = "$expt" ] then success "$url HTML title is '$expt'" else fail "$url HTML title is '$title' but '$expt' expected" fi rm $tmpxsl $tmphtml 2>/dev/null } check_audiostream() { # Uses one of mplayer or mpv to retrieve a small amount of stream # and attempt to decode it. # $1 Stream URL MPLAYER="`which mplayer`" MPV="`which mpv`" if [ -x $MPLAYER ] then check_audiostream_mplayer "$1" elif [ -x $MPV ] then check_audiostream_mpv "$1" else fail "Unable to find mplayer nor mpv" fi } check_audiostream_mplayer() { # Uses mplayer to fetch 128Kb (by default) of stream # $1 Stream URL # $2 size in kb without the kb suffix # $3 mplayer path tmpfile=$(mktemp -t check_audiostream.XXXXXXXXX) sz="$2" if [ -z "$sz" ] then sz="128" fi MPLAYER="$3" if [ -z "$MPLAYER" ] then MPLAYER="`which mplayer`" fi sz_kb="${sz}kb" expt_sz="`expr $sz \* 8`" if [ "$verbose" -gt 1 ] then logdate INFO "$tc_name: Running mplayer on '$1' for $sz_kb" 3 fi $MPLAYER -endpos $sz_kb -ao pcm:file=$tmpfile "$1" 1>/dev/null 2>/dev/null res=$? bytes=$(du -b $tmpfile | cut -f1) rm $tmpfile 2>/dev/null if [ "$bytes" -lt $expt_sz ] then fail "mplayer retrieved ${bytes}B of stream instead of expected $sz_kb" return fi if [ "$res" -eq 0 ] then success "mplayer retrieved $sz of stream on $1" else fail "mplayer failed to retrieve stream on $1" fi } check_audiostream_mpv() { # Uses mpv to fetch 4s of audio stream # $1 Stream URL # $2 time of stream to fetch # $3 mpv path time="$2" if [ -z "$time" ] then time="4" fi MPV="$3" if [ -z "$MPV" ] then MPV="`which mpv`" fi if [ "$verbose" -gt 1 ] then logdate INFO "$tc_name: Running mpv on '$1' for ${time}s" 3 fi start_time=`date "+%s"` $MPV --vo=null --ao=null --o=/dev/null --of=wav --length $time "$1" 1> /dev/null 2>/dev/null res=$? stop_time=`date "+%s"` run_time=`expr $stop_time - $start_time` if [ $run_time -lt $time ] then fail "mpv stopped running ${run_time} after launch but ${time}s should be received" return fi if [ $res -eq 0 ] then success "mpv retrieved ${time}s of stream on $1" else fail "mpv failed to retrieve ${time}s of stream on $1" fi } check_ping() { ns=$1 count=$2 if [ -z "$count" ] then count=5 fi ping -i 0.2 -c $count $ns 2>/dev/null >/dev/null res=$? if [ "$res" -ne 0 ] then fail "unable to ping '$ns'" else success "successfully send $count ping to '$ns'" fi } check_git_repo() { tmpdir=$(mktemp -d -t check_git.XXXXXXXXX) git clone $1 $tmpdir 2>/dev/null 1>/dev/null res=$? rm -Rf $tmpdir if [ "$res" -ne 0 ] then fail "unable to clone git repo '$1'" else success "git repo '$1' cloned'" fi } # # Jabber XMPP checks # __xmpp_probe() { serv=$1 timeout=$2 type=$3 payload="\n\n" if [ "$verbose" -gt 2 ] then echo -e $payload | sed -e "s/^/$(datetime) [ DEBUG] Sent : /" >&2 fi echo -e $payload sleep $timeout } _xmpp_probe() { serv=$1 port=$2 timeout=$3 type=$4 tmpres=$(mktemp -t xmpp_probe.XXXXXXXXX) echo "$(__xmpp_probe $serv $timeout $type| nc -q2 $serv $port)" | xmllint --format - > $tmpres if [ "$verbose" -gt 2 ] then cat $tmpres | sed -e "s/^/$(datetime) [ DEBUG] Recv : /" >&2 fi cat $tmpres rm $tmpres } _check_xmpp_ns() { ns1=$1 expt_ns=$2 expt_port=$3 dnsq="_xmpp-${4}._tcp.$1" rep="$(dig $dnsq srv +short | cut -d" " -f3,4)" if [ "$rep" = "$expt_port ${expt_ns}." ] then success "$dnsq = '$rep'" else fail "$dnsq = '$rep' but '$expt_port ${expt_ns}.' expected" fi } check_xmpp_serv_ns() { _check_xmpp_ns "$1" "$2" "$3" "server" } check_xmpp_client_ns() { _check_xmpp_ns "$1" "$2" "$3" "client" } _check_xmpp_server() { serv=$1 port=$2 timeout=$3 type=$4 if [ -z "$port" ] then port=5222 fi if [ -z "$timeout" ] then timeout=2 fi if [ "$verbose" -gt 1 ] then tpe="client" if [ "$type" = "server" ] then tpe="S2S" fi logdate INFO "$tc_name: Connecting to XMPP $serv $tpe port $port (timeout=${timeout}s)" 3 fi stream=$(_xmpp_probe $serv $port $timeout $type| head -n2 | tail -n1) if [ -z "$stream" ] then fail "Empty reply from $serv:$port" return fi if [ "$type" = "client" ] then infos=$(echo $stream | sed -E 's/^<([^ ]+).* xmlns="([^"]+)".* from="([^"]+)" .*$/\1 \2 \3/') else infos="$(echo $stream | sed -E 's/^<([^ ]+).* xmlns="([^"]+)" .*$/\1 \2/') $serv" fi if echo "$infos" | grep "jabber:$type" >/dev/null then success "Successfully connected to XMPP $type $serv:$port" else fail "Unexpected reply from $serv:$port : $infos" fi } check_xmpp_server_client() { _check_xmpp_server "$1" "$2" "$3" "client" } check_xmpp_server_s2s() { _check_xmpp_server "$1" "$2" "$3" "server" } check_xmpp_ssl() { serv=$1 port=$2 if [ -z "$port" ] then port=5222 fi openssl s_client -connect $serv:$port /dev/null 2>/dev/null rep=$? if [ "$rep" -eq 0 ] then success "Openssl successfully negociating XMPP ssl with $serv:$port" else fail "Openssl failed negociating XMPP ssl with $serv:$port" fi } # # SSH checks # check_ssh_nc() { host=$1 port=$2 if [ -z "$port" ] then port=22 fi rep="$(nc -w1 $host $port /dev/null then success "OpenSSH replied on $host:$port" else fail "Bad reply from $host:$port : '$rep'" fi } check_ssh_key() { host="$1" testkey="$2" keytype="$3" port="$4" if [ -z "$port" ] then port=22 fi if [ -z "$keytype" ] then keytype="rsa" fi key=$(ssh-keyscan -p $port -t $keytype $host 2>/dev/null | cut -d " " -f3) if [ -z "$key" ] then fail "SSH server not responding" return elif [ "$key" = "$testkey" ] then success "OpenSSH $host:$port key is $testkey" return else fail "OpenSSH $host:$port missmatch : " logdate ERR "Expected : $testkey" 1 logdate ERR "Received : $key" 1 return fi } check_mpc() { # check_mpc [$host [$port] ] # tests if you can contact an MPD server in a client way # returns the current state of the server host=$1 port=$2 if [ -z "$host" ] then host=127.0.0.1 fi if [ -z "$port" ] then port=6600 fi if echo "status" | nc -w 1 "$host" "$port" | head -1 | grep "^OK MPD" then success "MPD server can be contacted on $host:$port" status=$(echo "status" | nc -w 1 "$host" "$port" | grep "^state:"|cut -d: -f2) success "It's state is : $status" fi }