#!/bin/bash
# Script to check webpages to see if websites/machines are live
# for more info run with -h

# note that it is configured at present to make 10 (c loop) attempts
# and to timeout after 12 seconds (-T 12) on each attempt, with pauses
# starting at 1 secs and increasing to max 10 secs before any retries
# [Note that wget -w and --wait option does not seem to work]

VERSION="4.7.0 [03 Jan 2026]"
THIS=$(basename "$0")
COLUMNS=$(stty size 2>/dev/null||echo 30000); COLUMNS=${COLUMNS##* }
CHECKSTATUS=0
TMPF="${TMPDIR-/tmp}/$THIS-$(id -u)-$$"

check_site () {
	local NEGATIVE=0
	for (( c=1; c<=5; c++ )) do
		if [[ $c -gt 1 ]]; then
			[[ -n $DEBUG ]] && echo "Waiting ${c}s" >>/dev/tty
			sleep "$c"
		fi
		[[ $1 != "ditto" ]] && wget $WGETPARAMS --max-redirect 5 -t 1 -T 4 -O - "$1" >"$TMPF-http_site.htm" 2>&1
		[[ -n $DEBUG ]] && echo "Testing \$KEYTEXT: '$KEYTEXT'" >/dev/tty
		if [[ $NEGATIVE != 1 ]]; then
			[[ ${KEYTEXT:0:1} == "!" ]] && { KEYTEXT="${KEYTEXT:1}"; NEGATIVE=1; }
			[[ ${KEYTEXT:0:1} != "/" ]] && KEYTEXT="/${KEYTEXT}/p"
		fi
		[[ -n $DEBUG ]] && echo "for site '$1' doing: sed -n \"$KEYTEXT\" \"$TMPF-http_site.htm\"" >/dev/tty
		[[ -n $DEBUG && $NEGATIVE == 1 ]] && echo "negative test!" >/dev/tty
		[[ -n $(sed -n "$KEYTEXT" "$TMPF-http_site.htm" 2>/dev/null) ]] && FOUND=$((1-NEGATIVE)) || FOUND=$NEGATIVE
		[[ -n $DEBUG ]] && echo "Test outcome \$FOUND: '$FOUND'" >/dev/tty
		[[ $((FOUND+NEGATIVE)) -gt 0 || $1 == "ditto" ]] && break
		[[ -n $VERBOSE ]] && echo -n "." >/dev/tty
	done
	echo "$FOUND"
}

function plural () {
	# outputs 's' if param1 is a number other than 1 or -1
	# example usage:
	# COWS=3; HORSES=1; echo "We have $COWS cow$(plural $COWS) and $HORSES horse$(plural $HORSES)"
	# We have 3 cows and 1 horse
    [[ $1 =~ ^[-.0-9]+$ ]] && [[ $1 -ne 1 && $1 -ne -1 ]] && echo -n "s"
}

while getopts ":cdfqhlnsvw" optname; do
  case "$optname" in
		"c")	CHECKSTATUS=1;;
		"d")	DEBUG=y;;
		"f")	FORCE=y;;
		"q")	QUIET=y;;
		"h")	HELP=y;;
		"l")	CHANGELOG=y;;
		"n")	NO_HEADER=y;;
		"s")	SKIP1FAIL=y;;
		"v")	VERBOSE=y;;
		"w")	COLUMNS=30000;; #suppress line-breaking
		"?")	echo "Unknown option $OPTARG"; exit 1;;
		":")	echo "No argument value for option $OPTARG"; exit 1;;
		*)	echo "Unknown error while processing options"; exit 1;;
    esac
done
shift $((OPTIND-1))

[[ -z $QUIET$NO_HEADER ]] && echo -e "\n$THIS v$VERSION - by Dominic\n${THIS//?/=}\n"
for DIR in $LOG /var/log /log ~; do
	[ -r "$DIR" ] && [ -w "$DIR" ] && [ -x "$DIR" ] && { LOGDIR=$DIR; break; }
done
[ -z "$LOGDIR" ] && { echo "Unable to find a suitable log directory, aborting..." >&2; exit 2; }

if [[ -n $1 ]]; then
	if [[ ! -s $1 ]]; then
		# CONFFILE name here is designed so it will always be the same for the same command line text and user
		CONFFILE="${TMPDIR-/tmp}/$THIS-$(id -u)-$(echo "$*"|sha1sum|cut -d" " -f1)-preconf.conf"
		echo -e "$*" >"$CONFFILE"; chmod 640 "$CONFFILE"
		shift $#
	elif [[ $# -eq 1 ]]; then
		CONFFILE="$1"; shift 1
	else
		echo "Invalid parameters on command line: '$*', aborting" >&2
		exit 1
	fi
else
	HELP="y" #CONFFILE="$(dirname "$0")/$(basename "$0" .sh).conf"
fi
[[ -n $CONFFILE && -s $CONFFILE ]] || HELP="y"
if [[ -n $HELP  ]]; then
	echo -e "$THIS is a lightweight network monitoring program \
- see https://en.wikipedia.org/wiki/Network_monitoring, and runs under Linux. \
It's called 'tiny' because it's little, but sometimes the best things come in \
small packages!

It can be used for testing internet or intranet websites, mail servers, \
local or remote servers / computers, and peripherals such as printers, \
switches, wireless access points etc.

Based on the conf file you create (one line per device - example below), or \
the quoted conf_line on the command line, it checks a list \
of machines and/or webpages for a response i.e. that they are live. For a \
webpage, it uses wget to check that the response includes some specified \
key_text; otherwise, it uses ping or, if a port number is specified, nmap. \
It can also test for authenticated access to mail servers.

It can be run with -cq as a cron \
job at regular intervals, say every 5 minutes, and it will generate \
some text as a warning if any of the monitored devices go offline - \
cron can then send this to you as an email.

Usage  : $THIS [options] [conf_file || conf_line]

Example:
    ./$THIS -q

Options: -c  report only a change in status i.e. from failure to success or vice-versa (to reset i.e. to generate a report the next time the same conf parameters are tested with -c regardless of status, delete the appropriate log file from /var/tmp)
         -f  force mode - test all sites, including subtests that would normally be excluded
         -h  see this help text and exit
         -l  see changelog and exit
         -n  suppress header and final text
         -s  with -c option, report a failure only if it is repeated a consecutive time
         -v  verbose text output
         -q  quiet - text output only for a failure or status change

Conf_file:
      conf_file contains text lines with 2-4 fields like this \
(where square brackets indicate optional text):

           [+/-]address[:[U]port] reportname [key_text] [wget_options]

      each line is evaluated for the first match below:
           -  if address begins with 'http' then it is a \
webpage test: wget is used to retrieve the page which is then tested for \
key_text (see below)
           -  if address is 'ditto' (without the single quotes) then the webpage \
downloaded for the most recent http test in this session is re-used as the \
source for this test - so you can perform multiple tests on the same webpage \
without having to download it repeatedly
           -  if address begins with 'auth:' then the rest of the address \
is tested as a mailserver offering STARTTLS and user authentication via \
AUTH PLAIN - the port must be specified after the address i.e. :25 or :587, \
field3 (key_text) specifies a valid username and field4 (wget_options) \
specifies a valid password (it is recommended but not required that you \
install swaks if you want to use auth: - faster and more reliable)
           -  if a port number is specified (format address:[U]portnumber), \
nmap is used (precede port number with U for UDP, otherwise TCP) - any \
key_text and wget_options will be ignored
           -  if none of the above apply, ping is used - any key_text and \
wget_options will be ignored
           -  if address is preceded by a plus or minus sign then it is a \
'subtest', which is carried out only if the preceding non-subtest succeeded \
(+) or failed (-). Note that -f command-line option forces all subtests to \
be carried out regardless.

           -  reportname is the device's 'friendly' name i.e. that will \
appear in reports; any spaces in it must be escaped with \\

           -  key_text is used for webpages (i.e. with \
wget) and the retrieved data is tested to check that the key_text is present; \
the test is case-sensitive, and spaces in key_text (or in reportname) must \
be escaped with \\, not with quotes (\"). Any characters that would \
need to be escaped anyway will need to be double escaped with \\\\\\. \
Tests are performed using sed -n, prefixing '/' and suffixing '/p', so \
searching is sed regex-compatible. Negative tests can be performed by \
prefixing '!' to the test - in these cases outcome is deemed successful \
if the text is *not* found. \
You can perform more sophisticated (e.g. multiline) tests by starting \
key_text with /, and expecting output only if the test is successful, \
e.g. to search for the word 'Registered' in the line after \
'sip.voicetrading.com', use this key_text: \
/sip\\\\\\.voicetrading\\\\\\.com/{N;/Registered/p}

           -  wget_options are additional options if any for wget e.g: \
--http-user=myusername --http-password=mypassword

           -  a blank line, or any text after (and including) a hash (#), is ignored

Conf_file Example:
    # seek key_test 'Forbidden' on my webserver (lan-facing)
    httq://127.0.0.1/ internal Forbidden
    # seek key_text 'My Greatest Web Site' on my webserver (wan-facing)
    #  (note the escaping of spaces!)
    httq://my.wan.ip my\ wan\ ip My\ Greatest\ Web\ Site
    # if wan-facing test fails, ping-test my router lan-facing
    -192.168.1.2 my_router_lan_side
    # test mailserver with username postmanpat and password BlacknWhiteCat
    auth:remote.mailserver.tld:587 My\ Remote\ MX postmanpat BlacknWhiteCat
    # check my local network printer
    192.168.1.109:9100 My\ Printer

Conf_line:
Instead of specifying a conf_file you can specify the parameters for a \
single test on the \
command line. It is strongly advised to enclose them in quotes - spaces in \
key_text must still be escaped. It is possible to have multiple tests \
if all the parameters are enclosed in quotes and a \\\\n line separator is used \
between tests.

Files created:
A permanent results log is maintained at $LOGDIR/$THIS.log - the directory \
can be modified by setting \$LOG.

Temporary files during the run are stored at (and, unless the run is aborted, \
deleted from) ${TMPDIR-/tmp} - this can be modified by setting \$TMPDIR.
Files recording the most recent results are stored at /var/tmp/*-new.log \
and /var/tmp/*-old.log for use by the -c option test.

Note that other users on the same system may be able to read these files.

Further information:
When checking a webpage, $THIS checks both the page output and any output \
from wget - including error \
text - for the presence of the key_text. The downside of this is that you \
must guard against using a keyword that might appear unexpectedly anywhere \
in wget's output, but the upside is that \
that you can also check for specific error words. In particular, \
for password-protected sites, you can look for keywords returned by \
the site upon unauthorized access. This can be quicker than passing \
through the authorization username and password and means you do not have to \
put this sensitive data in the conf_file. For example:

    httq://my.cctv.website.com MyCCTV 401\ Authorisation\ Required
instead of

    httq://my.cctv.website.com MyCCTV i-Catcher --user=fred --password=bloggs

Exit code 99 indicates one or more destination failures.

License: Copyright © 2026 Dominic Raferd. Licensed under the Apache License, \
Version 2.0 (the \"License\"); you may not use this file except in compliance \
with the License. You may obtain a copy of the License at \
https://www.apache.org/licenses/LICENSE-2.0. Unless required by applicable \
law or agreed to in writing, software distributed under the License is \
distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY \
KIND, either express or implied. See the License for the specific language \
governing permissions and limitations under the License.
"|fold -sw"$COLUMNS"
fi
if [[ -n $CHANGELOG ]]; then
	echo "\
4.7 [03 Jan 2026]: breaking change: remove default conf_file
4.6 [30 Jan 2024]: add -n option
4.5 [27 Jan 2024]: much closer shellcheck compliance
4.4 [26 Nov 2023]: very minor bugfix to greps
4.3 [05 May 2022]: very minor change to final line of output
4.2 [19 May 2019]: fix long-standing bug in determining whether to run sub-tests
4.1 [18 May 2019]: fixes for negative/ditto tests
4.0 [19 Feb 2019]: add date and time to text reporting
3.9 [21 Jun 2018]: fix incorrect exit code in quiet mode
3.8 [11 Apr 2018]: add negative test option
3.7 [20 Mar 2018]: use swaks (if available) for auth: tests - faster, more reliable
3.6 [01 Mar 2018]: add graceful timeout for auth: tests
3.5 [09 Feb 2018]: add auth: testing option
3.4 [16 Oct 2017]: allow single conf line on command line
3.3 [23 Nov 2016]: remove ssh option that was added at 3.2
3.2 [15 Nov 2016]: nmap skips ping scan, prevents some false positives, add \
ssh option
3.1 [30 Sep 2016]: some log messages were going to a wrongly-named log file
3.0 [22 Sep 2016]: remove debug message that appeared in non-debug mode
2.9 [16 Jul 2016]: change names and locations of temporary/log files
2.8 [18 Nov 2015]: add 'ditto' option to allow multiple checks on a webpage, \
use sed -n for pattern matching and allow complex and/or multiline testing, \
use uniquely-named temporary files
2.7 [02 Sep 2015]: allow http redirects but require certificate checks
2.6 [14 Aug 2015]: port-based test (nmap) bugfixed
2.5 [13 Aug 2015]: subtest (+/-) options bugfixed
2.4 [14 Jul 2015]: -s option bugfixed
2.3 [01 Jul 2015]: add -s option
2.2 [18 May 2015]: tidy up a couple of temporary files after successful \
conclusion
2.1 [14 May 2015]: report nmap 'open|filtered' UDP ports 'Failed', not 'Found'
2.0 [16 Jul 2014]: remove '--open' switch from nmap so it \
works with older versions of nmap, add UDP port test option
1.9 [18 Mar 2014]: renamed from 'websites-live-checker' to \
'tiny-device-monitor' which better reflects its purpose, help text updated
1.8 [27 Nov 2013]: make change-in-status testing a command-line option, \
add force subtesting option, add subtest conditional on success (+ or -) - \
note changes required to any prior conf files, help updated
1.7 [02 Sep 2013]: small improvement to output text
1.6 [15 May 2013]: improved output text
1.5 [14 May 2013]: small change to help and output text
1.4 [24 May 2012]: small improvement to output text
1.3 [14 May 2012]: small improvement to conditional testing
1.2 [01 May 2012]: add '+' conditional test
1.1 [08 Mar 2012]: improved help text
1.0 [21 Sep 2011]: now compatible with cygwin & for non-privileged user"|fold -sw"$COLUMNS"
fi
[[ -n $HELP$CHANGELOG ]] && exit 0

umask 0027 # ensure that 'other' users cannot access temporary or log files
[[ -z $QUIET ]] && START=$(date +%s)
FAILED=0
if [ "$OSTYPE" != "cygwin" ]; then
	#  This looks for running processes with the same name as this, then filters out any
	#  with the same pid, parent pid or session id as this one, and then filters out any
	#  which are tagged as <defunct>
	#  in cygwin, ps works differently, so skip it here
	PRIOR=$(ps -C "$THIS" --no-headers -o "pid,ppid,sid,comm"|grep -av "$$ "|grep -av "<defunct>")
	if [[ -n $PRIOR ]]; then
		[[ -z $QUIET ]] && echo -e "Aborting because another instance of $THIS is already running"
		exit 1
	fi
fi
NEWLOG="/var/tmp/$(basename "$CONFFILE" .txt)-$(id -u)-new.log"
OLDLOG="/var/tmp/$(basename "$CONFFILE" .txt)-$(id -u)-old.log"
if [[ -z $QUIET$NO_HEADER ]]; then
	[[ $CONFFILE =~ preconf ]] || echo "Using $CONFFILE"
	[[ $CHECKSTATUS -eq 1 ]] && echo "Reporting change of status"
	[[ -n $FORCE ]] && echo "Force mode: all subtests will be included"
fi
if [ -e "$NEWLOG" ]; then mv -f "$NEWLOG" "$OLDLOG"; fi
MAINFOUND="0" # shouldnt be necessary to set this here but just in case
# tidy up the conf file before we start
sed 's/\r$//;s/#.*//;s/\s*$//;/^$/d' "$CONFFILE" >"$TMPF-conf.conf" || { echo "Error $? trying to tidy $CONFFILE, aborting" >&2; exit 1; }
[[ -n $DEBUG ]] && echo "Entries to parse:" && cat "$TMPF-conf.conf"
# shellcheck disable=SC2162
while read ADDRESS REPORTNAME KEYTEXT WGETPARAMS; do
	[[ -n $DEBUG ]] && echo "Read: '$ADDRESS' '$REPORTNAME' '$KEYTEXT' '$WGETPARAMS'"
	if [ "${#ADDRESS}" -gt 1 ]; then
		if [ "${ADDRESS:0:1}" != "#" ]; then
			ADDR0="${ADDRESS:0:1}"
			if [[ "$ADDR0" == "+" || "$ADDR0" == "-" ]]; then
				ADDRESS=${ADDRESS:1:999}
				[ "$ADDR0" = "+" ] && SUBTEST="ne" || SUBTEST="eq"
			else
				SUBTEST="ge"
			fi
			[ -n "$DEBUG" ] && echo -n "[</rtept> '$SUBTEST', FORCE is '$FORCE', MAINFOUND is '$MAINFOUND', ADDRESS is '$ADDRESS', ADDR0 is '$ADDR0' ] "
			unset PASSED_SUBTEST
			case $SUBTEST in
				ne) [[ $MAINFOUND -ne 0 ]] && PASSED_SUBTEST=y;;
				eq) [[ $MAINFOUND -eq 0 ]] && PASSED_SUBTEST=y;;
				ge) [[ $MAINFOUND -ge 0 ]] && PASSED_SUBTEST=y;;
				*) echo "Unknown subtest '$SUBTEST'"; exit 1;;
			esac
			if [[ -n $FORCE || -n $PASSED_SUBTEST ]]; then
				if [[ -e $OLDLOG ]]; then
					PREVIOUSSTATUS=$(grep -aF "@ $REPORTNAME @" "$OLDLOG"|awk '{print $1}')
				else
					PREVIOUSSTATUS=1
				fi
				# if a subtest website is not found in the previous status log it must then have
				# been skipped because it was assumed to be okay:
				[[ -n $SUBTEST && -z $PREVIOUSSTATUS ]] && PREVIOUSSTATUS="1"
				[[ ${ADDRESS:0:4} == "http" || $ADDRESS == "ditto" ]] && TOTEST="website" || TOTEST="machine"
				if [ -n "$VERBOSE" ]; then
					echo -e "\nTesting $TOTEST\t: $ADDRESS\nCheckstatus\t: $CHECKSTATUS\nReportname\t: $REPORTNAME"
					[ "$TOTEST" != "machine" ] && echo -e "Keytext\t\t: $KEYTEXT\nOptions\t\t: $WGETPARAMS"
					echo -e "Previous Status\t: $PREVIOUSSTATUS"
				fi
				if [ "$TOTEST" = "machine" ]; then
					# it's not a website
					ADDRESSCOLON=$(expr index "$ADDRESS" :)
					if [[ $ADDRESSCOLON -ne 0 ]]; then
						if [[ ${ADDRESS:0:5} == "auth:" ]]; then
							# does starttls and then auth
							TESTTYPE="auth"
							MX=${ADDRESS:5}
							rm -f -- "$TMPF-0"
							if swaks --version >/dev/null 2>&1; then
								# use swaks if available - better and faster
								time (swaks -tls --auth PLAIN --auth-hide-password "[password_hidden]" --auth-plaintext --auth-user "$KEYTEXT" --auth-password "$WGETPARAMS" --quit-after MAIL --server "$MX" >"$TMPF-0" 2>&1) 2>"$TMPF-timer.tmp"
							else
								# if no swaks, pipe command directly thru openssl s_client -starttls, it mostly works
								# username and password can be sent on AUTH PLAIN line but null separated and then base64'd
								# username is sent twice - first time as authorization-id and second time as authentication-id
								timeout 20 /bin/bash -c "{ time (sleep 2; echo \"EHLO $(hostname -f)\"; sleep 0.3; echo -n \"AUTH PLAIN \"; printf '%s\0%s\0%s' \"$KEYTEXT\" \"$KEYTEXT\" \"$WGETPARAMS\"|base64; sleep 0.3; echo \"QUIT\"; sleep 2; exit) | openssl s_client -connect $MX -starttls smtp 2>/dev/null >$TMPF-0; } 2>$TMPF-timer.tmp"
							fi
							if [[ -s $TMPF-0 ]]; then
								grep -caF "2.7.0 Auth" "$TMPF-0" >"$TMPF-found_site.tmp"
							else
								echo 0 >"$TMPF-0"
								echo "real 20" >"$TMPF-timer.tmp"
							fi
						else
							TESTTYPE="nmap"
							command -v nmap >/dev/null 2>&1 || echo "nmap not available, this test will fail..." >&2
							if [ "${ADDRESS:$ADDRESSCOLON:1}" = "U" ]; then
								ADDRESS_PORT="${ADDRESS:$((ADDRESSCOLON+1))}"
								NMAPOPTIONS="-sU"; PORT_TYPE="UDP"
							else
								ADDRESS_PORT="${ADDRESS:$ADDRESSCOLON}"
								unset NMAPOPTIONS; PORT_TYPE="TCP"
							fi
							ADDRESS_SANS_PORT=${ADDRESS%:*}
							[[ -n $VERBOSE ]] && echo -en "Testing by $TESTTYPE\nTesting IP\t: $ADDRESS_SANS_PORT\nPort\t\t: $ADDRESS_PORT $PORT_TYPE\nCurrent Status\t: "
							{ time nmap -Pn $NMAPOPTIONS -p "$ADDRESS_PORT" "$ADDRESS_SANS_PORT"|grep -ac "^$ADDRESS_PORT.* open " >"$TMPF-found_site.tmp"; } 2>"$TMPF-timer.tmp"
						fi
					else
						TESTTYPE="ping"
						[ -n "$VERBOSE" ] && echo -en "Testing by $TESTTYPE\nCurrent Status\t: "
						{ time ping -W 2 -c 1 "$ADDRESS" >/dev/null; echo $((1-$?)) >"$TMPF-found_site.tmp"; } 2>"$TMPF-timer.tmp"
					fi
				else
					TESTTYPE="http"
					[ -n "$VERBOSE" ] && echo -en "Current Status\t: "
					{ time check_site "$ADDRESS" >"$TMPF-found_site.tmp"; } 2>"$TMPF-timer.tmp"
				fi
				ELAPSED=$(awk '($1=="real") {print $2}' "$TMPF-timer.tmp")
				FOUND=$(cat "$TMPF-found_site.tmp")
				[[ $FOUND -gt 0 ]] && FOUND=1
				[[ -n $VERBOSE ]] && echo -en "$FOUND - "
				if [[ $FOUND == 0 ]]; then
					# connection failed
					(( FAILED++ )) || true
					if [[ $CHECKSTATUS == 0 ]] || [[ $CHECKSTATUS == 1 && -n $SKIP1FAIL && $PREVIOUSSTATUS == -1 ]] || [[ $CHECKSTATUS == 1 && -z $SKIP1FAIL && $PREVIOUSSTATUS == 1 ]]; then
						# connection failed and either: we are not checking status
						#                               we are checking status without SKIP1FAIL - it succeeded last time
						#                               we are checking status with SKIP1FAIL - this is the second consecutive it has failed
						#  - keep FOUND as 0
						echo "$(date +"%Y-%m-%d %H:%M:%S") Failed  $TESTTYPE ($ELAPSED) $REPORTNAME"
						echo "$(date +"%Y-%m-%d %H:%M:%S"), $FOUND, $REPORTNAME, $ELAPSED" >>"$LOGDIR/$THIS.log"
						[[ -n $DEBUG ]] && echo "(Debug here1: PREVIOUSSTATUS: '$PREVIOUSSTATUS' CHECKSTATUS: '$CHECKSTATUS')"
					else
						if [[ -z $QUIET ]]; then
							echo "$(date +"%Y-%m-%d %H:%M:%S") Failed  $TESTTYPE ($ELAPSED) $REPORTNAME (not logged)"
							[[ -n $DEBUG ]] && echo "(Debug here2: PREVIOUSSTATUS: '$PREVIOUSSTATUS' CHECKSTATUS: '$CHECKSTATUS')"
						fi
						if [[ -n $SKIP1FAIL && $PREVIOUSSTATUS == 1 ]]; then
							# connection failed, are checking with SKIP1FAIL - this is the first failure
							# so set FOUND as '-1' - only put to 0 when it fails a 2nd time
							FOUND=-1
							#[[ -n $DEBUG ]] && echo "adding to $NEWLOG: $FOUND @ $REPORTNAME @"	#for debug only
							echo "$(date +"%Y-%m-%d %H:%M:%S"), $FOUND, $REPORTNAME, $ELAPSED" >>"$LOGDIR/$THIS.log"
						fi
					fi
				else
					if [[ -z $QUIET ]] || [[ $CHECKSTATUS == 1 && $PREVIOUSSTATUS == 0 ]]; then
						if [ -n "$VERBOSE" ]; then
							echo "$(date +"%Y-%m-%d %H:%M:%S") Found after $ELAPSED"
						else
							echo "$(date +"%Y-%m-%d %H:%M:%S") Found   $TESTTYPE ($ELAPSED) $REPORTNAME"
						fi
						echo "$(date +"%Y-%m-%d %H:%M:%S"), $FOUND, $REPORTNAME, $ELAPSED" >>"$LOGDIR/$THIS.log"
					else
						[[ -n $DEBUG && $PREVIOUSSTATUS == -1 ]] && echo -e "skippable 1-off failure on previous call, $REPORTNAME now working"	# for debug only
						[[ -n $VERBOSE ]] && echo "$(date +"%Y-%m-%d %H:%M:%S") Found $REPORTNAME ($TESTTYPE, $ELAPSED) - result not logged"
					fi
				fi
				[[ -n $DEBUG ]] && echo "added to \$NEWLOG: $FOUND @ $REPORTNAME @"
				echo "$FOUND @ $REPORTNAME @" >>"$NEWLOG"
				[[ $SUBTEST == ge ]] && MAINFOUND="$FOUND"
			else
				if [[ -n $VERBOSE ]]; then
					echo -e "\nSkipping        : $ADDRESS"
				elif [[ -z $QUIET ]]; then
					echo "$(date +"%Y-%m-%d %H:%M:%S") Skipped                 $REPORTNAME"
				fi
			fi
		fi
	fi
done <"$TMPF-conf.conf"
if [[ -z $DEBUG && ${#TMPF} -gt 0 ]]; then
	rm -- "$TMPF-"* 2>/dev/null
else
	echo "Debug mode: retained temporary files:"
	ls -lrt "$TMPF-"*
fi
if [[ -z $QUIET$NO_HEADER ]]; then
	RUNTIME=$(( $(date +%s)-START ))
	echo -e "\n$FAILED failure$(plural "$FAILED") (testing took $RUNTIME second$(plural "$RUNTIME"))"
fi
exit $(( 99*(FAILED>0) ))
