Commit Diff


commit - d42ca95a4b25f9d79644176e870961292b0a7730
commit + 8f8a96f50c804cf9fe0aa6d32d88b3ff1f94e72d
blob - 71449054ffac117435924a92616193732f780a06
blob + c48936926ba30b62bdc13d9c9b2b5cc5572562e2
--- LICENSE
+++ LICENSE
@@ -1,7 +1,7 @@
 MIT License
 
-Copyright (c) 2021 Baptiste DE RENZO
-Copyright (c) 2023 Mischa Peters
+Copyright (c) 2021 Baptiste DE RENZO https://github.com/bderenzo/tinystatus
+Copyright (c) 2023 Mischa Peters https://git.high5.nl/uptimeatomic/
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
blob - 7ff32f2c496fcec89badf2343b782216b63b53f2
blob + f032586174c04a9d1484b5fec8b7c9d94f6ae88a
--- README.md
+++ README.md
@@ -1,6 +1,6 @@
-# Tinystatus
+# Uptime Atomic
 
-tinystatus generate an html status page via shell script.
+uptimeatomic alerts when downtime happens and generates an html status page via shell script.
 
 ## Features
 
@@ -15,17 +15,18 @@ tinystatus generate an html status page via shell scri
 
 ## Demo
 
-An example site is available [here](https://lab.bdro.fr/tinystatus/).
+An example site is available [here](https://ops.lowfive.nl/)
 
 ## Setup
 
-To install tinystatus:
+To install uptimeatomic:
 
 * Clone the repository and go to the created directory
-* Edit `tinystatus` variables to your liking
+* Edit `uptimeatomic` variables to your liking
 * Edit the checks file `checks.csv`
 * To add incidents or maintenance, edit `incidents.txt`
-* Generate status page `./tinystatus`
+* To add past incidents or maintenance, edit `pastincidents.txt`
+* Generate status page `./uptimeatomic`
 * Serve the page with your favorite web server
 
 ## Configuration file
@@ -33,6 +34,10 @@ To install tinystatus:
 The syntax of `checks.csv` file is:
 ```
 Command, Expected Code, Status Text, Host to check
+http, 200, Google Website, https://google.com
+maint, 200, Google Drive Maintenance, https://drive.google.com
+ping, 0, Google ping, 8.8.8.8
+port, 0, Google DNS, 8.8.8.8 53
 ```
 
 Command can be:
@@ -47,9 +52,10 @@ Note: `port4` and `port6` require OpenBSD `nc` binary.
 ## Parameters
 
 ```
-./tinystatus -c CHECKFILE -i INCIDENTSFILE -o HTMLFILE
+./uptimeatomic -c CHECKFILE -i INCIDENTSFILE -o HTMLFILE
 Default:
 	-c = checks.csv
 	-i = incidents.txt
+	-p = pastincidents.txt
 	-o = index.html
 ```
blob - c5e41e6584860bb9a604caba744d5fe79a4065cd (mode 755)
blob + /dev/null
--- tinystatus
+++ /dev/null
@@ -1,214 +0,0 @@
-#!/bin/ksh
-#
-# Tinystatus v20230530
-# https://git.high5.nl/tinystatus/
-#
-export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin
-
-CHECKFILE="checks.csv"
-HEADER="Global Status"
-HTMLDIR="/var/www/htdocs/default"
-HTMLFILE="index.html"
-INCIDENTSFILE="incidents.txt"
-PASTINCIDENTSFILE="pastincidents.txt"
-PUSHOVER="/home/mischa/bin/pushover.pl"
-PUSHOVER_CONF="/home/mischa/_pushover.conf"
-PUSHOVER_STATUS="status"
-RECIPIENT="alerts@high5.nl"
-REFRESH=60
-SENDER="tinystatus@high5.nl"
-TIMEOUT=10
-TITLE="Tiny Status"
-USERAGENT="User-Agent: Mozilla/5.0 (OpenBSD; Intel OpenBSD 7.4; rv:109.0) Gecko/20100101 Firefox/113.0"
-WORKDIR=/home/mischa/tinystatus
-
-usage() {
-        echo "usage: ${0##*/} [-c checksfile] [-i incidentsfile] [-p pastincidentsfile] [-o htmlfile]" 1>&2
-        exit
-}
-
-get_element() {
-	echo "${2}" | awk -v col="${1}" -F',' '{gsub(/^[ \t]+|[ \t]+$/, "", $col); print $col}'
-}
-
-notify() {
-	name="${1}"
-	status="${2}"
-	priority="${3}"
-
-	if [ ${priority} == "KO" ]; then
-		echo "${status}." | mail -r "${TITLE} <${SENDER}>" -s "${name} DOWN" ${RECIPIENT}
-		${PUSHOVER} -c ${PUSHOVER_CONF} -t "${TITLE}" -m "${name} DOWN ${status}." -p 1 >/dev/null 2>&1
-		touch "${PUSHOVER_STATUS}/${name}"
-	fi
-	if [ ${priority} == "OK" ]; then
-		_seconds=$(expr $(date +%s) - $(stat -r ${PUSHOVER_STATUS}/${name} | awk '{print $11}'))
-		_downtime=$(date -r${_seconds} -u +%H:%M:%S)
-
-		echo "${status} - down for ${_downtime}" | mail -r "${TITLE} <${SENDER}>" -s "${name} OK" ${RECIPIENT}
-		${PUSHOVER} -c ${PUSHOVER_CONF} -t "${TITLE}" -m "${name} OK ${status} - down for ${_downtime}" >/dev/null 2>&1
-		rm -rf "${PUSHOVER_STATUS}/${name}"
-	fi
-}
-
-check() {
-	ctype="${1}"
-	host="${2}"
-	name="${3}"
-	expectedcode="${4}"
-	IPv="${ctype#(http|ping|port)}"
-
-	case "${ctype}" in
-		http*)
-			statuscode="$(curl -${IPv}sSkLo /dev/null -H "${USERAGENT}" -m "${TIMEOUT}" -w "%{http_code}" "${host}" 2> "${tmp}/ko/${name}.error")"
-			if [ "${statuscode}" -ne "${expectedcode}" ]; then
-				if [ -s "${tmp}/ko/${name}.error" ]; then
-					sed -e 's,curl: ([0-9]*) ,,' "${tmp}/ko/${name}.error" > "${tmp}/ko/${name}.status"
-				else
-					echo "Status code: ${statuscode}" > "${tmp}/ko/${name}.status"
-				fi
-			else
-				echo "Status code: ${statuscode}" > "${tmp}/ok/${name}.status"
-			fi
-			;;
-		ping*)
-			ping -${IPv}w "${TIMEOUT}" -c 1 "${host}" >/dev/null 2>&1
-			statuscode=$?
-			if [ "${statuscode}" -ne "${expectedcode}" ]; then
-				echo "Host unreachable" > "${tmp}/ko/${name}.status"
-			else
-				echo "Host reachable" > "${tmp}/ok/${name}.status"
-			fi
-			;;
-		port*)
-			error="$(nc -${IPv}w "${TIMEOUT}" -zv ${host} 2>&1)"
-			statuscode=$?
-			if [ "${statuscode}" -ne "${expectedcode}" ]; then
-				echo "Connection refused" > "${tmp}/ko/${name}.status"
-			else
-				echo "Connection succeeded" > "${tmp}/ok/${name}.status"
-			fi
-			;;
-		maint*)
-			echo "Maintenance" > "${tmp}/maint/${name}.status"
-			;;
-	esac
-}
-
-while getopts c:i:o:h arg; do
-	case ${arg} in
-	c)      CHECKFILE=${OPTARG};;
-	i)      INCIDENTSFILE=${OPTARG};;
-	p)      PASTINCIDENTSFILE=${OPTARG};;
-	o)      HTMLFILE=${OPTARG};;
-	h)      usage;;
-	*)      usage;;
-	esac
-done
-
-cd ${WORKDIR}
-if [ ! -e "${CHECKFILE}" ]; then
-	echo "Checkfile ${WORKDIR}/${CHECKFILE} doesn't exist."
-	exit
-fi
-
-tmp="$(mktemp -d)"
-mkdir -p "${tmp}/ok" "${tmp}/ko" "${tmp}/maint" || exit 1
-_htmlfile="${tmp}/${HTMLFILE}"
-
-while IFS="$(printf '\n')" read -r line; do
-	ctype="$(get_element 1 "${line}")"
-	code="$(get_element 2 "${line}")"
-	name="$(get_element 3 "${line}")"
-	host="$(get_element 4 "${line}")"
-	check "${ctype}" "${host}" "${name}" "${code}" &
-done < "${CHECKFILE}"
-wait
-
-cat << EOF >> ${_htmlfile}
-<!DOCTYPE html>
-<html lang="en">
-<head>
-<meta charset="utf-8">
-<meta http-equiv="refresh" content="${REFRESH}">
-<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
-<title>${TITLE}</title>
-<style>
-body { font-family: segoe ui,Roboto,Oxygen-Sans,Ubuntu,Cantarell,helvetica neue,Verdana,sans-serif; }
-h1 { margin-top: 30px; }
-ul { padding: 0px; }
-li { list-style: none; margin-bottom: 2px; padding: 5px; border-bottom: 1px solid #ddd;  }
-.container { max-width: 600px; width: 100%; margin: 15px auto; }
-.panel { text-align: center; padding: 10px; border: 0px; border-radius: 5px; }
-.failed-bg  { color: white; background-color: #E25D6A; }
-.success-bg { color: white; background-color: #52B86A; }
-.maint-bg { color: white; background-color: #5DADE2; }
-.failed  { color: #E25D6A; }
-.success { color: #52B86A; }
-.maint { color: #5DADE2; }
-.small { font-size: 80%; }
-.status { float: right; }
-</style>
-</head>
-<body>
-<div class='container'>
-<h1>${HEADER}</h1>
-EOF
-_outage_count="$(find "${tmp}/ko" -mindepth 1 | grep -c 'status$')"
-_maint_count="$(find "${tmp}/maint" -mindepth 1 | grep -c 'status$')"
-if [ "${_outage_count}" -ne 0 ]; then
-	echo "<ul><li class='panel failed-bg'>${_outage_count} Outage(s)</li></ul>" >> ${_htmlfile}
-fi
-if [ "${_maint_count}" -ne 0 ]; then
-	echo "<ul><li class='panel maint-bg'>${_maint_count} Maintenance</li></ul>" >> ${_htmlfile}
-fi
-if [[ "${_outage_count}" -eq 0 && "${_maint_count}" -eq 0 ]]; then
-	echo "<ul><li class='panel success-bg'>All Systems Operational</li></ul>" >> ${_htmlfile}
-fi
-if [ -s "${INCIDENTSFILE}" ]; then
-	echo '<h1>Incidents</h1>' >> ${_htmlfile}
-	sed 's|^\(.*\)$|<p>\1</p>|' "${INCIDENTSFILE}" >> ${_htmlfile}
-fi
-cat << EOF >> ${_htmlfile}
-<h1>Services</h1>
-<ul>
-EOF
-ls ${tmp}/ko/*.status 2>/dev/null | sort -V | while read file; do
-	[ -e "${file}" ] || continue
-	name="$(basename "${file%.status}")"
-	status="$(cat "${file}")"
-	echo "<li>${name} <span class='small failed'>(${status})</span><span class='status failed'>Disrupted</span></li>" >> ${_htmlfile}
-	if [ ! -e "${PUSHOVER_STATUS}/${name}" ]; then
-		notify "${name}" "${status}" "KO"
-	fi
-done
-ls ${tmp}/maint/*.status 2>/dev/null | sort -V | while read file; do
-	[ -e "${file}" ] || continue
-	name="$(basename "${file%.status}")"
-	echo "<li>${name} <span class='status maint'>Maintenance</span></li>" >> ${_htmlfile}
-done
-ls ${tmp}/ok/*.status 2>/dev/null | sort -V | while read file; do
-	[ -e "${file}" ] || continue
-	name="$(basename "${file%.status}")"
-	status="$(cat "${file}")"
-	echo "<li>${name} <span class='status success'>Operational</span></li>" >> ${_htmlfile}
-	if [ -e "${PUSHOVER_STATUS}/${name}" ]; then
-		notify "${name}" "${status}" "OK"
-	fi
-done
-cat << EOF >> ${_htmlfile}
-</ul>
-<p class=small> Last check: $(date +%FT%T%z) <a href="https://git.high5.nl/tinystatus/">Tinystatus</a> loosely based on <a href="https://github.com/bderenzo/tinystatus">Tinystatus</a></p>
-EOF
-if [ -s "${PASTINCIDENTSFILE}" ]; then
-	echo '<h1>Past Incidents</h1>' >> ${_htmlfile}
-	sed 's|^\(.*\)$|<p>\1</p>|' "${PASTINCIDENTSFILE}" >> ${_htmlfile}
-fi
-cat << EOF >> ${_htmlfile}
-</div>
-</body>
-</html>
-EOF
-
-cp ${_htmlfile} ${HTMLDIR}/${HTMLFILE}
-rm -r "${tmp}" 2>/dev/null
blob - /dev/null
blob + d4a7e5d6bcc00694c87e01c24ca7bad7536b1e67 (mode 755)
--- /dev/null
+++ uptimeatomic
@@ -0,0 +1,215 @@
+#!/bin/ksh
+#
+# Uptime Atomic v20230531
+# https://git.high5.nl/uptimeatomic/
+#
+export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin
+
+CHECKFILE="checks.csv"
+HEADER="Global Status"
+HTMLDIR="/var/www/htdocs/default"
+HTMLFILE="index.html"
+INCIDENTSFILE="incidents.txt"
+PASTINCIDENTSFILE="pastincidents.txt"
+PUSHOVER="/home/mischa/bin/pushover.pl"
+PUSHOVER_CONF="/home/mischa/_pushover.conf"
+PUSHOVER_STATUS="status"
+RECIPIENT="alerts@high5.nl"
+REFRESH=60
+SENDER="uptimeatomic@high5.nl"
+TIMEOUT=10
+TITLE="Uptime Atomic"
+USERAGENT="User-Agent: Mozilla/5.0 (OpenBSD; Intel OpenBSD 7.4; rv:109.0) Gecko/20100101 Firefox/113.0"
+WORKDIR=/home/mischa/uptimeatomic
+
+usage() {
+        echo "usage: ${0##*/} [-c checksfile] [-i incidentsfile] [-p pastincidentsfile] [-o htmlfile]" 1>&2
+        exit
+}
+
+get_element() {
+	echo "${2}" | awk -v col="${1}" -F',' '{gsub(/^[ \t]+|[ \t]+$/, "", $col); print $col}'
+}
+
+notify() {
+	name="${1}"
+	status="${2}"
+	priority="${3}"
+
+	if [ ${priority} == "KO" ]; then
+		echo "${status}." | mail -r "${TITLE} <${SENDER}>" -s "${name} DOWN" ${RECIPIENT}
+		${PUSHOVER} -c ${PUSHOVER_CONF} -t "${TITLE}" -m "${name} DOWN ${status}." -p 1 >/dev/null 2>&1
+		touch "${PUSHOVER_STATUS}/${name}"
+	fi
+	if [ ${priority} == "OK" ]; then
+		_seconds=$(expr $(date +%s) - $(stat -r ${PUSHOVER_STATUS}/${name} | awk '{print $11}'))
+		_downtime=$(date -r${_seconds} -u +%H:%M:%S)
+
+		echo "${status} - down for ${_downtime}" | mail -r "${TITLE} <${SENDER}>" -s "${name} OK" ${RECIPIENT}
+		${PUSHOVER} -c ${PUSHOVER_CONF} -t "${TITLE}" -m "${name} OK ${status} - down for ${_downtime}" >/dev/null 2>&1
+		rm -rf "${PUSHOVER_STATUS}/${name}"
+	fi
+}
+
+check() {
+	ctype="${1}"
+	host="${2}"
+	name="${3}"
+	expectedcode="${4}"
+	IPv="${ctype#(http|ping|port)}"
+
+	case "${ctype}" in
+		http*)
+			statuscode="$(curl -${IPv}sSkLo /dev/null -H "${USERAGENT}" -m "${TIMEOUT}" -w "%{http_code}" "${host}" 2> "${tmp}/ko/${name}.error")"
+			if [ "${statuscode}" -ne "${expectedcode}" ]; then
+				if [ -s "${tmp}/ko/${name}.error" ]; then
+					sed -e 's,curl: ([0-9]*) ,,' "${tmp}/ko/${name}.error" > "${tmp}/ko/${name}.status"
+				else
+					echo "Status code: ${statuscode}" > "${tmp}/ko/${name}.status"
+				fi
+			else
+				echo "Status code: ${statuscode}" > "${tmp}/ok/${name}.status"
+			fi
+			;;
+		ping*)
+			ping -${IPv}w "${TIMEOUT}" -c 1 "${host}" >/dev/null 2>&1
+			statuscode=$?
+			if [ "${statuscode}" -ne "${expectedcode}" ]; then
+				echo "Host unreachable" > "${tmp}/ko/${name}.status"
+			else
+				echo "Host reachable" > "${tmp}/ok/${name}.status"
+			fi
+			;;
+		port*)
+			error="$(nc -${IPv}w "${TIMEOUT}" -zv ${host} 2>&1)"
+			statuscode=$?
+			if [ "${statuscode}" -ne "${expectedcode}" ]; then
+				echo "Connection refused" > "${tmp}/ko/${name}.status"
+			else
+				echo "Connection succeeded" > "${tmp}/ok/${name}.status"
+			fi
+			;;
+		maint*)
+			echo "Maintenance" > "${tmp}/maint/${name}.status"
+			;;
+	esac
+}
+
+while getopts c:i:o:h arg; do
+	case ${arg} in
+	c)      CHECKFILE=${OPTARG};;
+	i)      INCIDENTSFILE=${OPTARG};;
+	p)      PASTINCIDENTSFILE=${OPTARG};;
+	o)      HTMLFILE=${OPTARG};;
+	h)      usage;;
+	*)      usage;;
+	esac
+done
+
+cd ${WORKDIR}
+if [ ! -e "${CHECKFILE}" ]; then
+	echo "Checkfile ${WORKDIR}/${CHECKFILE} doesn't exist."
+	exit
+fi
+
+tmp="$(mktemp -d)"
+mkdir -p "${tmp}/ok" "${tmp}/ko" "${tmp}/maint" || exit 1
+_htmlfile="${tmp}/${HTMLFILE}"
+
+while IFS="$(printf '\n')" read -r line; do
+	ctype="$(get_element 1 "${line}")"
+	code="$(get_element 2 "${line}")"
+	name="$(get_element 3 "${line}")"
+	host="$(get_element 4 "${line}")"
+	check "${ctype}" "${host}" "${name}" "${code}" &
+done < "${CHECKFILE}"
+wait
+
+cat << EOF >> ${_htmlfile}
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta http-equiv="refresh" content="${REFRESH}">
+<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+<title>${TITLE}</title>
+<style>
+body { font-family: segoe ui,Roboto,Oxygen-Sans,Ubuntu,Cantarell,helvetica neue,Verdana,sans-serif; }
+h1 { margin-top: 30px; }
+ul { padding: 0px; }
+li { list-style: none; margin-bottom: 2px; padding: 5px; border-bottom: 1px solid #ddd;  }
+.container { max-width: 600px; width: 100%; margin: 15px auto; }
+.panel { text-align: center; padding: 10px; border: 0px; border-radius: 5px; }
+.failed-bg  { color: white; background-color: #E25D6A; }
+.success-bg { color: white; background-color: #52B86A; }
+.maint-bg { color: white; background-color: #5DADE2; }
+.failed  { color: #E25D6A; }
+.success { color: #52B86A; }
+.maint { color: #5DADE2; }
+.small { font-size: 80%; }
+.status { float: right; }
+</style>
+</head>
+<body>
+<div class='container'>
+<h1>${HEADER}</h1>
+EOF
+_outage_count="$(find "${tmp}/ko" -mindepth 1 | grep -c 'status$')"
+_maint_count="$(find "${tmp}/maint" -mindepth 1 | grep -c 'status$')"
+if [ "${_outage_count}" -ne 0 ]; then
+	echo "<ul><li class='panel failed-bg'>${_outage_count} Outage(s)</li></ul>" >> ${_htmlfile}
+fi
+if [ "${_maint_count}" -ne 0 ]; then
+	echo "<ul><li class='panel maint-bg'>${_maint_count} Maintenance</li></ul>" >> ${_htmlfile}
+fi
+if [[ "${_outage_count}" -eq 0 && "${_maint_count}" -eq 0 ]]; then
+	echo "<ul><li class='panel success-bg'>All Systems Operational</li></ul>" >> ${_htmlfile}
+fi
+if [ -s "${INCIDENTSFILE}" ]; then
+	echo '<h1>Incidents</h1>' >> ${_htmlfile}
+	sed 's|^\(.*\)$|<p>\1</p>|' "${INCIDENTSFILE}" >> ${_htmlfile}
+fi
+cat << EOF >> ${_htmlfile}
+<h1>Services</h1>
+<ul>
+EOF
+ls ${tmp}/ko/*.status 2>/dev/null | sort -V | while read file; do
+	[ -e "${file}" ] || continue
+	name="$(basename "${file%.status}")"
+	status="$(cat "${file}")"
+	echo "<li>${name} <span class='small failed'>(${status})</span><span class='status failed'>Disrupted</span></li>" >> ${_htmlfile}
+	if [ ! -e "${PUSHOVER_STATUS}/${name}" ]; then
+		notify "${name}" "${status}" "KO"
+	fi
+done
+ls ${tmp}/maint/*.status 2>/dev/null | sort -V | while read file; do
+	[ -e "${file}" ] || continue
+	name="$(basename "${file%.status}")"
+	echo "<li>${name} <span class='status maint'>Maintenance</span></li>" >> ${_htmlfile}
+done
+ls ${tmp}/ok/*.status 2>/dev/null | sort -V | while read file; do
+	[ -e "${file}" ] || continue
+	name="$(basename "${file%.status}")"
+	status="$(cat "${file}")"
+	echo "<li>${name} <span class='status success'>Operational</span></li>" >> ${_htmlfile}
+	if [ -e "${PUSHOVER_STATUS}/${name}" ]; then
+		notify "${name}" "${status}" "OK"
+	fi
+done
+cat << EOF >> ${_htmlfile}
+</ul>
+<p class=small>Last check: $(date +%FT%T%z)</p>
+EOF
+if [ -s "${PASTINCIDENTSFILE}" ]; then
+	echo '<h1>Past Incidents</h1>' >> ${_htmlfile}
+	sed 's|^\(.*\)$|<p>\1</p>|' "${PASTINCIDENTSFILE}" >> ${_htmlfile}
+fi
+cat << EOF >> ${_htmlfile}
+<p class=small><a href="https://git.high5.nl/uptimeatomic/">Uptime Atomic</a> loosely based on <a href="https://github.com/bderenzo/tinystatus">Tinystatus</a></p>
+</div>
+</body>
+</html>
+EOF
+
+cp ${_htmlfile} ${HTMLDIR}/${HTMLFILE}
+rm -r "${tmp}" 2>/dev/null