#!/usr/bin/bash
# vim: cindent:shiftwidth=4:tabstop=4:smarttab:textwidth=100

#$title$ Simple Network Time Protocol (SNTP)
#$check$ verify synchronization status to remote NTP server
#$ref$ timesyncd.conf(5), timedatectl(1)
#$author$ Rafal Rzeczkowski
#$version$ 0.7.0

set -o posix
set -o errexit
set -o pipefail
set -o nounset
#set -o xtrace

level_check short settling

#CHANGELOG
#0.5.0	create an initial version for systemd 252
#0.6.0	check offset from local NTP when synchronized via PTP
#0.6.1	select link-local NTP servers from networkctl, not FallbackNTP
#0.7.0	detect simultaneous use of PTP and SNTP

declare -r -a PROBLEMS=(TIMEDATECTL_CMD TIMEZONE_DEFAULT
	SNTP_ENABLE SNTP_SYNC PTP_SYNC PTP_AND_SNTP)
test -n ${#PROBLEMS[@]}

declare -r PTP4L_UDS_ADDRESS='/run/ptp4l'

eval $(timedatectl show --all | awk -F= '{print $1 "=" "\"" $2 "\""}')
#Timezone="America/Halifax"
#LocalRTC="no"
#CanNTP="yes"
#NTP="yes"
#NTPSynchronized="yes"
#TimeUSec="Tue 2023-03-14 17:44:41 ADT"
#RTCTimeUSec=""

if [[ -z "${LocalRTC+xxx}" ]]; then
	fail warning 'timedatectl query failed'
	helpmsg 'internal error - contact the plugin maintainer'
	problem TIMEDATECTL_CMD 0
	exit 1
fi

function check_timezone {
	if [[ "$Timezone" != 'UTC' ]]; then
		echodebug "Time zone: $Timezone"
	else
		fail warning 'time zone is not set'
		helpmsg 'use {timedatectl} to set the local time zone, e.g. {timedatectl set-timezone America/Halifax}'
		problem TIMEZONE_DEFAULT 0
		exit
	fi
}

function check_ptp {
	declare -r -i OFFSET_US_MAX=500
	#FallbackNTP=$(awk -F= '{if ($1=="FallbackNTP"){print $2;exit}}' '/etc/systemd/timesyncd.conf.d/ntp_servers.conf')

	declare -r GET_NTP_SERVERS_PL='
use feature qw{say};
#use Data::Dumper;
foreach ( @{ $_->{Interfaces} } ) {
	if (exists $_->{NTP}) {
		foreach ( @{ $_->{NTP} } ) {
			#say Dumper $_;
			say join q{.}, @{ $_->{Address} };
		}
	}
}'
	ntp_address=($(networkctl status --json=short | json_xs -t none -e"$GET_NTP_SERVERS_PL"))

	echodebug "link-local NTP servers: ${ntp_address[@]}"

	eval $(ntpdig -j ${ntp_address[@]} |
		json_xs -t none -e'use Data::Dumper; while ( my ($key, $value) = each(%{$_}) ) {print "$key=\"$value\"\n";}')
	# precision="0.000474"
	# ip="2607:f2c0:f00e:c901:207:32ff:fe24:866a"
	# offset="-4.3e-05"
	# stratum="1"
	# leap="no-leap"
	# adjusted="0"
	# host="0.ntp.rafal.ca."

	offset_us=$(awk --assign offset=$offset "BEGIN {print ((offset*1e6)^2)^0.5}")
	if [[ $offset_us -gt $OFFSET_US_MAX ]]; then
		fail warning "PTP out of sync at ${offset_us}us; expected maximum of ${OFFSET_US_MAX}us"
		## TODO: NATS Unicode message support so that "μs" can be used
		helpmsg 'check PTP client function'
		problem PTP_SYNC $offset_us
		exit
	else
		echodebug "PTP service running; NTP diagnostic offset OK at ${offset_us}μs"
	fi
}

function check_sntp {
	if [[ "$NTPSynchronized" = 'yes' ]]; then
		echodebug 'SNTP synchronized'
	else
		fail warning 'system clock not synchronized'
		helpmsg 'connectivity problem to the NTP server?'
		problem SNTP_SYNC 0
		exit
	fi
}

check_timezone

if [[ -e "$PTP4L_UDS_ADDRESS" ]]; then
	# PTP client running
	if [[ "$NTP" = 'yes' ]]; then
		fail warning 'systemd-timesyncd enabled together with PTP'
		problem PTP_AND_SNTP 0
		exit
	fi
	check_ptp
elif [[ "$NTP" = 'yes' ]]; then
	# SNTP client running
	echodebug 'SNTP service enabled (systemd-timesyncd)'
	check_sntp
else
	fail warning 'time synchronization not enabled'
	helpmsg 'system not configured yet?'
	problem SNTP_ENABLE 0
	exit
fi

ok
