#!/bin/ksh # # Uptime Atomic v20230603 # https://git.high5.nl/uptimeatomic/ # export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin CONFIGFILE="uptimeatomic.conf" WORKDIR=$(cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P) usage() { echo "usage: ${0##*/} [-c checksfile] [-i incidentsfile] [-p pastincidentsfile] [-o htmlfile]" 1>&2 exit } date_rss() { if [ -n "${1}" ]; then date -uj '+%a, %d %b %Y %H:%M:%S %z' ${1} else date -uj '+%a, %d %b %Y %H:%M:%S %z' fi } date_incident() { if [ -n "${1}" ]; then date -uj '+%F %H:%M %Z' ${1} else date -uj '+%F %H:%M %Z' fi } get_element() { echo "${2}" | awk -v col="${1}" -F',' '{gsub(/^[ \t]+|[ \t]+$/, "", $col); print $col}' } incidents() { _date=${1} _type=${2} _description=${3} [[ ${_type} == "Incident" ]] && color="failed" [[ ${_type} == "Maintenance" ]] && color="maint" echo "

$(date_incident ${_date}) - ${_type}
${_description}

" >> ${_HTMLFILE} if [ -n "${_RSSFILE}" ]; then cat << EOF >> ${_RSSFILE} ${_type} ${RSS_URL}/rss.xml ${RSS_URL}/rss.xml?$(date -j '+%s' $(echo "${_date}")) $(date_rss "${_date}") EOF fi } 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}" _expectedcode="${2}" _name="${3}" _host="${4}" _timeout="${5}" IPv="${_ctype#(http|ping|port)}" [[ -n "${_timeout}" ]] && TIMEOUT=${_timeout} 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 } process_status() { _status_files=${1} ls ${_TMP}/${_status_files}/*.status 2>/dev/null | sort -V | while read file; do [ -e "${file}" ] || continue _name="$(basename "${file%.status}")" _status="$(cat "${file}")" if [ ${_status_files} == "ko" ]; then echo "
  • ${_name} (${_status})Disrupted
  • " >> ${_HTMLFILE} if [ ! -e "${PUSHOVER_STATUS}/${_name}" ]; then notify "${_name}" "${status}" "${_status_files}" fi fi if [ ${_status_files} == "maint" ]; then echo "
  • ${_name} Maintenance
  • " >> ${_HTMLFILE} fi if [ ${_status_files} == "ok" ]; then echo "
  • ${_name} Operational
  • " >> ${_HTMLFILE} if [ -e "${PUSHOVER_STATUS}/${_name}" ]; then notify "${_name}" "${_status}" "${_status_files}" fi fi done } parse_file() { _file=${1} while IFS="$(printf '\n')" read -r line; do _col1="$(get_element 1 "${line}")" _col2="$(get_element 2 "${line}")" _col3="$(get_element 3 "${line}")" _col4="$(get_element 4 "${line}")" _col5="$(get_element 5 "${line}")" if [[ ${_file} == *".csv" ]]; then check "${_col1}" "${_col2}" "${_col3}" "${_col4}" "${_col5}" & else incidents "${_col1}" "${_col2}" "${_col3}" fi done < "${_file}" wait } cd ${WORKDIR} if [ -e "${CONFIGFILE}" ]; then . ${WORKDIR}/${CONFIGFILE} else echo "Configfile ${WORKDIR}/${CONFIGFILE} doesn't exist." exit fi while getopts c:i:o:r:h arg; do case ${arg} in c) CHECKFILE=${OPTARG};; i) INCIDENTSFILE=${OPTARG};; p) PASTINCIDENTSFILE=${OPTARG};; o) HTMLFILE=${OPTARG};; r) RSS_FILE=${OPTARG};; h) usage;; *) usage;; esac done 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}" [[ -n "${RSS_FILE}" ]] && _RSSFILE="${_TMP}/${RSS_FILE}" parse_file "${CHECKFILE}" # # HTML # cat << EOF > ${_HTMLFILE} ${TITLE}

    ${HEADER}

    EOF # # RSS # if [ -n "${_RSSFILE}" ]; then cat << EOF > ${_RSSFILE} ${TITLE} ${RSS_DESCRIPTION} ${RSS_URL} $(date_rss) EOF fi _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 "
    • ${_outage_count} Outage(s)
    " >> ${_HTMLFILE} fi if [ "${_maint_count}" -ne 0 ]; then echo "
    • ${_maint_count} Maintenance
    " >> ${_HTMLFILE} fi if [[ "${_outage_count}" -eq 0 && "${_maint_count}" -eq 0 ]]; then echo "
    • All Systems Operational
    " >> ${_HTMLFILE} fi if [ -s "${INCIDENTSFILE}" ]; then echo '

    Incidents / Maintenance

    ' >> ${_HTMLFILE} parse_file "${INCIDENTSFILE}" fi echo "

    Services

    " >> ${_HTMLFILE} echo "
      " >> ${_HTMLFILE} process_status "ko" process_status "maint" process_status "ok" echo "
    " >> ${_HTMLFILE} echo "

    Last check: $(date -u '+%FT%T %Z')

    " >> ${_HTMLFILE} if [ -s "${PASTINCIDENTSFILE}" ]; then echo '

    Past Incidents / Maintenance

    ' >> ${_HTMLFILE} parse_file "${PASTINCIDENTSFILE}" fi # # END HTML # cat << EOF >> ${_HTMLFILE}

    Uptime Atomic loosely based on Tinystatus EOF [[ -n "${_RSSFILE}" ]] && echo " - RSS" >> ${_HTMLFILE} cat << EOF >> ${_HTMLFILE}

    EOF # # END RSS # if [ -n "${_RSSFILE}" ]; then cat << EOF >> ${_RSSFILE} EOF fi if [[ -f "${HTMLDIR}/${RSS_FILE}" ]]; then _diff=$(diff "${_RSSFILE}" "${HTMLDIR}/${RSS_FILE}" | wc -l) if [ "${_diff}" -ne "4" ]; then cp ${_RSSFILE} ${HTMLDIR}/${RSS_FILE} fi elif [ -n "${RSS_FILE}" ]; then cp ${_RSSFILE} ${HTMLDIR}/${RSS_FILE} fi cp ${_HTMLFILE} ${HTMLDIR}/${HTMLFILE} rm -r "${_TMP}" 2>/dev/null