Blame ntpstat

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