|
Packit |
09aa44 |
#!/bin/bash
|
|
Packit |
09aa44 |
#
|
|
Packit |
09aa44 |
# This is a shell script which prints the ntpd or chronyd synchronisation
|
|
Packit |
09aa44 |
# status, using the ntpq or chronyc program. It emulates the original
|
|
Packit |
09aa44 |
# ntpstat program written in C by G. Richard Keech, which implemented a
|
|
Packit |
09aa44 |
# subset of the mode6 protocol supported by ntpd.
|
|
Packit |
09aa44 |
#
|
|
Packit |
09aa44 |
# Copyright (C) 2016 Miroslav Lichvar <mlichvar@redhat.com>
|
|
Packit |
09aa44 |
#
|
|
Packit |
09aa44 |
# Permission is hereby granted, free of charge, to any person obtaining
|
|
Packit |
09aa44 |
# a copy of this software and associated documentation files (the
|
|
Packit |
09aa44 |
# "Software"), to deal in the Software without restriction, including
|
|
Packit |
09aa44 |
# without limitation the rights to use, copy, modify, merge, publish,
|
|
Packit |
09aa44 |
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
Packit |
09aa44 |
# permit persons to whom the Software is furnished to do so, subject to
|
|
Packit |
09aa44 |
# the following conditions:
|
|
Packit |
09aa44 |
#
|
|
Packit |
09aa44 |
# The above copyright notice and this permission notice shall be included
|
|
Packit |
09aa44 |
# in all copies or substantial portions of the Software.
|
|
Packit |
09aa44 |
#
|
|
Packit |
09aa44 |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
Packit |
09aa44 |
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
Packit |
09aa44 |
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
Packit |
09aa44 |
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
Packit |
09aa44 |
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
Packit |
09aa44 |
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
Packit |
09aa44 |
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
CHRONYC=("chronyc" "-n")
|
|
Packit |
09aa44 |
NTPQ=("ntpq" "-c" "timeout 100" "-c" "raw")
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
export LC_ALL=C
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
parse_tracking_field() {
|
|
Packit |
09aa44 |
local tracking=$1 name=$2 field
|
|
Packit |
09aa44 |
field=$(echo "$tracking" | grep "^$name")
|
|
Packit |
09aa44 |
echo "${field#* : }"
|
|
Packit |
09aa44 |
}
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
get_chronyd_state() {
|
|
Packit |
09aa44 |
local output line disp delay
|
|
Packit |
09aa44 |
local leap source address stratum distance poll
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
output=$("${CHRONYC[@]}" tracking 2> /dev/null) || return 2
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
leap=$(parse_tracking_field "$output" "Leap status")
|
|
Packit |
09aa44 |
case "$leap" in
|
|
Packit |
09aa44 |
"Normal") leap="0";;
|
|
Packit |
09aa44 |
"Insert second") leap="1";;
|
|
Packit |
09aa44 |
"Delete second") leap="2";;
|
|
Packit |
09aa44 |
"Not synchronised") leap="3";;
|
|
Packit |
09aa44 |
esac
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
address=$(parse_tracking_field "$output" "Reference ID")
|
|
Packit |
09aa44 |
address=${address%)*}
|
|
Packit |
09aa44 |
address=${address#*(}
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
stratum=$(parse_tracking_field "$output" "Stratum")
|
|
Packit |
09aa44 |
delay=$(parse_tracking_field "$output" "Root delay")
|
|
Packit |
09aa44 |
delay=${delay% seconds}
|
|
Packit |
09aa44 |
disp=$(parse_tracking_field "$output" "Root dispersion")
|
|
Packit |
09aa44 |
disp=${disp% seconds}
|
|
Packit |
09aa44 |
offset=$(parse_tracking_field "$output" "System time")
|
|
Packit |
09aa44 |
offset=${offset% seconds*}
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
distance=$(echo "$delay $disp $offset" | \
|
|
Packit |
09aa44 |
awk '{ printf "%.3f", ($1 / 2.0 + $2 + $3) * 1e3 }')
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
if [ -n "$address" ]; then
|
|
Packit |
09aa44 |
line=$("${CHRONYC[@]}" sources 2> /dev/null | \
|
|
Packit |
09aa44 |
grep " $address ") || return 3
|
|
Packit |
09aa44 |
poll=$(echo "$line" | awk '{ print $4 }')
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
case "${line:0:1}" in
|
|
Packit |
09aa44 |
"*"|"=") source="NTP server";;
|
|
Packit |
09aa44 |
"#") source="reference clock";;
|
|
Packit |
09aa44 |
*) source="unknown source";;
|
|
Packit |
09aa44 |
esac
|
|
Packit |
09aa44 |
fi
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
echo "$leap,NTP server,$address,$stratum,$distance,$poll"
|
|
Packit |
09aa44 |
}
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
parse_rv_field() {
|
|
Packit |
09aa44 |
local rv=$1 name=$2 field
|
|
Packit |
09aa44 |
field=$(echo "$rv" | grep -o "$name=[^,]*")
|
|
Packit |
09aa44 |
echo "${field#*=}"
|
|
Packit |
09aa44 |
}
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
get_ntpd_state() {
|
|
Packit |
09aa44 |
local output syspeer_id disp delay
|
|
Packit |
09aa44 |
local leap source address stratum distance poll
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
output=$("${NTPQ[@]}" -c "rv 0" 2> /dev/null) || return 2
|
|
Packit |
09aa44 |
[[ $output == *"associd"*"status"* ]] || return 3
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
leap=$(parse_rv_field "$output" "leap")
|
|
Packit |
09aa44 |
source=$(parse_rv_field "$output" "status" | \
|
|
Packit |
09aa44 |
awk '{ print and(rshift(strtonum($1), 8), 0x3f) }')
|
|
Packit |
09aa44 |
case "$source" in
|
|
Packit |
09aa44 |
0) source="unspecified";;
|
|
Packit |
09aa44 |
1) source="atomic clock";;
|
|
Packit |
09aa44 |
2) source="VLF radio";;
|
|
Packit |
09aa44 |
3) source="HF radio";;
|
|
Packit |
09aa44 |
4) source="UHF radio";;
|
|
Packit |
09aa44 |
5) source="local net";;
|
|
Packit |
09aa44 |
6) source="NTP server";;
|
|
Packit |
09aa44 |
7) source="UDP/TIME";;
|
|
Packit |
09aa44 |
8) source="wristwatch";;
|
|
Packit |
09aa44 |
9) source="modem";;
|
|
Packit |
09aa44 |
*) source="unknown source";;
|
|
Packit |
09aa44 |
esac
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
stratum=$(parse_rv_field "$output" "stratum")
|
|
Packit |
09aa44 |
delay=$(parse_rv_field "$output" "rootdelay")
|
|
Packit |
09aa44 |
disp=$(parse_rv_field "$output" "rootdisp")
|
|
Packit |
09aa44 |
distance=$(echo "$delay $disp" | awk '{ printf "%.3f", $1 / 2.0 + $2 }')
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
syspeer_id=$("${NTPQ[@]}" -c associations 2> /dev/null |\
|
|
Packit |
09aa44 |
grep 'sys\.peer' | awk '{ print $2 }') || return 4
|
|
Packit |
09aa44 |
output=$("${NTPQ[@]}" -c "rv $syspeer_id" 2> /dev/null) || return 5
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
if [ "$source" = "NTP server" ]; then
|
|
Packit |
09aa44 |
address=$(parse_rv_field "$output" "srcadr")
|
|
Packit |
09aa44 |
fi
|
|
Packit |
09aa44 |
poll=$(parse_rv_field "$output" "hpoll")
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
echo "$leap,$source,$address,$stratum,$distance,$poll"
|
|
Packit |
09aa44 |
}
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
max_distance=""
|
|
Packit |
09aa44 |
while getopts "m:h" opt; do
|
|
Packit |
09aa44 |
case $opt in
|
|
Packit |
09aa44 |
m)
|
|
Packit |
09aa44 |
max_distance=$OPTARG
|
|
Packit |
09aa44 |
;;
|
|
Packit |
09aa44 |
*)
|
|
Packit |
09aa44 |
echo "Usage: $0 [-m MAXERROR]"
|
|
Packit |
09aa44 |
exit 3
|
|
Packit |
09aa44 |
;;
|
|
Packit |
09aa44 |
esac
|
|
Packit |
09aa44 |
done
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
if ! state=$(get_chronyd_state) && ! state=$(get_ntpd_state); then
|
|
Packit |
09aa44 |
echo "Unable to talk to NTP daemon. Is it running?" >&2
|
|
Packit |
09aa44 |
exit 2
|
|
Packit |
09aa44 |
fi
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
IFS=, read -r leap source address stratum distance poll <<< "$state"
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
if [ "$leap" -ge 0 -a "$leap" -le 2 ]; then
|
|
Packit |
09aa44 |
printf "synchronised to %s" "$source"
|
|
Packit |
09aa44 |
if [ -n "$address" ]; then
|
|
Packit |
09aa44 |
printf " (%s)" "$address"
|
|
Packit |
09aa44 |
fi
|
|
Packit |
09aa44 |
if [ -n "$stratum" ]; then
|
|
Packit |
09aa44 |
printf " at stratum %d\n" "$stratum"
|
|
Packit |
09aa44 |
else
|
|
Packit |
09aa44 |
printf ", stratum unknown\n"
|
|
Packit |
09aa44 |
fi
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
if [ -n "$distance" ]; then
|
|
Packit |
09aa44 |
printf " time correct to within %.0f ms" "$distance"
|
|
Packit |
09aa44 |
if [ -n "$max_distance" ] &&
|
|
Packit |
09aa44 |
echo "$distance $max_distance" | awk '{ exit $1 <= $2 }'; then
|
|
Packit |
09aa44 |
printf " (exceeded maximum of %s ms)\n" "$max_distance"
|
|
Packit |
09aa44 |
status=1
|
|
Packit |
09aa44 |
else
|
|
Packit |
09aa44 |
printf "\n"
|
|
Packit |
09aa44 |
status=0
|
|
Packit |
09aa44 |
fi
|
|
Packit |
09aa44 |
else
|
|
Packit |
09aa44 |
printf "accuracy unknown\n"
|
|
Packit |
09aa44 |
[ -n "$max_distance" ] && status=1 || status=0
|
|
Packit |
09aa44 |
fi
|
|
Packit |
09aa44 |
else
|
|
Packit |
09aa44 |
printf "unsynchronised\n"
|
|
Packit |
09aa44 |
status=1
|
|
Packit |
09aa44 |
fi
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
if [ -n "$poll" ]; then
|
|
Packit |
09aa44 |
printf " polling server every %d s\n" "$[2**$poll]"
|
|
Packit |
09aa44 |
else
|
|
Packit |
09aa44 |
printf "poll interval unknown\n"
|
|
Packit |
09aa44 |
fi
|
|
Packit |
09aa44 |
|
|
Packit |
09aa44 |
exit $status
|